From fab7bd9ef53416a7d7ee0f1b69e6f64087a77a2f Mon Sep 17 00:00:00 2001 From: Matthew Fuller Date: Wed, 21 Oct 2020 19:56:40 +0000 Subject: [PATCH] Add config integration to airshipui Integrates airshipctl's config functionality with Airship UI to allow users to view and set airship configuration settings. Known issues: - Manifests currently only shows the primary (phase) repo. We'll probably need a separate repo sub-component to allow for showing / editing multiple repos - There are some boolean values which once set, cannot be unset using airshipctl's setters. We may need to write custom setters to set the Config struct values directly - It's possible to make edits to the config file that render the config invalid, so the CTL client cannot be initialized for subsequent edits. We'll probably want to make a copy of the original config, test the changes by initializing a new client, and only persist the changes if valid. - Lots and lots of cosmetic work remains to make the output more readable and easier to manage Change-Id: Ib29f3f6cf3e420b6e0e2cdc6afddd48c7e403137 --- .../config-context.component.css | 17 ++ .../config-context.component.html | 36 +++ .../config-context.component.spec.ts | 62 +++++ .../config-context.component.ts | 84 ++++++ .../config-context/config-context.module.ts | 34 +++ .../config-encryption.component.css | 17 ++ .../config-encryption.component.html | 39 +++ .../config-encryption.component.spec.ts | 62 +++++ .../config-encryption.component.ts | 79 ++++++ .../config-encryption.module.ts | 34 +++ .../config-management.component.css | 17 ++ .../config-management.component.html | 36 +++ .../config-management.component.spec.ts | 62 +++++ .../config-management.component.ts | 84 ++++++ .../config-management.module.ts | 38 +++ .../config-manifest.component.css | 17 ++ .../config-manifest.component.html | 66 +++++ .../config-manifest.component.spec.ts | 68 +++++ .../config-manifest.component.ts | 127 +++++++++ .../config-manifest/config-manifest.module.ts | 36 +++ .../src/app/ctl/config/config.component.css | 17 ++ .../src/app/ctl/config/config.component.html | 27 +- .../app/ctl/config/config.component.spec.ts | 35 ++- client/src/app/ctl/config/config.component.ts | 90 ++++++- client/src/app/ctl/config/config.models.ts | 111 ++++++++ client/src/app/ctl/config/config.module.ts | 33 ++- client/src/app/ctl/ctl.module.ts | 2 + client/src/assets/icons/lock.svg | 1 + client/src/assets/icons/lock_open.svg | 1 + client/src/services/icon/icons.enum.ts | 4 +- go.mod | 4 +- go.sum | 76 +++++- pkg/configs/configs.go | 20 +- pkg/ctl/airshipctl.go | 9 +- pkg/ctl/config.go | 248 ++++++++++++++++-- pkg/ctl/document.go | 2 +- pkg/ctl/image.go | 2 +- pkg/ctl/tree.go | 2 +- util/utiltest/utiltest.go | 2 +- 39 files changed, 1650 insertions(+), 51 deletions(-) create mode 100644 client/src/app/ctl/config/config-context/config-context.component.css create mode 100644 client/src/app/ctl/config/config-context/config-context.component.html create mode 100644 client/src/app/ctl/config/config-context/config-context.component.spec.ts create mode 100644 client/src/app/ctl/config/config-context/config-context.component.ts create mode 100644 client/src/app/ctl/config/config-context/config-context.module.ts create mode 100644 client/src/app/ctl/config/config-encryption/config-encryption.component.css create mode 100644 client/src/app/ctl/config/config-encryption/config-encryption.component.html create mode 100644 client/src/app/ctl/config/config-encryption/config-encryption.component.spec.ts create mode 100644 client/src/app/ctl/config/config-encryption/config-encryption.component.ts create mode 100644 client/src/app/ctl/config/config-encryption/config-encryption.module.ts create mode 100644 client/src/app/ctl/config/config-management/config-management.component.css create mode 100644 client/src/app/ctl/config/config-management/config-management.component.html create mode 100644 client/src/app/ctl/config/config-management/config-management.component.spec.ts create mode 100644 client/src/app/ctl/config/config-management/config-management.component.ts create mode 100644 client/src/app/ctl/config/config-management/config-management.module.ts create mode 100644 client/src/app/ctl/config/config-manifest/config-manifest.component.css create mode 100644 client/src/app/ctl/config/config-manifest/config-manifest.component.html create mode 100644 client/src/app/ctl/config/config-manifest/config-manifest.component.spec.ts create mode 100644 client/src/app/ctl/config/config-manifest/config-manifest.component.ts create mode 100644 client/src/app/ctl/config/config-manifest/config-manifest.module.ts create mode 100644 client/src/app/ctl/config/config.component.css create mode 100644 client/src/app/ctl/config/config.models.ts create mode 100644 client/src/assets/icons/lock.svg create mode 100644 client/src/assets/icons/lock_open.svg diff --git a/client/src/app/ctl/config/config-context/config-context.component.css b/client/src/app/ctl/config/config-context/config-context.component.css new file mode 100644 index 0000000..7318c2c --- /dev/null +++ b/client/src/app/ctl/config/config-context/config-context.component.css @@ -0,0 +1,17 @@ +/* +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +*/ + +.grey-icon { + color: grey; +} diff --git a/client/src/app/ctl/config/config-context/config-context.component.html b/client/src/app/ctl/config/config-context/config-context.component.html new file mode 100644 index 0000000..0fb1ec4 --- /dev/null +++ b/client/src/app/ctl/config/config-context/config-context.component.html @@ -0,0 +1,36 @@ + + + + {{context.name}} + + + + +

ContextKubeconf: + + + +

+

Manifest: + + + +

+

EncryptionConfig: + + + +

+

ManagementConfiguration: + + + +

+
+ + + +
diff --git a/client/src/app/ctl/config/config-context/config-context.component.spec.ts b/client/src/app/ctl/config/config-context/config-context.component.spec.ts new file mode 100644 index 0000000..aca76d7 --- /dev/null +++ b/client/src/app/ctl/config/config-context/config-context.component.spec.ts @@ -0,0 +1,62 @@ +/* +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +*/ + +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatCardModule } from '@angular/material/card'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatIconModule } from '@angular/material/icon'; +import { MatInputModule } from '@angular/material/input'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { ToastrModule } from 'ngx-toastr'; +import { Context } from '../config.models'; + +import { ConfigContextComponent } from './config-context.component'; + +describe('ConfigContextComponent', () => { + let component: ConfigContextComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ConfigContextComponent ], + imports: [ + BrowserAnimationsModule, + MatCardModule, + FormsModule, + MatInputModule, + MatIconModule, + MatCheckboxModule, + MatButtonModule, + ReactiveFormsModule, + ToastrModule.forRoot() + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ConfigContextComponent); + component = fixture.componentInstance; + + component.context = new Context(); + + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/client/src/app/ctl/config/config-context/config-context.component.ts b/client/src/app/ctl/config/config-context/config-context.component.ts new file mode 100644 index 0000000..0bc5fac --- /dev/null +++ b/client/src/app/ctl/config/config-context/config-context.component.ts @@ -0,0 +1,84 @@ +/* +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +*/ + +import { Component, OnInit, Input } from '@angular/core'; +import { Context, ContextOptions } from '../config.models'; +import { WebsocketService } from '../../../../services/websocket/websocket.service'; +import { FormControl } from '@angular/forms'; +import { WebsocketMessage } from 'src/services/websocket/websocket.models'; + +@Component({ + selector: 'app-config-context', + templateUrl: './config-context.component.html', + styleUrls: ['./config-context.component.css'] +}) +export class ConfigContextComponent implements OnInit { + @Input() context: Context; + type = 'ctl'; + component = 'config'; + + locked = true; + + name = new FormControl({value: '', disabled: true}); + contextKubeconf = new FormControl({value: '', disabled: true}); + manifest = new FormControl({value: '', disabled: true}); + managementConfiguration = new FormControl({value: '', disabled: true}); + encryptionConfig = new FormControl({value: '', disabled: true}); + + controlsArray = [this.name, this.contextKubeconf, this.manifest, this.managementConfiguration, this.encryptionConfig]; + + constructor(private websocketService: WebsocketService) {} + + ngOnInit(): void { + this.name.setValue(this.context.name); + this.contextKubeconf.setValue(this.context.contextKubeconf); + this.manifest.setValue(this.context.manifest); + this.encryptionConfig.setValue(this.context.encryptionConfig); + this.managementConfiguration.setValue(this.context.managementConfiguration); + } + + toggleLock(): void { + for (const control of this.controlsArray) { + if (this.locked) { + control.enable(); + } else { + control.disable(); + } + } + + this.locked = !this.locked; + } + + setContext(): void { + const opts: ContextOptions = { + Name: this.name.value, + Manifest: this.manifest.value, + ManagementConfiguration: this.managementConfiguration.value, + EncryptionConfig: this.encryptionConfig.value, + }; + + const msg = new WebsocketMessage(this.type, this.component, 'setContext'); + msg.data = JSON.parse(JSON.stringify(opts)); + msg.name = this.name.value; + + this.websocketService.sendMessage(msg); + this.toggleLock(); + } + + useContext(name: string): void { + const msg = new WebsocketMessage(this.type, this.component, 'useContext'); + msg.name = name; + this.websocketService.sendMessage(msg); + } +} diff --git a/client/src/app/ctl/config/config-context/config-context.module.ts b/client/src/app/ctl/config/config-context/config-context.module.ts new file mode 100644 index 0000000..464f851 --- /dev/null +++ b/client/src/app/ctl/config/config-context/config-context.module.ts @@ -0,0 +1,34 @@ +/* +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +*/ + +import { NgModule } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatCardModule } from '@angular/material/card'; +import { MatInputModule } from '@angular/material/input'; + + +@NgModule({ + imports: [ + FormsModule, + MatInputModule, + MatCardModule, + MatButtonModule, + ReactiveFormsModule, + ], + declarations: [ + ], + providers: [] + }) + export class ConfigContextModule { } diff --git a/client/src/app/ctl/config/config-encryption/config-encryption.component.css b/client/src/app/ctl/config/config-encryption/config-encryption.component.css new file mode 100644 index 0000000..abc1f3f --- /dev/null +++ b/client/src/app/ctl/config/config-encryption/config-encryption.component.css @@ -0,0 +1,17 @@ +/* +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +*/ + +.grey-icon { + color: grey; +} \ No newline at end of file diff --git a/client/src/app/ctl/config/config-encryption/config-encryption.component.html b/client/src/app/ctl/config/config-encryption/config-encryption.component.html new file mode 100644 index 0000000..8a66b53 --- /dev/null +++ b/client/src/app/ctl/config/config-encryption/config-encryption.component.html @@ -0,0 +1,39 @@ + + + + {{config.name}} + + + +
+

EncryptionKeyPath: + + + +

+

DecryptionKeyPath: + + + +

+
+
+

KeySecretName: + + + +

+

KeySecretNamespace: + + + +

+
+
+ + + +
diff --git a/client/src/app/ctl/config/config-encryption/config-encryption.component.spec.ts b/client/src/app/ctl/config/config-encryption/config-encryption.component.spec.ts new file mode 100644 index 0000000..c9395d5 --- /dev/null +++ b/client/src/app/ctl/config/config-encryption/config-encryption.component.spec.ts @@ -0,0 +1,62 @@ +/* +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +*/ + +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatCardModule } from '@angular/material/card'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatIconModule } from '@angular/material/icon'; +import { MatInputModule } from '@angular/material/input'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { ToastrModule } from 'ngx-toastr'; +import { EncryptionConfig } from '../config.models'; + +import { ConfigEncryptionComponent } from './config-encryption.component'; + +describe('ConfigEncryptionComponent', () => { + let component: ConfigEncryptionComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ConfigEncryptionComponent ], + imports: [ + BrowserAnimationsModule, + FormsModule, + MatCardModule, + MatInputModule, + MatIconModule, + MatCheckboxModule, + MatButtonModule, + ReactiveFormsModule, + ToastrModule.forRoot() + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ConfigEncryptionComponent); + component = fixture.componentInstance; + + component.config = new EncryptionConfig(); + + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/client/src/app/ctl/config/config-encryption/config-encryption.component.ts b/client/src/app/ctl/config/config-encryption/config-encryption.component.ts new file mode 100644 index 0000000..7e18471 --- /dev/null +++ b/client/src/app/ctl/config/config-encryption/config-encryption.component.ts @@ -0,0 +1,79 @@ +/* +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +*/ + +import { Component, OnInit, Input } from '@angular/core'; +import { FormControl } from '@angular/forms'; +import { EncryptionConfig, EncryptionConfigOptions } from '../config.models'; +import { WebsocketService } from '../../../../services/websocket/websocket.service'; +import { WebsocketMessage } from '../../../../services/websocket/websocket.models'; + +@Component({ + selector: 'app-config-encryption', + templateUrl: './config-encryption.component.html', + styleUrls: ['./config-encryption.component.css'] +}) +export class ConfigEncryptionComponent implements OnInit { + @Input() config: EncryptionConfig; + type = 'ctl'; + component = 'config'; + + locked = true; + name = new FormControl({value: '', disabled: true}); + encryptionKeyPath = new FormControl({value: '', disabled: true}); + decryptionKeyPath = new FormControl({value: '', disabled: true}); + keySecretName = new FormControl({value: '', disabled: true}); + keySecretNamespace = new FormControl({value: '', disabled: true}); + + controlsArray = [this.encryptionKeyPath, this.decryptionKeyPath, this.keySecretName, this.keySecretNamespace]; + + constructor(private websocketService: WebsocketService) {} + + ngOnInit(): void { + this.name.setValue(this.config.name); + this.encryptionKeyPath.setValue(this.config.encryptionKeyPath); + this.decryptionKeyPath.setValue(this.config.decryptionKeyPath); + this.keySecretName.setValue(this.config.keySecretName); + this.keySecretNamespace.setValue(this.config.keySecretNamespace); + } + + toggleLock(): void { + for (const control of this.controlsArray) { + if (this.locked) { + control.enable(); + } else { + control.disable(); + } + } + + this.locked = !this.locked; + } + + setEncryptionConfig(): void { + const opts: EncryptionConfigOptions = { + Name: this.name.value, + EncryptionKeyPath: this.encryptionKeyPath.value, + DecryptionKeyPath: this.decryptionKeyPath.value, + KeySecretName: this.keySecretName.value, + KeySecretNamespace: this.keySecretNamespace.value, + }; + + const msg = new WebsocketMessage(this.type, this.component, 'setEncryptionConfig'); + msg.data = JSON.parse(JSON.stringify(opts)); + msg.name = this.name.value; + + this.websocketService.sendMessage(msg); + this.toggleLock(); + } + +} diff --git a/client/src/app/ctl/config/config-encryption/config-encryption.module.ts b/client/src/app/ctl/config/config-encryption/config-encryption.module.ts new file mode 100644 index 0000000..11f3f95 --- /dev/null +++ b/client/src/app/ctl/config/config-encryption/config-encryption.module.ts @@ -0,0 +1,34 @@ +/* +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +*/ + +import { NgModule } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatInputModule } from '@angular/material/input'; +import { MatCardModule } from '@angular/material/card'; + + +@NgModule({ + imports: [ + FormsModule, + MatInputModule, + MatCardModule, + MatButtonModule, + ReactiveFormsModule, + ], + declarations: [ + ], + providers: [] + }) + export class ConfigEncryptionModule { } diff --git a/client/src/app/ctl/config/config-management/config-management.component.css b/client/src/app/ctl/config/config-management/config-management.component.css new file mode 100644 index 0000000..7318c2c --- /dev/null +++ b/client/src/app/ctl/config/config-management/config-management.component.css @@ -0,0 +1,17 @@ +/* +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +*/ + +.grey-icon { + color: grey; +} diff --git a/client/src/app/ctl/config/config-management/config-management.component.html b/client/src/app/ctl/config/config-management/config-management.component.html new file mode 100644 index 0000000..ea47225 --- /dev/null +++ b/client/src/app/ctl/config/config-management/config-management.component.html @@ -0,0 +1,36 @@ + + + + {{config.name}} + + + +

+ Insecure: +

+

SystemActionRetries: + + + +

+

SystemRebootDelay: + + + +

+

Type: + + + +

+

+ UseProxy: +

+
+ + + +
diff --git a/client/src/app/ctl/config/config-management/config-management.component.spec.ts b/client/src/app/ctl/config/config-management/config-management.component.spec.ts new file mode 100644 index 0000000..6fba7d5 --- /dev/null +++ b/client/src/app/ctl/config/config-management/config-management.component.spec.ts @@ -0,0 +1,62 @@ +/* +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +*/ + +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatCardModule } from '@angular/material/card'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatIconModule } from '@angular/material/icon'; +import { MatInputModule } from '@angular/material/input'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { ToastrModule } from 'ngx-toastr'; +import { ManagementConfig } from '../config.models'; + +import { ConfigManagementComponent } from './config-management.component'; + +describe('ConfigManagementComponent', () => { + let component: ConfigManagementComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ConfigManagementComponent ], + imports: [ + BrowserAnimationsModule, + MatCardModule, + FormsModule, + MatInputModule, + MatIconModule, + MatCheckboxModule, + MatButtonModule, + ReactiveFormsModule, + ToastrModule.forRoot(), + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ConfigManagementComponent); + component = fixture.componentInstance; + + component.config = new ManagementConfig(); + + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/client/src/app/ctl/config/config-management/config-management.component.ts b/client/src/app/ctl/config/config-management/config-management.component.ts new file mode 100644 index 0000000..7c5f699 --- /dev/null +++ b/client/src/app/ctl/config/config-management/config-management.component.ts @@ -0,0 +1,84 @@ +/* +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +*/ + +import { Component, Input, OnInit } from '@angular/core'; +import { ManagementConfig } from '../config.models'; +import { FormControl, Validators } from '@angular/forms'; +import { WebsocketService } from 'src/services/websocket/websocket.service'; +import { WebsocketMessage } from 'src/services/websocket/websocket.models'; + +@Component({ + selector: 'app-config-management', + templateUrl: './config-management.component.html', + styleUrls: ['./config-management.component.css'] +}) +export class ConfigManagementComponent implements OnInit { + @Input() config: ManagementConfig; + msgType = 'ctl'; + component = 'config'; + + locked = true; + + name = new FormControl({value: '', disabled: true}); + insecure = new FormControl({value: false, disabled: true}); + systemActionRetries = new FormControl({value: '', disabled: true}, Validators.pattern('[0-9]*')); + systemRebootDelay = new FormControl({value: '', disabled: true}, Validators.pattern('[0-9]*')); + type = new FormControl({value: '', disabled: true}); + useproxy = new FormControl({value: false, disabled: true}); + + controlsArray = [this.name, this.insecure, this.systemRebootDelay, this.systemActionRetries, this.type, this.useproxy]; + + constructor(private websocketService: WebsocketService) { } + + ngOnInit(): void { + this.name.setValue(this.config.name); + this.insecure.setValue(this.config.insecure); + this.systemActionRetries.setValue(this.config.systemActionRetries); + this.systemRebootDelay.setValue(this.config.systemRebootDelay); + this.type.setValue(this.config.type); + this.useproxy.setValue(this.config.useproxy); + } + + toggleLock(): void { + for (const control of this.controlsArray) { + if (this.locked) { + control.enable(); + } else { + control.disable(); + } + } + + this.locked = !this.locked; + } + + setManagementConfig(): void { + const msg = new WebsocketMessage(this.msgType, this.component, 'setManagementConfig'); + msg.name = this.name.value; + + const cfg: ManagementConfig = { + name: this.name.value, + insecure: this.insecure.value, + // TODO(mfuller): need to validate these are numerical values in the form + systemActionRetries: +this.systemActionRetries.value, + systemRebootDelay: +this.systemRebootDelay.value, + type: this.type.value, + useproxy: this.useproxy.value + }; + + msg.data = JSON.parse(JSON.stringify(cfg)); + this.websocketService.sendMessage(msg); + this.toggleLock(); + } + +} diff --git a/client/src/app/ctl/config/config-management/config-management.module.ts b/client/src/app/ctl/config/config-management/config-management.module.ts new file mode 100644 index 0000000..e65c8a8 --- /dev/null +++ b/client/src/app/ctl/config/config-management/config-management.module.ts @@ -0,0 +1,38 @@ +/* +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +*/ + +import { NgModule } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatInputModule } from '@angular/material/input'; +import { MatCardModule } from '@angular/material/card'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatIconModule } from '@angular/material/icon'; + + +@NgModule({ + imports: [ + FormsModule, + MatInputModule, + MatCardModule, + MatButtonModule, + ReactiveFormsModule, + MatCheckboxModule, + MatIconModule + ], + declarations: [ + ], + providers: [] + }) + export class ConfigManagementModule { } diff --git a/client/src/app/ctl/config/config-manifest/config-manifest.component.css b/client/src/app/ctl/config/config-manifest/config-manifest.component.css new file mode 100644 index 0000000..7318c2c --- /dev/null +++ b/client/src/app/ctl/config/config-manifest/config-manifest.component.css @@ -0,0 +1,17 @@ +/* +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +*/ + +.grey-icon { + color: grey; +} diff --git a/client/src/app/ctl/config/config-manifest/config-manifest.component.html b/client/src/app/ctl/config/config-manifest/config-manifest.component.html new file mode 100644 index 0000000..5868f7b --- /dev/null +++ b/client/src/app/ctl/config/config-manifest/config-manifest.component.html @@ -0,0 +1,66 @@ + + + + {{manifest.name}} + + + +

RepoName: + + + +

+

URL: + + + +

+

Branch: + + + +

+

CommitHash: + + + +

+

Tag: + + + +

+

RemoteRef: + + + +

+

+ Force: +

+

+ IsPhase: +

+

SubPath: + + + +

+

TargetPath: + + + +

+

MetadataPath: + + + +

+
+ + + +
diff --git a/client/src/app/ctl/config/config-manifest/config-manifest.component.spec.ts b/client/src/app/ctl/config/config-manifest/config-manifest.component.spec.ts new file mode 100644 index 0000000..d3b4d3c --- /dev/null +++ b/client/src/app/ctl/config/config-manifest/config-manifest.component.spec.ts @@ -0,0 +1,68 @@ +/* +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +*/ + +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatCardModule } from '@angular/material/card'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatIconModule } from '@angular/material/icon'; +import { MatInputModule } from '@angular/material/input'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { ToastrModule } from 'ngx-toastr'; +import { CtlManifest, Manifest, RepoCheckout, Repository } from '../config.models'; + +import { ConfigManifestComponent } from './config-manifest.component'; + +describe('ConfigManifestComponent', () => { + let component: ConfigManifestComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ConfigManifestComponent ], + imports: [ + BrowserAnimationsModule, + MatCardModule, + FormsModule, + MatInputModule, + MatIconModule, + MatCheckboxModule, + MatButtonModule, + ReactiveFormsModule, + ToastrModule.forRoot() + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ConfigManifestComponent); + component = fixture.componentInstance; + + component.manifest = new Manifest(); + component.manifest.manifest = new CtlManifest(); + const repoName = 'fakerepo'; + component.manifest.manifest.phaseRepositoryName = repoName; + component.manifest.manifest.repositories = {}; + component.manifest.manifest.repositories[repoName] = new Repository(); + component.manifest.manifest.repositories[repoName].checkout = new RepoCheckout(); + + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/client/src/app/ctl/config/config-manifest/config-manifest.component.ts b/client/src/app/ctl/config/config-manifest/config-manifest.component.ts new file mode 100644 index 0000000..0302b44 --- /dev/null +++ b/client/src/app/ctl/config/config-manifest/config-manifest.component.ts @@ -0,0 +1,127 @@ +/* +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +*/ + +import { Component, Input, OnInit } from '@angular/core'; +import { Manifest, ManifestOptions, Repository } from '../config.models'; +import { FormControl } from '@angular/forms'; +import { WebsocketService } from 'src/services/websocket/websocket.service'; +import { WebsocketMessage } from 'src/services/websocket/websocket.models'; + + +@Component({ + selector: 'app-config-manifest', + templateUrl: './config-manifest.component.html', + styleUrls: ['./config-manifest.component.css'] +}) +export class ConfigManifestComponent implements OnInit { + @Input() manifest: Manifest; + + type = 'ctl'; + component = 'config'; + + locked = true; + Name = new FormControl({value: '', disabled: true}); + RepoName = new FormControl({value: '', disabled: true}); + URL = new FormControl({value: '', disabled: true}); + Branch = new FormControl({value: '', disabled: true}); + CommitHash = new FormControl({value: '', disabled: true}); + Tag = new FormControl({value: '', disabled: true}); + RemoteRef = new FormControl({value: '', disabled: true}); + Force = new FormControl({value: false, disabled: true}); + IsPhase = new FormControl({value: false, disabled: true}); + SubPath = new FormControl({value: '', disabled: true}); + TargetPath = new FormControl({value: '', disabled: true}); + MetadataPath = new FormControl({value: '', disabled: true}); + + controlsArray = [ + this.Name, + this.RepoName, + this.URL, + this.Branch, + this.CommitHash, + this.Tag, + this.RemoteRef, + this.Force, + this.IsPhase, + this.SubPath, + this.TargetPath, + this.MetadataPath + ]; + + constructor(private websocketService: WebsocketService) { } + + ngOnInit(): void { + this.Name.setValue(this.manifest.name); + + // TODO(mfuller): not sure yet how to handle multiple repositories, + // so for now, I'm just showing the phase repository (primary) + const repoName = this.manifest.manifest.phaseRepositoryName; + this.RepoName.setValue(repoName); + const primaryRepo: Repository = this.manifest.manifest.repositories[repoName]; + this.URL.setValue(primaryRepo.url); + this.Branch.setValue(primaryRepo.checkout.branch); + this.CommitHash.setValue(primaryRepo.checkout.commitHash); + this.Tag.setValue(primaryRepo.checkout.tag); + this.RemoteRef.setValue(primaryRepo.checkout.remoteRef); + this.Force.setValue(primaryRepo.checkout.force); + // TODO(mfuller): this value doesn't come from the config file, but if set to true, + // it appears to set the phaseRepositoryName key, and since that's + // the only repo I'm showing, set to true for now + this.IsPhase.setValue(true); + this.SubPath.setValue(this.manifest.manifest.subPath); + this.TargetPath.setValue(this.manifest.manifest.targetPath); + this.MetadataPath.setValue(this.manifest.manifest.metadataPath); + } + + toggleLock(): void { + for (const control of this.controlsArray) { + if (this.locked) { + control.enable(); + } else { + control.disable(); + } + } + + this.locked = !this.locked; + } + + setManifest(): void { + const msg = new WebsocketMessage(this.type, this.component, 'setManifest'); + msg.name = this.manifest.name; + + // TODO(mfuller): since "Force" and "IsPhase" can only be set by passing in + // CLI flags rather than passing in values, there doesn't appear to be a way + // to unset them once they're true without manually editing the config file. + // Open a bug for this? Or is this intentional? I may have to write a custom + // setter to set the value directly in the Config struct + const opts: ManifestOptions = { + Name: this.Name.value, + RepoName: this.RepoName.value, + URL: this.URL.value, + Branch: this.Branch.value, + CommitHash: this.CommitHash.value, + Tag: this.Tag.value, + RemoteRef: this.RemoteRef.value, + Force: this.Force.value, + IsPhase: this.IsPhase.value, + SubPath: this.SubPath.value, + TargetPath: this.TargetPath.value, + MetadataPath: this.MetadataPath.value + }; + + msg.data = JSON.parse(JSON.stringify(opts)); + this.websocketService.sendMessage(msg); + this.toggleLock(); + } +} diff --git a/client/src/app/ctl/config/config-manifest/config-manifest.module.ts b/client/src/app/ctl/config/config-manifest/config-manifest.module.ts new file mode 100644 index 0000000..fcf3a69 --- /dev/null +++ b/client/src/app/ctl/config/config-manifest/config-manifest.module.ts @@ -0,0 +1,36 @@ +/* +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +*/ + +import { NgModule } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatCardModule } from '@angular/material/card'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatInputModule } from '@angular/material/input'; + + +@NgModule({ + imports: [ + FormsModule, + MatInputModule, + MatCardModule, + MatButtonModule, + ReactiveFormsModule, + MatCheckboxModule + ], + declarations: [ + ], + providers: [] + }) + export class ConfigManifestModule { } diff --git a/client/src/app/ctl/config/config.component.css b/client/src/app/ctl/config/config.component.css new file mode 100644 index 0000000..396bd76 --- /dev/null +++ b/client/src/app/ctl/config/config.component.css @@ -0,0 +1,17 @@ +/* +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +*/ + +.grey-icon { + color: grey; +} diff --git a/client/src/app/ctl/config/config.component.html b/client/src/app/ctl/config/config.component.html index e1367b0..18d7f13 100755 --- a/client/src/app/ctl/config/config.component.html +++ b/client/src/app/ctl/config/config.component.html @@ -1 +1,26 @@ -

Config component

\ No newline at end of file +

Airship Configuration Operations

+ +
+

Contexts - Current Context: {{currentContext}}

+
+ +
+ + +

Manifests

+
+ +
+ + +

Encryption Configurations

+
+ +
+ + +

Management Configurations

+
+ +
+
diff --git a/client/src/app/ctl/config/config.component.spec.ts b/client/src/app/ctl/config/config.component.spec.ts index efc3588..6119e93 100755 --- a/client/src/app/ctl/config/config.component.spec.ts +++ b/client/src/app/ctl/config/config.component.spec.ts @@ -15,6 +15,20 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ConfigComponent } from './config.component'; import { ToastrModule } from 'ngx-toastr'; +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatInputModule } from '@angular/material/input'; +import { MatCardModule } from '@angular/material/card'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { ConfigContextModule } from './config-context/config-context.module'; +import { ConfigManagementModule } from './config-management/config-management.module'; +import { ConfigManifestModule } from './config-manifest/config-manifest.module'; +import { ConfigEncryptionModule } from './config-encryption/config-encryption.module'; +import { ConfigManifestComponent } from './config-manifest/config-manifest.component'; +import { ConfigManagementComponent } from './config-management/config-management.component'; +import { ConfigEncryptionComponent } from './config-encryption/config-encryption.component'; +import { ConfigContextComponent } from './config-context/config-context.component'; describe('ConfigComponent', () => { let component: ConfigComponent; @@ -23,11 +37,26 @@ describe('ConfigComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ imports: [ - ToastrModule.forRoot() + ToastrModule.forRoot(), + FormsModule, + MatButtonModule, + MatInputModule, + MatCardModule, + MatCheckboxModule, + ConfigContextModule, + ConfigManagementModule, + ConfigManifestModule, + ConfigEncryptionModule, + ReactiveFormsModule ], declarations: [ - ConfigComponent - ] + ConfigComponent, + ConfigManifestComponent, + ConfigManagementComponent, + ConfigEncryptionComponent, + ConfigContextComponent + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA] }) .compileComponents(); })); diff --git a/client/src/app/ctl/config/config.component.ts b/client/src/app/ctl/config/config.component.ts index adc3697..3dbc12d 100755 --- a/client/src/app/ctl/config/config.component.ts +++ b/client/src/app/ctl/config/config.component.ts @@ -12,33 +12,115 @@ # limitations under the License. */ -import { Component } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { WebsocketService } from '../../../services/websocket/websocket.service'; import { WebsocketMessage, WSReceiver } from '../../../services/websocket/websocket.models'; import { Log } from '../../../services/log/log.service'; import { LogMessage } from '../../../services/log/log-message'; +import { Context, ManagementConfig, Manifest, EncryptionConfig } from './config.models'; @Component({ selector: 'app-bare-metal', templateUrl: './config.component.html', }) -export class ConfigComponent implements WSReceiver { +export class ConfigComponent implements WSReceiver, OnInit { className = this.constructor.name; // TODO (aschiefe): extract these strings to constants type = 'ctl'; component = 'config'; + currentContext: string; + contexts: Context[] = []; + manifests: Manifest[] = []; + managementConfigs: ManagementConfig[] = []; + encryptionConfigs: EncryptionConfig[] = []; + constructor(private websocketService: WebsocketService) { this.websocketService.registerFunctions(this); } + ngOnInit(): void { + this.getConfig(); + } + async receiver(message: WebsocketMessage): Promise { if (message.hasOwnProperty('error')) { this.websocketService.printIfToast(message); } else { - // TODO (aschiefe): determine what should be notifications and what should be 86ed - Log.Debug(new LogMessage('Message received in config', this.className, message)); + switch (message.subComponent) { + case 'getCurrentContext': + this.currentContext = message.message; + break; + case 'getContexts': + Object.assign(this.contexts, message.data); + break; + case 'getManifests': + Object.assign(this.manifests, message.data); + break; + case 'getEncryptionConfigs': + Object.assign(this.encryptionConfigs, message.data); + break; + case 'getManagementConfigs': + Object.assign(this.managementConfigs, message.data); + break; + case 'useContext': + this.getCurrentContext(); + break; + case 'setContext': + this.websocketService.printIfToast(message); + break; + case 'setEncryptionConfig': + this.websocketService.printIfToast(message); + break; + case 'setManifest': + this.websocketService.printIfToast(message); + break; + case 'setManagementConfig': + this.websocketService.printIfToast(message); + break; + default: + Log.Error(new LogMessage('Config message sub component not handled', this.className, message)); + break; + } } } + + getConfig(): void { + this.getCurrentContext(); + this.getContexts(); + this.getManifests(); + this.getManagementConfigs(); + this.getEncryptionConfigs(); + } + + getCurrentContext(): void { + this.websocketService.sendMessage(new WebsocketMessage( + this.type, this.component, 'getCurrentContext') + ); + } + + getContexts(): void { + this.websocketService.sendMessage(new WebsocketMessage( + this.type, this.component, 'getContexts') + ); + } + + getManifests(): void { + this.websocketService.sendMessage(new WebsocketMessage( + this.type, this.component, 'getManifests') + ); + } + + getEncryptionConfigs(): void { + this.websocketService.sendMessage(new WebsocketMessage( + this.type, this.component, 'getEncryptionConfigs') + ); + } + + getManagementConfigs(): void { + this.websocketService.sendMessage(new WebsocketMessage( + this.type, this.component, 'getManagementConfigs') + ); + } } diff --git a/client/src/app/ctl/config/config.models.ts b/client/src/app/ctl/config/config.models.ts new file mode 100644 index 0000000..783a7c6 --- /dev/null +++ b/client/src/app/ctl/config/config.models.ts @@ -0,0 +1,111 @@ +/* +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +*/ + +export class EncryptionConfig { + name: string; + encryptionKeyPath: string; + decryptionKeyPath: string; + keySecretName: string; + keySecretNamespace: string; +} + +export class Context { + name: string; + contextKubeconf: string; + manifest: string; + encryptionConfig: string; + managementConfiguration: string; +} + +export class ContextOptions { + Name: string; + Manifest: string; + ManagementConfiguration: string; + EncryptionConfig: string; +} + +export class ManagementConfig { + name: string; + insecure: boolean; + systemActionRetries: number; + systemRebootDelay: number; + type: string; + useproxy: boolean; +} + +export class Manifest { + name: string; + manifest: CtlManifest; +} + +export class CtlManifest { + phaseRepositoryName: string; + repositories: object; + targetPath: string; + subPath: string; + metadataPath: string; +} + +export class Repository { + url: string; + auth: RepoAuth; + checkout: RepoCheckout; +} + +export class RepoAuth { + type: string; + keyPass: string; + sshKey: string; + httpPass: string; + sshPass: string; + username: string; +} + +export class RepoCheckout { + commitHash: string; + branch: string; + tag: string; + remoteRef: string; + force: boolean; +} + +// TODO(mfuller): this isn't currently settable from the CLI +// should we allow it in UI? +export class Permissions { + DirectoryPermission: number; + FilePermission: number; +} + +export class ManifestOptions { + Name: string; + RepoName: string; + URL: string; + Branch: string; + CommitHash: string; + Tag: string; + RemoteRef: string; + Force: boolean; + IsPhase: boolean; + SubPath: string; + TargetPath: string; + MetadataPath: string; +} + +export class EncryptionConfigOptions { + Name: string; + EncryptionKeyPath: string; + DecryptionKeyPath: string; + KeySecretName: string; + KeySecretNamespace: string; +} diff --git a/client/src/app/ctl/config/config.module.ts b/client/src/app/ctl/config/config.module.ts index d40df4d..94cf58c 100755 --- a/client/src/app/ctl/config/config.module.ts +++ b/client/src/app/ctl/config/config.module.ts @@ -12,15 +12,42 @@ # limitations under the License. */ -import { NgModule } from '@angular/core'; +import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; import { ConfigComponent } from './config.component'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatCardModule } from '@angular/material/card'; +import { MatInputModule } from '@angular/material/input'; +import { MatDividerModule } from '@angular/material/divider'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; +import { ConfigContextComponent } from './config-context/config-context.component'; +import { ConfigEncryptionComponent } from './config-encryption/config-encryption.component'; +import { ConfigManagementComponent } from './config-management/config-management.component'; +import { ConfigManifestComponent } from './config-manifest/config-manifest.component'; +import { MatCheckboxModule } from '@angular/material/checkbox'; + @NgModule({ imports: [ + CommonModule, + FormsModule, + ReactiveFormsModule, + MatCardModule, + MatInputModule, + MatDividerModule, + MatButtonModule, + MatIconModule, + MatCheckboxModule ], declarations: [ - ConfigComponent + ConfigComponent, + ConfigContextComponent, + ConfigEncryptionComponent, + ConfigManagementComponent, + ConfigManifestComponent ], - providers: [] + providers: [], + schemas: [CUSTOM_ELEMENTS_SCHEMA] }) export class ConfigModule { } diff --git a/client/src/app/ctl/ctl.module.ts b/client/src/app/ctl/ctl.module.ts index 1ef5195..287dfb4 100644 --- a/client/src/app/ctl/ctl.module.ts +++ b/client/src/app/ctl/ctl.module.ts @@ -21,11 +21,13 @@ import { ClusterModule } from './cluster/cluster.module'; import { CtlRoutingModule } from './ctl-routing.module'; import { PhaseModule } from './phase/phase.module'; import { SecretModule } from './secret/secret.module'; +import { ConfigModule } from './config/config.module'; @NgModule({ imports: [ CtlRoutingModule, ClusterModule, + ConfigModule, RouterModule, DocumentModule, BaremetalModule, diff --git a/client/src/assets/icons/lock.svg b/client/src/assets/icons/lock.svg new file mode 100644 index 0000000..8fd4418 --- /dev/null +++ b/client/src/assets/icons/lock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/assets/icons/lock_open.svg b/client/src/assets/icons/lock_open.svg new file mode 100644 index 0000000..ff8ff01 --- /dev/null +++ b/client/src/assets/icons/lock_open.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/services/icon/icons.enum.ts b/client/src/services/icon/icons.enum.ts index 1360085..c0d2052 100644 --- a/client/src/services/icon/icons.enum.ts +++ b/client/src/services/icon/icons.enum.ts @@ -31,5 +31,7 @@ export enum Icons { list_alt = 'list_alt', devices = 'devices', check_circle = 'check_circle', - close = 'close' + close = 'close', + lock = 'lock', + lock_open = 'lock_open' } diff --git a/go.mod b/go.mod index 7ac2f1b..c1c89bc 100644 --- a/go.mod +++ b/go.mod @@ -10,8 +10,8 @@ require ( github.com/pkg/errors v0.9.1 github.com/spf13/cobra v1.0.0 github.com/stretchr/testify v1.6.1 - opendev.org/airship/airshipctl v0.0.0-20201007194648-8d6851511840 - sigs.k8s.io/cli-utils v0.18.1 + opendev.org/airship/airshipctl v0.0.0-20201021221027-46a0a79066d3 + sigs.k8s.io/cli-utils v0.20.6 sigs.k8s.io/kustomize/api v0.5.1 ) diff --git a/go.sum b/go.sum index 1a25fa2..356d120 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,14 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3 h1:AVXDdKsrtX33oR9fbCMu/+c1o8Ofjq6Ku/MInaLVg5Y= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= github.com/360EntSecGroup-Skylar/excelize v1.4.1/go.mod h1:vnax29X2usfl7HHkBrX5EvSCJcmH3dT9luvxzu8iGAE= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= @@ -180,6 +188,8 @@ github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjr github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -295,6 +305,7 @@ github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18h github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.0.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -336,6 +347,8 @@ github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.1 h1:/exdXoGamhu5ONeUJH0deniYLWYvQwW66yvlfiiKTu0= github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= @@ -346,6 +359,7 @@ github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= @@ -353,6 +367,7 @@ github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk= github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= @@ -436,11 +451,15 @@ github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM52 github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= @@ -530,6 +549,8 @@ github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1 h1:mFwc4LvZ0xpSvDZ3E+k8Yte0hLOMxXUlP+yXtJqkYfQ= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.13.0 h1:M76yO2HkZASFjXL0HSoZJ1AYEmQxNJmY41Jx1zNUq1Y= +github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.3.0/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -595,6 +616,7 @@ github.com/russross/blackfriday v0.0.0-20170610170232-067529f716f4/go.mod h1:JO/ github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= github.com/securego/gosec v0.0.0-20191002120514-e680875ea14d/go.mod h1:w5+eXa0mYznDkHaMCXA4XYffjlH+cy1oyKbfzJXa2Do= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= @@ -608,6 +630,8 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= @@ -683,6 +707,7 @@ go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qL go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.starlark.net v0.0.0-20190528202925-30ae18b8564f/go.mod h1:c1/X6cHgvdXj6pUlmWKMkuqRnW4K8x2vwt6JAaaircg= go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc= go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= @@ -706,25 +731,33 @@ golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180112015858-5ccada7d0a7b/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -743,6 +776,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190328230028-74de082e2cca/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -757,6 +792,8 @@ golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3ob golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -786,12 +823,16 @@ golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -802,6 +843,9 @@ golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0 golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980 h1:OjiUf46hAmXblsZdnoSXsEUSKU8r1UEzcL5RVZ4gO9Y= +golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20171227012246-e19ae1496984/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -832,39 +876,56 @@ golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190719005602-e377ae9d6386/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190930201159-7c411dea38b0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.0.1 h1:xyiBuvkD2g5n7cYzx6u2sxQvsAy4QJsZFCzGVdzOXZ0= gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU= gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51 h1:Ex1mq5jaJof+kRnYi3SlYJ8KKa9Ao3NHyIT5XJ1gF6U= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg= @@ -879,6 +940,7 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -916,6 +978,7 @@ gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= k8s.io/api v0.0.0-20190918155943-95b840bb6a1f/go.mod h1:uWuOHnjmNrtQomJrvEBg0c0HRNyQ+8KTEERVsK0PW48= @@ -993,14 +1056,15 @@ modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw= -opendev.org/airship/airshipctl v0.0.0-20201007194648-8d6851511840 h1:FdeXz/3JxL20ZLOX5RtTy4BHxGJn/bi9lHnIxv/+rTg= -opendev.org/airship/airshipctl v0.0.0-20201007194648-8d6851511840/go.mod h1:uSXCXgsecl6Em2fHfjSXVsWItbzi8UWjKON+m6YdrjE= +opendev.org/airship/airshipctl v0.0.0-20201021221027-46a0a79066d3 h1:aqscOCgADezXB7gwOhtKfn8v1mas4vAxzrHwfcptFxA= +opendev.org/airship/airshipctl v0.0.0-20201021221027-46a0a79066d3/go.mod h1:qnuSTEEmInUS+zk7fMvNeDEloiTqlHlRsEmWFXlT+pU= opendev.org/airship/go-redfish v0.0.0-20200318103738-db034d1d753a h1:4ggAMTwpfu/w3ZXOIJ9tfYF37JIYn+eNCA4O10NduZ0= opendev.org/airship/go-redfish v0.0.0-20200318103738-db034d1d753a/go.mod h1:FEjYcb3bYBWGpQIqtvVM0NrT5eyjlCOCj5JNf4lI+6s= opendev.org/airship/go-redfish/client v0.0.0-20200318103738-db034d1d753a h1:S1dmsP5Cc6OQjAd6OgIKMcNPBiGjh5TDbijVjNE/VGU= opendev.org/airship/go-redfish/client v0.0.0-20200318103738-db034d1d753a/go.mod h1:s0hwuUpBsRXOrhN0NR+fNVivXGyWgHKpqtyq7qYjpew= -sigs.k8s.io/cli-utils v0.18.1 h1:K4usJmMlI98mL+z+TdAnKfzng64/m8bRXZKPwy3ZCWw= -sigs.k8s.io/cli-utils v0.18.1/go.mod h1:B7KdqkSkHNIUn3cFbaR4aKUZMKtr+Benboi1w/HW/Fg= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +sigs.k8s.io/cli-utils v0.20.6 h1:leIIk2NOzacXqhuNnm0tB328RSzp+1QR9PkAdSP1aRI= +sigs.k8s.io/cli-utils v0.20.6/go.mod h1:Lsj0EXMtqcSYyIPY9IXemMQW/muYDHMEWNfQI/DctrA= sigs.k8s.io/cluster-api v0.3.10 h1:iUbnDdFQjp406hclEV1/rRMO7/NpyJ7IxozAaAA9Zns= sigs.k8s.io/cluster-api v0.3.10/go.mod h1:XBBDBiaczcyNlH4D7FNjSKc5bBofYRppfg0ZgaP2x1U= sigs.k8s.io/controller-runtime v0.4.0/go.mod h1:ApC79lpY3PHW9xj/w9pj+lYkLgwAAUZwfXkME1Lajns= diff --git a/pkg/configs/configs.go b/pkg/configs/configs.go index 82fdcba..2f75e37 100644 --- a/pkg/configs/configs.go +++ b/pkg/configs/configs.go @@ -137,15 +137,17 @@ const ( Status WsSubComponentType = "status" // ctl config subcomponets - GetContext WsSubComponentType = "getContext" - GetEncryptionConfig WsSubComponentType = "getEncryptionConfig" - GetManagementConfig WsSubComponentType = "getManagementConfig" - GetManifest WsSubComponentType = "getManifest" - SetContext WsSubComponentType = "setContext" - SetEncryptionConfig WsSubComponentType = "setEncryptionConfig" - SetManagementConfig WsSubComponentType = "setManagementConfig" - SetManifest WsSubComponentType = "setManifest" - UseContext WsSubComponentType = "useContext" + GetContexts WsSubComponentType = "getContexts" + GetCurrentContext WsSubComponentType = "getCurrentContext" + GetEncryptionConfigs WsSubComponentType = "getEncryptionConfigs" + GetManagementConfigs WsSubComponentType = "getManagementConfigs" + GetManifests WsSubComponentType = "getManifests" + SetContext WsSubComponentType = "setContext" + SetEncryptionConfig WsSubComponentType = "setEncryptionConfig" + SetManagementConfig WsSubComponentType = "setManagementConfig" + SetManifest WsSubComponentType = "setManifest" + UseContext WsSubComponentType = "useContext" + GetConfig WsSubComponentType = "getConfig" // ctl document subcomponents Plugin WsSubComponentType = "plugin" diff --git a/pkg/ctl/airshipctl.go b/pkg/ctl/airshipctl.go index a775c0c..560b2b5 100644 --- a/pkg/ctl/airshipctl.go +++ b/pkg/ctl/airshipctl.go @@ -64,9 +64,12 @@ func Init() { } // NewDefaultClient initializes the airshipctl client for external usage with default logging. -func NewDefaultClient(airshipConfigPath, kubeConfigPath *string) (*Client, error) { - cfgFactory := config.CreateFactory(airshipConfigPath, kubeConfigPath) +func NewDefaultClient(airshipConfigPath *string) (*Client, error) { + cfgFactory := config.CreateFactory(airshipConfigPath) + // TODO(mfuller): Factory doesn't throw an error if there's no + // config file, it calls log.Fatal and kills the app. Not sure + // how to handle this yet conf, err := cfgFactory() if err != nil { return nil, err @@ -84,7 +87,7 @@ func NewDefaultClient(airshipConfigPath, kubeConfigPath *string) (*Client, error // NewClient initializes the airshipctl client for external usage with the logging overridden. func NewClient(airshipConfigPath, kubeConfigPath *string, request configs.WsMessage) (*Client, error) { - client, err := NewDefaultClient(airshipConfigPath, kubeConfigPath) + client, err := NewDefaultClient(airshipConfigPath) if err != nil { return nil, err } diff --git a/pkg/ctl/config.go b/pkg/ctl/config.go index 8e4bcc4..c7b9a7d 100644 --- a/pkg/ctl/config.go +++ b/pkg/ctl/config.go @@ -15,9 +15,12 @@ package ctl import ( + "encoding/json" "fmt" + ctlconfig "opendev.org/airship/airshipctl/pkg/config" "opendev.org/airship/airshipui/pkg/configs" + "opendev.org/airship/airshipui/pkg/log" ) // HandleConfigRequest will flop between requests so we don't have to have them all mapped as function calls @@ -25,35 +28,54 @@ import ( func HandleConfigRequest(user *string, request configs.WsMessage) configs.WsMessage { response := configs.WsMessage{ Type: configs.CTL, - Component: configs.Baremetal, + Component: configs.CTLConfig, SubComponent: request.SubComponent, + Name: request.Name, } var err error var message *string + client, err := NewClient(AirshipConfigPath, KubeConfigPath, request) + if err != nil { + e := fmt.Sprintf("Error initializing airshipctl client: %s", err) + response.Error = &e + return response + } + subComponent := request.SubComponent switch subComponent { - case configs.GetContext: - err = fmt.Errorf("Subcomponent %s not implemented", request.SubComponent) - case configs.GetEncryptionConfig: - err = fmt.Errorf("Subcomponent %s not implemented", request.SubComponent) - case configs.GetManagementConfig: - err = fmt.Errorf("Subcomponent %s not implemented", request.SubComponent) - case configs.GetManifest: - err = fmt.Errorf("Subcomponent %s not implemented", request.SubComponent) + case configs.GetCurrentContext: + context := client.Config.CurrentContext + message = &context + case configs.GetContexts: + response.Data = GetContexts(client) + case configs.GetEncryptionConfigs: + response.Data = GetEncryptionConfigs(client) + case configs.GetManagementConfigs: + response.Data = GetManagementConfigs(client) + case configs.GetManifests: + response.Data = GetManifests(client) case configs.Init: - err = fmt.Errorf("Subcomponent %s not implemented", request.SubComponent) + err = InitAirshipConfig(AirshipConfigPath) case configs.SetContext: - err = fmt.Errorf("Subcomponent %s not implemented", request.SubComponent) + response.Data, err = SetContext(client, request) + str := fmt.Sprintf("Context '%s' has been modified", request.Name) + message = &str case configs.SetEncryptionConfig: - err = fmt.Errorf("Subcomponent %s not implemented", request.SubComponent) + response.Data, err = SetEncryptionConfig(client, request) + str := fmt.Sprintf("Encryption configuration '%s' has been modified", request.Name) + message = &str case configs.SetManagementConfig: - err = fmt.Errorf("Subcomponent %s not implemented", request.SubComponent) + err = SetManagementConfig(client, request) + str := fmt.Sprintf("Management configuration '%s' has been modified", request.Name) + message = &str case configs.SetManifest: - err = fmt.Errorf("Subcomponent %s not implemented", request.SubComponent) + response.Data, err = SetManifest(client, request) + str := fmt.Sprintf("Manifest '%s' has been modified", request.Name) + message = &str case configs.UseContext: - err = fmt.Errorf("Subcomponent %s not implemented", request.SubComponent) + err = UseContext(client, request) default: err = fmt.Errorf("Subcomponent %s not found", request.SubComponent) } @@ -67,3 +89,199 @@ func HandleConfigRequest(user *string, request configs.WsMessage) configs.WsMess return response } + +// InitAirshipConfig wrapper function for CTL's CreateConfig using the specified path +func InitAirshipConfig(path *string) error { + return ctlconfig.CreateConfig(*path) +} + +// Context wrapper struct to include context name with CTL's Context +type Context struct { + Name string `json:"name"` + ctlconfig.Context +} + +// GetContexts returns a slice of wrapper Context structs so we know the name of each +// for display in the UI +func GetContexts(client *Client) []Context { + contexts := []Context{} + for name, context := range client.Config.Contexts { + contexts = append(contexts, Context{ + name, + ctlconfig.Context{ + NameInKubeconf: context.NameInKubeconf, + Manifest: context.Manifest, + EncryptionConfig: context.EncryptionConfig, + ManagementConfiguration: context.ManagementConfiguration, + }, + }) + } + + return contexts +} + +// Manifest wraps CTL's Manifest to include the manifest name +type Manifest struct { + Name string `json:"name"` + Manifest *ctlconfig.Manifest `json:"manifest"` +} + +// GetManifests returns a slice of wrapper Manifest structs so we know the name of each +// for display in the UI +func GetManifests(client *Client) []Manifest { + manifests := []Manifest{} + + for name, manifest := range client.Config.Manifests { + manifests = append(manifests, Manifest{ + Name: name, + Manifest: manifest, + }) + } + + return manifests +} + +// ManagementConfig wrapper struct for CTL's ManagementConfiguration that +// includes a name +type ManagementConfig struct { + Name string `json:"name"` + ctlconfig.ManagementConfiguration +} + +// GetManagementConfigs function to retrieve all management configs +func GetManagementConfigs(client *Client) []ManagementConfig { + configs := []ManagementConfig{} + for name, conf := range client.Config.ManagementConfiguration { + configs = append(configs, ManagementConfig{ + name, + ctlconfig.ManagementConfiguration{ + Insecure: conf.Insecure, + SystemActionRetries: conf.SystemActionRetries, + SystemRebootDelay: conf.SystemRebootDelay, + Type: conf.Type, + UseProxy: conf.UseProxy, + }, + }) + } + return configs +} + +// EncryptionConfig wrapper struct for CTL's EncryptionConfiguration that +// includes a name +type EncryptionConfig struct { + Name string `json:"name"` + ctlconfig.EncryptionConfig +} + +// GetEncryptionConfigs returns a slice of wrapper EncryptionConfig structs so we +// know the name of each for display in the UI +func GetEncryptionConfigs(client *Client) []EncryptionConfig { + configs := []EncryptionConfig{} + for name, config := range client.Config.EncryptionConfigs { + configs = append(configs, EncryptionConfig{ + name, + ctlconfig.EncryptionConfig{ + EncryptionKeyFileSource: config.EncryptionKeyFileSource, + EncryptionKeySecretSource: config.EncryptionKeySecretSource, + }, + }) + } + + return configs +} + +// SetContext wrapper function for CTL's RunSetContext, using a UI client +func SetContext(client *Client, message configs.WsMessage) (bool, error) { + bytes, err := json.Marshal(message.Data) + if err != nil { + return false, err + } + + var opts ctlconfig.ContextOptions + err = json.Unmarshal(bytes, &opts) + if err != nil { + return false, err + } + + err = opts.Validate() + if err != nil { + return false, err + } + + return ctlconfig.RunSetContext(&opts, client.Config, true) +} + +// SetEncryptionConfig wrapper function for CTL's RunSetEncryptionConfig, using a UI client +func SetEncryptionConfig(client *Client, message configs.WsMessage) (bool, error) { + bytes, err := json.Marshal(message.Data) + if err != nil { + return false, err + } + + var opts ctlconfig.EncryptionConfigOptions + err = json.Unmarshal(bytes, &opts) + if err != nil { + return false, err + } + + err = opts.Validate() + if err != nil { + return false, err + } + + return ctlconfig.RunSetEncryptionConfig(&opts, client.Config, true) +} + +// SetManagementConfig sets the specified management configuration with values +// received from the frontend client +// TODO(mfuller): there's currently no setter for this in the CTL config pkg +// so we'll set the values manually and then persist the config +func SetManagementConfig(client *Client, message configs.WsMessage) error { + bytes, err := json.Marshal(message.Data) + if err != nil { + return err + } + + if mCfg, found := client.Config.ManagementConfiguration[message.Name]; found { + err = json.Unmarshal(bytes, mCfg) + if err != nil { + return err + } + + err = client.Config.PersistConfig() + if err != nil { + return err + } + } else { + return fmt.Errorf("Management configuration '%s' not found", message.Name) + } + + return nil +} + +// SetManifest wrapper function for CTL's RunSetManifest, using a UI client +func SetManifest(client *Client, message configs.WsMessage) (bool, error) { + bytes, err := json.Marshal(message.Data) + if err != nil { + return false, err + } + + var opts ctlconfig.ManifestOptions + err = json.Unmarshal(bytes, &opts) + if err != nil { + return false, err + } + + log.Infof("Unmarshaled options: %+v", opts) + err = opts.Validate() + if err != nil { + return false, err + } + + return ctlconfig.RunSetManifest(&opts, client.Config, true) +} + +// UseContext wrapper function for CTL's RunUseConfig, using a UI client +func UseContext(client *Client, message configs.WsMessage) error { + return ctlconfig.RunUseContext(message.Name, client.Config) +} diff --git a/pkg/ctl/document.go b/pkg/ctl/document.go index 0f42cd7..6699ac1 100644 --- a/pkg/ctl/document.go +++ b/pkg/ctl/document.go @@ -62,7 +62,7 @@ func HandleDocumentRequest(user *string, request configs.WsMessage) configs.WsMe func (c *Client) docPull() (*string, error) { var message *string - cfgFactory := config.CreateFactory(AirshipConfigPath, KubeConfigPath) + cfgFactory := config.CreateFactory(AirshipConfigPath) // 2nd arg is noCheckout, I assume we want to checkout the repo, // so setting to false err := pull.Pull(cfgFactory, false) diff --git a/pkg/ctl/image.go b/pkg/ctl/image.go index 98301c3..c8d2b47 100644 --- a/pkg/ctl/image.go +++ b/pkg/ctl/image.go @@ -66,7 +66,7 @@ func HandleImageRequest(user *string, request configs.WsMessage) configs.WsMessa // generate iso now just runs a phase and not an individual command func (c *Client) generateIso() (*string, error) { - cfgFactory := config.CreateFactory(AirshipConfigPath, KubeConfigPath) + cfgFactory := config.CreateFactory(AirshipConfigPath) p := &phase.RunCommand{ Factory: cfgFactory, } diff --git a/pkg/ctl/tree.go b/pkg/ctl/tree.go index 66d8408..639f163 100644 --- a/pkg/ctl/tree.go +++ b/pkg/ctl/tree.go @@ -38,7 +38,7 @@ func getHelper() (ifc.Helper, error) { return helper, nil } - c, err := NewDefaultClient(AirshipConfigPath, KubeConfigPath) + c, err := NewDefaultClient(AirshipConfigPath) if err != nil { return nil, err } diff --git a/util/utiltest/utiltest.go b/util/utiltest/utiltest.go index 186a357..776b22a 100644 --- a/util/utiltest/utiltest.go +++ b/util/utiltest/utiltest.go @@ -75,7 +75,7 @@ func InitConfig(t *testing.T) (conf *config.Config, configPath string, require.NoError(t, err) conf = config.NewConfig() - err = conf.LoadConfig(configPath, kubeConfigPath, false) + err = conf.LoadConfig() require.NoError(t, err) return conf, configPath, kubeConfigPath, cleanup }