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
This commit is contained in:
Matthew Fuller 2020-10-21 19:56:40 +00:00
parent afb291a868
commit fab7bd9ef5
39 changed files with 1650 additions and 51 deletions

View File

@ -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;
}

View File

@ -0,0 +1,36 @@
<mat-card class="context-card">
<mat-card-header>
<mat-card-title>
<button mat-icon-button (click)="toggleLock()">
<mat-icon *ngIf="locked" class="grey-icon" svgIcon="lock"></mat-icon>
<mat-icon *ngIf="!locked" class="grey-icon" svgIcon="lock_open"></mat-icon>
</button>{{context.name}}
<button mat-raised-button (click)="useContext(context.name)">Use Context</button>
</mat-card-title>
</mat-card-header>
<mat-card-content>
<p>ContextKubeconf:
<mat-form-field appearance="fill">
<input [formControl]="contextKubeconf" matInput>
</mat-form-field>
</p>
<p>Manifest:
<mat-form-field appearance="fill">
<input [formControl]="manifest" matInput>
</mat-form-field>
</p>
<p>EncryptionConfig:
<mat-form-field appearance="fill">
<input [formControl]="encryptionConfig" matInput>
</mat-form-field>
</p>
<p>ManagementConfiguration:
<mat-form-field appearance="fill">
<input [formControl]="managementConfiguration" matInput>
</mat-form-field>
</p>
</mat-card-content>
<mat-card-actions>
<button mat-raised-button [disabled]="locked" (click)="setContext()" color="primary">Set</button>
</mat-card-actions>
</mat-card>

View File

@ -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<ConfigContextComponent>;
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();
});
});

View File

@ -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);
}
}

View File

@ -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 { }

View File

@ -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;
}

View File

@ -0,0 +1,39 @@
<mat-card class="encryption-config-card">
<mat-card-header>
<mat-card-title>
<button mat-icon-button (click)="toggleLock()">
<mat-icon *ngIf="locked" class="grey-icon" svgIcon="lock"></mat-icon>
<mat-icon *ngIf="!locked" class="grey-icon" svgIcon="lock_open"></mat-icon>
</button>{{config.name}}
</mat-card-title>
</mat-card-header>
<mat-card-content>
<div *ngIf="config.hasOwnProperty('encryptionKeyPath')">
<p>EncryptionKeyPath:
<mat-form-field appearance="fill">
<input matInput [formControl]="encryptionKeyPath">
</mat-form-field>
</p>
<p>DecryptionKeyPath:
<mat-form-field appearance="fill">
<input matInput [formControl]="decryptionKeyPath">
</mat-form-field>
</p>
</div>
<div *ngIf="config.hasOwnProperty('keySecretName')">
<p>KeySecretName:
<mat-form-field appearance="fill">
<input matInput [formControl]="keySecretName">
</mat-form-field>
</p>
<p>KeySecretNamespace:
<mat-form-field appearance="fill">
<input matInput [formControl]="keySecretNamespace">
</mat-form-field>
</p>
</div>
</mat-card-content>
<mat-card-actions>
<button mat-raised-button [disabled]="locked" (click)="setEncryptionConfig()" color="primary">Set</button>
</mat-card-actions>
</mat-card>

View File

@ -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<ConfigEncryptionComponent>;
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();
});
});

View File

@ -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();
}
}

View File

@ -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 { }

View File

@ -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;
}

View File

@ -0,0 +1,36 @@
<mat-card class="management-config-card">
<mat-card-header>
<mat-card-title>
<button mat-icon-button (click)="toggleLock()">
<mat-icon *ngIf="locked" class="grey-icon" svgIcon="lock"></mat-icon>
<mat-icon *ngIf="!locked" class="grey-icon" svgIcon="lock_open"></mat-icon>
</button>{{config.name}}
</mat-card-title>
</mat-card-header>
<mat-card-content>
<p>
<mat-checkbox [formControl]="insecure" labelPosition="before">Insecure: </mat-checkbox>
</p>
<p>SystemActionRetries:
<mat-form-field appearance="fill">
<input matInput [formControl]="systemActionRetries" pattern="[0-9]*">
</mat-form-field>
</p>
<p>SystemRebootDelay:
<mat-form-field appearance="fill">
<input matInput [formControl]="systemRebootDelay" pattern="[0-9]*">
</mat-form-field>
</p>
<p>Type:
<mat-form-field appearance="fill">
<input matInput [formControl]="type">
</mat-form-field>
</p>
<p>
<mat-checkbox [formControl]="useproxy" labelPosition="before">UseProxy: </mat-checkbox>
</p>
</mat-card-content>
<mat-card-actions>
<button mat-raised-button [disabled]="locked" (click)="setManagementConfig()" color="primary">Set</button>
</mat-card-actions>
</mat-card>

View File

@ -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<ConfigManagementComponent>;
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();
});
});

View File

@ -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();
}
}

View File

@ -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 { }

View File

@ -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;
}

View File

@ -0,0 +1,66 @@
<mat-card class="manifest-config-card">
<mat-card-header>
<mat-card-title>
<button mat-icon-button (click)="toggleLock()">
<mat-icon *ngIf="locked" class="grey-icon" svgIcon="lock"></mat-icon>
<mat-icon *ngIf="!locked" class="grey-icon" svgIcon="lock_open"></mat-icon>
</button>{{manifest.name}}
</mat-card-title>
</mat-card-header>
<mat-card-content>
<p>RepoName:
<mat-form-field appearance="fill">
<input matInput [formControl]="RepoName">
</mat-form-field>
</p>
<p>URL:
<mat-form-field appearance="fill">
<input matInput [formControl]="URL">
</mat-form-field>
</p>
<p>Branch:
<mat-form-field appearance="fill">
<input matInput [formControl]="Branch">
</mat-form-field>
</p>
<p>CommitHash:
<mat-form-field appearance="fill">
<input matInput [formControl]="CommitHash">
</mat-form-field>
</p>
<p>Tag:
<mat-form-field appearance="fill">
<input matInput [formControl]="Tag">
</mat-form-field>
</p>
<p>RemoteRef:
<mat-form-field appearance="fill">
<input matInput [formControl]="RemoteRef">
</mat-form-field>
</p>
<p>
<mat-checkbox [formControl]="Force" labelPosition="before">Force: </mat-checkbox>
</p>
<p>
<mat-checkbox [formControl]="IsPhase" labelPosition="before">IsPhase: </mat-checkbox>
</p>
<p>SubPath:
<mat-form-field appearance="fill">
<input matInput [formControl]="SubPath">
</mat-form-field>
</p>
<p>TargetPath:
<mat-form-field appearance="fill">
<input matInput [formControl]="TargetPath">
</mat-form-field>
</p>
<p>MetadataPath:
<mat-form-field appearance="fill">
<input matInput [formControl]="MetadataPath">
</mat-form-field>
</p>
</mat-card-content>
<mat-card-actions>
<button mat-raised-button [disabled]="locked" (click)="setManifest()" color="primary">Set</button>
</mat-card-actions>
</mat-card>

View File

@ -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<ConfigManifestComponent>;
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();
});
});

View File

@ -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();
}
}

View File

@ -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 { }

View File

@ -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;
}

View File

@ -1 +1,26 @@
<h1>Config component</h1>
<h1>Airship Configuration Operations</h1>
<mat-divider></mat-divider>
<div class="config-container">
<h3>Contexts - Current Context: {{currentContext}}</h3>
<div class="contexts" *ngFor="let context of contexts">
<app-config-context [context]="context"></app-config-context>
</div>
<mat-divider></mat-divider>
<h3>Manifests</h3>
<div class="manifests" *ngFor="let manifest of manifests">
<app-config-manifest [manifest]="manifest"></app-config-manifest>
</div>
<mat-divider></mat-divider>
<h3>Encryption Configurations</h3>
<div class="encryption-configs" *ngFor="let config of encryptionConfigs">
<app-config-encryption [config]="config"></app-config-encryption>
</div>
<mat-divider></mat-divider>
<h3>Management Configurations</h3>
<div class="management-configs" *ngFor="let config of managementConfigs">
<app-config-management [config]="config"></app-config-management>
</div>
</div>

View File

@ -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();
}));

View File

@ -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<void> {
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')
);
}
}

View File

@ -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;
}

View File

@ -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 { }

View File

@ -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,

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z"/></svg>

After

Width:  |  Height:  |  Size: 363 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 17c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm6-9h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6h1.9c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm0 12H6V10h12v10z"/></svg>

After

Width:  |  Height:  |  Size: 369 B

View File

@ -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'
}

4
go.mod
View File

@ -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
)

76
go.sum
View File

@ -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=

View File

@ -137,15 +137,17 @@ const (
Status WsSubComponentType = "status"
// ctl config subcomponets
GetContext WsSubComponentType = "getContext"
GetEncryptionConfig WsSubComponentType = "getEncryptionConfig"
GetManagementConfig WsSubComponentType = "getManagementConfig"
GetManifest WsSubComponentType = "getManifest"
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"

View File

@ -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
}

View File

@ -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)
}

View File

@ -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)

View File

@ -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,
}

View File

@ -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
}

View File

@ -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
}