From 37be06e6dca1f9804f21d042aa9adc34598158c1 Mon Sep 17 00:00:00 2001 From: "Schiefelbein, Andrew" Date: Mon, 6 Apr 2020 15:04:31 -0500 Subject: [PATCH] [WIP] transform airship ui into a standalone electron app Removing octant reference for now, it will need to be later embedded into the ui Working on this will require these commands to be run in the web directory: npm install npm install --save-dev electron behind a proxy npm install may have issues, this might work: npx cross-env ELECTRON_GET_USE_PROXY=true GLOBAL_AGENT_HTTPS_PROXY= http://: npm install -D electron@latest Change-Id: I5bd054a767fe8ab7b0461a16eced1921c4de11f6 --- .gitignore | 3 + .golangci.yaml | 22 +- Makefile | 4 +- cmd/argoui/README.md | 9 - cmd/argoui/main.go | 28 - cmd/openstack/main.go | 28 - go.mod | 5 +- go.sum | 273 ++++++- internal/commands/dash.go | 129 --- internal/commands/root.go | 58 +- internal/commands/version.go | 12 +- internal/electron/app.go | 42 + internal/environment/version.go | 10 - .../plugin/airshipopenstack}/README.md | 0 .../compute.go | 83 +- .../identity.go | 64 +- .../network.go | 54 +- .../plugin.go} | 83 +- .../{openstack => airshipopenstack}/volume.go | 37 +- internal/plugin/argoui/register.go | 135 ---- internal/plugin/argoui/register_test.go | 54 -- internal/plugin/openstack/register_test.go | 58 -- internal/webservice/server.go | 162 ++++ web/images/airship-color.png | Bin 0 -> 64414 bytes web/images/searchicon.png | Bin 0 -> 860 bytes web/index.html | 34 + web/js/common.js | 96 +++ web/js/main.js | 61 ++ web/js/websocket.js | 121 +++ web/package-lock.json | 737 ++++++++++++++++++ web/package.json | 17 + web/style.css | 167 ++++ 32 files changed, 1860 insertions(+), 726 deletions(-) delete mode 100644 cmd/argoui/README.md delete mode 100644 cmd/argoui/main.go delete mode 100755 cmd/openstack/main.go delete mode 100644 internal/commands/dash.go create mode 100755 internal/electron/app.go delete mode 100644 internal/environment/version.go rename {cmd/openstack => internal/plugin/airshipopenstack}/README.md (100%) rename internal/plugin/{openstack => airshipopenstack}/compute.go (58%) rename internal/plugin/{openstack => airshipopenstack}/identity.go (64%) rename internal/plugin/{openstack => airshipopenstack}/network.go (62%) rename internal/plugin/{openstack/register.go => airshipopenstack/plugin.go} (62%) rename internal/plugin/{openstack => airshipopenstack}/volume.go (57%) delete mode 100644 internal/plugin/argoui/register.go delete mode 100644 internal/plugin/argoui/register_test.go delete mode 100755 internal/plugin/openstack/register_test.go create mode 100755 internal/webservice/server.go create mode 100755 web/images/airship-color.png create mode 100755 web/images/searchicon.png create mode 100755 web/index.html create mode 100755 web/js/common.js create mode 100755 web/js/main.js create mode 100755 web/js/websocket.js create mode 100755 web/package-lock.json create mode 100755 web/package.json create mode 100755 web/style.css diff --git a/.gitignore b/.gitignore index 471580c..8415248 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,6 @@ bin # Developer IDE .idea/ + +# Node modules +web/node_modules diff --git a/.golangci.yaml b/.golangci.yaml index 1e98d53..a73ef86 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -1,3 +1,15 @@ +# 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. + # This file contains all available configuration options # with their documentation @@ -134,10 +146,10 @@ linters-settings: # simplify code: gofmt with `-s` option, true by default simplify: true - goimports: - # put imports beginning with prefix after 3rd-party packages; - # it's a comma-separated list of prefixes - #local-prefixes: opendev.org/airship/airshipui + #goimports: + # # put imports beginning with prefix after 3rd-party packages; + # # it's a comma-separated list of prefixes + # local-prefixes: opendev.org/airship/airshipui govet: # report about shadowed variables @@ -198,4 +210,4 @@ linters: - unparam # Reports unused function parameters - unused # Checks Go code for unused constants, variables, functions and types - varcheck # Finds unused global variables and constants - - whitespace # Tool for detection of leading and trailing whitespace NOTE(howell): This linter does _not_ check for trailing whitespace in multiline strings + - whitespace # Tool for detection of leading and trailing whitespace NOTE(howell): This linter does _not_ check for trailing whitespace in multiline strings \ No newline at end of file diff --git a/Makefile b/Makefile index 080a335..1af3bac 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ COVERAGE_OUTPUT := coverage.out TESTFLAGS ?= # Override the value of the version variable in main.go -LD_FLAGS= '-X opendev.org/airship/airshipui/internal/environment.version=$(GIT_VERSION)' +LD_FLAGS= '-X opendev.org/airship/airshipui/internal/commands.version=$(GIT_VERSION)' GO_FLAGS := -ldflags=$(LD_FLAGS) BUILD_DIR := bin @@ -34,7 +34,7 @@ else OCTANT_PLUGINSTUB_DIR ?= ${HOME}/.config/octant/plugins endif -DIRS = internal +DIRS = internal/plugin RECURSIVE_DIRS = $(addprefix ./, $(addsuffix /..., $(DIRS))) .PHONY: build diff --git a/cmd/argoui/README.md b/cmd/argoui/README.md deleted file mode 100644 index cc63928..0000000 --- a/cmd/argoui/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Argo UI Plugin - -## Getting Started - -1. Install [Argo CLI](https://github.com/argoproj/argo/blob/master/docs/getting-started.md) -2. Install [Argo UI](https://github.com/argoproj/argo/blob/master/README.md) onto your Kubernetes Cluster -3. Install the Argo UI Plugin using `make install-plugins` - -The next time you run Airship UI the Argo UI will be available at http://127.0.0.1:7777/#/argo-ui diff --git a/cmd/argoui/main.go b/cmd/argoui/main.go deleted file mode 100644 index 544b115..0000000 --- a/cmd/argoui/main.go +++ /dev/null @@ -1,28 +0,0 @@ -package main - -import ( - "fmt" - "log" - - "opendev.org/airship/airshipui/internal/environment" - plugin "opendev.org/airship/airshipui/internal/plugin/argoui" -) - -var pluginName = "argo-ui" - -func main() { - // Remove the prefix from the go logger since Octant will print logs with timestamps. - log.SetPrefix("") - - description := fmt.Sprintf("%s version %s", pluginName, environment.Version()) - - // Use the plugin service helper to register this plugin. - p, err := plugin.Register(pluginName, description) - if err != nil { - log.Fatal("Unable to start ", pluginName, err) - } - - // The plugin can log and the log messages will show up in Octant. - log.Printf("%s plugin is starting", pluginName) - p.Serve() -} diff --git a/cmd/openstack/main.go b/cmd/openstack/main.go deleted file mode 100755 index f6c9bca..0000000 --- a/cmd/openstack/main.go +++ /dev/null @@ -1,28 +0,0 @@ -package main - -import ( - "fmt" - "log" - - "opendev.org/airship/airshipui/internal/environment" - plugin "opendev.org/airship/airshipui/internal/plugin/openstack" -) - -var pluginName = "openstack" - -func main() { - // Remove the prefix from the go logger since Octant will print logs with timestamps. - log.SetPrefix("") - - description := fmt.Sprintf("%s version %s", pluginName, environment.Version()) - - // Use the plugin service helper to register this plugin. - p, err := plugin.Register(pluginName, description) - if err != nil { - log.Fatal("Unable to start ", pluginName, err) - } - - // The plugin can log and the log messages will show up in Octant. - log.Printf("%s is starting", pluginName) - p.Serve() -} diff --git a/go.mod b/go.mod index c171d2b..2072520 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,11 @@ module opendev.org/airship/airshipui go 1.12 require ( - github.com/go-cmd/cmd v1.2.0 - github.com/golang/mock v1.4.3 github.com/gophercloud/gophercloud v0.9.0 + github.com/gorilla/websocket v1.4.2 github.com/spf13/cobra v0.0.6 github.com/spf13/pflag v1.0.5 - github.com/vmware-tanzu/octant v0.11.0 + golang.org/x/net v0.0.0-20191204025024-5ee1b9f4859a k8s.io/api v0.17.4 k8s.io/apimachinery v0.17.4 opendev.org/airship/airshipctl v0.0.0-20200324160507-db6217f011b9 diff --git a/go.sum b/go.sum index acf5c52..4dce1d0 100644 --- a/go.sum +++ b/go.sum @@ -32,7 +32,9 @@ github.com/Microsoft/go-winio v0.4.12 h1:xAfWHN1IrQ0NJ9TBC0KBZoqLjzDTr1ML+4MywiU github.com/Microsoft/go-winio v0.4.12/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= @@ -40,11 +42,14 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= @@ -53,23 +58,34 @@ github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/bombsimon/wsl v1.2.5/go.mod h1:43lEF/i0kpXbLCeDXL9LMT8c92HyBywXb0AsgMHYngM= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= github.com/chai2010/gettext-go v0.0.0-20170215093142-bf70f2a70fb1 h1:HD4PLRzjuCVW79mQ0/pdsalOLHJ+FaEoqJLxfltpb2U= github.com/chai2010/gettext-go v0.0.0-20170215093142-bf70f2a70fb1/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= 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= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/daaku/go.zipexe v1.0.0 h1:VSOgZtH418pH9L16hC/JrgSNJbbAL26pj7lmD1+CGdY= github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E= github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -88,9 +104,12 @@ github.com/docker/go-connections v0.3.0 h1:3lOnM9cSzgGwx8VfK/NGOW5fLQ0GjIlCkaktF github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= 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/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/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= @@ -115,6 +134,7 @@ github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwC github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= @@ -126,29 +146,91 @@ github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 h1:Mn26/9ZMNWSw9C9ER 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= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/go-cmd/cmd v1.2.0 h1:Aohz0ZG0nQbvT4z55Mh+fdegX48GSAXL3cSsbYxRfvI= github.com/go-cmd/cmd v1.2.0/go.mod h1:XgKkd0L6sv9WcYV0FS8RfG1RJCSTVHTsLeAD2pTgHt0= +github.com/go-critic/go-critic v0.3.5-0.20190904082202-d79a9f0c64db/go.mod h1:+sE8vrLDS2M0pZkBk0wy6+nLdKexVDrl/jBqQOTDThA= +github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= +github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= +github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agRrHM= +github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-git-fixtures/v4 v4.0.1/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw= +github.com/go-git/go-git/v5 v5.0.0 h1:k5RWPm4iJwYtfWoxIJy4wJX9ON7ihPeZZYC1fLYDnpg= +github.com/go-git/go-git/v5 v5.0.0/go.mod h1:oYD8y9kWsGINPFJoLdaScGCN6dlKg23blmClfZwtUVA= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-lintpack/lintpack v0.5.2/go.mod h1:NwZuYi2nUHho8XEIZ6SIxihrnPoqBTDqfpXvXAN0sXM= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +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= +github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= +github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU= +github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= +github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= github.com/go-openapi/jsonpointer v0.19.2 h1:A9+F4Dc/MCNB5jibxf6rRvOvR/iFgQdyNx9eIhnGqq0= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= +github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= +github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk= +github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= +github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= +github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= +github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= github.com/go-openapi/spec v0.19.2 h1:SStNd1jRcYtfKCN7R0laGNs80WYYvn5CbBjM2sOmCrE= github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= +github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= +github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.19.2 h1:jvO6bCMBEilGwMfHhrd61zIID4oIFdwb76V17SM88dE= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= +github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= +github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-test/deep v1.0.5 h1:AKODKU3pDH1RzZzm6YZu77YWtEAq6uh1rLIAQlay2qc= github.com/go-test/deep v1.0.5/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8= +github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4= +github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ= +github.com/go-toolsmith/astequal v0.0.0-20180903214952-dcb477bfacd6/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY= +github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY= +github.com/go-toolsmith/astfmt v0.0.0-20180903215011-8f8ee99c3086/go.mod h1:mP93XdblcopXwlyN4X4uodxXQhldPGZbcEJIimQHrkg= +github.com/go-toolsmith/astfmt v1.0.0/go.mod h1:cnWmsOAuq4jJY6Ct5YWlVLmcmLMn1JUPuQIHCY7CJDw= +github.com/go-toolsmith/astinfo v0.0.0-20180906194353-9809ff7efb21/go.mod h1:dDStQCHtmZpYOmjRP/8gHHnCCch3Zz3oEgCdZVdtweU= +github.com/go-toolsmith/astp v0.0.0-20180903215135-0af7e3c24f30/go.mod h1:SV2ur98SGypH1UjcPpCatrV5hPazG6+IfNHbkDXBRrk= +github.com/go-toolsmith/astp v1.0.0/go.mod h1:RSyrtpVlfTFGDYRbrjyWP1pYu//tSFcvdYrA8meBmLI= +github.com/go-toolsmith/pkgload v0.0.0-20181119091011-e9e65178eee8/go.mod h1:WoMrjiy4zvdS+Bg6z9jZH82QXwkcgCBX6nOfnmdaHks= +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/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= 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.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= @@ -175,6 +257,21 @@ github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y 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/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +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= +github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8= +github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3/go.mod h1:JXrF4TWy4tXYn62/9x8Wm/K/dm06p8tCKwFRDPZG/1o= +github.com/golangci/gocyclo v0.0.0-20180528134321-2becd97e67ee/go.mod h1:ozx7R9SIwqmqf5pRP90DhR2Oay2UIjGuKheCBCNwAYU= +github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU= +github.com/golangci/golangci-lint v1.21.0/go.mod h1:phxpHK52q7SE+5KpPnti4oZTdFCEsn/tKN+nFvCKXfk= +github.com/golangci/ineffassign v0.0.0-20190609212857-42439a7714cc/go.mod h1:e5tpTHCfVze+7EpLEozzMB3eafxo2KT5veNg1k6byQU= +github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg= +github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o= +github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA= +github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21/go.mod h1:tf5+bzsHdTM0bsB7+8mt0GUMvjCgwLpTapNZHU8AajI= +github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0/go.mod h1:qOQCunEYvmd/TLamH+7LlVccLvUH5kZNhbCgTHoBbp4= +github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ= github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAONeMXrxql8uvOKuAZSu8aM5RUGv+1C6IJaEho= github.com/golangplus/fmt v0.0.0-20150411045040-2a5d6d7d2995/go.mod h1:lJgMEyOkYFkPcDKwRXegd+iM6E7matEszMG5HhwytU8= github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk= @@ -191,6 +288,9 @@ github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= @@ -213,16 +313,24 @@ github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.2 h1:zoNxOV7WjqXptQOVngLmcSQgXmgk4NMz1HibBchjl/I= github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +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= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +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/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/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= github.com/hashicorp/go-hclog v0.8.0 h1:z3ollgGRg8RjfJH6UVBaG54R70GFd++QOkvnJH3VSBY= github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= @@ -263,6 +371,10 @@ github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -273,19 +385,34 @@ 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/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffktY= github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= +github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190620125010-da37f6c1e481 h1:IaSjLMT6WvkoZZjspGxy3rdaTEmWLoRm49WbtVUi9sA= github.com/mailru/easyjson v0.0.0-20190620125010-da37f6c1e481/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk= github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= @@ -301,15 +428,20 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/mozilla/tls-observatory v0.0.0-20190404164649-a3c1b6cfecfd/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E= github.com/nkovacs/streamquote v1.0.0/go.mod h1:BN+NaZ2CmdKqUuTUXUEm9j95B2TRbpOWpxbJYzzgUsc= github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= 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.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -329,6 +461,7 @@ github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQ 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/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= @@ -341,10 +474,12 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= 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_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -352,20 +487,31 @@ github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7q github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/securego/gosec v0.0.0-20191002120514-e680875ea14d/go.mod h1:w5+eXa0mYznDkHaMCXA4XYffjlH+cy1oyKbfzJXa2Do= github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/shirou/gopsutil v0.0.0-20190901111213-e4ec7b275ada/go.mod h1:WWnYX4lzhCH5h/3YBfyVA3VbLYjlMZZAQcW9ojMexNc= +github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc= +github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= +github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= @@ -373,6 +519,7 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/skratchdot/open-golang v0.0.0-20190402232053-79abb63cd66e h1:VAzdS5Nw68fbf5RZ8RDVlUvPXNU6Z3jtPCK/qvm4FoQ= github.com/skratchdot/open-golang v0.0.0-20190402232053-79abb63cd66e/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34cd2MNlA9u1mE= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= @@ -389,6 +536,7 @@ github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHN github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -410,11 +558,22 @@ github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJy github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/ultraware/funlen v0.0.2/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA= +github.com/ultraware/whitespace v0.0.4/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/uudashr/gocognit v0.0.0-20190926065955-1655d0de0517/go.mod h1:j44Ayx2KW4+oB6SWMv8KsmHzZrOInQav7D3cQMJ5JUM= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= +github.com/valyala/quicktemplate v1.2.0/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4= +github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= +github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= github.com/vishvananda/netlink v0.0.0-20171020171820-b2de5d10e38e/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= github.com/vishvananda/netns v0.0.0-20171111001504-be1fbeda1936/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= github.com/vmware-tanzu/octant v0.10.2-0.20200302175445-a52392c6f48e h1:nayXBqq/FKj9MF3+QbKwfvuInEhuGS3XDUJMRPBgBXo= @@ -426,12 +585,19 @@ github.com/vmware-tanzu/octant v0.11.0/go.mod h1:ree4Qu6ULumg1MlGrAG7avEQx3+NjWP github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70= github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.1 h1:8dP3SGL7MPB94crU3bEPplMPe83FI4EouesJUeFHv50= go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= @@ -443,11 +609,18 @@ golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 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-20190611184440-5c40567a22f8 h1:1wopBVtVdWnn03fZelqdXTqk7U7zPQCb+T4rbU9ZEoU= 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-20190701094942-4def268fd1a4/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-20191202143827-86a70503ff7e h1:egKlR8l7Nu9vHGWbcUV8lqR4987UfUbBd7GbhqGzNYU= golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e/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/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -457,10 +630,13 @@ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvx golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -468,6 +644,7 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -475,12 +652,15 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc h1:gkKoSkUmnU6bpS/VhkuO27bzQeSA51uaEfbOW5dNb68= golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +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-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191204025024-5ee1b9f4859a h1:+HHJiFUXVOIS9mr1ThqkQD1N8vpFCfCShqADBM12KTc= golang.org/x/net v0.0.0-20191204025024-5ee1b9f4859a/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/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-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -490,7 +670,6 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -506,46 +685,61 @@ golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f h1:25KHgbfyiSm6vwQLbM3zZIe1v9p/3ea4Rz+nnM5K/i4= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190621203818-d432491b9138 h1:t8BZD9RDjkm9/h7yYN6kE8oaeov5r9aztkB7zKA5Tkg= golang.org/x/sys v0.0.0-20190621203818-d432491b9138/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e h1:9vRrk9YW2BTzLP0VCB9ZDjU4cPqkg+IDWL7XgxA1yxQ= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/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/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/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.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/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/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181117154741-2ddaf7f79a09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a h1:mEQZbbaBjWyLNy0tmZmgEuQAR8XOQ3hL8GYi3J/NG64= +golang.org/x/tools v0.0.0-20190719005602-e377ae9d6386/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= +golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190930201159-7c411dea38b0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191203134012-c197fd4bf371/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -554,11 +748,9 @@ gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6d gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.3.2/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= -google.golang.org/api v0.4.0 h1:KKgc1aqhV8wDPbDzlDtpvyjZFY3vjz85FP7p4wcQUyI= 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/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -573,32 +765,34 @@ google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiq google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 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 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg= gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= -gopkg.in/src-d/go-git-fixtures.v3 v3.5.0 h1:ivZFOIltbce2Mo8IjzUHAFoq/IylO9WHhNOAJK+LsJg= gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g= gopkg.in/src-d/go-git.v4 v4.13.1 h1:SRtFyV8Kxc0UP7aCHcijOMQGPxHSmMOPrzulQWolkYE= gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 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 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -607,89 +801,92 @@ honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWh 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/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= k8s.io/api v0.0.0-20190313235455-40a48860b5ab/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA= k8s.io/api v0.0.0-20191016110408-35e52d86657a/go.mod h1:/L5qH+AD540e7Cetbui1tuJeXdmNhO8jM6VkXeDdDhQ= k8s.io/api v0.0.0-20191016225839-816a9b7df678/go.mod h1:LZQaT8MvVpl7Bg2lYFcQm7+Mpdxq8p1NFl3yh+5DCwY= -k8s.io/api v0.0.0-20191114100352-16d7abae0d2a h1:86XISgFlG7lPOWj6wYLxd+xqhhVt/WQjS4Tf39rP09s= k8s.io/api v0.0.0-20191114100352-16d7abae0d2a/go.mod h1:qetVJgs5i8jwdFIdoOZ70ks0ecgU+dYwqZ2uD1srwOU= -k8s.io/api v0.17.3 h1:XAm3PZp3wnEdzekNkcmj/9Y1zdmQYJ1I4GKSBBZ8aG0= +k8s.io/api v0.17.0/go.mod h1:npsyOePkeP0CPwyGfXDHxvypiYMJxBWAMpQxCaJ4ZxI= k8s.io/api v0.17.3/go.mod h1:YZ0OTkuw7ipbe305fMpIdf3GLXZKRigjtZaV5gzC2J0= k8s.io/api v0.17.4 h1:HbwOhDapkguO8lTAE8OX3hdF2qp8GtpC9CW/MQATXXo= k8s.io/api v0.17.4/go.mod h1:5qxx6vjmwUVG2nHQTKGlLts8Tbok8PzHl4vHtVFuZCA= k8s.io/apiextensions-apiserver v0.0.0-20181213153335-0fe22c71c476 h1:Ws9zfxsgV19Durts9ftyTG7TO0A/QLhmu98VqNWLiH8= k8s.io/apiextensions-apiserver v0.0.0-20181213153335-0fe22c71c476/go.mod h1:IxkesAMoaCRoLrPJdZNZUQp9NfZnzqaVzLhb2VEQzXE= +k8s.io/apiextensions-apiserver v0.17.3/go.mod h1:CJbCyMfkKftAd/X/V6OTHYhVn7zXnDdnkUjS1h0GTeY= k8s.io/apimachinery v0.0.0-20190313205120-d7deff9243b1/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0= k8s.io/apimachinery v0.0.0-20191004115801-a2eda9f80ab8/go.mod h1:llRdnznGEAqC3DcNm6yEj472xaFVfLM7hnYofMb12tQ= k8s.io/apimachinery v0.0.0-20191016225534-b1267f8c42b4/go.mod h1:92mWDd8Ji2sw2157KIgino5wCxffA8KSvhW2oY4ypdw= k8s.io/apimachinery v0.0.0-20191028221656-72ed19daf4bb h1:ZUNsbuPdXWrj0rZziRfCWcFg9ZP31OKkziqCbiphznI= k8s.io/apimachinery v0.0.0-20191028221656-72ed19daf4bb/go.mod h1:llRdnznGEAqC3DcNm6yEj472xaFVfLM7hnYofMb12tQ= +k8s.io/apimachinery v0.17.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg= k8s.io/apimachinery v0.17.3 h1:f+uZV6rm4/tHE7xXgLyToprg6xWairaClGVkm2t8omg= k8s.io/apimachinery v0.17.3/go.mod h1:gxLnyZcGNdZTCLnq3fgzyg2A5BVCHTNDFrw8AmuJ+0g= k8s.io/apimachinery v0.17.4 h1:UzM+38cPUJnzqSQ+E1PY4YxMHIzQyCg29LOoGfo79Zw= k8s.io/apimachinery v0.17.4/go.mod h1:gxLnyZcGNdZTCLnq3fgzyg2A5BVCHTNDFrw8AmuJ+0g= -k8s.io/cli-runtime v0.0.0-20191114110141-0a35778df828 h1:g5LpwfPTlzVksjrqp3EuSFg2ihl9LZVaRhnMas5sB/s= +k8s.io/apiserver v0.17.3/go.mod h1:iJtsPpu1ZpEnHaNawpSV0nYTGBhhX2dUlnn7/QS7QiY= k8s.io/cli-runtime v0.0.0-20191114110141-0a35778df828/go.mod h1:r9ARs2FUnSgInbeN4+mo9nFzf7oqUtRZ3tcuEcoelR4= +k8s.io/cli-runtime v0.17.3/go.mod h1:X7idckYphH4SZflgNpOOViSxetiMj6xI0viMAjM81TA= 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/client-go v0.17.0/go.mod h1:TYgR6EUHs6k45hb6KWjVD6jFZvJV4gHDikv/It0xz+k= +k8s.io/client-go v0.17.3/go.mod h1:cLXlTMtWHkuK4tD360KpWz2gG2KtdWEr/OT02i3emRQ= +k8s.io/client-go v11.0.0+incompatible h1:LBbX2+lOwY9flffWlJM7f1Ct8V2SRNiMRDFeiwnJo9o= +k8s.io/client-go v11.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= k8s.io/code-generator v0.0.0-20191004115455-8e001e5d1894/go.mod h1:mJUgkl06XV4kstAnLHAIzJPVCOzVR+ZcfPIv4fUsFCY= -k8s.io/component-base v0.0.0-20191114102325-35a9586014f7 h1:hP5E/l4EbHF45xX0vq3kDz/p0UNX0t1AmOC0MUyFXME= +k8s.io/code-generator v0.17.3/go.mod h1:l8BLVwASXQZTo2xamW5mQNFCe1XPiAesVq7Y1t7PiQQ= k8s.io/component-base v0.0.0-20191114102325-35a9586014f7/go.mod h1:9rNMvrwbqPF4MxI+VQYETrWqMKxi8yAd8YZLdSJ9EDw= +k8s.io/component-base v0.17.3/go.mod h1:GeQf4BrgelWm64PXkIXiPh/XS0hnO42d9gx9BtbZRp8= 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= k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v0.3.3/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.4.0 h1:lCJCxf/LIowc2IGS9TPjWDyXY4nOmdGdfcwwDQCOURQ= 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/kube-openapi v0.0.0-20190603182131-db7b694dc208/go.mod h1:nfDlWeOsu3pUf4yWGL+ERqohP4YsZcBJXWMK+gkzOA4= -k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf h1:EYm5AW/UUDbnmnI+gK0TJDVK9qPLhM+sRHYanNKw0EQ= 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/kubectl v0.0.0-20191114113550-6123e1c827f7 h1:J4a49dHJowmwpCnPsIlbUlh1XE+Elq4P++/uSbKPtR0= k8s.io/kubectl v0.0.0-20191114113550-6123e1c827f7/go.mod h1:MYrMrU6JgEeGVz2sFggJizAfyoRjwsP4iTQcP8iQS00= -k8s.io/kubernetes v1.14.0 h1:6T2iAEoOYQnzQb3WvPlUkcczEEXZ7+YPlAO8olwujRw= +k8s.io/kubectl v0.17.3/go.mod h1:NUn4IBY7f7yCMwSop2HCXlw/MVYP4HJBiUmOR3n9w28= k8s.io/kubernetes v1.14.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/metrics v0.0.0-20191016113814-3b1a734dba6e/go.mod h1:ve7/vMWeY5lEBkZf6Bt5TTbGS3b8wAxwGbdXAsufjRs= -k8s.io/metrics v0.0.0-20191114105837-a4a2842dc51b h1:m3J9OgwaQIvqJ+QZxIPfC/+gXGjmJn9lH6wEZN9qxk8= k8s.io/metrics v0.0.0-20191114105837-a4a2842dc51b/go.mod h1:+OP14I2yuLAiYCsEB4pC2101W6tZ2rC9uSZFvR3DEJg= +k8s.io/metrics v0.17.3/go.mod h1:HEJGy1fhHOjHggW9rMDBJBD3YuGroH3Y1pnIRw9FFaI= +k8s.io/utils v0.0.0-20190801114015-581e00157fb1 h1:+ySTxfHnfzZb9ys375PXNlLhkJPLKgHajBU0N62BDvE= k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20191010214722-8d271d903fe4 h1:Gi+/O1saihwDqnlmC8Vhv1M5Sp4+rbOmK9TbsLn8ZEA= k8s.io/utils v0.0.0-20191010214722-8d271d903fe4/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= 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= modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= -opendev.org/airship/airshipctl v0.0.0-20200319213630-b2a602fa07e0 h1:PPW+Zyjw96vwdnrKUnlpOjtajihO/1unrOe8C5bH1ok= -opendev.org/airship/airshipctl v0.0.0-20200319213630-b2a602fa07e0/go.mod h1:2Ox64Dscbq8HGJjP9s3Mvpfgcn+lRxBKP7n1vD/RnwE= +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-20200324160507-db6217f011b9 h1:0zG4z8113K+eWcWwGJuLv6YhupuoW87ugYZQjr0cW6s= opendev.org/airship/airshipctl v0.0.0-20200324160507-db6217f011b9/go.mod h1:2Ox64Dscbq8HGJjP9s3Mvpfgcn+lRxBKP7n1vD/RnwE= -opendev.org/airship/go-redfish v0.0.0-20200110185254-3ab47e28bae8 h1:wIvgHcZH/l7H/eTIjZXGfqurtarlaAv2CtnO/8xYCis= +opendev.org/airship/airshipctl v0.0.0-20200423182827-585f885409be h1:dDM/pWMJy+oFG8jKRvlAK6YUNPqyRmFCOAbnhMtz4QA= +opendev.org/airship/airshipctl v0.0.0-20200423182827-585f885409be/go.mod h1:ZNIAIaAHPhE+RsT+fmzDIZpl9PsI/bxE2rjHoyGp6G8= opendev.org/airship/go-redfish v0.0.0-20200110185254-3ab47e28bae8/go.mod h1:FEjYcb3bYBWGpQIqtvVM0NrT5eyjlCOCj5JNf4lI+6s= 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-20200110185254-3ab47e28bae8 h1:ZDtcVfkn6EzA6Kr5I7C3EuN8qsuByeMPII9TnTx1zXQ= opendev.org/airship/go-redfish/client v0.0.0-20200110185254-3ab47e28bae8/go.mod h1:LPq6GmtxEl3Pwf0Ora40EOm46Hz425T9kjsOfG5HKmQ= 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= -rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0= sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU= -sigs.k8s.io/kustomize/v3 v3.2.0 h1:EKcEubO29vCbigcMoNynfyZH+ANWkML2UHWibt1Do7o= +sigs.k8s.io/kustomize/api v0.3.1/go.mod h1:A+ATnlHqzictQfQC1q3KB/T6MSr0UWQsrrLxMWkge2E= sigs.k8s.io/kustomize/v3 v3.2.0/go.mod h1:ztX4zYc/QIww3gSripwF7TBOarBTm5BvyAMem0kCzOE= -sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e h1:4Z09Hglb792X0kfOBBJUPFEyvVfQWrYT/l8h5EKA6JQ= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= -sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= -sigs.k8s.io/structured-merge-diff/v3 v3.0.0 h1:dOmIZBMfhcHS09XZkMyUgkq5trg3/jRyJYFZUiaOp8E= -sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= +sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18= sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= +vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI= diff --git a/internal/commands/dash.go b/internal/commands/dash.go deleted file mode 100644 index 162dc94..0000000 --- a/internal/commands/dash.go +++ /dev/null @@ -1,129 +0,0 @@ -/* -Copyright (c) 2019 the Octant contributors. All Rights Reserved. -SPDX-License-Identifier: Apache-2.0 -*/ - -package commands - -import ( - "fmt" - "os" - "os/signal" - "syscall" - "time" - - gocmd "github.com/go-cmd/cmd" - "github.com/spf13/cobra" -) - -var verboseLevel int - -func RunOctantWithOptions(kubeConfigPath string, args []string) { - // cobra command processing has already taken place and has processed and removed all flags and - // their options from args. Since this is a temporary workaround it is not worth the effort to - // extract all of these flags back out of cmd and reconstruct the original command line. But - // the most important options will be reconstructed and forwarded - myArgs := args - for i := 0; i < verboseLevel; i++ { - myArgs = append(myArgs, "-v") - } - - if len(kubeConfigPath) > 0 { - myArgs = append(myArgs, "--kubeconfig", kubeConfigPath) - } - - // The code in this function should be replaced by the body of the Run function in octant's - // internal/commands/dash.go in order to launch the octant code directly as a function call. But - // as of v0.9.1 octant still cannot be called directly due to incorrect leakage of private types - // into its public interface. This is captured in issue 448 - // (https://github.com/vmware-tanzu/octant/issues/448). Specifically, the logger that is - // required to be passed to dash.Run is an *internal* type! As a temporary workaround, launch a - // separate instance of octant exec.Command will find the given command in the PATH. - - // golang does not have any direct support for executing a command and forwarding along its - // stdout/stderr. The only safe approach is to use a 3rd-party library, go-cmd, that takes - // care of race conditions and other anomalies but still requires supplying functions to read - // the data from a stream and explicitly write it to stderr or stdout How stupid! - // The code here is based on the example from go-cmd: - // https://github.com/go-cmd/cmd/blob/master/examples/blocking-streaming/main.go - - sigs := make(chan os.Signal, 1) - - signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) - - cmdOptions := gocmd.Options{ - Buffered: false, - Streaming: true, - } - - command := gocmd.NewCmdOptions(cmdOptions, "octant", myArgs...) - - // print stdout and stderr lines streaming from command - go func() { - for { - select { - case line := <-command.Stdout: - fmt.Println(line) - case line := <-command.Stderr: - fmt.Fprintln(os.Stderr, line) - } - } - }() - - // Start the command to grab its channel but do not block yet - statusChan := command.Start() - - // If the parent (airshipui) receives a signal to terminate, then kill the child octant process - go func() { - <-sigs - stat := command.Status() - if stat.PID > 0 { - proc, err := os.FindProcess(stat.PID) - if err == nil { - err = proc.Kill() - if err != nil { - fmt.Printf("error trying to kill process: %s\n", err) - } - } - } - }() - - // run and wait for octant to exit - status := <-statusChan - - // command has finished but wait for goroutine to print all lines - for len(command.Stdout) > 0 || len(command.Stderr) > 0 { - time.Sleep(10 * time.Millisecond) - } - - if status.Exit != 0 { - if status.Error != nil { - fmt.Println(status.Error) - } - os.Exit(status.Exit) - } -} - -// some day this may need to get refactored if the options become transportable from external sources -func addDashboardFlags(cmd *cobra.Command) { - cmd.Flags().SortFlags = true - - // octant specific flags - cmd.Flags().StringP("context", "", "", "initial context") - cmd.Flags().BoolP("disable-cluster-overview", "", false, "disable cluster overview") - cmd.Flags().BoolP("enable-feature-applications", "", false, "enable applications feature") - cmd.Flags().String("kubeconfig", "", "absolute path to kubeConfig file") - cmd.Flags().StringP("namespace", "n", "", "initial namespace") - cmd.Flags().StringP("plugin-path", "", "", "plugin path") - cmd.Flags().BoolP("verbose", "v", false, "turn on debug logging") - cmd.Flags().StringP("accepted-hosts", "", "", "accepted hosts list [DEV]") - cmd.Flags().Float32P("client-qps", "", 200, "maximum QPS for client [DEV]") - cmd.Flags().IntP("client-burst", "", 400, "maximum burst for client throttle [DEV]") - cmd.Flags().BoolP("disable-open-browser", "", false, "disable automatic launching of the browser [DEV]") - cmd.Flags().BoolP("enable-opencensus", "c", false, "enable open census [DEV]") - cmd.Flags().IntP("klog-verbosity", "", 0, "klog verbosity level [DEV]") - cmd.Flags().StringP("listener-addr", "", "", "listener address for the octant frontend [DEV]") - cmd.Flags().StringP("local-content", "", "", "local content path [DEV]") - cmd.Flags().StringP("proxy-frontend", "", "", "url to send frontend request to [DEV]") - cmd.Flags().String("ui-url", "", "dashboard url [DEV]") -} diff --git a/internal/commands/root.go b/internal/commands/root.go index 7ee5fd9..4fec29f 100644 --- a/internal/commands/root.go +++ b/internal/commands/root.go @@ -5,31 +5,32 @@ SPDX-License-Identifier: Apache-2.0 package commands import ( - "fmt" + "log" "os" "github.com/spf13/cobra" "github.com/spf13/pflag" "opendev.org/airship/airshipctl/pkg/config" - ctlenv "opendev.org/airship/airshipctl/pkg/environment" - "opendev.org/airship/airshipui/internal/environment" + "opendev.org/airship/airshipctl/pkg/environment" + "opendev.org/airship/airshipui/internal/electron" + "opendev.org/airship/airshipui/internal/webservice" ) var ( - settings *ctlenv.AirshipCTLSettings + settings *environment.AirshipCTLSettings ) // rootCmd represents the base command when called without any subcommands var rootCmd = &cobra.Command{ Use: "airshipui", Short: "airshipui is a graphical user interface for airship", - Run: launchOctant, - Version: environment.Version(), + Run: launch, + Version: Version(), } func init() { - settings = &ctlenv.AirshipCTLSettings{} + settings = &environment.AirshipCTLSettings{} // Add options to rootCmd for configuration of airshipctl and kube config settings.InitFlags(rootCmd) @@ -44,44 +45,27 @@ func init() { addDashboardFlags(rootCmd) } -func launchOctant(cmd *cobra.Command, args []string) { +func launch(cmd *cobra.Command, args []string) { // only process args if there are any if cmd.Flags().NFlag() > 0 { args = append(args, getFlags(cmd)...) - fmt.Printf("Executing Octant with the following args: %v\n", args) + log.Printf("Executing AirshipUI with the following args: %v\n", args) } - kubeConfig := "" - airshipKubeConfig := settings.KubeConfigPath() + // start the webservice + go webservice.WebServer() - // If the kubeconfig specified on the command line (or defaulted to) does not exist, - // then do not pass specify a kubeconfig. This will permit the underlying octant - // to use its own default, which can be either ~/.kube/config or grabbed from the - // environment - if fileExists(airshipKubeConfig) { - kubeConfig = airshipKubeConfig - } - - RunOctantWithOptions(kubeConfig, args) + // start the electron app + electron.RunElectron() } func Execute() { if err := rootCmd.Execute(); err != nil { - fmt.Println(err) + log.Println(err) os.Exit(1) } } -// Determine whether the given filename exists and is accessible -func fileExists(filename string) bool { - f, err := os.Stat(filename) - if err != nil { - return false - } - - return !f.IsDir() -} - // this function pulls the passed command line options and renders unto octant what is octant's func getFlags(cmd *cobra.Command) []string { var args []string @@ -106,3 +90,15 @@ func getFlags(cmd *cobra.Command) []string { return args } + +// some day this may need to get refactored if the options become transportable from external sources +func addDashboardFlags(cmd *cobra.Command) { + cmd.Flags().SortFlags = true + + // octant specific flags + cmd.Flags().StringP("context", "", "", "initial context") + cmd.Flags().String("kubeconfig", "", "absolute path to kubeConfig file") + cmd.Flags().StringP("namespace", "n", "", "initial namespace") + cmd.Flags().StringP("plugin-path", "", "", "plugin path") + cmd.Flags().BoolP("verbose", "v", false, "turn on debug logging") +} diff --git a/internal/commands/version.go b/internal/commands/version.go index 5d3a8c9..ee0024f 100644 --- a/internal/commands/version.go +++ b/internal/commands/version.go @@ -9,7 +9,11 @@ import ( "fmt" "github.com/spf13/cobra" - env "opendev.org/airship/airshipui/internal/environment" +) + +var ( + // version will be overridden by ldflags supplied in Makefile + version = "(dev-version)" ) func newVersionCmd() *cobra.Command { @@ -20,8 +24,12 @@ func newVersionCmd() *cobra.Command { Run: func(cmd *cobra.Command, args []string) { out := cmd.OutOrStdout() - fmt.Fprintln(out, "airshipui version", env.Version()) + fmt.Fprintln(out, "airshipui version", Version()) }, } return versionCmd } + +func Version() string { + return version +} diff --git a/internal/electron/app.go b/internal/electron/app.go new file mode 100755 index 0000000..35ec6bb --- /dev/null +++ b/internal/electron/app.go @@ -0,0 +1,42 @@ +/* +Copyright (c) 2020 AT&T. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ +package electron + +import ( + "log" + "os/exec" + "syscall" +) + +// RunElectron executes the standalone electron app which serves up our web components +func RunElectron() { + cmd := exec.Command("npm", "start", "--prefix", "web") + + // make sure the logs are coming out of electron app + //cmd.Stdout = os.Stdout + // cmd.Stderr = os.Stderr + + if err := cmd.Run(); err != nil { + if exitError, ok := err.(*exec.ExitError); ok { + log.Fatalf("Exit with error: %v", exitError) + } + } + + if err := cmd.Wait(); err != nil { + if exiterr, ok := err.(*exec.ExitError); ok { + // The program has exited with an exit code != 0 + + // This works on both Unix and Windows. Although package + // syscall is generally platform dependent, WaitStatus is + // defined for both Unix and Windows and in both cases has + // an ExitStatus() method with the same signature. + if status, ok := exiterr.Sys().(syscall.WaitStatus); ok { + log.Printf("Exit Status: %d", status.ExitStatus()) + } + } else { + log.Fatalf("Electron app close detected, exiting") + } + } +} diff --git a/internal/environment/version.go b/internal/environment/version.go deleted file mode 100644 index af290d2..0000000 --- a/internal/environment/version.go +++ /dev/null @@ -1,10 +0,0 @@ -package environment - -var ( - // version will be overridden by ldflags supplied in Makefile - version = "(dev-version)" -) - -func Version() string { - return version -} diff --git a/cmd/openstack/README.md b/internal/plugin/airshipopenstack/README.md similarity index 100% rename from cmd/openstack/README.md rename to internal/plugin/airshipopenstack/README.md diff --git a/internal/plugin/openstack/compute.go b/internal/plugin/airshipopenstack/compute.go similarity index 58% rename from internal/plugin/openstack/compute.go rename to internal/plugin/airshipopenstack/compute.go index 347d084..25c5a4a 100755 --- a/internal/plugin/openstack/compute.go +++ b/internal/plugin/airshipopenstack/compute.go @@ -3,7 +3,7 @@ Copyright (c) 2020 AT&T. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ -package plugin +package airshipopenstack import ( "log" @@ -16,13 +16,13 @@ import ( "github.com/gophercloud/gophercloud/openstack/compute/v2/images" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" "github.com/gophercloud/gophercloud/pagination" - "github.com/vmware-tanzu/octant/pkg/view/component" ) // gets OpenStack flavors that are available for the tenant // https://docs.openstack.org/nova/latest/user/flavors.html -func getFlavors(osp *OpenstackPlugin) component.Component { - rows := []component.TableRow{} +func GetFlavors() []map[string]string { + osp := NewOpenstackPlugin() + m := make([]map[string]string, 0) err := flavors.ListDetail(computeClientHelper(osp), flavors.ListOpts{AccessType: flavors.AllAccess}).EachPage( func(page pagination.Page) (bool, error) { @@ -36,15 +36,15 @@ func getFlavors(osp *OpenstackPlugin) component.Component { name := flavor.Name flavorCache[flavor.ID] = name - rows = append(rows, component.TableRow{ - "Name": component.NewText(name), - "VCPUs": component.NewText(strconv.Itoa(flavor.VCPUs)), - "RAM": component.NewText(strconv.Itoa(flavor.RAM)), - "Root Disk": component.NewText(strconv.Itoa(flavor.Disk)), - "Ephemeral Disk": component.NewText(strconv.Itoa(flavor.Ephemeral)), - "Swap Disk": component.NewText(strconv.Itoa(flavor.Swap)), - "RX/TX factor": component.NewText(strconv.FormatFloat(flavor.RxTxFactor, 'f', 1, 64)), - "Public": component.NewText(strconv.FormatBool(flavor.IsPublic)), + m = append(m, map[string]string{ + "Name": name, + "VCPUs": strconv.Itoa(flavor.VCPUs), + "RAM": strconv.Itoa(flavor.RAM), + "Root Disk": strconv.Itoa(flavor.Disk), + "Ephemeral Disk": strconv.Itoa(flavor.Ephemeral), + "Swap Disk": strconv.Itoa(flavor.Swap), + "RX/TX factor": strconv.FormatFloat(flavor.RxTxFactor, 'f', 1, 64), + "Public": strconv.FormatBool(flavor.IsPublic), }) } @@ -55,18 +55,14 @@ func getFlavors(osp *OpenstackPlugin) component.Component { log.Printf("compute flavor list error: %s\n", err) } - return component.NewTableWithRows( - "Flavors", - "No flavors found", - component.NewTableCols("Name", "VCPUs", "RAM", "Root Disk", "Ephemeral Disk", - "Swap Disk", "RX/TX factor", "Public"), - rows) + return m } // gets OpenStack images that are available for the tenant // https://docs.openstack.org/image-guide/ -func getImages(osp *OpenstackPlugin) component.Component { - rows := []component.TableRow{} +func GetImages() []map[string]string { + osp := NewOpenstackPlugin() + m := make([]map[string]string, 0) err := images.ListDetail(computeClientHelper(osp), images.ListOpts{}).EachPage( func(page pagination.Page) (bool, error) { @@ -81,10 +77,10 @@ func getImages(osp *OpenstackPlugin) component.Component { name := image.Name imageCache[image.ID] = name - rows = append(rows, component.TableRow{ - "Name": component.NewText(name), - "Type": component.NewText(strconv.Itoa(image.Progress)), - "Status": component.NewText(image.Status), + m = append(m, map[string]string{ + "Name": name, + "Type": strconv.Itoa(image.Progress), + "Status": image.Status, }) } @@ -95,16 +91,14 @@ func getImages(osp *OpenstackPlugin) component.Component { log.Printf("compute image list error: %s\n", err) } - return component.NewTableWithRows( - "Images", - "No images found", - component.NewTableCols("Name", "Type", "Status"), rows) + return m } // gets OpenStack vms that are configured for the tenant // https://docs.openstack.org/python-openstackclient/latest/cli/command-objects/server.html -func getVMs(osp *OpenstackPlugin) component.Component { - rows := []component.TableRow{} +func GetVMs() []map[string]string { + osp := NewOpenstackPlugin() + m := make([]map[string]string, 0) err := servers.List(computeClientHelper(osp), servers.ListOpts{AllTenants: true}).EachPage( func(page pagination.Page) (bool, error) { @@ -134,14 +128,20 @@ func getVMs(osp *OpenstackPlugin) component.Component { addresses = tmp[0] } - rows = append(rows, component.TableRow{ - "Instance Name": component.NewText(name), - "Image Name": component.NewText(imageCache[server.Image["id"].(string)]), - "IP Address": component.NewText(addresses), - "Flavor": component.NewText(flavorCache[server.Flavor["id"].(string)]), - "Key Pair": component.NewText(server.KeyName), - "Status": component.NewText(server.Status), - "Created": component.NewText(server.Created.Local().String()), + if len(imageCache) == 0 { + GetImages() + } + if len(flavorCache) == 0 { + GetFlavors() + } + m = append(m, map[string]string{ + "Instance Name": name, + "Image Name": imageCache[server.Image["id"].(string)], + "IP Address": addresses, + "Flavor": flavorCache[server.Flavor["id"].(string)], + "Key Pair": server.KeyName, + "Status": server.Status, + "Created": server.Created.Local().String(), }) } @@ -152,10 +152,7 @@ func getVMs(osp *OpenstackPlugin) component.Component { log.Printf("compute server list error: %s\n", err) } - return component.NewTableWithRows( - "Servers", - "No servers found", - component.NewTableCols("Instance Name", "Image Name", "IP Address", "Flavor", "Key Pair", "Status", "Created"), rows) + return m } // helper function to create a compute specific gophercloud client diff --git a/internal/plugin/openstack/identity.go b/internal/plugin/airshipopenstack/identity.go similarity index 64% rename from internal/plugin/openstack/identity.go rename to internal/plugin/airshipopenstack/identity.go index 7842492..21d8b0d 100755 --- a/internal/plugin/openstack/identity.go +++ b/internal/plugin/airshipopenstack/identity.go @@ -2,7 +2,7 @@ Copyright (c) 2020 AT&T. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ -package plugin +package airshipopenstack import ( "encoding/json" @@ -15,13 +15,13 @@ import ( "github.com/gophercloud/gophercloud/openstack/identity/v3/projects" "github.com/gophercloud/gophercloud/openstack/identity/v3/users" "github.com/gophercloud/gophercloud/pagination" - "github.com/vmware-tanzu/octant/pkg/view/component" ) // gets OpenStack domains that are viewable by the tenant // https://docs.openstack.org/security-guide/identity/domains.html -func getDomains(osp *OpenstackPlugin) component.Component { - rows := []component.TableRow{} +func GetDomains() []map[string]string { + osp := NewOpenstackPlugin() + m := make([]map[string]string, 0) err := domains.List(identityClientHelper(osp), domains.ListOpts{}).EachPage( func(page pagination.Page) (bool, error) { @@ -35,10 +35,10 @@ func getDomains(osp *OpenstackPlugin) component.Component { for _, domain := range domainList { name := domain.Name domainCache[domain.ID] = name - rows = append(rows, component.TableRow{ - "Name": component.NewText(name), - "Description": component.NewText(domain.Description), - "Enabled": component.NewText(strconv.FormatBool(domain.Enabled)), + m = append(m, map[string]string{ + "Name": name, + "Description": domain.Description, + "Enabled": strconv.FormatBool(domain.Enabled), }) } @@ -49,16 +49,14 @@ func getDomains(osp *OpenstackPlugin) component.Component { log.Printf("identity domain list error: %s\n", err) } - return component.NewTableWithRows( - "Domains", - "No domains found", - component.NewTableCols("Name", "Description", "Enabled"), rows) + return m } // gets OpenStack projects that are viewable by the tenant // https://docs.openstack.org/keystone/latest/admin/cli-manage-projects-users-and-roles.html -func getProjects(osp *OpenstackPlugin) component.Component { - rows := []component.TableRow{} +func GetProjects() []map[string]string { + osp := NewOpenstackPlugin() + m := make([]map[string]string, 0) err := projects.List(identityClientHelper(osp), projects.ListOpts{}).EachPage( func(page pagination.Page) (bool, error) { @@ -73,10 +71,10 @@ func getProjects(osp *OpenstackPlugin) component.Component { name := project.Name projectCache[project.ID] = name - rows = append(rows, component.TableRow{ - "Enabled": component.NewText(strconv.FormatBool(project.Enabled)), - "Description": component.NewText(project.Description), - "Name": component.NewText(name), + m = append(m, map[string]string{ + "Enabled": strconv.FormatBool(project.Enabled), + "Description": project.Description, + "Name": name, }) } @@ -87,16 +85,14 @@ func getProjects(osp *OpenstackPlugin) component.Component { log.Printf("identity project list error: %s\n", err) } - return component.NewTableWithRows( - "Projects", - "No projects found", - component.NewTableCols("Name", "Enabled", "Description"), rows) + return m } // gets OpenStack uesrs that are viewable by the tenant // https://docs.openstack.org/keystone/latest/user/index.html -func getUsers(osp *OpenstackPlugin) component.Component { - rows := []component.TableRow{} +func GetUsers() []map[string]string { + osp := NewOpenstackPlugin() + m := make([]map[string]string, 0) err := users.List(identityClientHelper(osp), users.ListOpts{}).EachPage( func(page pagination.Page) (bool, error) { @@ -118,12 +114,15 @@ func getUsers(osp *OpenstackPlugin) component.Component { email = string(b) } - rows = append(rows, component.TableRow{ - "Name": component.NewText(user.Name), - "Email": component.NewText(email), - "Description": component.NewText(user.Description), - "Enabled": component.NewText(strconv.FormatBool(user.Enabled)), - "Domain Name": component.NewText(domainCache[user.DomainID]), + if len(domainCache) == 0 { + GetDomains() + } + m = append(m, map[string]string{ + "Name": user.Name, + "Email": email, + "Description": user.Description, + "Enabled": strconv.FormatBool(user.Enabled), + "Domain Name": domainCache[user.DomainID], }) } @@ -134,10 +133,7 @@ func getUsers(osp *OpenstackPlugin) component.Component { log.Printf("identity user list error: %s\n", err) } - return component.NewTableWithRows( - "Users", - "No users found", - component.NewTableCols("Name", "Email", "Description", "Enabled", "Domain Name"), rows) + return m } // helper function to create an identity specific gophercloud client diff --git a/internal/plugin/openstack/network.go b/internal/plugin/airshipopenstack/network.go similarity index 62% rename from internal/plugin/openstack/network.go rename to internal/plugin/airshipopenstack/network.go index 9ecc077..cf01242 100755 --- a/internal/plugin/openstack/network.go +++ b/internal/plugin/airshipopenstack/network.go @@ -3,7 +3,7 @@ Copyright (c) 2020 AT&T. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ -package plugin +package airshipopenstack import ( "log" @@ -15,13 +15,13 @@ import ( "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" "github.com/gophercloud/gophercloud/openstack/networking/v2/subnets" "github.com/gophercloud/gophercloud/pagination" - "github.com/vmware-tanzu/octant/pkg/view/component" ) // gets OpenStack networks available for the tenant // https://docs.openstack.org/api-ref/network/v2/#networks -func getNetworks(osp *OpenstackPlugin) component.Component { - rows := []component.TableRow{} +func GetNetworks() []map[string]string { + osp := NewOpenstackPlugin() + m := make([]map[string]string, 0) err := networks.List(networkClientHelper(osp), networks.ListOpts{}).EachPage( func(page pagination.Page) (bool, error) { @@ -48,14 +48,17 @@ func getNetworks(osp *OpenstackPlugin) component.Component { subnetString = subnetCache[n.Subnets[0]] } - rows = append(rows, component.TableRow{ - "Name": component.NewText(n.Name), - "Project": component.NewText(projectCache[n.ProjectID]), - "Subnets": component.NewText(subnetString), - "Shared": component.NewText(strconv.FormatBool(n.Shared)), - "Status": component.NewText(n.Status), - "Admin State": component.NewText(strconv.FormatBool(n.AdminStateUp)), - "Availability Zones": component.NewText(strings.Join(n.AvailabilityZoneHints, ", ")), + if len(projectCache) == 0 { + GetProjects() + } + m = append(m, map[string]string{ + "Name": n.Name, + "Project": projectCache[n.ProjectID], + "Subnets": subnetString, + "Shared": strconv.FormatBool(n.Shared), + "Status": n.Status, + "Admin State": strconv.FormatBool(n.AdminStateUp), + "Availability Zones": strings.Join(n.AvailabilityZoneHints, ", "), }) } @@ -66,18 +69,14 @@ func getNetworks(osp *OpenstackPlugin) component.Component { log.Printf("network list error: %s\n", err) } - return component.NewTableWithRows( - "Networks", - "No networks found", - component.NewTableCols("Name", "Project", "Subnets", "Shared", "Status", - "Admin State", "Availability Zones"), - rows) + return m } // gets OpenStack subnets available for the tenant // https://docs.openstack.org/api-ref/network/v2/#subnets -func getSubnets(osp *OpenstackPlugin) component.Component { - rows := []component.TableRow{} +func GetSubnets() []map[string]string { + osp := NewOpenstackPlugin() + m := make([]map[string]string, 0) err := subnets.List(networkClientHelper(osp), subnets.ListOpts{}).EachPage( func(page pagination.Page) (bool, error) { @@ -93,11 +92,11 @@ func getSubnets(osp *OpenstackPlugin) component.Component { name := subnet.Name subnetCache[subnet.ID] = name + ": " + cidr - rows = append(rows, component.TableRow{ - "Name": component.NewText(name), - "Network Address": component.NewText(cidr), - "IP Version": component.NewText("IPv" + strconv.Itoa(subnet.IPVersion)), - "Gateway IP": component.NewText(subnet.GatewayIP), + m = append(m, map[string]string{ + "Name": name, + "Network Address": cidr, + "IP Version": "IPv" + strconv.Itoa(subnet.IPVersion), + "Gateway IP": subnet.GatewayIP, }) } @@ -107,10 +106,7 @@ func getSubnets(osp *OpenstackPlugin) component.Component { if err != nil { log.Printf("network subnet list error: %s\n", err) } - return component.NewTableWithRows( - "Subnets", - "No subnets found", - component.NewTableCols("Name", "Network Address", "IP Version", "Gateway IP"), rows) + return m } // helper function to create a network specific gophercloud client diff --git a/internal/plugin/openstack/register.go b/internal/plugin/airshipopenstack/plugin.go similarity index 62% rename from internal/plugin/openstack/register.go rename to internal/plugin/airshipopenstack/plugin.go index a0aeda3..9358187 100755 --- a/internal/plugin/openstack/register.go +++ b/internal/plugin/airshipopenstack/plugin.go @@ -3,7 +3,7 @@ Copyright (c) 2020 AT&T. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ -package plugin +package airshipopenstack import ( "encoding/json" @@ -14,10 +14,6 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack" - "github.com/vmware-tanzu/octant/pkg/navigation" - "github.com/vmware-tanzu/octant/pkg/plugin" - "github.com/vmware-tanzu/octant/pkg/plugin/service" - "github.com/vmware-tanzu/octant/pkg/view/component" ) // OpenstackPlugin is an example of a plugin that registers navigation and action handlers @@ -61,20 +57,24 @@ var subnetCache = map[string]string{} // This is updated whenever we go into the getVms compute function var vmCache = map[string]string{} +var ospConn *OpenstackPlugin + // NewOpenstackPlugin authenticates to Openstack and return a new openstack plugin struct // this requires an openstack.json in the home dir of the user running the process func NewOpenstackPlugin() *OpenstackPlugin { - opts, err := getOpenstackOpts() - if err != nil { - log.Printf("Error getting authentication opts %s\n", err) - } + if ospConn == nil { + opts, err := getOpenstackOpts() + if err != nil { + log.Printf("Error getting authentication opts %s\n", err) + } - client, err := openstack.AuthenticatedClient(opts) - if err != nil { - log.Printf("Error establishing client %s\n", err) + client, err := openstack.AuthenticatedClient(opts) + if err != nil { + log.Printf("Error establishing client %s\n", err) + } + ospConn = &OpenstackPlugin{client} } - - return &OpenstackPlugin{client} + return ospConn } // helper function to bring in the connection opts for OpenStack @@ -148,58 +148,3 @@ func getOptsFromFile() (gophercloud.AuthOptions, error) { return opts, err } - -// Register the plugin with octant -func Register(name string, description string) (*service.Plugin, error) { - osp := NewOpenstackPlugin() - - // Remove the prefix from the go logger since Octant will print logs with timestamps. - log.SetPrefix("") - - // Tell Octant to call this plugin when printing configuration or tabs for Pods - capabilities := &plugin.Capabilities{ - IsModule: true, - } - - // Set up what should happen when Octant calls this plugin. - options := []service.PluginOption{ - service.WithNavigation(osp.handleNavigation, osp.initRoutes), - } - - // Use the plugin service helper to register this plugin. - return service.Register(name, description, capabilities, options...) -} - -// handlePrint creates a navigation tree for this plugin. Navigation is dynamic and will -// be called frequently from Octant. Navigation is a tree of `Navigation` structs. -// The plugin can use whatever paths it likes since these paths can be namespaced to the -// the plugin. -func (osp *OpenstackPlugin) handleNavigation(request *service.NavigationRequest) (navigation.Navigation, error) { - return navigation.Navigation{ - Title: "OpenStack", - Path: request.GeneratePath(), - IconName: "cloud", - }, nil -} - -// initRoutes routes for this plugin. In this example, there is a global catch all route -// that will return the content for every single path. -func (osp *OpenstackPlugin) initRoutes(router *service.Router) { - router.HandleFunc("", osp.routeHandler) -} - -// Adds the OpenStack components to the visualization -func (osp *OpenstackPlugin) routeHandler(request service.Request) (component.ContentResponse, error) { - response := component.NewContentResponse(component.TitleFromString("OpenStack")) - response.Add(getDomains(osp)) - response.Add(getUsers(osp)) - response.Add(getFlavors(osp)) - response.Add(getSubnets(osp)) - response.Add(getNetworks(osp)) - response.Add(getImages(osp)) - response.Add(getProjects(osp)) - response.Add(getVMs(osp)) - response.Add(getVolumes(osp)) - - return *response, nil -} diff --git a/internal/plugin/openstack/volume.go b/internal/plugin/airshipopenstack/volume.go similarity index 57% rename from internal/plugin/openstack/volume.go rename to internal/plugin/airshipopenstack/volume.go index 270289e..af11e90 100755 --- a/internal/plugin/openstack/volume.go +++ b/internal/plugin/airshipopenstack/volume.go @@ -2,7 +2,7 @@ Copyright (c) 2020 AT&T. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ -package plugin +package airshipopenstack import ( "log" @@ -13,13 +13,13 @@ import ( "github.com/gophercloud/gophercloud/openstack" "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes" "github.com/gophercloud/gophercloud/pagination" - "github.com/vmware-tanzu/octant/pkg/view/component" ) // gets OpenStack volumes viewable by the tenant // https://docs.openstack.org/cinder/latest/ -func getVolumes(osp *OpenstackPlugin) component.Component { - rows := []component.TableRow{} +func GetVolumes() []map[string]string { + osp := NewOpenstackPlugin() + m := make([]map[string]string, 0) err := volumes.List(volumeClientHelper(osp), volumes.ListOpts{}).EachPage( func(page pagination.Page) (bool, error) { @@ -33,21 +33,24 @@ func getVolumes(osp *OpenstackPlugin) component.Component { for _, volume := range volumeList { // extract the potentially multiple devices var attachedDevices []string + if len(vmCache) == 0 { + GetVMs() + } for index := range volume.Attachments { attachment := volume.Attachments[index] attachedDevices = append(attachedDevices, vmCache[attachment.ServerID]+" at "+attachment.Device) } - rows = append(rows, component.TableRow{ - "Name": component.NewText(volume.Name), - "Description": component.NewText(volume.Description), - "Size": component.NewText(strconv.Itoa(volume.Size) + "GiB"), - "Status": component.NewText(volume.Status), - "Type": component.NewText(volume.VolumeType), - "Attached To": component.NewText(strings.Join(attachedDevices, ", ")), - "Availability Zone": component.NewText(volume.AvailabilityZone), - "Bootable": component.NewText(volume.Bootable), - "Encrypted": component.NewText(strconv.FormatBool(volume.Encrypted)), + m = append(m, map[string]string{ + "Name": volume.Name, + "Description": volume.Description, + "Size": strconv.Itoa(volume.Size) + "GiB", + "Status": volume.Status, + "Type": volume.VolumeType, + "Attached To": strings.Join(attachedDevices, ", "), + "Availability Zone": volume.AvailabilityZone, + "Bootable": volume.Bootable, + "Encrypted": strconv.FormatBool(volume.Encrypted), }) } @@ -58,11 +61,7 @@ func getVolumes(osp *OpenstackPlugin) component.Component { log.Printf("volume list error: %s\n", err) } - return component.NewTableWithRows( - "Volumes", - "No volumes found", - component.NewTableCols("Name", "Description", "Size", "Status", "Group", "Type", "Attached To", - "Availability Zone", "Bootable", "Encrypted"), rows) + return m } // helper function to create an volume specific gophercloud client diff --git a/internal/plugin/argoui/register.go b/internal/plugin/argoui/register.go deleted file mode 100644 index 3a26d09..0000000 --- a/internal/plugin/argoui/register.go +++ /dev/null @@ -1,135 +0,0 @@ -/* -Copyright (c) 2019 VMware, Inc. All Rights Reserved. -SPDX-License-Identifier: Apache-2.0 -*/ - -package plugin - -import ( - "fmt" - "net/url" - - "k8s.io/apimachinery/pkg/runtime" - - "github.com/vmware-tanzu/octant/pkg/navigation" - "github.com/vmware-tanzu/octant/pkg/plugin" - "github.com/vmware-tanzu/octant/pkg/plugin/service" - "github.com/vmware-tanzu/octant/pkg/store" - "github.com/vmware-tanzu/octant/pkg/view/component" - v1 "k8s.io/api/core/v1" -) - -// ArgoPlugin is an example of a plugin that registers navigation and action handlers -type ArgoPlugin struct{} - -// NewArgoPlugin returns the basic argo plugin struct as required by octant -func NewArgoPlugin() *ArgoPlugin { - return &ArgoPlugin{} -} - -// Register registers the plugin with Octant -func Register(name string, description string) (*service.Plugin, error) { - a := NewArgoPlugin() - - capabilities := &plugin.Capabilities{ - IsModule: true, - } - - // Set up what should happen when Octant calls this plugin. - options := []service.PluginOption{ - service.WithNavigation(a.handleNavigation, a.initRoutes), - } - - // Use the plugin service helper to register this plugin. - return service.Register(name, description, capabilities, options...) -} - -// handlePrint creates a navigation tree for this plugin. Navigation is dynamic and will -// be called frequently from Octant. Navigation is a tree of `Navigation` structs. -// The plugin can use whatever paths it likes since these paths can be namespaced to the -// the plugin. -func (a *ArgoPlugin) handleNavigation(request *service.NavigationRequest) (navigation.Navigation, error) { - return navigation.Navigation{ - Title: "Argo UI", - Path: request.GeneratePath(), - IconName: "cloud", - }, nil -} - -// initRoutes routes for this plugin. In this example, there is a global catch all route -// that will return the content for every single path. -func (a *ArgoPlugin) initRoutes(router *service.Router) { - router.HandleFunc("", func(request service.Request) (component.ContentResponse, error) { - response := component.NewContentResponse(component.TitleFromString("Argo UI")) - - u, err := getArgoUIURL(request) - if err != nil || u == nil { - errMsg := "The Argo UI is not available." - response.Add(component.NewText(errMsg)) - } else { - response.Add(component.NewIFrame(u.String(), "Argo UI")) - } - - return *response, nil - }) -} - -func getArgoUIURL(request service.Request) (u *url.URL, err error) { - ctx := request.Context() - client := request.DashboardClient() - - key := store.Key{ - APIVersion: "v1", - Kind: "Endpoints", - Name: "argo-ui", - Namespace: "argo", - } - - o, err := client.Get(ctx, key) - if err != nil { - return u, err - } - - end := &v1.Endpoints{} - if o != nil { - err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.Object, end) - if err != nil { - return u, err - } - } else { - return u, err - } - - var addr string - var port int32 - - /* Move through the subsets, address, and port arrays to construct a url - More information on the structure can be found in the Kubernetes document below - https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#endpointsubset-v1-core - */ - for _, s := range end.Subsets { - for _, a := range s.Addresses { - addr = a.IP - break - } - - for _, p := range s.Ports { - port = p.Port - break - } - - if addr != "" || port != 0 { - break - } - } - if addr == "" || port == 0 { - return u, err - } - - u = &url.URL{ - Scheme: "http", - Host: fmt.Sprintf("%s:%d", addr, port), - } - - return u, nil -} diff --git a/internal/plugin/argoui/register_test.go b/internal/plugin/argoui/register_test.go deleted file mode 100644 index c917d71..0000000 --- a/internal/plugin/argoui/register_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package plugin - -import ( - "fmt" - "testing" - - "github.com/vmware-tanzu/octant/pkg/plugin/service" -) - -func TestRegister(t *testing.T) { - plugin, err := Register("argo-ui", "Argo UI test version") - if err != nil { - t.Fatalf("Registering the plugin returned an error") - } - - err = plugin.Validate() - if err != nil { - t.Fatalf("Validating the plugin returned an error") - } -} - -func TestRoutes(t *testing.T) { - router := service.NewRouter() - NewArgoPlugin().initRoutes(router) - - tests := []struct { - path string - exists bool - }{ - { - path: "", - exists: true, - }, - { - path: "/not-real", - exists: false, - }, - } - - for _, test := range tests { - test := test // pin the value so that the following function literal binds to it - t.Run(fmt.Sprintf("Path='%s'", test.path), func(t *testing.T) { - _, found := router.Match(test.path) - - if test.exists != found { - if found { - t.Errorf("Found path '%s' when it should not exist.", test.path) - } else { - t.Errorf("Didn't find path '%s' when it should exist.", test.path) - } - } - }) - } -} diff --git a/internal/plugin/openstack/register_test.go b/internal/plugin/openstack/register_test.go deleted file mode 100755 index a09e4e0..0000000 --- a/internal/plugin/openstack/register_test.go +++ /dev/null @@ -1,58 +0,0 @@ -/* -Copyright (c) 2020 AT&T. All Rights Reserved. -SPDX-License-Identifier: Apache-2.0 -*/ -package plugin - -import ( - "fmt" - "testing" - - "github.com/vmware-tanzu/octant/pkg/plugin/service" -) - -func TestRegister(t *testing.T) { - plugin, err := Register("openstack", "OpenStack test version") - if err != nil { - t.Fatalf("Registering the plugin returned an error") - } - - err = plugin.Validate() - if err != nil { - t.Fatalf("Validating the plugin returned an error") - } -} - -func TestRoutes(t *testing.T) { - router := service.NewRouter() - NewOpenstackPlugin().initRoutes(router) - - tests := []struct { - path string - exists bool - }{ - { - path: "", - exists: true, - }, - { - path: "/not-real", - exists: false, - }, - } - - for _, test := range tests { - test := test // pin the value so that the following function literal binds to it - t.Run(fmt.Sprintf("Path='%s'", test.path), func(t *testing.T) { - _, found := router.Match(test.path) - - if test.exists != found { - if found { - t.Errorf("Found path '%s' when it should not exist.", test.path) - } else { - t.Errorf("Didn't find path '%s' when it should exist.", test.path) - } - } - }) - } -} diff --git a/internal/webservice/server.go b/internal/webservice/server.go new file mode 100755 index 0000000..7e061ac --- /dev/null +++ b/internal/webservice/server.go @@ -0,0 +1,162 @@ +/* + Copyright (c) 2020 AT&T. All Rights Reserved. + + 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 + + https://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. +*/ +package webservice + +import ( + "fmt" + "log" + "net/http" + + "github.com/gorilla/websocket" + "opendev.org/airship/airshipui/internal/plugin/airshipopenstack" +) + +// just a base structure to return from the web service +type Message struct { + ID int `json:"id,omitempty"` + Sender string `json:"sender"` + Message string `json:"message"` +} + +type wsRequest struct { + ID string `json:"id,omitempty"` + Type string `json:"type,omitempty"` + Component string `json:"component,omitempty"` + Error string `json:"error"` + Data []map[string]string `json:"data"` +} + +// gorilla ws specific HTTP upgrade to WebSockets +var upgrader = websocket.Upgrader{ + ReadBufferSize: 1024, + WriteBufferSize: 1024, +} + +// this is a way to allow for arbitrary messages to be processed by the backend +// most likely we will need to have sub components register with the system +// TODO: make this a dynamic registration of components +var functionMap = map[string]map[string]func() []map[string]string{ + "openstack": { + "getFlavors": airshipopenstack.GetFlavors, + "getImages": airshipopenstack.GetImages, + "getVMs": airshipopenstack.GetVMs, + "getDomains": airshipopenstack.GetDomains, + "getProjects": airshipopenstack.GetProjects, + "getUsers": airshipopenstack.GetUsers, + "getNetworks": airshipopenstack.GetNetworks, + "getSubnets": airshipopenstack.GetSubnets, + "getVolumes": airshipopenstack.GetVolumes, + }, + "electron": { + "keepalive": basicReply, + "getID": basicReply, + }, +} + +var ws *websocket.Conn + +// handle the origin request & upgrade to websocket +func onOpen(w http.ResponseWriter, r *http.Request) { + // gorilla ws will give a 403 on a cross origin request, so we silence its complaints + // This happens with electron because it's sending an origin of 'file://' instead of 'localhost:8080' + upgrader.CheckOrigin = func(r *http.Request) bool { return true } + + // upgrade to websocket protocol over http + log.Printf("Establishing the websocket") + wsConn, err := upgrader.Upgrade(w, r, nil) + if err != nil { + log.Printf("Could not open websocket connection from: %s\n", r.Host) + http.Error(w, "Could not open websocket connection", http.StatusBadRequest) + } + + ws = wsConn + log.Printf("WebSocket established with %s\n", ws.RemoteAddr().String()) + go onMessage() +} + +// handle messaging to the client +func onMessage() { + // just in case clean up the websocket + defer onClose() + + for { + var request wsRequest + err := ws.ReadJSON(&request) + if err != nil { + onError(err) + break + } + + // look through the function map to find the type to handle the request + if reqType, ok := functionMap[request.Type]; ok { + // the function map may have a component (function) to process the request + if component, ok := reqType[request.Component]; ok { + request.Data = component() + if err = ws.WriteJSON(request); err != nil { + onError(err) + break + } + } else { + request.Error = fmt.Sprintf("Requested component: %s, not found", request.Component) + if err = ws.WriteJSON(request); err != nil { + onError(err) + break + } + log.Printf("Requested component: %s, not found\n", request.Component) + } + } else { + request.Error = fmt.Sprintf("Requested type: %s, not found", request.Type) + if err = ws.WriteJSON(request); err != nil { + onError(err) + break + } + log.Printf("Requested type: %s, not found\n", request.Type) + } + } +} + +func basicReply() []map[string]string { + m := make([]map[string]string, 0) + m = append(m, map[string]string{ + "ID": "foo", + "type": "bar", + "component": "glitch", + }) + return m +} + +// common websocket close with logging +func onClose() { + log.Printf("Closing websocket") + // ws.Close() +} + +// common websocket error handling with logging +func onError(err error) { + log.Printf("Error receiving / sending message: %s\n", err) +} + +// WebServer will run the handler functions for both normal REST requests and WebSockets +func WebServer() { + http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) { + onOpen(w, r) + }) + + log.Println("Attempting to start webservice on localhost:8080") + if err := http.ListenAndServe(":8080", nil); err != nil { + log.Fatal("ListenAndServe:", err) + } +} diff --git a/web/images/airship-color.png b/web/images/airship-color.png new file mode 100755 index 0000000000000000000000000000000000000000..dd3cd4495ebafef0d7a00b56254cda0bdf65cb96 GIT binary patch literal 64414 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

IBx73bxd2Wh+b5XRJV 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 D%HLGi literal 0 HcmV?d00001 diff --git a/web/index.html b/web/index.html new file mode 100755 index 0000000..ed7782f --- /dev/null +++ b/web/index.html @@ -0,0 +1,34 @@ + + + + + + Airship UI + + + + + + + + +

+
+

TODO: Airship integration

+
+
+
+
+
+

© The Airship Authors.https://wiki.openstack.org/wiki/Airship
All Rights Reserved.

+
+
+ + + \ No newline at end of file diff --git a/web/js/common.js b/web/js/common.js new file mode 100755 index 0000000..37ed550 --- /dev/null +++ b/web/js/common.js @@ -0,0 +1,96 @@ +/* + Copyright (c) 2020 AT&T. All Rights Reserved. + + 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 + + https://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. +*/ + +// add the footer and header when the page loads +if (document.addEventListener) { + document.addEventListener("DOMContentLoaded", function() { + window.onscroll = function() { + let header = document.getElementById("HeaderDiv"); + let sticky = header.offsetTop; + + if (window.pageYOffset > sticky) { + header.classList.add("sticky"); + } else { + header.classList.remove("sticky"); + } + }; + }, false); +} + +function removeElement(id) { + if (document.contains(document.getElementById(id))) { + document.getElementById(id).remove(); + } +} + +// based on w3school: https://www.w3schools.com/howto/howto_js_sort_table.asp +function sortTable(tableID, column) { + var table, rows, switching, i, x, y, shouldSwitch, dir, switchcount = 0; + table = document.getElementById(tableID); + switching = true; + // Set the sorting direction to ascending: + dir = "asc"; + /* Make a loop that will continue until + no switching has been done: */ + while (switching) { + // Start by saying: no switching is done: + switching = false; + rows = table.rows; + /* Loop through all table rows (except the + first, which contains table headers): */ + for (i = 1; i < (rows.length - 1); i++) { + // Start by saying there should be no switching: + shouldSwitch = false; + /* Get the two elements you want to compare, + one from current row and one from the next: */ + x = rows[i].getElementsByTagName("TD")[column]; + y = rows[i + 1].getElementsByTagName("TD")[column]; + + if (x !== undefined && y !== undefined) { + /* Check if the two rows should switch place, + based on the direction, asc or desc: */ + if (dir == "asc") { + if (x.innerHTML.toLowerCase() > y.innerHTML.toLowerCase()) { + // If so, mark as a switch and break the loop: + shouldSwitch = true; + break; + } + } else if (dir == "desc") { + if (x.innerHTML.toLowerCase() < y.innerHTML.toLowerCase()) { + // If so, mark as a switch and break the loop: + shouldSwitch = true; + break; + } + } + } + } + if (shouldSwitch) { + /* If a switch has been marked, make the switch + and mark that a switch has been done: */ + rows[i].parentNode.insertBefore(rows[i + 1], rows[i]); + switching = true; + // Each time a switch is done, increase this count by 1: + switchcount++; + } else { + /* If no switching has been done AND the direction is "asc", + set the direction to "desc" and run the while loop again. */ + if (switchcount == 0 && dir == "asc") { + dir = "desc"; + switching = true; + } + } + } +} \ No newline at end of file diff --git a/web/js/main.js b/web/js/main.js new file mode 100755 index 0000000..dadc915 --- /dev/null +++ b/web/js/main.js @@ -0,0 +1,61 @@ +/* + Copyright (c) 2020 AT&T. All Rights Reserved. + + 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 + + https://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. +*/ +const { app, BrowserWindow } = require('electron') + +function createWindow () { + // Create the browser window. + const win = new BrowserWindow({ + webPreferences: { + nodeIntegration: true + }, + show: false + }) + + // start electron maximized + win.maximize(); + win.show(); + + // disable the default menu bar + //win.setMenu(null); + + // and load the index.html of the app. + win.loadFile('index.html') + + // Open the DevTools. + // win.webContents.openDevTools() +} + +// This method will be called when Electron has finished +// initialization and is ready to create browser windows. +// Some APIs can only be used after this event occurs. +app.whenReady().then(createWindow) + +// Quit when all windows are closed. +app.on('window-all-closed', () => { + // On macOS it is common for applications and their menu bar + // to stay active until the user quits explicitly with Cmd + Q + if (process.platform !== 'darwin') { + app.quit() + } +}) + +app.on('activate', () => { + // On macOS it's common to re-create a window in the app when the + // dock icon is clicked and there are no other windows open. + if (BrowserWindow.getAllWindows().length === 0) { + createWindow() + } +}) diff --git a/web/js/websocket.js b/web/js/websocket.js new file mode 100755 index 0000000..7920a78 --- /dev/null +++ b/web/js/websocket.js @@ -0,0 +1,121 @@ +/* + Copyright (c) 2020 AT&T. All Rights Reserved. + + 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 + + https://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. +*/ +var ws = null; +var timeout = null; + +// establish a session when browser is open +if (document.addEventListener) { + document.addEventListener("DOMContentLoaded", function() { + // register the webservice so it's available the entire time of the process + register(); + }, false); +} + +// listen for the unload event and close the web socket if open +document.addEventListener('unload', function () { + if (ws !== null) { + if (ws.readyState !== ws.CLOSED) { ws.close(); } + } +}); + +function register() { + if (ws != null) { + ws.close(); + ws = null; + } + + ws = new WebSocket("ws://localhost:8080/ws"); + + ws.onmessage = function (event) { + handleMessages(event); + } + + ws.onerror = function (event) { + console.log("Web Socket received an error: ", event.code); + close(event.code); + } + + ws.onopen = function (event) { + open(); + } + + ws.onclose = function (event) { + close(event.code); + } +} + +function handleMessages(message) { + var json = JSON.parse(message.data); + // keepalives aren't interesting to other pages + if (json["type"] === "electron") { + document.getElementById("OutputDiv").innerHTML = JSON.stringify(json); + } else { + // events based on the type are interesting to other pages + // create and dispatch an event based on the data received + document.dispatchEvent(new CustomEvent(json["type"], {detail: json})); + } + console.log("Received message: " + message.data); +} + +function open() { + console.log("Websocket established"); + var json = {"id":"poc","type":"electron","component":"getID"}; + ws.send(JSON.stringify(json)); + // start up the keepalive so the websocket stays open + keepAlive(); +} + +function close(code) { + switch (code) { + case 1000: console.log("Web Socket Closed: Normal closure: ", code); break; + case 1001: console.log("Web Socket Closed: An endpoint is \"going away\", such as a server going down or a browser having navigated away from a page:", code); break; + case 1002: console.log("Web Socket Closed: terminating the connection due to a protocol error: ", code); break; + case 1003: console.log("Web Socket Closed: terminating the connection because it has received a type of data it cannot accept: ", code); break; + case 1004: console.log("Web Socket Closed: Reserved. The specific meaning might be defined in the futur: ", code); break; + case 1005: console.log("Web Socket Closed: No status code was actually present: ", code); break; + case 1006: console.log("Web Socket Closed: The connection was closed abnormally: ", code); break; + case 1007: console.log("Web Socket Closed: terminating the connection because it has received data within a message that was not consistent with the type of the message: ", code); break; + case 1008: console.log("Web Socket Closed: terminating the connection because it has received a message that \"violates its policy\": ", code); break; + case 1009: console.log("Web Socket Closed: terminating the connection because it has received a message that is too big for it to process: ", code); break; + case 1010: console.log("Web Socket Closed: client is terminating the connection because it has expected the server to negotiate one or more extension, but the server didn't return them in the response message of the WebSocket handshake: ", code); break; + case 1011: console.log("Web Socket Closed: server is terminating the connection because it encountered an unexpected condition that prevented it from fulfilling the request: ", code); break; + case 1015: console.log("Web Socket Closed: closed due to a failure to perform a TLS handshake (e.g., the server certificate can't be verified): ", code); break; + default: console.log("Web Socket Closed: unknown error code: ", code); break; + } + + ws = null; +} + +function keepAlive() { + if (ws !== null) { + if (ws.readyState !== ws.CLOSED) { + // clear the previously set timeout + window.clearTimeout(timeout); + window.clearInterval(timeout); + var json = {"id":"poc","type":"electron","component":"keepalive"}; + ws.send(JSON.stringify(json)); + timeout = window.setTimeout(keepAlive,60000); + } + } +} + +function sendMessage(json) { + if (ws.readyState === WebSocket.CLOSED) { + register(); + } + console.log("Attempting to send: ", json); + ws.send(JSON.stringify(json)); +} \ No newline at end of file diff --git a/web/package-lock.json b/web/package-lock.json new file mode 100755 index 0000000..48cb127 --- /dev/null +++ b/web/package-lock.json @@ -0,0 +1,737 @@ +{ + "name": "electron-poc", + "version": "0.0.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@electron/get": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-1.10.0.tgz", + "integrity": "sha512-hlueNXU51c3CwQjBw/i5fwt+VfQgSQVUTdicpCHkhEjNZaa4CXJ5W1GaxSwtLE2dvRmAHjpIjUMHTqJ53uojfg==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "global-agent": "^2.0.2", + "global-tunnel-ng": "^2.7.1", + "got": "^9.6.0", + "progress": "^2.0.3", + "sanitize-filename": "^1.6.2", + "sumchecker": "^3.0.1" + } + }, + "@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "dev": true + }, + "@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "dev": true, + "requires": { + "defer-to-connect": "^1.0.1" + } + }, + "@types/node": { + "version": "12.12.35", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.35.tgz", + "integrity": "sha512-ASYsaKecA7TUsDrqIGPNk3JeEox0z/0XR/WsJJ8BIX/9+SkMSImQXKWfU/yBrSyc7ZSE/NPqLu36Nur0miCFfQ==", + "dev": true + }, + "boolean": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.0.1.tgz", + "integrity": "sha512-HRZPIjPcbwAVQvOTxR4YE3o8Xs98NqbbL1iEZDCz7CL8ql0Lt5iOyJFxfnAB0oFs8Oh02F/lLlg30Mexv46LjA==", + "dev": true, + "optional": true + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "dev": true + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "dev": true, + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "dependencies": { + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true + } + } + }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "config-chain": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz", + "integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==", + "dev": true, + "optional": true, + "requires": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "core-js": { + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", + "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==", + "dev": true, + "optional": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "optional": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "detect-node": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", + "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==", + "dev": true, + "optional": true + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "dev": true + }, + "electron": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/electron/-/electron-8.2.2.tgz", + "integrity": "sha512-GH4RCbpuzEn3XpTmsf+wLaJ2KOPSOoBJvQ0s6ftTLs5+IQEgKZvkdYCj8TCBNXFhss31RT3BUqoEQQUyZErK0A==", + "dev": true, + "requires": { + "@electron/get": "^1.0.1", + "@types/node": "^12.0.12", + "extract-zip": "^1.0.3" + } + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true, + "optional": true + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "env-paths": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.0.tgz", + "integrity": "sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA==", + "dev": true + }, + "es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true, + "optional": true + }, + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "optional": true + }, + "extract-zip": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz", + "integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==", + "dev": true, + "requires": { + "concat-stream": "^1.6.2", + "debug": "^2.6.9", + "mkdirp": "^0.5.4", + "yauzl": "^2.10.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "dev": true, + "requires": { + "pend": "~1.2.0" + } + }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "global-agent": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-2.1.8.tgz", + "integrity": "sha512-VpBe/rhY6Rw2VDOTszAMNambg+4Qv8j0yiTNDYEXXXxkUNGWLHp8A3ztK4YDBbFNcWF4rgsec6/5gPyryya/+A==", + "dev": true, + "optional": true, + "requires": { + "boolean": "^3.0.0", + "core-js": "^3.6.4", + "es6-error": "^4.1.1", + "matcher": "^2.1.0", + "roarr": "^2.15.2", + "semver": "^7.1.2", + "serialize-error": "^5.0.0" + } + }, + "global-tunnel-ng": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz", + "integrity": "sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg==", + "dev": true, + "optional": true, + "requires": { + "encodeurl": "^1.0.2", + "lodash": "^4.17.10", + "npm-conf": "^1.1.3", + "tunnel": "^0.0.6" + } + }, + "globalthis": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.1.tgz", + "integrity": "sha512-mJPRTc/P39NH/iNG4mXa9aIhNymaQikTrnspeCa2ZuJ+mH2QN/rXwtX3XwKrHqWgUQFbNZKtHM105aHzJalElw==", + "dev": true, + "optional": true, + "requires": { + "define-properties": "^1.1.3" + } + }, + "got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "dev": true, + "requires": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + } + }, + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "dev": true + }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "dev": true + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true, + "optional": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true, + "optional": true + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "dev": true, + "requires": { + "json-buffer": "3.0.0" + } + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true, + "optional": true + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true + }, + "matcher": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-2.1.0.tgz", + "integrity": "sha512-o+nZr+vtJtgPNklyeUKkkH42OsK8WAfdgaJE2FNxcjLPg+5QbeEoT6vRj8Xq/iv18JlQ9cmKsEu0b94ixWf1YQ==", + "dev": true, + "optional": true, + "requires": { + "escape-string-regexp": "^2.0.0" + } + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "normalize-url": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", + "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", + "dev": true + }, + "npm-conf": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz", + "integrity": "sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==", + "dev": true, + "optional": true, + "requires": { + "config-chain": "^1.1.11", + "pify": "^3.0.0" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "dev": true + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "dev": true + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true, + "optional": true + }, + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", + "dev": true, + "optional": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "dev": true, + "requires": { + "lowercase-keys": "^1.0.0" + } + }, + "roarr": { + "version": "2.15.3", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.3.tgz", + "integrity": "sha512-AEjYvmAhlyxOeB9OqPUzQCo3kuAkNfuDk/HqWbZdFsqDFpapkTjiw+p4svNEoRLvuqNTxqfL+s+gtD4eDgZ+CA==", + "dev": true, + "optional": true, + "requires": { + "boolean": "^3.0.0", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "sanitize-filename": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", + "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", + "dev": true, + "requires": { + "truncate-utf8-bytes": "^1.0.0" + } + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true, + "optional": true + }, + "semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", + "dev": true, + "optional": true + }, + "serialize-error": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-5.0.0.tgz", + "integrity": "sha512-/VtpuyzYf82mHYTtI4QKtwHa79vAdU5OQpNPAmE/0UDdlGT0ZxHwC+J6gXkw29wwoVI8fMPsfcVHOwXtUQYYQA==", + "dev": true, + "optional": true, + "requires": { + "type-fest": "^0.8.0" + } + }, + "sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", + "dev": true, + "optional": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "sumchecker": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", + "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", + "dev": true, + "requires": { + "debug": "^4.1.0" + } + }, + "to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", + "dev": true + }, + "truncate-utf8-bytes": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", + "integrity": "sha1-QFkjkJWS1W94pYGENLC3hInKXys=", + "dev": true, + "requires": { + "utf8-byte-length": "^1.0.1" + } + }, + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "dev": true, + "optional": true + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "optional": true + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "dev": true, + "requires": { + "prepend-http": "^2.0.0" + } + }, + "utf8-byte-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz", + "integrity": "sha1-9F8VDExm7uloGGUFq5P8u4rWv2E=", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "xmlhttprequest": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", + "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=" + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "dev": true, + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + } + } +} diff --git a/web/package.json b/web/package.json new file mode 100755 index 0000000..b5974b4 --- /dev/null +++ b/web/package.json @@ -0,0 +1,17 @@ +{ + "name": "electron-poc", + "version": "0.0.1", + "main": "js/main.js", + "scripts": { + "start": "electron ." + }, + "description": "An attempt to create a small electron app for eventual usage with airship-ui", + "author": "Andrew J. Schiefelbein, andrew.schiefelbein@att.com", + "license": "ISC", + "devDependencies": { + "electron": "^8.2.2" + }, + "dependencies": { + "xmlhttprequest": "^1.8.0" + } +} diff --git a/web/style.css b/web/style.css new file mode 100755 index 0000000..a5b1c56 --- /dev/null +++ b/web/style.css @@ -0,0 +1,167 @@ +/* taken from w3school: https://www.w3schools.com/howto/howto_js_filter_table.asp */ +#searchInput { + background-image: url('/css/searchicon.png'); /* Add a search icon to input */ + background-position: 10px 12px; /* Position the search icon */ + background-repeat: no-repeat; /* Do not repeat the icon image */ + width: 100%; /* Full-width */ + font-size: 16px; /* Increase font-size */ + padding: 12px 20px 12px 40px; /* Add some padding */ + border: 1px solid #ddd; /* Add a grey border */ + margin-bottom: 12px; /* Add some space below the input */ +} + +.airshipTable { + border-collapse: collapse; /* Collapse borders */ + width: 100%; /* Full-width */ + border: 1px solid #ddd; /* Add a grey border */ + font-size: 18px; /* Increase font-size */ +} + +.airshipTable th, .airshipTable td { + text-align: left; /* Left-align text */ + padding: 12px; /* Add padding */ +} + +.airshipTable tr { + /* Add a bottom border to all table rows */ + border-bottom: 1px solid #ddd; +} + +.airshipTable tr.header, .airshipTable tr:hover { + /* Add a grey background color to the table header and on hover */ + background-color: #f1f1f1; +} + +/* Add a black background color to the top navigation */ +.topnav { + background-color: #0077d1; + overflow: hidden; +width: 100%; +z-index:3; +} + +.sticky { +position: fixed; +top: 0; +width: 100% +} + +.sticky + .content { +padding-top: 102px; +} + +.container { +position:relative; +z-index:1; +} + +/* Style the links inside the navigation bar */ +.topnav a { + float: left; + display: block; + color: #ffffff; + text-align: center; + padding: 12px 12px; + text-decoration: none; + font-size: 14px; +} + +.topnavsuite { + float: left; + display: block; + color: #bfbfbf; + text-align: center; + padding: 12px 12px; + text-decoration: none; + font-size: 14px; +} + +.topnavname { + float: right; + display: block; + color: #bfbfbf; + text-align: center; + padding: 12px 12px; + text-decoration: none; + font-size: 14px; +} + + +/* Change the color of links on hover */ +.topnav a:hover { + background-color: #ddd; + color: black; +} + +@media screen and (max-width: 745px) { +.topnav a { + float: none; + display: block; + text-align: left; +} + +.topnavsuite { + float: none; +} + +.topnavname { +display: none; +} +} + +/* Add an active class to highlight the current page */ +.active { + background-color: #4CAF50; + color: white; +} + +.highlight { + background-color: #fff2ac; + background-image: linear-gradient(to right, #ffe359 0%, #fff2ac 100%); +} + + /* Tooltip container */ + .tooltip { + position: relative; + display: inline-block; + border-bottom: 1px dotted black; /* If you want dots under the hoverable text */ +} + +/* Tooltip text */ +.tooltip .tooltiptext { + visibility: hidden; + background-color: #555; + color: #fff; + text-align: center; + padding: 5px 0; + border-radius: 6px; + + /* Position the tooltip text */ + position: absolute; + z-index: 1; + bottom: 125%; + left: 50%; + margin-left: -60px; + + /* Fade in tooltip */ + opacity: 0; + transition: opacity 0.3s; +} + +/* Tooltip arrow */ +.tooltip .tooltiptext::after { + content: ""; + position: absolute; + top: 100%; + left: 50%; + margin-left: -5px; + border-width: 5px; + border-style: solid; + border-color: #555 transparent transparent transparent; +} + +/* Show the tooltip text when you mouse over the tooltip container */ +.tooltip:hover .tooltiptext { + visibility: visible; + opacity: 1; +} \ No newline at end of file