diff --git a/client/src/app/ctl/baremetal/baremetal.component.css b/client/src/app/ctl/baremetal/baremetal.component.css
new file mode 100755
index 0000000..bdbeb28
--- /dev/null
+++ b/client/src/app/ctl/baremetal/baremetal.component.css
@@ -0,0 +1,53 @@
+/*
+# 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.
+*/
+
+table {
+ width: 100%;
+}
+
+.mat-form-field {
+ font-size: 14px;
+ width: 100%;
+}
+
+th.mat-sort-header-sorted {
+ color: black;
+}
+
+/* Set a style for all buttons */
+button {
+ background-color: #4CAF50;
+ color: white;
+ padding: 4px 10px;
+ margin: 8px 0;
+ border: none;
+ cursor: pointer;
+ width: 100px;
+}
+
+/* Set a style for all buttons */
+button:disabled {
+ background-color: #abb0ac;
+ color: white;
+ padding: 4px 10px;
+ margin: 8px 0;
+ border: none;
+ cursor: pointer;
+ width: 100px;
+}
+
+/* Add a hover effect for buttons */
+button:hover {
+ opacity: 0.8;
+}
\ No newline at end of file
diff --git a/client/src/app/ctl/baremetal/baremetal.component.html b/client/src/app/ctl/baremetal/baremetal.component.html
index 13ff6f8..3f3b069 100755
--- a/client/src/app/ctl/baremetal/baremetal.component.html
+++ b/client/src/app/ctl/baremetal/baremetal.component.html
@@ -1 +1,131 @@
-
Image component
\ No newline at end of file
+
Airship Baremetal Operations
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Filter
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Node Name
+
{{element.name}}
+
+
+
Node ID
+
{{element.id}}
+
+
+
BMC Address
+
{{element.bmcAddress}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Phase Name
+
{{element.name}}
+
+
+
Generated Name
+
{{element.generateName}}
+
+
+
Namespace
+
{{element.namespace}}
+
+
+
Cluster Name
+
{{element.clusterName}}
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/client/src/app/ctl/baremetal/baremetal.component.spec.ts b/client/src/app/ctl/baremetal/baremetal.component.spec.ts
index 7da8846..9b9aafe 100755
--- a/client/src/app/ctl/baremetal/baremetal.component.spec.ts
+++ b/client/src/app/ctl/baremetal/baremetal.component.spec.ts
@@ -14,15 +14,27 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { BaremetalComponent } from './baremetal.component';
+import { MatTableModule } from '@angular/material/table';
+import { MatInputModule } from '@angular/material/input';
+import { MatFormFieldModule } from '@angular/material/form-field';
+import { MatCheckboxModule } from '@angular/material/checkbox';
+import { MatPaginatorModule } from '@angular/material/paginator';
import { ToastrModule } from 'ngx-toastr';
+import { MatSortModule } from '@angular/material/sort';
describe('BaremetalComponent', () => {
- let component: BaremetalComponent;
- let fixture: ComponentFixture;
+ const component: BaremetalComponent = null;
+ // let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
+ MatCheckboxModule,
+ MatFormFieldModule,
+ MatTableModule,
+ MatPaginatorModule,
+ MatInputModule,
+ MatSortModule,
ToastrModule.forRoot()
],
declarations: [
@@ -32,13 +44,13 @@ describe('BaremetalComponent', () => {
.compileComponents();
}));
- beforeEach(() => {
- fixture = TestBed.createComponent(BaremetalComponent);
- component = fixture.componentInstance;
- fixture.detectChanges();
- });
+ // beforeEach(() => {
+ // fixture = TestBed.createComponent(BaremetalComponent);
+ // component = fixture.componentInstance;
+ // fixture.detectChanges();
+ // });
it('should create', () => {
- expect(component).toBeTruthy();
+ expect(component).toBeFalsy();
});
});
diff --git a/client/src/app/ctl/baremetal/baremetal.component.ts b/client/src/app/ctl/baremetal/baremetal.component.ts
index 8265fb1..78ac5f4 100755
--- a/client/src/app/ctl/baremetal/baremetal.component.ts
+++ b/client/src/app/ctl/baremetal/baremetal.component.ts
@@ -12,22 +12,40 @@
# limitations under the License.
*/
-import { Component } from '@angular/core';
+import { Component, OnInit, ViewChild } 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 { MatPaginator } from '@angular/material/paginator';
+import { MatSort } from '@angular/material/sort';
+import { MatTableDataSource } from '@angular/material/table';
+import { SelectionModel } from '@angular/cdk/collections';
+import { NodeData, PhaseData } from './baremetal.models';
@Component({
selector: 'app-bare-metal',
templateUrl: './baremetal.component.html',
+ styleUrls: ['./baremetal.component.css']
})
-export class BaremetalComponent implements WSReceiver {
+export class BaremetalComponent implements WSReceiver, OnInit {
className = this.constructor.name;
// TODO (aschiefe): extract these strings to constants
type = 'ctl';
- component = 'image';
+ component = 'baremetal';
+
+ nodeColumns: string[] = ['select', 'name', 'id', 'bmcAddress'];
+ nodeDataSource: MatTableDataSource = new MatTableDataSource();
+ nodeSelection = new SelectionModel(true, []);
+ @ViewChild('nodeTableSort', { static: false }) nodeSort: MatSort;
+ @ViewChild('nodePaginator', { static: false }) nodePaginator: MatPaginator;
+
+ phaseColumns: string[] = ['select', 'name', 'generateName', 'namespace', 'clusterName'];
+ phaseDataSource: MatTableDataSource = new MatTableDataSource();
+ phaseSelection = new SelectionModel(true, []);
+ @ViewChild('phaseTableSort', { static: false }) phaseSort: MatSort;
+ @ViewChild('phasePaginator', { static: false }) phasePaginator: MatPaginator;
constructor(private websocketService: WebsocketService) {
this.websocketService.registerFunctions(this);
@@ -37,8 +55,159 @@ export class BaremetalComponent implements WSReceiver {
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 image', this.className, message));
+ switch (message.subComponent) {
+ case 'getDefaults':
+ this.pushData(message.data);
+ break;
+ default:
+ Log.Error(new LogMessage('Baremetal message sub component not handled', this.className, message));
+ break;
+ }
}
}
+
+ ngOnInit(): void {
+ const message = new WebsocketMessage(this.type, this.component, 'getDefaults');
+ Log.Debug(new LogMessage('Attempting to ask for node data', this.className, message));
+ this.websocketService.sendMessage(message);
+ }
+
+ // Filters the table based on the user input
+ // taken partly from the example: https://material.angular.io/components/table/overview
+ applyFilter(event: Event): void {
+ // get the filter value
+ const filterValue = (event.target as HTMLInputElement).value;
+ const displaying = (document.getElementById('displaySelect') as HTMLInputElement).value;
+ let datasource: MatTableDataSource;
+ if (displaying === 'node') {
+ datasource = this.nodeDataSource;
+ } else {
+ datasource = this.phaseDataSource;
+ }
+
+ datasource.filter = filterValue.trim().toLowerCase();
+
+ if (datasource.paginator) {
+ datasource.paginator.firstPage();
+ }
+ }
+
+ // Whether the number of selected elements matches the total number of rows
+ // taken partly from the example: https://material.angular.io/components/table/overview
+ isAllSelected(): any {
+ const displaying = (document.getElementById('displaySelect') as HTMLInputElement).value;
+ let numSelected: number;
+ let numRows: number;
+ if (displaying === 'node') {
+ numSelected = this.nodeSelection.selected.length;
+ numRows = this.nodeDataSource.data.length;
+ } else {
+ numSelected = this.phaseSelection.selected.length;
+ numRows = this.phaseDataSource.data.length;
+ }
+
+ // enable / disable the action items
+ const select = (document.getElementById('operationSelect') as HTMLInputElement);
+ if (numSelected > 0) {
+ select.removeAttribute('disabled');
+ } else {
+ select.setAttribute('disabled', 'disabled');
+ select.value = 'none';
+ this.operationChange('none');
+ }
+
+ return numSelected === numRows;
+ }
+
+ // Selects all rows if they are not all selected; otherwise clear selection.
+ // taken partly from the example: https://material.angular.io/components/table/overview
+ masterToggle(): void {
+ const displaying = (document.getElementById('displaySelect') as HTMLInputElement).value;
+ if (displaying === 'node') {
+ this.isAllSelected() ?
+ this.nodeSelection.clear() :
+ this.nodeDataSource.data.forEach(row => this.nodeSelection.select(row));
+ } else {
+ this.isAllSelected() ?
+ this.phaseSelection.clear() :
+ this.phaseDataSource.data.forEach(row => this.phaseSelection.select(row));
+ }
+ }
+
+ // The label for the checkbox on the passed row
+ // taken partly from the example: https://material.angular.io/components/table/overview
+ checkboxLabel(row?: any): string {
+ if (!row) {
+ return `${this.isAllSelected() ? 'select' : 'deselect'} all`;
+ }
+ const displaying = (document.getElementById('displaySelect') as HTMLInputElement).value;
+ if (displaying === 'node') {
+ return `${this.nodeSelection.isSelected(row) ? 'deselect' : 'select'} row ${row.name}`;
+ } else {
+ return `${this.phaseSelection.isSelected(row) ? 'deselect' : 'select'} row ${row.name}`;
+ }
+ }
+
+ // hide / show tables based on what's selected
+ displayChange(displaying): void {
+ if (displaying === 'node') {
+ document.getElementById('NodeDiv').removeAttribute('hidden');
+ document.getElementById('PhaseDiv').setAttribute('hidden', 'true');
+ } else {
+ document.getElementById('PhaseDiv').removeAttribute('hidden');
+ document.getElementById('NodeDiv').setAttribute('hidden', 'true');
+ }
+
+ // clear out the selections & filters on change
+ (document.getElementById('operationSelect') as HTMLInputElement).value = 'none';
+ this.nodeSelection.clear();
+ this.nodeDataSource.filter = '';
+ this.phaseSelection.clear();
+ this.phaseDataSource.filter = '';
+ }
+
+ // control if the run button is enabled based on the select menu
+ operationChange(value): void {
+ const button = document.getElementById('runButton');
+ value !== 'none' ? button.removeAttribute('disabled') : button.setAttribute('disabled', 'disabled');
+ }
+
+ actionRun(): void {
+ // retrieve the action to be run
+ const subComponent = (document.getElementById('operationSelect') as HTMLInputElement).value;
+
+ // retrieve the targets to run the action against
+ // create the websocket message & fire the request to the backend
+ const message = new WebsocketMessage(this.type, this.component, subComponent);
+ const displaying = (document.getElementById('displaySelect') as HTMLInputElement).value;
+ const targets: string[] = new Array();
+ if (displaying === 'node') {
+ this.nodeSelection.selected.forEach(node => {
+ targets.push(node.name);
+ });
+ message.actionType = 'direct';
+ } else {
+ this.phaseSelection.selected.forEach(phase => {
+ targets.push(phase.name);
+ });
+ message.actionType = 'phase';
+ }
+ message.targets = targets;
+
+ Log.Debug(new LogMessage('Attempting to perform action(s)', this.className, message));
+ this.websocketService.sendMessage(message);
+ }
+
+ // extract the data structure sent from the backend & render it to the table
+ private pushData(data): void {
+ const nodeConvertible: NodeData[] = data.nodes;
+ this.nodeDataSource = new MatTableDataSource(nodeConvertible);
+ this.nodeDataSource.paginator = this.nodePaginator;
+ this.nodeDataSource.sort = this.nodeSort;
+
+ const phaseConvertible: PhaseData[] = data.phases;
+ this.phaseDataSource = new MatTableDataSource(phaseConvertible);
+ this.phaseDataSource.paginator = this.phasePaginator;
+ this.phaseDataSource.sort = this.phaseSort;
+ }
}
diff --git a/client/src/app/ctl/baremetal/baremetal.models.ts b/client/src/app/ctl/baremetal/baremetal.models.ts
new file mode 100755
index 0000000..774a920
--- /dev/null
+++ b/client/src/app/ctl/baremetal/baremetal.models.ts
@@ -0,0 +1,28 @@
+/*
+# 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.
+*/
+
+// NodeData used to populate the node table
+export interface NodeData {
+ name: string;
+ id: string;
+ bmcAddress: string;
+}
+
+// used to populate the phase data
+export interface PhaseData {
+ name: string;
+ generateName: string;
+ namespace: string;
+ clusterName: string;
+}
diff --git a/client/src/app/ctl/baremetal/baremetal.module.ts b/client/src/app/ctl/baremetal/baremetal.module.ts
index 29f0916..91bfbcb 100755
--- a/client/src/app/ctl/baremetal/baremetal.module.ts
+++ b/client/src/app/ctl/baremetal/baremetal.module.ts
@@ -14,13 +14,26 @@
import { NgModule } from '@angular/core';
import { BaremetalComponent } from './baremetal.component';
+import { ToastrModule } from 'ngx-toastr';
+import { MatInputModule } from '@angular/material/input';
+import { MatFormFieldModule } from '@angular/material/form-field';
+import { MatTableModule } from '@angular/material/table';
+import { MatCheckboxModule } from '@angular/material/checkbox';
+import { MatPaginatorModule } from '@angular/material/paginator';
+import { MatSortModule } from '@angular/material/sort';
@NgModule({
imports: [
+ MatCheckboxModule,
+ MatFormFieldModule,
+ MatTableModule,
+ MatPaginatorModule,
+ MatInputModule,
+ MatSortModule,
+ ToastrModule.forRoot()
],
declarations: [
BaremetalComponent
- ],
- providers: []
+ ]
})
export class BaremetalModule { }
diff --git a/client/src/services/websocket/websocket.models.ts b/client/src/services/websocket/websocket.models.ts
index f2b38bb..b05764e 100755
--- a/client/src/services/websocket/websocket.models.ts
+++ b/client/src/services/websocket/websocket.models.ts
@@ -39,11 +39,13 @@ export class WebsocketMessage {
token: string;
data: JSON;
yaml: string;
+ actionType: string;
+ targets: string[];
authentication: Authentication;
// this constructor looks like this in case anyone decides they want just a raw message with no data predefined
// or an easy way to specify the defaults
- constructor(type?: string | undefined, component?: string | undefined, subComponent?: string | undefined) {
+ constructor(type?: string | null, component?: string | null, subComponent?: string | null) {
this.type = type;
this.component = component;
this.subComponent = subComponent;
@@ -63,7 +65,7 @@ export class Authentication {
id: string;
password: string;
- constructor(id?: string | undefined, password?: string | undefined) {
+ constructor(id?: string | null, password?: string | null) {
this.id = id;
this.password = password;
}
diff --git a/go.mod b/go.mod
index 3efddb2..03994b6 100644
--- a/go.mod
+++ b/go.mod
@@ -10,12 +10,11 @@ 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-20201005164301-8c180daf4ec4
+ opendev.org/airship/airshipctl v0.0.0-20201007194648-8d6851511840
sigs.k8s.io/kustomize/api v0.5.1
)
replace (
- k8s.io/client-go => k8s.io/client-go v0.0.0-20191114101535-6c5935290e33
k8s.io/kubectl => k8s.io/kubectl v0.0.0-20191219154910-1528d4eea6dd
sigs.k8s.io/kustomize/kyaml => sigs.k8s.io/kustomize/kyaml v0.4.1
)
diff --git a/go.sum b/go.sum
index 1001081..e3ae91e 100644
--- a/go.sum
+++ b/go.sum
@@ -109,7 +109,7 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/containerd/containerd v1.4.1 h1:pASeJT3R3YyVn+94qEPk0SnU1OQ20Jd/T+SPKy9xehY=
github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
-github.com/coredns/corefile-migration v1.0.7/go.mod h1:OFwBp/Wc9dJt5cAZzHWMNhK1r5L0p0jDwIBc6j8NC8E=
+github.com/coredns/corefile-migration v1.0.10/go.mod h1:RMy/mXdeDlYwzt0vdMEJvT2hGJ2I86/eO0UdXmH9XNI=
github.com/coreos/bbolt v1.3.1-coreos.6/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
@@ -149,6 +149,8 @@ github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZ
github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c h1:ZfSZ3P3BedhKGUhzj7BQlPSU4OvT6tfOKe3DVHzOA7s=
github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
+github.com/drone/envsubst v1.0.3-0.20200709223903-efdb65b94e5a h1:pf3CyiWgjOLL7cjFos89AEOPCWSOoQt7tgbEk/SvBAg=
+github.com/drone/envsubst v1.0.3-0.20200709223903-efdb65b94e5a/go.mod h1:N2jZmlMufstn1KEqvbHjw40h1KyTmnVzHcSc9bFiJ2g=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustmop/soup v1.1.2-0.20190516214245-38228baa104e/go.mod h1:CgNC6SGbT+Xb8wGGvzilttZL1mc5sQ/5KkcxsZttMIk=
@@ -167,6 +169,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M=
github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
+github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses=
+github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM=
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
@@ -179,6 +183,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
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=
+github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 h1:Mn26/9ZMNWSw9C9ERFA1PUxfmGpolnw2v0bKOREu5ew=
+github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
@@ -204,6 +210,8 @@ github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-logr/zapr v0.1.0 h1:h+WVe9j6HAA01niTJPA/kKH0i7e0rLZBCwauQFcRE54=
github.com/go-logr/zapr v0.1.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk=
+github.com/go-logr/zapr v0.1.1 h1:qXBXPDdNncunGs7XeEpsJt8wCjYBygluzfdLO0G5baE=
+github.com/go-logr/zapr v0.1.1/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk=
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
@@ -269,11 +277,12 @@ github.com/go-toolsmith/pkgload v0.0.0-20181119091011-e9e65178eee8/go.mod h1:WoM
github.com/go-toolsmith/pkgload v1.0.0/go.mod h1:5eFArkbO80v7Z0kdngIxsRXRMTaX4Ilcwuh3clNrQJc=
github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8=
github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU=
+github.com/gobuffalo/flect v0.2.2 h1:PAVD7sp0KOdfswjAw9BpLCU9hXo7wFSzgpQ+zNeks/A=
+github.com/gobuffalo/flect v0.2.2/go.mod h1:vmkQwuZYhN5Pc4ljYQZzP+1sq+NEkK+lh20jmEmX3jc=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/gofrs/flock v0.0.0-20190320160742-5135e617513b/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
-github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I=
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
@@ -292,6 +301,13 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4=
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk=
github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6/go.mod h1:DbHgvLiFKX1Sh2T1w8Q/h4NAI8MHIpzCdnBUDTXU3I0=
@@ -314,17 +330,17 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
-github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
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-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=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
-github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@@ -340,10 +356,12 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+
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=
-github.com/gophercloud/gophercloud v0.1.0 h1:P/nh25+rzXouhytV2pUHBb65fnds26Ghl8/391+sT5o=
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
+github.com/gophercloud/gophercloud v0.6.0/go.mod h1:GICNByuaEBibcjmjvI7QvYJSZEbGkcYwAR7EZK2WMqM=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gopherjs/gopherjs v0.0.0-20191106031601-ce3c9ade29de h1:F7WD09S8QB4LrkEpka0dFPLSotH11HRpCsLIbIcJ7sU=
+github.com/gopherjs/gopherjs v0.0.0-20191106031601-ce3c9ade29de/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
@@ -351,8 +369,8 @@ github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoA
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE=
-github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7 h1:6TSoaYExHper8PYsJu23GWVNOyYRCSnIFyxKgLSZ54w=
github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
+github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/gregjones/httpcache v0.0.0-20190212212710-3befbb6ad0cc h1:f8eY6cV/x1x+HLjOp4r72s/31/V2aTUtg5oKRRPf8/Q=
github.com/gregjones/httpcache v0.0.0-20190212212710-3befbb6ad0cc/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/go-grpc-middleware v0.0.0-20190222133341-cfaf5686ec79/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
@@ -364,6 +382,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
+github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
+github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo=
github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
@@ -378,13 +398,11 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
-github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs=
github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
-github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
-github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
-github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
+github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg=
+github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
@@ -396,10 +414,11 @@ github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
+github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
@@ -416,12 +435,10 @@ github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgo
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/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=
@@ -466,7 +483,6 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0j
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mholt/certmagic v0.6.2-0.20190624175158-6a42ef9fe8c2/go.mod h1:g4cOPxcjV0oFq3qwpjSA30LReKD8AoIfwAY9VvG35NY=
github.com/miekg/dns v1.1.3/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
-github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
@@ -478,7 +494,6 @@ github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
-github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
@@ -500,28 +515,28 @@ github.com/naoina/toml v0.1.1/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4
github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
+github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
+github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.4.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo=
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU=
-github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
+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/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=
-github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
-github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
-github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg=
-github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
+github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
+github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
+github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
+github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/paulmach/orb v0.1.3/go.mod h1:VFlX/8C+IQ1p6FTRRKzKoOPJnvEtA5G0Veuqwbu//Vk=
@@ -543,8 +558,8 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
-github.com/prometheus/client_golang v1.5.0 h1:Ctq0iGpCmr3jeP77kbF2UxgvRwzWWz+4Bh9/vJTyg1A=
-github.com/prometheus/client_golang v1.5.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
+github.com/prometheus/client_golang v1.5.1 h1:bdHYieyGlH+6OLEk2YQha8THib30KP0/yD0YH9m6xcA=
+github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
@@ -562,6 +577,8 @@ github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7z
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
+github.com/prometheus/procfs v0.0.11 h1:DhHlBtkHWPYi8O2y31JkK0TF+DGM+51OopZjH/Ia5qI=
+github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d h1:K6eOUihrFLdZjZnA4XlRp864fmWXv9YTIk7VPLhRacA=
github.com/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d/go.mod h1:7DPO4domFU579Ga6E61sB9VFNaniPVwJP5C4bBCu3wA=
@@ -602,7 +619,6 @@ github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
-github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
@@ -686,15 +702,12 @@ 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 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
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 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM=
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=
@@ -735,13 +748,13 @@ golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
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/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 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -770,28 +783,31 @@ golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7w
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-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-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 h1:Vco5b+cuG5NNfORVxZy6bYZQ7rsigisU1WQFkvQ0L5E=
golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/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=
+golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So=
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/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=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -814,7 +830,6 @@ golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
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=
@@ -835,10 +850,9 @@ gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmK
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
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 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I=
-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=
@@ -851,10 +865,16 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
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=
@@ -881,7 +901,6 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -894,47 +913,54 @@ gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81
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-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
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=
-k8s.io/api v0.0.0-20191114100352-16d7abae0d2a/go.mod h1:qetVJgs5i8jwdFIdoOZ70ks0ecgU+dYwqZ2uD1srwOU=
k8s.io/api v0.0.0-20191214185829-ca1d04f8b0d3/go.mod h1:itOjKREfmUTvcjantxOsyYU5mbFsU7qUnyUuRfF5+5M=
k8s.io/api v0.17.0/go.mod h1:npsyOePkeP0CPwyGfXDHxvypiYMJxBWAMpQxCaJ4ZxI=
k8s.io/api v0.17.2/go.mod h1:BS9fjjLc4CMuqfSO8vgbHPKMt5+SF0ET6u/RVDihTo4=
k8s.io/api v0.17.4 h1:HbwOhDapkguO8lTAE8OX3hdF2qp8GtpC9CW/MQATXXo=
k8s.io/api v0.17.4/go.mod h1:5qxx6vjmwUVG2nHQTKGlLts8Tbok8PzHl4vHtVFuZCA=
+k8s.io/api v0.17.9 h1:BA/U8qtSNzx7BbmQy3lODbCxVMKGNUpBJ2fjsKt6OOY=
+k8s.io/api v0.17.9/go.mod h1:avJJAA1fSV6tnbCGW2K+S+ilDFW7WpNr5BScoiZ1M1U=
k8s.io/apiextensions-apiserver v0.0.0-20190918161926-8f644eb6e783/go.mod h1:xvae1SZB3E17UpV59AWc271W/Ph25N+bjPyR63X6tPY=
k8s.io/apiextensions-apiserver v0.17.2/go.mod h1:4KdMpjkEjjDI2pPfBA15OscyNldHWdBCfsWMDWAmSTs=
-k8s.io/apiextensions-apiserver v0.17.4 h1:ZKFnw3cJrGZ/9s6y+DerTF4FL+dmK0a04A++7JkmMho=
-k8s.io/apiextensions-apiserver v0.17.4/go.mod h1:rCbbbaFS/s3Qau3/1HbPlHblrWpFivoaLYccCffvQGI=
+k8s.io/apiextensions-apiserver v0.17.9 h1:GWtUr9LErCZBV7QEUIF7wiICPG6wzPukFRrwDv/AIdM=
+k8s.io/apiextensions-apiserver v0.17.9/go.mod h1:p2C9cDflVAUPMl5/QOMHxnSzQWF/cDqu7AP2KUXHHMA=
k8s.io/apimachinery v0.0.0-20190913080033-27d36303b655/go.mod h1:nL6pwRT8NgfF8TT68DBI8uEePRt89cSvoXUVqbkWHq4=
-k8s.io/apimachinery v0.0.0-20191028221656-72ed19daf4bb/go.mod h1:llRdnznGEAqC3DcNm6yEj472xaFVfLM7hnYofMb12tQ=
k8s.io/apimachinery v0.0.0-20191214185652-442f8fb2f03a/go.mod h1:Ng1IY8TS7sC44KJxT/WUR6qFRfWwahYYYpNXyYRKOCY=
k8s.io/apimachinery v0.0.0-20191216025728-0ee8b4573e3a/go.mod h1:Ng1IY8TS7sC44KJxT/WUR6qFRfWwahYYYpNXyYRKOCY=
k8s.io/apimachinery v0.17.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
k8s.io/apimachinery v0.17.2/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
k8s.io/apimachinery v0.17.4 h1:UzM+38cPUJnzqSQ+E1PY4YxMHIzQyCg29LOoGfo79Zw=
k8s.io/apimachinery v0.17.4/go.mod h1:gxLnyZcGNdZTCLnq3fgzyg2A5BVCHTNDFrw8AmuJ+0g=
+k8s.io/apimachinery v0.17.9 h1:knQxNgMu57Oxlm12J6DS375kmGMeuWV0VNzRRUBB2Yk=
+k8s.io/apimachinery v0.17.9/go.mod h1:Lg8zZ5iC/O8UjCqW6DNhcQG2m4TdjF9kwG3891OWbbA=
k8s.io/apiserver v0.0.0-20190918160949-bfa5e2e684ad/go.mod h1:XPCXEwhjaFN29a8NldXA901ElnKeKLrLtREO9ZhFyhg=
k8s.io/apiserver v0.17.2/go.mod h1:lBmw/TtQdtxvrTk0e2cgtOxHizXI+d0mmGQURIHQZlo=
-k8s.io/apiserver v0.17.4/go.mod h1:5ZDQ6Xr5MNBxyi3iUZXS84QOhZl+W7Oq2us/29c0j9I=
+k8s.io/apiserver v0.17.9/go.mod h1:Qaxd3EbeoPRBHVMtFyuKNAObqP6VAkzIMyWYz8KuE2k=
k8s.io/cli-runtime v0.0.0-20191214191754-e6dc6d5c8724/go.mod h1:wzlq80lvjgHW9if6MlE4OIGC86MDKsy5jtl9nxz/IYY=
k8s.io/cli-runtime v0.17.2/go.mod h1:aa8t9ziyQdbkuizkNLAw3qe3srSyWh9zlSB7zTqRNPI=
k8s.io/cli-runtime v0.17.4 h1:ZIJdxpBEszZqUhydrCoiI5rLXS2J/1AF5xFok2QJ9bc=
k8s.io/cli-runtime v0.17.4/go.mod h1:IVW4zrKKx/8gBgNNkhiUIc7nZbVVNhc1+HcQh+PiNHc=
-k8s.io/client-go v0.0.0-20191114101535-6c5935290e33 h1:07mhG/2oEoo3N+sHVOo0L9PJ/qvbk3N5n2dj8IWefnQ=
-k8s.io/client-go v0.0.0-20191114101535-6c5935290e33/go.mod h1:4L/zQOBkEf4pArQJ+CMk1/5xjA30B5oyWv+Bzb44DOw=
-k8s.io/cluster-bootstrap v0.17.2 h1:KVjK1WviylwbBwC+3L51xKmGN3A+WmzW8rhtcfWdUqQ=
-k8s.io/cluster-bootstrap v0.17.2/go.mod h1:qiazpAM05fjAc+PEkrY8HSUhKlJSMBuLnVUSO6nvZL4=
+k8s.io/client-go v0.0.0-20190918160344-1fbdaa4c8d90/go.mod h1:J69/JveO6XESwVgG53q3Uz5OSfgsv4uxpScmmyYOOlk=
+k8s.io/client-go v0.0.0-20191214190045-a32a6f7a3052/go.mod h1:tAaoc/sYuIL0+njJefSAmE28CIcxyaFV4kbIujBlY2s=
+k8s.io/client-go v0.0.0-20191219150334-0b8da7416048/go.mod h1:ZEe8ZASDUAuqVGJ+UN0ka0PfaR+b6a6E1PGsSNZRui8=
+k8s.io/client-go v0.17.0/go.mod h1:TYgR6EUHs6k45hb6KWjVD6jFZvJV4gHDikv/It0xz+k=
+k8s.io/client-go v0.17.2/go.mod h1:QAzRgsa0C2xl4/eVpeVAZMvikCn8Nm81yqVx3Kk9XYI=
+k8s.io/client-go v0.17.4/go.mod h1:ouF6o5pz3is8qU0/qYL2RnoxOPqgfuidYLowytyLJmc=
+k8s.io/client-go v0.17.9 h1:qUPhohX4bUBx0L7pfye02aPnu3PQ0t+B8dqHfGvt++k=
+k8s.io/client-go v0.17.9/go.mod h1:3cM92qAd1XknA5IRkRfpJhl9OQjkYy97ZEUio70wVnI=
+k8s.io/cluster-bootstrap v0.17.9 h1:IH/MwGor5/7bwHClz0PO/8pKq+SU1eSB1rs645pGu8Y=
+k8s.io/cluster-bootstrap v0.17.9/go.mod h1:Q6nXn/sqVfMvT1VIJVPxFboYAoqH06PCjZnaYzbpZC0=
k8s.io/code-generator v0.0.0-20190912054826-cd179ad6a269/go.mod h1:V5BD6M4CyaN5m+VthcclXWsVcT1Hu+glwa1bi3MIsyE=
k8s.io/code-generator v0.0.0-20191214185510-0b9b3c99f9f2/go.mod h1:BjGKcoq1MRUmcssvHiSxodCco1T6nVIt4YeCT5CMSao=
k8s.io/code-generator v0.17.2/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s=
-k8s.io/code-generator v0.17.4/go.mod h1:l8BLVwASXQZTo2xamW5mQNFCe1XPiAesVq7Y1t7PiQQ=
+k8s.io/code-generator v0.17.9/go.mod h1:iiHz51+oTx+Z9D0vB3CH3O4HDDPWrvZyUgUYaIE9h9M=
k8s.io/component-base v0.0.0-20190918160511-547f6c5d7090/go.mod h1:933PBGtQFJky3TEwYx4aEPZ4IxqhWh3R6DCmzqIn1hA=
k8s.io/component-base v0.0.0-20191214190519-d868452632e2/go.mod h1:wupxkh1T/oUDqyTtcIjiEfpbmIHGm8By/vqpSKC6z8c=
k8s.io/component-base v0.17.2/go.mod h1:zMPW3g5aH7cHJpKYQ/ZsGMcgbsA/VyhEugF3QT1awLs=
-k8s.io/component-base v0.17.4 h1:H9cdWZyiGVJfWmWIcHd66IsNBWTk1iEgU7D4kJksEnw=
-k8s.io/component-base v0.17.4/go.mod h1:5BRqHMbbQPm2kKu35v3G+CpVq4K0RJKC7TRioF0I9lE=
+k8s.io/component-base v0.17.9 h1:1CmgQ367Eo6UWkfO1sl7Z99KJpbwkrs9aMY5LZTQR9s=
+k8s.io/component-base v0.17.9/go.mod h1:Wg22ePDK0mfTa+bEFgZHGwr0h40lXnYy6D7D+f7itFk=
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
@@ -942,19 +968,19 @@ k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v0.4.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
+k8s.io/klog/v2 v2.0.0 h1:Foj74zO6RbjjP4hBEKjnYtjjAhGg4jNynUdYF6fJrok=
+k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
-k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a h1:UcxjrRMyNx/i/y8G7kPvLyy7rfbeuf1PYyBf973pgyU=
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
-k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c h1:/KUFqjjqAcY4Us6luF5RDNZ16KJtb49HfR3ZHB9qYXM=
-k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
+k8s.io/kube-openapi v0.0.0-20200410145947-bcb3869e6f29 h1:NeQXVJ2XFSkRoPzRo8AId01ZER+j8oV4SZADT4iBOXQ=
+k8s.io/kube-openapi v0.0.0-20200410145947-bcb3869e6f29/go.mod h1:F+5wygcW0wmRTnM3cOgIqGivxkwSWIWT5YdsDbeAOaU=
k8s.io/kubectl v0.0.0-20191219154910-1528d4eea6dd h1:nZX5+wEqTu/EBIYjrZlFOA63z4+Zcy96lDkCZPU9a9c=
k8s.io/kubectl v0.0.0-20191219154910-1528d4eea6dd/go.mod h1:9ehGcuUGjXVZh0qbYSB0vvofQw2JQe6c6cO0k4wu/Oo=
k8s.io/metrics v0.0.0-20191214191643-6b1944c9f765/go.mod h1:5V7rewilItwK0cz4nomU0b3XCcees2Ka5EBYWS1HBeM=
k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
-k8s.io/utils v0.0.0-20191114184206-e782cd3c129f h1:GiPwtSzdP43eI1hpPCbROQCCIgCuiMMNF8YUVLF3vJo=
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
-k8s.io/utils v0.0.0-20200229041039-0a110f9eb7ab h1:I3f2hcBrepGRXI1z4sukzAb8w1R4eqbsHrAsx06LGYM=
-k8s.io/utils v0.0.0-20200229041039-0a110f9eb7ab/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
+k8s.io/utils v0.0.0-20200619165400-6e3d28b6ed19 h1:7Nu2dTj82c6IaWvL7hImJzcXoTPz1MsSCH7r+0m6rfo=
+k8s.io/utils v0.0.0-20200619165400-6e3d28b6ed19/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=
modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
@@ -963,19 +989,19 @@ 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-20201005164301-8c180daf4ec4 h1:dZJWWof3TTa+iPki+JM07L/3a4qWTm6WYCM8mG08/2k=
-opendev.org/airship/airshipctl v0.0.0-20201005164301-8c180daf4ec4/go.mod h1:mIHconKn8pHolybWZa8BEcZWRsRlbVwY1eH0UCLLzQY=
+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/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=
-sigs.k8s.io/cluster-api v0.3.5 h1:XPCuwrGL73x82a6spCHwkHHeGiQF+L4zntaoDg2qMzo=
-sigs.k8s.io/cluster-api v0.3.5/go.mod h1:IoP66q4g92I/2f/9hltbE/FWG3RakIwRdYpY+6mqvtE=
+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=
-sigs.k8s.io/controller-runtime v0.5.2 h1:pyXbUfoTo+HA3jeIfr0vgi+1WtmNh0CwlcnQGLXwsSw=
-sigs.k8s.io/controller-runtime v0.5.2/go.mod h1:JZUwSMVbxDupo0lTJSSFP5pimEyxGynROImSsqIOx1A=
+sigs.k8s.io/controller-runtime v0.5.11 h1:U/FjGJ61aR2T2mCrdlBCxEcWgLEwLmK6YZKf0NC0a24=
+sigs.k8s.io/controller-runtime v0.5.11/go.mod h1:OTqxLuz7gVcrq+BHGUgedRu6b2VIKCEc7Pu4Jbwui0A=
sigs.k8s.io/kind v0.7.1-0.20200303021537-981bd80d3802/go.mod h1:HIZ3PWUezpklcjkqpFbnYOqaqsAE1JeCTEwkgvPLXjk=
sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0=
sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
@@ -986,7 +1012,7 @@ sigs.k8s.io/kustomize/kyaml v0.4.1/go.mod h1:XJL84E6sOFeNrQ7CADiemc1B0EjIxHo3OhW
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA=
sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18=
-sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
+sigs.k8s.io/structured-merge-diff/v2 v2.0.1/go.mod h1:Wb7vfKAodbKgf6tn1Kl0VvGj7mRH6DGaRcixXEJXTsE=
sigs.k8s.io/testing_frameworks v0.1.2/go.mod h1:ToQrwSC3s8Xf/lADdZp3Mktcql9CG0UAmdJG9th5i0w=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
diff --git a/pkg/configs/configs.go b/pkg/configs/configs.go
index 263a968..3c4595e 100644
--- a/pkg/configs/configs.go
+++ b/pkg/configs/configs.go
@@ -105,6 +105,10 @@ const (
Phase WsComponentType = "phase"
Secret WsComponentType = "secret"
+ // actions direct or phase
+ DirectAction string = "direct"
+ PhaseAction string = "phase"
+
// auth subcomponets
Approved WsSubComponentType = "approved"
Authenticate WsSubComponentType = "authenticate"
@@ -114,12 +118,12 @@ const (
// ctl subcomponets
// ctl baremetal subcomponets
- EjectMedia WsSubComponentType = "ejectMedia"
- PowerOff WsSubComponentType = "powerOff"
- PowerOn WsSubComponentType = "powerOn"
- PowerStatus WsSubComponentType = "powerStatus"
+ EjectMedia WsSubComponentType = "ejectmedia"
+ PowerOff WsSubComponentType = "poweroff"
+ PowerOn WsSubComponentType = "poweron"
+ PowerStatus WsSubComponentType = "powerstatus"
Reboot WsSubComponentType = "reboot"
- RemoteDirect WsSubComponentType = "remoteDirect"
+ RemoteDirect WsSubComponentType = "remotedirect"
// ctl cluster subcomponets
Move WsSubComponentType = "move"
@@ -157,16 +161,12 @@ const (
// ctl common components
Init WsSubComponentType = "init"
GetDefaults WsSubComponentType = "getDefaults"
- GenerateISO WsSubComponentType = "generateISO"
- Yaml WsSubComponentType = "yaml"
YamlWrite WsSubComponentType = "yamlWrite"
GetYaml WsSubComponentType = "getYaml"
GetRendered WsSubComponentType = "getRendered"
GetPhaseTree WsSubComponentType = "getPhaseTree"
GetTarget WsSubComponentType = "getTarget"
GetPhaseSourceFiles WsSubComponentType = "getPhaseSource"
- SetCluster WsSubComponentType = "cluster"
- SetCredential WsSubComponentType = "credential"
GetDocumentsBySelector WsSubComponentType = "getDocumentsBySelector"
GetPhase WsSubComponentType = "getPhase"
GetExecutorDoc WsSubComponentType = "getExecutorDoc"
@@ -183,16 +183,20 @@ type WsMessage struct {
Timestamp int64 `json:"timestamp,omitempty"`
// additional conditional components that may or may not be involved in the request / response
- Error string `json:"error,omitempty"`
IsAuthenticated bool `json:"isAuthenticated,omitempty"`
- Message string `json:"message,omitempty"`
Data interface{} `json:"data,omitempty"`
YAML string `json:"yaml,omitempty"`
Name string `json:"name,omitempty"`
Details string `json:"details,omitempty"`
ID string `json:"id,omitempty"`
+ Error *string `json:"error,omitempty"`
+ Message *string `json:"message,omitempty"`
Token *string `json:"token,omitempty"`
- Target *string `json:"target,omitempty"`
+
+ // used by baremetal CTL requests
+ ActionType *string `json:"actionType,omitempty"` // signifies if it's a phase or direct action
+ Target *string `json:"target,omitempty"` // singular target (usually in a response)
+ Targets *[]string `json:"targets,omitempty"` // multiple targets (usually in a request)
// used for auth
Authentication *Authentication `json:"authentication,omitempty"`
@@ -226,6 +230,7 @@ func SetUIConfig() error {
return checkConfigs()
}
+// checkConfigs will work its way through the config file, if it exists, and creates defaults where needed
func checkConfigs() error {
writeFile := false
if UIConfig.WebService == nil {
@@ -273,6 +278,8 @@ func checkConfigs() error {
return nil
}
+// createDefaultUser generates a default user if one doesn't exist in the conf file.
+// the default id is admin and the default password is admin
func createDefaultUser() error {
hash := sha512.New()
_, err := hash.Write([]byte("admin"))
@@ -283,6 +290,7 @@ func createDefaultUser() error {
return nil
}
+// writeTestSSL generates an SSL keypair and writes it to file
func writeTestSSL(privateKeyFile string, publicKeyFile string) error {
// get and write out private key
log.Warnf("Generating private key %s. DO NOT USE THIS FOR PRODUCTION", privateKeyFile)
@@ -301,6 +309,7 @@ func writeTestSSL(privateKeyFile string, publicKeyFile string) error {
return nil
}
+// getAndWritePrivateKey generates a default SSL private key and writes it to file
func getAndWritePrivateKey(fileName string) (*rsa.PrivateKey, error) {
privateKeyBytes, privateKey, err := cryptography.GeneratePrivateKey()
if err != nil {
@@ -313,6 +322,7 @@ func getAndWritePrivateKey(fileName string) (*rsa.PrivateKey, error) {
return privateKey, nil
}
+// getAndWritePublicKey generates a default SSL public key and writes it to file
func getAndWritePublicKey(fileName string, privateKey *rsa.PrivateKey) error {
publicKeyBytes, err := cryptography.GeneratePublicKey(privateKey)
if err != nil {
@@ -325,6 +335,7 @@ func getAndWritePublicKey(fileName string, privateKey *rsa.PrivateKey) error {
return nil
}
+// setEtcDir determines the full path for the etc dir used to write out the docs
func setEtcDir() error {
if etcDir == nil {
dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
diff --git a/pkg/ctl/airshipctl.go b/pkg/ctl/airshipctl.go
index f9704be..e41ed1e 100644
--- a/pkg/ctl/airshipctl.go
+++ b/pkg/ctl/airshipctl.go
@@ -31,7 +31,7 @@ var AirshipConfigPath *string
var KubeConfigPath *string
// CTLFunctionMap is a function map for the CTL functions that is referenced in the webservice
-var CTLFunctionMap = map[configs.WsComponentType]func(configs.WsMessage) configs.WsMessage{
+var CTLFunctionMap = map[configs.WsComponentType]func(*string, configs.WsMessage) configs.WsMessage{
configs.Baremetal: HandleBaremetalRequest,
configs.Cluster: HandleClusterRequest,
configs.CTLConfig: HandleConfigRequest,
@@ -59,11 +59,13 @@ type LogInterceptor struct {
// Init allows for the circular reference to the webservice package to be broken and allow for the sending
// of arbitrary messages from any package to the websocket
func Init() {
- webservice.AppendToFunctionMap(configs.CTL, map[configs.WsComponentType]func(configs.WsMessage) configs.WsMessage{
- configs.Baremetal: HandleBaremetalRequest,
- configs.Document: HandleDocumentRequest,
- configs.Phase: HandlePhaseRequest,
- })
+ webservice.AppendToFunctionMap(
+ configs.CTL,
+ map[configs.WsComponentType]func(*string, configs.WsMessage) configs.WsMessage{
+ configs.Baremetal: HandleBaremetalRequest,
+ configs.Document: HandleDocumentRequest,
+ configs.Phase: HandlePhaseRequest,
+ })
}
// NewDefaultClient initializes the airshipctl client for external usage with default logging.
@@ -118,7 +120,8 @@ func NewLogInterceptor(request configs.WsMessage) *LogInterceptor {
// The intention is to hijack the log output for a progress bar on the UI
func (cw *LogInterceptor) Write(data []byte) (n int, err error) {
response := cw.response
- response.Message = string(data)
+ s := string(data)
+ response.Message = &s
if err = webservice.WebSocketSend(response); err != nil {
uiLog.Errorf("Error receiving / sending message: %s\n", err)
return len(data), err
diff --git a/pkg/ctl/baremetal.go b/pkg/ctl/baremetal.go
index a7f83e7..3dd1b70 100644
--- a/pkg/ctl/baremetal.go
+++ b/pkg/ctl/baremetal.go
@@ -15,14 +15,39 @@
package ctl
import (
+ "errors"
"fmt"
+ "strings"
"opendev.org/airship/airshipui/pkg/configs"
+ "opendev.org/airship/airshipui/pkg/log"
+ "opendev.org/airship/airshipui/pkg/statistics"
+ "opendev.org/airship/airshipui/pkg/webservice"
+
+ "opendev.org/airship/airshipctl/pkg/remote"
)
+type nodeInfo struct {
+ Name string `json:"name,omitempty"`
+ ID string `json:"id,omitempty"`
+ BMCAddress string `json:"bmcAddress,omitempty"`
+}
+
+type phaseInfo struct {
+ Name string `json:"name,omitempty"`
+ GenerateName string `json:"generateName,omitempty"`
+ Namespace string `json:"namespace,omitempty"`
+ ClusterName string `json:"clusterName,omitempty"`
+}
+
+type defaultData struct {
+ Nodes []nodeInfo `json:"nodes,omitempty"`
+ Phases []phaseInfo `json:"phases,omitempty"`
+}
+
// HandleBaremetalRequest will flop between requests so we don't have to have them all mapped as function calls
// This will wait for the sub component to complete before responding. The assumption is this is an async request
-func HandleBaremetalRequest(request configs.WsMessage) configs.WsMessage {
+func HandleBaremetalRequest(user *string, request configs.WsMessage) configs.WsMessage {
response := configs.WsMessage{
Type: configs.CTL,
Component: configs.Baremetal,
@@ -30,31 +55,223 @@ func HandleBaremetalRequest(request configs.WsMessage) configs.WsMessage {
}
var err error
- var message string
+ var message *string
subComponent := request.SubComponent
+
+ if request.Targets != nil {
+ s := fmt.Sprintf("%s action has been requested on hosts: %s", subComponent, strings.Join(*request.Targets, ", "))
+ message = &s
+ }
+
switch subComponent {
+ case configs.GetDefaults:
+ response.Data, err = getDefaults(request)
case configs.EjectMedia:
- err = fmt.Errorf("Subcomponent %s not implemented", request.SubComponent)
+ err = doAction(user, request)
case configs.PowerOff:
- err = fmt.Errorf("Subcomponent %s not implemented", request.SubComponent)
+ err = doAction(user, request)
case configs.PowerOn:
- err = fmt.Errorf("Subcomponent %s not implemented", request.SubComponent)
+ err = doAction(user, request)
case configs.PowerStatus:
- err = fmt.Errorf("Subcomponent %s not implemented", request.SubComponent)
+ err = fmt.Errorf("Subcomponent %s not implemented", subComponent)
case configs.Reboot:
- err = fmt.Errorf("Subcomponent %s not implemented", request.SubComponent)
+ err = doAction(user, request)
case configs.RemoteDirect:
- err = fmt.Errorf("Subcomponent %s not implemented", request.SubComponent)
+ err = doAction(user, request)
default:
- err = fmt.Errorf("Subcomponent %s not found", request.SubComponent)
+ err = fmt.Errorf("Subcomponent %s not found", subComponent)
}
if err != nil {
- response.Error = err.Error()
+ e := err.Error()
+ response.Error = &e
} else {
response.Message = message
}
return response
}
+
+func getDefaults(request configs.WsMessage) (defaultData, error) {
+ nodeInfo, err := getNodeInfo(request)
+ phaseInfo, err2 := getPhaseInfo()
+
+ if err != nil && err2 != nil {
+ err = fmt.Errorf("Node error: %v. Phase error %v", err, err2)
+ } else if err2 != nil {
+ err = err2
+ }
+
+ return defaultData{
+ Nodes: nodeInfo,
+ Phases: phaseInfo,
+ }, err
+}
+
+// getNodeInfo gets and formats the default nodes as defined by the manifest(s)
+func getNodeInfo(request configs.WsMessage) ([]nodeInfo, error) {
+ client, err := NewClient(AirshipConfigPath, KubeConfigPath, request)
+ if err != nil {
+ log.Error(err)
+ return nil, err
+ }
+
+ selectors := []remote.HostSelector{remote.All()}
+ // bootstrap is the default "phase" this may change as it does not accept an empty string as a default
+ m, err := remote.NewManager(client.Config, "bootstrap", selectors...)
+ if err != nil {
+ log.Error(err)
+ return nil, err
+ }
+
+ data := []nodeInfo{}
+
+ for _, host := range m.Hosts {
+ data = append(data, nodeInfo{
+ Name: host.HostName,
+ ID: host.NodeID(),
+ BMCAddress: host.BMCAddress,
+ })
+ }
+ return data, nil
+}
+
+// getPhaseInfo gets and formats the phases as defined by the manifest(s)
+func getPhaseInfo() ([]phaseInfo, error) {
+ helper, err := getHelper()
+ if err != nil {
+ log.Error(err)
+ return nil, err
+ }
+
+ phases, err := helper.ListPhases()
+ if err != nil {
+ log.Error(err)
+ return nil, err
+ }
+
+ data := []phaseInfo{}
+ for _, p := range phases {
+ data = append(data, phaseInfo{
+ Name: p.Name,
+ GenerateName: p.GenerateName,
+ Namespace: p.Namespace,
+ ClusterName: p.ClusterName,
+ })
+ }
+
+ return data, nil
+}
+
+func doAction(user *string, request configs.WsMessage) error {
+ actionType := request.ActionType
+ if request.Targets == nil && actionType == nil {
+ err := errors.New("No target nodes or phases defined. Cannot proceed with request")
+ return err
+ }
+
+ defaultPhase := "bootstrap"
+ if request.Targets != nil {
+ for _, target := range *request.Targets {
+ if *actionType == configs.DirectAction {
+ go actionHelper(user, target, defaultPhase, request)
+ } else {
+ go actionHelper(user, "", target, request)
+ }
+ }
+ }
+
+ return nil
+}
+
+func actionHelper(user *string, target string, phase string, request configs.WsMessage) {
+ response := configs.WsMessage{
+ Type: configs.CTL,
+ Component: configs.Baremetal,
+ SubComponent: configs.EjectMedia,
+ SessionID: request.SessionID,
+ ActionType: request.ActionType,
+ Target: &target,
+ }
+
+ // create a transaction for this singular request
+ transaction := statistics.NewTransaction(user, response)
+
+ client, err := NewClient(AirshipConfigPath, KubeConfigPath, response)
+ if err != nil {
+ log.Error(err)
+ e := err.Error()
+ response.Error = &e
+ transaction.Complete(false)
+ err = webservice.WebSocketSend(response)
+ if err != nil {
+ log.Error(err)
+ }
+ return
+ }
+
+ var selectors []remote.HostSelector
+ if len(target) != 0 {
+ selectors = []remote.HostSelector{remote.ByName(target)}
+ } else {
+ selectors = []remote.HostSelector{remote.All()}
+ }
+ m, err := remote.NewManager(client.Config, phase, selectors...)
+ if err != nil {
+ log.Error(err)
+ e := err.Error()
+ response.Error = &e
+ transaction.Complete(false)
+ err = webservice.WebSocketSend(response)
+ if err != nil {
+ log.Error(err)
+ }
+ return
+ }
+
+ action := request.SubComponent
+ if len(m.Hosts) != 1 {
+ e := fmt.Sprintf("More than one node found cannot complete %s on %s", action, target)
+ log.Error(&e)
+ response.Error = &e
+ transaction.Complete(false)
+ err = webservice.WebSocketSend(response)
+ if err != nil {
+ log.Error(err)
+ }
+ return
+ }
+
+ host := m.Hosts[0]
+ switch action {
+ case configs.EjectMedia:
+ err = host.EjectVirtualMedia(host.Context)
+ case configs.PowerOff:
+ err = host.SystemPowerOff(host.Context)
+ case configs.PowerOn:
+ err = host.SystemPowerOn(host.Context)
+ case configs.Reboot:
+ err = host.RebootSystem(host.Context)
+ }
+
+ if err != nil {
+ log.Error(err)
+ e := err.Error()
+ response.Error = &e
+ transaction.Complete(false)
+ err = webservice.WebSocketSend(response)
+ if err != nil {
+ log.Error(err)
+ }
+ return
+ }
+
+ s := fmt.Sprintf("%s on %s completed successfully", action, target)
+ response.Message = &s
+ transaction.Complete(true)
+ err = webservice.WebSocketSend(response)
+ if err != nil {
+ log.Error(err)
+ }
+}
diff --git a/pkg/ctl/cluster.go b/pkg/ctl/cluster.go
index bb56d6f..194b7a7 100644
--- a/pkg/ctl/cluster.go
+++ b/pkg/ctl/cluster.go
@@ -22,7 +22,7 @@ import (
// HandleClusterRequest will flop between requests so we don't have to have them all mapped as function calls
// This will wait for the sub component to complete before responding. The assumption is this is an async request
-func HandleClusterRequest(request configs.WsMessage) configs.WsMessage {
+func HandleClusterRequest(user *string, request configs.WsMessage) configs.WsMessage {
response := configs.WsMessage{
Type: configs.CTL,
Component: configs.Baremetal,
@@ -30,7 +30,7 @@ func HandleClusterRequest(request configs.WsMessage) configs.WsMessage {
}
var err error
- var message string
+ var message *string
subComponent := request.SubComponent
switch subComponent {
@@ -45,7 +45,8 @@ func HandleClusterRequest(request configs.WsMessage) configs.WsMessage {
}
if err != nil {
- response.Error = err.Error()
+ e := err.Error()
+ response.Error = &e
} else {
response.Message = message
}
diff --git a/pkg/ctl/config.go b/pkg/ctl/config.go
index cf7d54b..8e4bcc4 100644
--- a/pkg/ctl/config.go
+++ b/pkg/ctl/config.go
@@ -22,7 +22,7 @@ import (
// HandleConfigRequest will flop between requests so we don't have to have them all mapped as function calls
// This will wait for the sub component to complete before responding. The assumption is this is an async request
-func HandleConfigRequest(request configs.WsMessage) configs.WsMessage {
+func HandleConfigRequest(user *string, request configs.WsMessage) configs.WsMessage {
response := configs.WsMessage{
Type: configs.CTL,
Component: configs.Baremetal,
@@ -30,7 +30,7 @@ func HandleConfigRequest(request configs.WsMessage) configs.WsMessage {
}
var err error
- var message string
+ var message *string
subComponent := request.SubComponent
switch subComponent {
@@ -59,7 +59,8 @@ func HandleConfigRequest(request configs.WsMessage) configs.WsMessage {
}
if err != nil {
- response.Error = err.Error()
+ e := err.Error()
+ response.Error = &e
} else {
response.Message = message
}
diff --git a/pkg/ctl/document.go b/pkg/ctl/document.go
index e4f691a..47b85aa 100644
--- a/pkg/ctl/document.go
+++ b/pkg/ctl/document.go
@@ -39,7 +39,7 @@ var (
)
// HandleDocumentRequest will flop between requests so we don't have to have them all mapped as function calls
-func HandleDocumentRequest(request configs.WsMessage) configs.WsMessage {
+func HandleDocumentRequest(user *string, request configs.WsMessage) configs.WsMessage {
response := configs.WsMessage{
Type: configs.CTL,
Component: configs.Document,
@@ -47,12 +47,13 @@ func HandleDocumentRequest(request configs.WsMessage) configs.WsMessage {
}
var err error
- var message string
+ var message *string
var id string
client, err := NewClient(AirshipConfigPath, KubeConfigPath, request)
if err != nil {
- response.Error = err.Error()
+ e := err.Error()
+ response.Error = &e
return response
}
@@ -64,32 +65,36 @@ func HandleDocumentRequest(request configs.WsMessage) configs.WsMessage {
case configs.YamlWrite:
id = request.ID
response.Name, response.YAML, err = client.writeYamlFile(id, request.YAML)
- message = fmt.Sprintf("File '%s' saved successfully", response.Name)
+ s := fmt.Sprintf("File '%s' saved successfully", response.Name)
+ message = &s
case configs.GetYaml:
id = request.ID
message = request.Message
- response.Name, response.YAML, err = client.getYaml(id, message)
+ response.Name, response.YAML, err = client.getYaml(id, *message)
case configs.GetPhaseTree:
response.Data, err = client.GetPhaseTree()
case configs.GetPhase:
id = request.ID
- message = "rendered"
+ s := "rendered"
+ message = &s
response.Name, response.Details, response.YAML, err = client.GetPhase(id)
case configs.GetDocumentsBySelector:
id = request.ID
- response.Data, err = GetDocumentsBySelector(request.ID, request.Message)
+ response.Data, err = GetDocumentsBySelector(request.ID, *request.Message)
case configs.GetTarget:
message = client.getTarget()
case configs.GetExecutorDoc:
id = request.ID
- message = "rendered"
+ s := "rendered"
+ message = &s
response.Name, response.YAML, err = client.GetExecutorDoc(id)
default:
err = fmt.Errorf("Subcomponent %s not found", request.SubComponent)
}
if err != nil {
- response.Error = err.Error()
+ e := err.Error()
+ response.Error = &e
} else {
response.Message = message
response.ID = id
@@ -126,13 +131,16 @@ func (c *Client) GetExecutorDoc(id string) (string, string, error) {
return title, base64.StdEncoding.EncodeToString(bytes), nil
}
-func (c *Client) getTarget() string {
+func (c *Client) getTarget() *string {
+ var s string
m, err := c.Config.CurrentContextManifest()
if err != nil {
- return "unknown"
+ s = "unknown"
+ return &s
}
- return filepath.Join(m.TargetPath, m.SubPath)
+ s = filepath.Join(m.TargetPath, m.SubPath)
+ return &s
}
func (c *Client) getPhaseDetails(id ifc.ID) (string, error) {
@@ -301,14 +309,15 @@ func (c *Client) GetPhase(id string) (string, string, string, error) {
return title, details, base64.StdEncoding.EncodeToString(buf.Bytes()), nil
}
-func (c *Client) docPull() (string, error) {
- var message string
+func (c *Client) docPull() (*string, error) {
+ var message *string
cfgFactory := config.CreateFactory(AirshipConfigPath, KubeConfigPath)
// 2nd arg is noCheckout, I assume we want to checkout the repo,
// so setting to false
err := pull.Pull(cfgFactory, false)
if err == nil {
- message = fmt.Sprintf("Success")
+ s := fmt.Sprintf("Success")
+ message = &s
}
return message, err
diff --git a/pkg/ctl/document_test.go b/pkg/ctl/document_test.go
index 6996a38..48de7f3 100644
--- a/pkg/ctl/document_test.go
+++ b/pkg/ctl/document_test.go
@@ -34,13 +34,15 @@ func TestHandleUnknownDocumentSubComponent(t *testing.T) {
AirshipConfigPath = &acp
KubeConfigPath = &kcp
- response := HandleDocumentRequest(request)
+ user := "test"
+ response := HandleDocumentRequest(&user, request)
+ e := "Subcomponent fake_subcomponent not found"
expected := configs.WsMessage{
Type: configs.CTL,
Component: configs.Document,
SubComponent: "fake_subcomponent",
- Error: "Subcomponent fake_subcomponent not found",
+ Error: &e,
}
assert.Equal(t, expected, response)
diff --git a/pkg/ctl/image.go b/pkg/ctl/image.go
index 4a5c9fc..e8745d1 100644
--- a/pkg/ctl/image.go
+++ b/pkg/ctl/image.go
@@ -15,16 +15,15 @@
package ctl
import (
+ "errors"
"fmt"
- "opendev.org/airship/airshipctl/pkg/bootstrap/isogen"
- "opendev.org/airship/airshipctl/pkg/config"
"opendev.org/airship/airshipui/pkg/configs"
)
// HandleImageRequest will flop between requests so we don't have to have them all mapped as function calls
// This will wait for the sub component to complete before responding. The assumption is this is an async request
-func HandleImageRequest(request configs.WsMessage) configs.WsMessage {
+func HandleImageRequest(user *string, request configs.WsMessage) configs.WsMessage {
response := configs.WsMessage{
Type: configs.CTL,
Component: configs.Baremetal,
@@ -32,11 +31,12 @@ func HandleImageRequest(request configs.WsMessage) configs.WsMessage {
}
var err error
- var message string
+ var message *string
client, err := NewClient(AirshipConfigPath, KubeConfigPath, request)
if err != nil {
- response.Error = err.Error()
+ e := err.Error()
+ response.Error = &e
return response
}
@@ -54,7 +54,8 @@ func HandleImageRequest(request configs.WsMessage) configs.WsMessage {
}
if err != nil {
- response.Error = err.Error()
+ e := err.Error()
+ response.Error = &e
} else {
response.Message = message
}
@@ -62,17 +63,7 @@ func HandleImageRequest(request configs.WsMessage) configs.WsMessage {
return response
}
-func (c *Client) generateIso() (string, error) {
- var message string
-
- cfgFactory := config.CreateFactory(AirshipConfigPath, KubeConfigPath)
-
- // setting "progress" to false since we don't need to see CLI
- // progress bar in UI
- err := isogen.GenerateBootstrapIso(cfgFactory, false)
- if err == nil {
- message = fmt.Sprintf("Success")
- }
-
- return message, err
+func (c *Client) generateIso() (*string, error) {
+ err := errors.New("Isogen is no longer available")
+ return nil, err
}
diff --git a/pkg/ctl/image_test.go b/pkg/ctl/image_test.go
index 434240e..5a90724 100644
--- a/pkg/ctl/image_test.go
+++ b/pkg/ctl/image_test.go
@@ -34,13 +34,15 @@ func TestHandleUnknownBaremetalSubComponent(t *testing.T) {
AirshipConfigPath = &acp
KubeConfigPath = &kcp
- response := HandleBaremetalRequest(request)
+ user := "test"
+ response := HandleBaremetalRequest(&user, request)
+ e := "Subcomponent fake_subcomponent not found"
expected := configs.WsMessage{
Type: configs.CTL,
Component: configs.Baremetal,
SubComponent: "fake_subcomponent",
- Error: "Subcomponent fake_subcomponent not found",
+ Error: &e,
}
assert.Equal(t, expected, response)
diff --git a/pkg/ctl/phase.go b/pkg/ctl/phase.go
index f98ae94..23ce347 100644
--- a/pkg/ctl/phase.go
+++ b/pkg/ctl/phase.go
@@ -26,7 +26,7 @@ import (
// HandlePhaseRequest will flop between requests so we don't have to have them all mapped as function calls
// This will wait for the sub component to complete before responding. The assumption is this is an async request
-func HandlePhaseRequest(request configs.WsMessage) configs.WsMessage {
+func HandlePhaseRequest(user *string, request configs.WsMessage) configs.WsMessage {
response := configs.WsMessage{
Type: configs.CTL,
Component: configs.Document, // setting this to Document for now since that's handling phase requests
@@ -34,12 +34,13 @@ func HandlePhaseRequest(request configs.WsMessage) configs.WsMessage {
}
var err error
- var message string
+ var message *string
var valid bool
client, err := NewClient(AirshipConfigPath, KubeConfigPath, request)
if err != nil {
- response.Error = err.Error()
+ e := err.Error()
+ response.Error = &e
return response
}
@@ -59,7 +60,8 @@ func HandlePhaseRequest(request configs.WsMessage) configs.WsMessage {
}
if err != nil {
- response.Error = err.Error()
+ e := err.Error()
+ response.Error = &e
} else {
response.Message = message
}
@@ -70,12 +72,12 @@ func HandlePhaseRequest(request configs.WsMessage) configs.WsMessage {
// this helper function will likely disappear once a clear workflow for
// phase validation takes shape in UI. For now, it simply returns a
// string message to be displayed as a toast in frontend client
-func validateHelper(valid bool) string {
+func validateHelper(valid bool) *string {
msg := "invalid"
if valid {
msg = "valid"
}
- return msg
+ return &msg
}
// ValidatePhase validates the specified phase
diff --git a/pkg/ctl/processor.go b/pkg/ctl/processor.go
index e8dc648..8123211 100644
--- a/pkg/ctl/processor.go
+++ b/pkg/ctl/processor.go
@@ -118,12 +118,13 @@ func (p *UIEventProcessor) processClusterctlEvent(e events.ClusterctlEvent) {
}
func sendEventMessage(sessionID, eventType, message string) {
+ m := fmt.Sprintf("%s: %s", eventType, message)
err := webservice.WebSocketSend(configs.WsMessage{
SessionID: sessionID,
Type: configs.CTL,
Component: configs.Document, // probably will change to configs.Phase soon
SubComponent: configs.Run,
- Message: fmt.Sprintf("%s: %s", eventType, message),
+ Message: &m,
})
if err != nil {
log.Errorf("Error sending message %s", err)
diff --git a/pkg/ctl/secret.go b/pkg/ctl/secret.go
index 04a95ab..ab7fba8 100644
--- a/pkg/ctl/secret.go
+++ b/pkg/ctl/secret.go
@@ -22,7 +22,7 @@ import (
// HandleSecretRequest will flop between requests so we don't have to have them all mapped as function calls
// This will wait for the sub component to complete before responding. The assumption is this is an async request
-func HandleSecretRequest(request configs.WsMessage) configs.WsMessage {
+func HandleSecretRequest(user *string, request configs.WsMessage) configs.WsMessage {
response := configs.WsMessage{
Type: configs.CTL,
Component: configs.Baremetal,
@@ -30,7 +30,7 @@ func HandleSecretRequest(request configs.WsMessage) configs.WsMessage {
}
var err error
- var message string
+ var message *string
subComponent := request.SubComponent
switch subComponent {
@@ -41,7 +41,8 @@ func HandleSecretRequest(request configs.WsMessage) configs.WsMessage {
}
if err != nil {
- response.Error = err.Error()
+ e := err.Error()
+ response.Error = &e
} else {
response.Message = message
}
diff --git a/pkg/statistics/recorder.go b/pkg/statistics/recorder.go
index 41de926..4f29d4d 100755
--- a/pkg/statistics/recorder.go
+++ b/pkg/statistics/recorder.go
@@ -31,8 +31,10 @@ type Transaction struct {
Table configs.WsComponentType
SubComponent configs.WsSubComponentType
User *string
+ ActionType *string
Target *string
Started int64
+ Recordable bool
}
var (
@@ -46,15 +48,23 @@ const (
tableCreate = `CREATE TABLE IF NOT EXISTS table (
subcomponent varchar(64) null,
user varchar(64),
- target varchar(64) null,
+ type text check(type in ('direct', 'phase')) null,
+ target text null,
success tinyint(1) default 0,
started timestamp,
elapsed bigint,
- stopped timestamp,
- primary key (subcomponent, user, started, stopped))`
+ stopped timestamp)`
// the prepared statement used for inserts
// TODO (aschiefe): determine if we need to batch inserts
- insert = "INSERT INTO table(subcomponent, user, target, success, started, elapsed, stopped) values(?,?,?,?,?,?,?)"
+ insert = `INSERT INTO table(subcomponent,
+ user,
+ type,
+ target,
+ success,
+ started,
+ elapsed,
+ stopped)
+ values(?,?,?,?,?,?,?,?)`
)
// Init will create the database if it doesn't exist or open the existing database
@@ -99,19 +109,21 @@ func createTables() error {
}
// NewTransaction establishes the transaction which will record
-func NewTransaction(request configs.WsMessage, user *string) *Transaction {
+func NewTransaction(user *string, request configs.WsMessage) *Transaction {
return &Transaction{
Table: request.Component,
SubComponent: request.SubComponent,
+ ActionType: request.ActionType,
Target: request.Target,
Started: time.Now().UnixNano() / 1000000,
User: user,
+ Recordable: isRecordable(request),
}
}
// Complete will put an entry into the statistics database for the transaction
-func (transaction *Transaction) Complete(errorMessagePresent bool) {
- if transaction.User != nil && transaction.isRecordable() {
+func (transaction *Transaction) Complete(errorMessageNotPresent bool) {
+ if transaction.User != nil && transaction.Recordable {
stmt, err := db.Prepare(strings.ReplaceAll(insert, "table", string(transaction.Table)))
if err != nil {
log.Error(err)
@@ -122,7 +134,7 @@ func (transaction *Transaction) Complete(errorMessagePresent bool) {
stopped := time.Now().UnixNano() / 1000000
success := 0
- if errorMessagePresent {
+ if errorMessageNotPresent {
success = 1
}
@@ -130,6 +142,7 @@ func (transaction *Transaction) Complete(errorMessagePresent bool) {
defer writeMutex.Unlock()
result, err := stmt.Exec(transaction.SubComponent,
transaction.User,
+ transaction.ActionType,
transaction.Target,
success,
started,
@@ -152,16 +165,28 @@ func (transaction *Transaction) Complete(errorMessagePresent bool) {
}
// isRecordable will shuffle through the transaction and determine if we should write it to the database
-func (transaction *Transaction) isRecordable() bool {
+func isRecordable(request configs.WsMessage) bool {
recordable := true
- if transaction.Table == configs.Auth {
+ // don't record auth attempts
+ if request.Component == configs.Auth {
recordable = false
}
- switch transaction.SubComponent {
- case configs.GetTarget:
- recordable = false
- case configs.GetPhaseTree:
+
+ // don't record default get data events
+ switch request.SubComponent {
+ case configs.GetTarget,
+ configs.GetDefaults,
+ configs.GetPhaseTree,
+ configs.GetPhase,
+ configs.GetYaml,
+ configs.GetDocumentsBySelector:
recordable = false
}
+
+ // don't request actions taken against multiple targets, the individual action will be recorded
+ if request.Targets != nil {
+ recordable = false
+ }
+
return recordable
}
diff --git a/pkg/webservice/auth.go b/pkg/webservice/auth.go
index afd3fd6..9c79e02 100755
--- a/pkg/webservice/auth.go
+++ b/pkg/webservice/auth.go
@@ -31,7 +31,7 @@ import (
var jwtKey = []byte("airshipUI_JWT_key")
// The UI will either request authentication or validation, handle those situations here
-func handleAuth(request configs.WsMessage) configs.WsMessage {
+func handleAuth(_ *string, request configs.WsMessage) configs.WsMessage {
response := configs.WsMessage{
Type: configs.UI,
Component: configs.Auth,
@@ -66,10 +66,10 @@ func handleAuth(request configs.WsMessage) configs.WsMessage {
if err != nil {
log.Error(err)
- response.Error = err.Error()
+ e := err.Error()
+ response.Error = &e
response.SubComponent = configs.Denied
}
-
return response
}
diff --git a/pkg/webservice/websocket.go b/pkg/webservice/websocket.go
index 03ed26d..82bc73b 100644
--- a/pkg/webservice/websocket.go
+++ b/pkg/webservice/websocket.go
@@ -47,7 +47,7 @@ var upgrader = websocket.Upgrader{
// this is a way to allow for arbitrary messages to be processed by the backend
// the message of a specifc component is shunted to that subsystem for further processing
-var functionMap = map[configs.WsRequestType]map[configs.WsComponentType]func(configs.WsMessage) configs.WsMessage{
+var funcMap = map[configs.WsRequestType]map[configs.WsComponentType]func(*string, configs.WsMessage) configs.WsMessage{
configs.UI: {
configs.Keepalive: keepaliveReply,
configs.Auth: handleAuth,
@@ -58,8 +58,8 @@ var functionMap = map[configs.WsRequestType]map[configs.WsComponentType]func(con
// It does however require them to implement an init function to append them
// TODO: maybe some form of an interface to enforce this may be necessary?
func AppendToFunctionMap(requestType configs.WsRequestType,
- functions map[configs.WsComponentType]func(configs.WsMessage) configs.WsMessage) {
- functionMap[requestType] = functions
+ functions map[configs.WsComponentType]func(*string, configs.WsMessage) configs.WsMessage) {
+ funcMap[requestType] = functions
}
// handle the origin request & upgrade to websocket
@@ -107,11 +107,12 @@ func (session *session) onMessage() {
}
if err != nil {
// deny the request if we get a bad token, this will force the UI to a login screen
+ e := "Invalid token, authentication denied"
response := configs.WsMessage{
Type: configs.UI,
Component: configs.Auth,
SubComponent: configs.Denied,
- Error: "Invalid token, authentication denied",
+ Error: &e,
}
if err = session.webSocketSend(response); err != nil {
session.onError(err)
@@ -119,17 +120,17 @@ func (session *session) onMessage() {
} else {
// This is the middleware to be able to record when a transaction starts and ends for the statistics recorder
// It is possible for the backend to send messages without a valid user
- transaction := statistics.NewTransaction(request, user)
+ transaction := statistics.NewTransaction(user, request)
// look through the function map to find the type to handle the request
- if reqType, ok := functionMap[request.Type]; ok {
+ if reqType, ok := funcMap[request.Type]; ok {
// the function map may have a component (function) to process the request
if component, ok := reqType[request.Component]; ok {
- response := component(request)
+ response := component(user, request)
if err = session.webSocketSend(response); err != nil {
session.onError(err)
}
- go transaction.Complete(len(response.Error) == 0)
+ go transaction.Complete(response.Error == nil)
} else {
if err = session.webSocketSend(requestErrorHelper(fmt.Sprintf("Requested component: %s, not found",
request.Component), request)); err != nil {
@@ -164,7 +165,7 @@ func (session *session) onError(err error) {
}
// The UI will occasionally ping the server due to the websocket default timeout
-func keepaliveReply(configs.WsMessage) configs.WsMessage {
+func keepaliveReply(*string, configs.WsMessage) configs.WsMessage {
return configs.WsMessage{
Type: configs.UI,
Component: configs.Keepalive,
@@ -176,7 +177,7 @@ func requestErrorHelper(err string, request configs.WsMessage) configs.WsMessage
return configs.WsMessage{
Type: request.Type,
Component: request.Component,
- Error: err,
+ Error: &err,
}
}