From 50792c4621a51e14b7659dce09681a40f7445609 Mon Sep 17 00:00:00 2001 From: aiordache Date: Tue, 19 Jan 2021 11:49:50 +0100 Subject: [PATCH] Add Kubernetes backend Signed-off-by: aiordache --- Makefile | 2 +- api/context/store/contextmetadata.go | 6 + api/context/store/store.go | 6 + cli/cmd/context/create_kube.go | 86 ++ go.mod | 9 +- go.sum | 212 +++++ kube/backend.go | 84 ++ kube/charts/charts.go | 126 +++ kube/charts/helm/chart.go | 108 +++ kube/charts/helm/helm.go | 135 +++ kube/charts/kubernetes/kube.go | 232 +++++ kube/charts/kubernetes/placement.go | 144 ++++ kube/charts/kubernetes/placement_test.go | 181 ++++ kube/charts/kubernetes/pod.go | 367 ++++++++ kube/charts/kubernetes/pod_test.go | 1005 ++++++++++++++++++++++ kube/charts/kubernetes/volumes.go | 246 ++++++ kube/compose.go | 106 +++ kube/context.go | 27 + kube/utils/config.go | 142 +++ kube/utils/errors.go | 56 ++ kube/utils/labels.go | 35 + kube/utils/utils.go | 34 + 22 files changed, 3344 insertions(+), 5 deletions(-) create mode 100644 cli/cmd/context/create_kube.go create mode 100644 kube/backend.go create mode 100644 kube/charts/charts.go create mode 100644 kube/charts/helm/chart.go create mode 100644 kube/charts/helm/helm.go create mode 100644 kube/charts/kubernetes/kube.go create mode 100644 kube/charts/kubernetes/placement.go create mode 100644 kube/charts/kubernetes/placement_test.go create mode 100644 kube/charts/kubernetes/pod.go create mode 100644 kube/charts/kubernetes/pod_test.go create mode 100644 kube/charts/kubernetes/volumes.go create mode 100644 kube/compose.go create mode 100644 kube/context.go create mode 100644 kube/utils/config.go create mode 100644 kube/utils/errors.go create mode 100644 kube/utils/labels.go create mode 100644 kube/utils/utils.go diff --git a/Makefile b/Makefile index 6e51ca7c9..961f2067e 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ protos: ## Generate go code from .proto files cli: ## Compile the cli @docker build . --target cli \ --platform local \ - --build-arg BUILD_TAGS=e2e \ + --build-arg BUILD_TAGS=e2e,kube \ --build-arg GIT_TAG=$(GIT_TAG) \ --output ./bin diff --git a/api/context/store/contextmetadata.go b/api/context/store/contextmetadata.go index f8b73211c..5ec187320 100644 --- a/api/context/store/contextmetadata.go +++ b/api/context/store/contextmetadata.go @@ -55,6 +55,12 @@ type EcsContext struct { Profile string `json:",omitempty"` } +// KubeContext is the context for a kube backend +type KubeContext struct { + Endpoint string `json:",omitempty"` + FromEnvironment bool +} + // AwsContext is the context for the ecs plugin type AwsContext EcsContext diff --git a/api/context/store/store.go b/api/context/store/store.go index b79e862b6..5427f7529 100644 --- a/api/context/store/store.go +++ b/api/context/store/store.go @@ -55,6 +55,9 @@ const ( // LocalContextType is the endpoint key in the context endpoints for a new // local backend LocalContextType = "local" + // KubeContextType is the endpoint key in the context endpoints for a new + // kube backend + KubeContextType = "kube" ) const ( @@ -328,5 +331,8 @@ func getters() map[string]func() interface{} { LocalContextType: func() interface{} { return &LocalContext{} }, + KubeContextType: func() interface{} { + return &KubeContext{} + }, } } diff --git a/cli/cmd/context/create_kube.go b/cli/cmd/context/create_kube.go new file mode 100644 index 000000000..c11e204f6 --- /dev/null +++ b/cli/cmd/context/create_kube.go @@ -0,0 +1,86 @@ +// +build kube + +/* + Copyright 2020 Docker Compose CLI authors + + 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. +*/ + +package context + +import ( + "context" + "fmt" + + "github.com/pkg/errors" + "github.com/spf13/cobra" + + "github.com/docker/compose-cli/api/context/store" + "github.com/docker/compose-cli/api/errdefs" + "github.com/docker/compose-cli/kube" +) + +func init() { + extraCommands = append(extraCommands, createKubeCommand) + extraHelp = append(extraHelp, ` +Create a Kube context: +$ docker context create kube CONTEXT [flags] +(see docker context create kube --help) +`) +} + +func createKubeCommand() *cobra.Command { + var opts kube.ContextParams + cmd := &cobra.Command{ + Use: "kube CONTEXT [flags]", + Short: "Create context for a Kubernetes Cluster", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + opts.Name = args[0] + + if opts.Endpoint != "" { + opts.FromEnvironment = false + } + return runCreateKube(cmd.Context(), args[0], opts) + }, + } + + addDescriptionFlag(cmd, &opts.Description) + cmd.Flags().StringVar(&opts.Endpoint, "endpoint", "", "The endpoint of the Kubernetes manager") + cmd.Flags().BoolVar(&opts.FromEnvironment, "from-env", true, "Get endpoint and creds from env vars") + return cmd +} + +func runCreateKube(ctx context.Context, contextName string, opts kube.ContextParams) error { + if contextExists(ctx, contextName) { + return errors.Wrapf(errdefs.ErrAlreadyExists, "context %q", contextName) + } + + contextData, description, err := createContextData(ctx, opts) + if err != nil { + return err + } + return createDockerContext(ctx, contextName, store.KubeContextType, description, contextData) +} + +func createContextData(ctx context.Context, opts kube.ContextParams) (interface{}, string, error) { + description := "" + if opts.Description != "" { + description = fmt.Sprintf("%s (%s)", opts.Description, description) + } + + return store.KubeContext{ + Endpoint: opts.Endpoint, + FromEnvironment: opts.FromEnvironment, + }, description, nil +} diff --git a/go.mod b/go.mod index dc10eec8d..074731c7c 100644 --- a/go.mod +++ b/go.mod @@ -39,14 +39,13 @@ require ( github.com/joho/godotenv v1.3.0 github.com/labstack/echo v3.3.10+incompatible github.com/labstack/gommon v0.3.0 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/moby/buildkit v0.8.1-0.20201205083753-0af7b1b9c693 github.com/moby/term v0.0.0-20201110203204-bea5bbe245bf github.com/morikuni/aec v1.0.0 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.0.1 github.com/pkg/errors v0.9.1 - github.com/prometheus/procfs v0.2.0 // indirect + github.com/prometheus/common v0.10.0 github.com/prometheus/tsdb v0.10.0 github.com/sanathkr/go-yaml v0.0.0-20170819195128-ed9d249f429b github.com/sirupsen/logrus v1.7.0 @@ -57,13 +56,15 @@ require ( golang.org/x/net v0.0.0-20201110031124-69a78807bb2b golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 - google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a // indirect google.golang.org/grpc v1.33.2 google.golang.org/protobuf v1.25.0 gopkg.in/ini.v1 v1.62.0 + gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c gotest.tools v2.2.0+incompatible gotest.tools/v3 v3.0.3 - k8s.io/client-go v0.20.1 // indirect + helm.sh/helm/v3 v3.5.0 + k8s.io/api v0.20.1 + k8s.io/apimachinery v0.20.1 sigs.k8s.io/kustomize/kyaml v0.10.5 ) diff --git a/go.sum b/go.sum index b872ec88b..cd907ba47 100644 --- a/go.sum +++ b/go.sum @@ -128,16 +128,30 @@ github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBp github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= +github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/Djarvur/go-err113 v0.0.0-20200410182137-af658d038157/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= github.com/Djarvur/go-err113 v0.1.0/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0= github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0= github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20191009163259-e802c2cb94ae/go.mod h1:mjwGPas4yKduTyubHvD1Atl9r1rUq8DfVy+gkVvZ+oo= github.com/GoogleCloudPlatform/k8s-cloud-provider v0.0.0-20190822182118-27a4ced34534/go.mod h1:iroGtC8B3tQiqtds1l+mgk/BBOrxbqjH+eUfFQYRc14= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd h1:sjQovDkwrZp8u+gxLtPgKGjk5hCxuy2hrRejBTA9xFU= +github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= +github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= +github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/semver/v3 v3.0.3/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= +github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/Masterminds/sprig/v3 v3.2.0 h1:P1ekkbuU73Ui/wS0nK1HOM37hh4xdfZo485UPf8rc+Y= +github.com/Masterminds/sprig/v3 v3.2.0/go.mod h1:tWhwTbUTndesPNeF0C900vKoq283u6zp4APT9vaF3SI= +github.com/Masterminds/squirrel v1.5.0 h1:JukIZisrUXadA9pl3rMkjhiamxiB0cXiu+HGp/Y8cY8= +github.com/Masterminds/squirrel v1.5.0/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= +github.com/Masterminds/vcs v1.13.1/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= github.com/Microsoft/go-winio v0.4.15-0.20200908182639-5b44b70ab3ab/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= @@ -164,10 +178,13 @@ github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= 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/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 h1:w1UutsfOrms1J05zt7ISrnJIXKzwaspym5BTKGx93EI= github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0= @@ -175,14 +192,17 @@ github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4Rq github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= github.com/alecthomas/kingpin v2.2.6+incompatible/go.mod h1:59OFYbFVLKQKq+mqrL6Rw5bR0c3ACQaawgXx0QYndlE= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apex/log v1.1.4/go.mod h1:AlpoD9aScyQfJDVHmLMEcx4oU6LqzkWp4Mg9GdAcEvQ= github.com/apex/log v1.3.0/go.mod h1:jd8Vpsr46WAe3EZSQ/IUMs2qQD/GOycT5rPWCO1yGcs= github.com/apex/logs v0.0.4/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDwo= @@ -195,8 +215,12 @@ github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hC github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= 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/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 h1:4daAzAu0S6Vi7/lbWECcX0j45yZReDZ56BQsrVBOEEY= +github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= +github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= github.com/aws/aws-sdk-go v1.15.27/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= github.com/aws/aws-sdk-go v1.15.90/go.mod h1:es1KtYUFs7le0xQ3rOihkuoVD90z7D0fR2Qm4S00/gU= @@ -205,10 +229,12 @@ github.com/aws/aws-sdk-go v1.19.18/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpi github.com/aws/aws-sdk-go v1.19.45/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.25.11/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.27.1/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.31.6/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/aws/aws-sdk-go v1.35.33 h1:8qPRZqCRok5i7VNN51k/Ky7CuyoXMdSs4mUfKyCqvPw= github.com/aws/aws-sdk-go v1.35.33/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= +github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/awslabs/goformation/v4 v4.15.6 h1:9F0MbtJVSMkuI19G6Fm+qHc1nqScHcOIf+3YRRv+Ohc= github.com/awslabs/goformation/v4 v4.15.6/go.mod h1:wB5lKZf1J0MYH1Lt4B9w3opqz0uIjP7MMCAcib3QkwA= github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= @@ -218,28 +244,35 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= +github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y= github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI= github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/bombsimon/wsl/v2 v2.0.0/go.mod h1:mf25kr/SqFEPhhcxW1+7pxzGlW+hIl/hYTKY95VwV8U= github.com/bombsimon/wsl/v2 v2.2.0/go.mod h1:Azh8c3XGEJl9LyX0/sFC+CKMc7Ssgua0g+6abzXN4Pg= github.com/bombsimon/wsl/v3 v3.0.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc= github.com/bombsimon/wsl/v3 v3.1.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= +github.com/bshuster-repo/logrus-logstash-hook v0.4.1 h1:pgAtgj+A31JBVtEHu2uHuEx0n+2ukqUJnS2vVe5pQNA= github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= github.com/buger/goterm v0.0.0-20200322175922-2f3e71b85129 h1:gfAMKE626QEuKG3si0pdTRcr/YEbBoxY+3GOH3gWvl4= github.com/buger/goterm v0.0.0-20200322175922-2f3e71b85129/go.mod h1:u9UyCz2eTrSGy6fbupqJ54eY5c4IC8gREQ1053dK12U= github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= github.com/bugsnag/bugsnag-go v1.4.1/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= +github.com/bugsnag/bugsnag-go v1.5.0 h1:tP8hiPv1pGGW3LA6LKy5lW6WG+y9J2xWUdPd3WC452k= github.com/bugsnag/bugsnag-go v1.5.0/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= +github.com/bugsnag/panicwrap v1.2.0 h1:OzrKrRvXis8qEvOkfcxNcYbOd2O7xXS2nnKMEMABFQA= github.com/bugsnag/panicwrap v1.2.0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= github.com/caarlos0/ctrlc v1.0.0/go.mod h1:CdXpj4rmq0q/1Eb44M9zi2nKB0QraNKuRGYGrrHhcQw= github.com/campoy/unique v0.0.0-20180121183637-88950e537e7e/go.mod h1:9IOqJGCPMSc6E5ydlp5NIonxObaeu/Iub/X03EKPVYo= +github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e/go.mod h1:oDpT4efm8tSYHXV5tHSdRvBet/b/QzxZ+XyyPehvm3A= github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= @@ -251,6 +284,7 @@ github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghf github.com/cespare/xxhash/v2 v2.1.0/go.mod h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tjxl5dIMyVM= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= @@ -258,6 +292,7 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775 h1:cHzBGGVew0ezFsq2grfy2RsB8hO/eNyBgOLHBCqfR1U= github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc= +github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/backoff v0.0.0-20161212185259-647f3cdfc87a/go.mod h1:rzgs2ZOiguV6/NpiDgADjRLPNyZlApIWxKpkT+X8SdY= github.com/cloudflare/cfssl v0.0.0-20181213083726-b94e044bb51e/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA= @@ -273,6 +308,7 @@ github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:z github.com/codahale/hdrhistogram v0.0.0-20160425231609-f8ad88b59a58/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/compose-spec/compose-go v0.0.0-20210119095023-cd294eea46e9 h1:fk9KYzKkVy6q1ETSXOPDHxeoj2ZBKZFP27XVfVMRMUM= github.com/compose-spec/compose-go v0.0.0-20210119095023-cd294eea46e9/go.mod h1:rz7rjxJGA/pWpLdBmDdqymGm2okEDYgBE7yx569xW+I= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340 h1:9atoWyI9RtXFwf7UDbme/6M8Ud0rFrx+Q3ZWgSnsxtw= @@ -286,12 +322,14 @@ github.com/containerd/containerd v1.2.7/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMX github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.4/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.4.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.4.1-0.20201117152358-0edc412565dc/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.4.3 h1:ijQT13JedHSHrQGWFcGEwzcNKrAGIiZ+jSD5QQG07SY= github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/continuity v0.0.0-20181203112020-004b46473808/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20200107194136-26c1120b8d41/go.mod h1:Dq467ZllaHgAtVp4p1xUQWBrFXR9s/wyoTpG8zOJGkY= github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= github.com/containerd/continuity v0.0.0-20200928162600-f2cc35102c2a h1:jEIoR0aA5GogXZ8pP3DUzE+zrhaF6/1rYZy+7KkYEWM= github.com/containerd/continuity v0.0.0-20200928162600-f2cc35102c2a/go.mod h1:W0qIOTD7mp2He++YVq+kgfXezRYqzP1uDuMVH1bITDY= @@ -338,14 +376,19 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cyphar/filepath-securejoin v0.2.2 h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= 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= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE= +github.com/deislabs/oras v0.8.1 h1:If674KraJVpujYR00rzdi0QAmW4BxzMJPVAZJKuhQ0c= +github.com/deislabs/oras v0.8.1/go.mod h1:Mx0rMSbBNaNfY9hjpccEnxkOqJL6KGjtxNHPLC4G4As= github.com/denisenkom/go-mssqldb v0.0.0-20190315220205-a8ed825ac853/go.mod h1:xN/JuLBIz4bjkxNmByTiV1IbhfnYb6oo99phBn4Eqhc= github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM= +github.com/denisenkom/go-mssqldb v0.0.0-20191001013358-cfbb681360f0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY= github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= @@ -358,6 +401,7 @@ github.com/docker/buildx v0.5.1 h1:lkMHQPKHyUUDbO2QO2JE9LLi8mlOULpzSoV7B/2XKso= github.com/docker/buildx v0.5.1/go.mod h1:YlxswdEKSMrxCCSYWU2p/Ii1oOOwu8lT3tJzJDpP7J4= github.com/docker/cli v0.0.0-20190925022749-754388324470/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v0.0.0-20200130152716-5d0cf8839492/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli v20.10.0-beta1.0.20201029214301-1d20b15adc38+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli v20.10.1+incompatible h1:7wfpCo1keo9xXmH6fFu29m0BCK0+Uzu4oIopGUS09bM= github.com/docker/cli v20.10.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= @@ -369,6 +413,7 @@ github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r github.com/docker/docker v1.4.2-0.20180531152204-71cd53e4a197/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v1.4.2-0.20181229214054-f76d6a078d88/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v1.4.2-0.20200203170920-46ec8731fbce/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v17.12.0-ce-rc1.0.20200730172259-9f28837c1d93+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v20.10.0-beta1.0.20201110211921-af34b94a78a1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v20.10.1+incompatible h1:u0HIBLwOJdemyBdTCkoBX34u3lb5KyBo0rQE3a5Yg+E= @@ -396,6 +441,7 @@ github.com/docker/libtrust v0.0.0-20150526203908-9cbd2a1374f4/go.mod h1:cyGadeNE github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4= github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= 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/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= @@ -404,33 +450,47 @@ github.com/dustmop/soup v1.1.2-0.20190516214245-38228baa104e/go.mod h1:CgNC6SGbT 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= +github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/elazarl/goproxy v0.0.0-20191011121108-aa519ddbe484 h1:pEtiCjIXx3RvGjlUJuCNxNOw0MNblyR9Wi+vJGBFh+8= github.com/elazarl/goproxy v0.0.0-20191011121108-aa519ddbe484/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= +github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2 h1:dWB6v3RcOy03t/bUadywsbyrQwCqZeNIEX6M1OtSZOM= github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM= +github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= +github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= +github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= +github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7 h1:LofdAjjjqCSXMwLGgOgnE+rdPuvX9DxCqaHwKy7i/ko= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/getsentry/raven-go v0.0.0-20180121060056-563b81fc02b7/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= @@ -446,10 +506,14 @@ github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3I github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.10.0 h1:dXFJfIHVvUcpSgDOV+Ne6t7jXri8Tfv2uOLHUZ2XNuo= +github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= 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 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0 h1:QvGt2nLcHH0WK9orKa+ppBPAxREcH364nPUedEpK0TY= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= @@ -506,6 +570,7 @@ github.com/go-openapi/validate v0.19.8/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85n github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -525,7 +590,17 @@ github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslW github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU= github.com/go-toolsmith/typep v1.0.2/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU= github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= +github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/envy v1.7.1 h1:OQl5ys5MBea7OGCdvPbBJWRgnhC/fGona6QKfvFeau8= +github.com/gobuffalo/envy v1.7.1/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w= github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM= +github.com/gobuffalo/logger v1.0.1 h1:ZEgyRGgAm4ZAhAO45YXMs5Fp+bzGLESFewzAVBMKuTg= +github.com/gobuffalo/logger v1.0.1/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs= +github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4= +github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q= +github.com/gobuffalo/packr/v2 v2.7.1 h1:n3CIW5T17T8v4GGK5sWXLVWJhCz7b5aNLSxW6gYim4o= +github.com/gobuffalo/packr/v2 v2.7.1/go.mod h1:qYEvAazPaVxy7Y7KR0W8qYEE+RymX74kETFqjFoFlOc= +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/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= @@ -539,10 +614,15 @@ github.com/godbus/dbus v4.1.0+incompatible h1:WqqLRTsQic3apZUK9qC5sGNfXthmPXzUZ7 github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godror/godror v0.13.3/go.mod h1:2ouUT4kdhUBk7TAkHWD4SN0CdI0pgEQbo8FVHhbSKWg= github.com/gofrs/flock v0.0.0-20190320160742-5135e617513b/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/flock v0.7.3 h1:I0EKY9l8HZCXTMYC4F80vwT6KNypV9uYKP3Alm/hjmQ= github.com/gofrs/flock v0.7.3/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gofrs/flock v0.8.0 h1:MSdYClljsF3PbENUUEx85nkWfJSGfzYI9yEBZOJz6CY= +github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= github.com/gogo/googleapis v1.3.0/go.mod h1:d+q1s/xVJxZGKWwC/6UfPIF33J+G1Tq4GYv9Y+Tg/EU= github.com/gogo/googleapis v1.3.2 h1:kX1es4djPJrsDhY7aZKJy7aZasdcB5oSOEphMjSB53c= @@ -554,6 +634,7 @@ github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5 github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -608,8 +689,12 @@ github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21/go.mod h1:tf5+bz github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0/go.mod h1:qOQCunEYvmd/TLamH+7LlVccLvUH5kZNhbCgTHoBbp4= github.com/golangci/revgrep v0.0.0-20180812185044-276a5c0a1039/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= github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg= github.com/google/crfs v0.0.0-20191108021818-71d77da419c9/go.mod h1:etGhoOqfwPkooV6aqoX3eBGQOJblqdoc9XvWOeuxpPw= @@ -674,6 +759,7 @@ github.com/goreleaser/goreleaser v0.136.0/go.mod h1:wiKrPUeSNh6Wu8nUHxZydSOVQ/OZ github.com/goreleaser/nfpm v1.2.1/go.mod h1:TtWrABZozuLOttX2uDlYyECfQX7x5XYkVxhjYcR6G9w= github.com/goreleaser/nfpm v1.3.0/go.mod h1:w0p7Kc9TAUgWMyrub63ex3M2Mgw88M4GZXoTq5UCb40= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33 h1:893HsJqtxp9z1SF76gg6hY70hRY1wVlTSnC/h1yUDCo= github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= @@ -685,8 +771,11 @@ github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoA 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/gostaticanalysis/analysisutil v0.0.3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= +github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= +github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY= github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/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= @@ -705,7 +794,9 @@ github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMW github.com/hanwen/go-fuse v1.0.0/go.mod h1:unqXarDXqzAk0rt98O2tVndEPIpUgLD9+rwFisZH3Ok= github.com/hanwen/go-fuse/v2 v2.0.3/go.mod h1:0EQM6aH2ctVpvZ6a+onrQ/vaykxh2GH7hy3e13vzTUY= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -743,6 +834,9 @@ github.com/hashicorp/uuid v0.0.0-20160311170451-ebb0a03e909c/go.mod h1:fHzc09Uny github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDGgJGQpNflI3+MJSBhsgT5PCtzBQ= github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs= +github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/iancoleman/strcase v0.1.2 h1:gnomlvw9tnV3ITTAxzKSgTF+8kFWcU/f+TgttpXGz1U= github.com/iancoleman/strcase v0.1.2/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -753,6 +847,7 @@ github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/ishidawataru/sctp v0.0.0-20191218070446-00ab2ac2db07/go.mod h1:co9pwDoBCm1kGxawmb4sPq0cSIOOWNPT4KnHotMP1Zg= github.com/jarcoal/httpmock v1.0.5/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= @@ -776,6 +871,8 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGw github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmhodges/clock v0.0.0-20160418191101-880ee4c33548/go.mod h1:hGT6jSUVzF6no3QaDSMLGLEHtHSBSefs+MgcDWnmhmo= github.com/jmoiron/sqlx v0.0.0-20180124204410-05cef0741ade/go.mod h1:IiEW3SEiiErVyFdH8NTuWjSifiEQKUoyK3LNqr2kCHU= +github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= +github.com/jmoiron/sqlx v1.2.1-0.20190826204134-d7d95172beb5 h1:lrdPtrORjGv1HbbEvKWDUAy97mPpFm4B8hp77tcCUJY= github.com/jmoiron/sqlx v1.2.1-0.20190826204134-d7d95172beb5/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8= github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= @@ -795,6 +892,7 @@ github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= +github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= @@ -813,6 +911,7 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -829,11 +928,23 @@ github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8 github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s= github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= +github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= +github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= +github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= +github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= github.com/lib/pq v0.0.0-20180201184707-88edab080323/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.9.0 h1:L8nSXQQzAYByakOFMTwpjRoHsMJklur4Gi59b6VivR8= +github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +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/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= +github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= +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/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -865,12 +976,17 @@ github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2y github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-oci8 v0.0.7/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mNXJwGI= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= +github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-shellwords v1.0.10 h1:Y7Xqm8piKOO3v10Thp7Z36h4FYFjt5xB//6XvOrs2Gw= github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-sqlite3 v1.12.0 h1:u/x3mp++qUxvYfulZ4HKOvVO0JWhk7HtE8lWhbGz/Do= +github.com/mattn/go-sqlite3 v1.12.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-zglob v0.0.1/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= @@ -884,12 +1000,16 @@ github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3N github.com/miekg/pkcs11 v0.0.0-20190322140431-074fd7a1ed19/go.mod h1:WCBAbTOdfhHhz7YXujeZMF7owC4tPb1naKFsgfUISjo= github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= 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 v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= +github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= @@ -899,6 +1019,8 @@ github.com/mitchellh/mapstructure v1.3.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR github.com/mitchellh/mapstructure v1.4.0 h1:7ks8ZkOP5/ujthUsT07rNv+nkLXCQWKNHuwzOAesEks= github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= +github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/moby/buildkit v0.8.1-0.20201205083753-0af7b1b9c693 h1:WD0QJVZm1JRoUYhnu37QoCHC1wsrJYIUnRaPzX55Xlc= github.com/moby/buildkit v0.8.1-0.20201205083753-0af7b1b9c693/go.mod h1:/kyU1hKy/aYCuP39GZA9MaKioovHku57N6cqlKZIaiQ= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= @@ -910,6 +1032,7 @@ github.com/moby/sys/mountinfo v0.1.0/go.mod h1:w2t2Avltqx8vE7gX5l+QiBKxODu2TX0+S github.com/moby/sys/mountinfo v0.1.3/go.mod h1:w2t2Avltqx8vE7gX5l+QiBKxODu2TX0+Syr3h52Tw4o= github.com/moby/sys/mountinfo v0.4.0 h1:1KInV3Huv18akCu58V7lzNlt+jFmqlu1EaErnEHE/VM= github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= +github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= github.com/moby/term v0.0.0-20200915141129-7f0af18e79f2/go.mod h1:TjQg8pa4iejrUrjiz0MCtMV38jdMNW4doKSiBrEvCQQ= github.com/moby/term v0.0.0-20201110203204-bea5bbe245bf h1:Un6PNx5oMK6CCwO3QTUyPiK2mtZnPrpDl5UnZ64eCkw= github.com/moby/term v0.0.0-20201110203204-bea5bbe245bf/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= @@ -934,6 +1057,13 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m 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/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/nakabonne/nestif v0.3.0/go.mod h1:dI314BppzXjJ4HsCnbo7XzrJHPszZsjnk5wEBSYHI2c= +github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= +github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= +github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= +github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= +github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= @@ -941,8 +1071,12 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= +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/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/olekukonko/tablewriter v0.0.2/go.mod h1:rSAaSIOAGT9odnlyGlUfAJaoc5w2fSBUmeGDbRWPxyQ= 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= @@ -984,20 +1118,32 @@ github.com/opencontainers/runtime-spec v1.0.3-0.20200728170252-4d89ac9fbff6/go.m github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= github.com/opencontainers/selinux v1.6.0 h1:+bIAS/Za3q5FTwWym4fTB0vObnfCf3G/NC7K6Jx62mY= github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= +github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= github.com/opentracing-contrib/go-stdlib v1.0.0/go.mod h1:qtI1ogk+2JhVPIXVc6q+NHziSmy2W5GbdQZFUHADCBU= +github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= +github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/openzipkin/zipkin-go v0.1.3/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/paulmach/orb v0.1.3/go.mod h1:VFlX/8C+IQ1p6FTRRKzKoOPJnvEtA5G0Veuqwbu//Vk= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bAOTRnLElKs= +github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= +github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7FXDQlpCiw2j81fOmAwQLnZnLGXVKUzeKQXIAw= +github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc= +github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= +github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pivotal/image-relocation v0.0.0-20191111101224-e94aff6df06c/go.mod h1:/JNbQwGylYm3AQh8q+MBF8e/h0W1Jy20JGTvozuXYTE= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -1005,6 +1151,7 @@ github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pkg/profile v1.5.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18= 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= @@ -1017,12 +1164,14 @@ github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= +github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= 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= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= @@ -1043,6 +1192,7 @@ github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7z github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4= @@ -1062,15 +1212,23 @@ github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uY github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.4.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.5.2 h1:qLvObTrvO/XRCqmkKxUlOBc48bI3efyDuAZe25QiF0w= github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rubenv/sql-migrate v0.0.0-20200616145509-8d140a17f351 h1:HXr/qUllAWv9riaI4zh2eXWKmCSDqVS/XH1MRHLKRwk= +github.com/rubenv/sql-migrate v0.0.0-20200616145509-8d140a17f351/go.mod h1:DCgfY80j8GYL7MLEfvcpSFvjD0L5yZq/aZUJmhZklyg= github.com/rubiojr/go-vhd v0.0.0-20160810183302-0bfd3b39853c/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto= +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/ryancurrah/gomodguard v1.0.4/go.mod h1:9T/Cfuxs5StfsocWr4WzDL36HqnX0fVb9d5fSEaLhoE= github.com/ryancurrah/gomodguard v1.1.0/go.mod h1:4O8tr7hBODaGE6VIhfJDHcwzh5GUccKSJBU0UMXJFVM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/sanathkr/go-yaml v0.0.0-20170819195128-ed9d249f429b h1:jUK33OXuZP/l6babJtnLo1qsGvq6G9so9KMflGAm4YA= github.com/sanathkr/go-yaml v0.0.0-20170819195128-ed9d249f429b/go.mod h1:8458kAagoME2+LN5//WxE71ysZ3B7r22fdgb7qVmXSY= github.com/sanathkr/yaml v0.0.0-20170819201035-0056894fa522 h1:fOCp11H0yuyAt2wqlbJtbyPzSgaxHTv8uN1pMpkG1t8= @@ -1090,6 +1248,8 @@ github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNX github.com/serialx/hashring v0.0.0-20190422032157-8b2912629002/go.mod h1:/yeG0My1xr/u+HZrFQ1tOQQQQrOawfyMUH13ai5brBc= 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/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= 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= @@ -1110,12 +1270,14 @@ github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIK github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34cd2MNlA9u1mE= github.com/sourcegraph/go-diff v0.5.3/go.mod h1:v9JDtjCE4HHHCZGId75rg8gkKKa98RVjBcBGsVmMmak= 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/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= @@ -1136,6 +1298,9 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.6.1/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -1232,11 +1397,15 @@ github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1: github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI= github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= +github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE= github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= +github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= github.com/zclconf/go-cty v1.4.0/go.mod h1:nHzOclRkoj++EU9ZjSrZvRG0BXIWt8c7loYc0qXAFGQ= +github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= github.com/zmap/rc2 v0.0.0-20131011165748-24b9757f5521/go.mod h1:3YZ9o3WnatTIZhuOtot4IcUfzoKVjUHqu6WALIyI0nE= github.com/zmap/zcertificate v0.0.0-20180516150559-0e3d58b1bac4/go.mod h1:5iU54tB79AMBcySS0R2XIyZBAVmeHranShAFELYx7is= @@ -1247,6 +1416,7 @@ go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= 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= @@ -1255,6 +1425,7 @@ go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.19.1/go.mod h1:gug0GbSHa8Pafr0d2urOSgoXHZ6x/RUlaiT0d9pqb4A= go.opencensus.io v0.19.2/go.mod h1:NO/8qkisMZLZ1FCsKNqtJPwc8/TaclWyY0B6wcYNg9M= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -1265,8 +1436,12 @@ go.starlark.net v0.0.0-20190528202925-30ae18b8564f/go.mod h1:c1/X6cHgvdXj6pUlmWK go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= gocloud.dev v0.19.0/go.mod h1:SmKwiR8YwIMMJvQBKLsC3fHNyMwXLw3PMDO+VVteJMI= golang.org/x/build v0.0.0-20190314133821-5284462c4bec/go.mod h1:atTaCNAy0f16Ah5aV1gMSwgiKVHwu/JncqDpuRr7lS4= @@ -1285,6 +1460,7 @@ golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -1292,6 +1468,7 @@ golang.org/x/crypto v0.0.0-20191028145041-f83a4685e152/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9 h1:phUcVbl53swtrUN8kQEXFhUxPlIlWyBfKmidCu7P95o= @@ -1431,6 +1608,7 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1457,6 +1635,7 @@ golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1544,8 +1723,11 @@ golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191014205221-18e3458ac98b/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1557,6 +1739,7 @@ golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200102140908-9497f49d5709/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -1576,8 +1759,10 @@ golang.org/x/tools v0.0.0-20200422022333-3d57cf2e726e/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200502202811-ed308ab3e770/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= @@ -1675,6 +1860,7 @@ google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmE google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1703,6 +1889,7 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= +gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= 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-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1716,8 +1903,11 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fatih/pool.v2 v2.0.0/go.mod h1:8xVGeu1/2jr2wm5V9SPuMht2H5AEmf5aFMGSQixtjTY= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= gopkg.in/gorethink/gorethink.v3 v3.0.5/go.mod h1:+3yIIHJUGMBK+wyPH+iN5TP+88ikFDfZdqTlK3Y9q8I= +gopkg.in/gorp.v1 v1.7.2 h1:j3DWlAyGVv8whO7AcIWznQ2Yj7yJkn34B8s63GViAAw= +gopkg.in/gorp.v1 v1.7.2/go.mod h1:Wo3h+DBQZIxATwftsglhdD/62zRFPhGhTiu5jUJmCaw= 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= @@ -1731,6 +1921,7 @@ gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76 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.1/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +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= @@ -1749,6 +1940,8 @@ gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= +helm.sh/helm/v3 v3.5.0 h1:uqIT3Bh4hVEyZRThyTPik8FkiABj3VJIY+POvDFT3a4= +helm.sh/helm/v3 v3.5.0/go.mod h1:bjwXfmGAF+SEuJZ2AtN1xmTuz4FqaNYOJrXP+vtj6Tw= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20180920025451-e3ad64cb4ed3/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1766,6 +1959,8 @@ k8s.io/api v0.19.0 h1:XyrFIJqTYZJ2DU7FBE/bSPz7b1HvbVBuBf07oeo6eTc= k8s.io/api v0.19.0/go.mod h1:I1K45XlvTrDjmj5LoM5LuP/KYrhWbjUKT/SoPG0qTjw= k8s.io/api v0.20.1 h1:ud1c3W3YNzGd6ABJlbFfKXBKXO+1KdGfcgGGNgFR03E= k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= +k8s.io/apiextensions-apiserver v0.20.1 h1:ZrXQeslal+6zKM/HjDXLzThlz/vPSxrfK3OqL8txgVQ= +k8s.io/apiextensions-apiserver v0.20.1/go.mod h1:ntnrZV+6a3dB504qwC5PN/Yg9PBiDNt1EVqbW2kORVk= k8s.io/apimachinery v0.0.0-20180904193909-def12e63c512/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0= k8s.io/apimachinery v0.0.0-20190806215851-162a2dabc72f/go.mod h1:+ntn62igV2hyNj7/0brOvXSMONE2KxcePkSxK7/9FFQ= k8s.io/apimachinery v0.0.0-20191004115801-a2eda9f80ab8/go.mod h1:llRdnznGEAqC3DcNm6yEj472xaFVfLM7hnYofMb12tQ= @@ -1776,6 +1971,10 @@ k8s.io/apimachinery v0.20.1 h1:LAhz8pKbgR8tUwn7boK+b2HZdt7MiTu2mkYtFMUjTRQ= k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apiserver v0.17.4 h1:bYc9LvDPEF9xAL3fhbDzqNOQOAnNF2ZYCrMW8v52/mE= k8s.io/apiserver v0.17.4/go.mod h1:5ZDQ6Xr5MNBxyi3iUZXS84QOhZl+W7Oq2us/29c0j9I= +k8s.io/apiserver v0.20.1 h1:yEqdkxlnQbxi/3e74cp0X16h140fpvPrNnNRAJBDuBk= +k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= +k8s.io/cli-runtime v0.20.1 h1:fJhRQ9EfTpJpCqSFOAqnYLuu5aAM7yyORWZ26qW1jJc= +k8s.io/cli-runtime v0.20.1/go.mod h1:6wkMM16ZXTi7Ow3JLYPe10bS+XBnIkL6V9dmEz0mbuY= k8s.io/client-go v0.0.0-20180910083459-2cefa64ff137/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= k8s.io/client-go v0.0.0-20191016111102-bec269661e48/go.mod h1:hrwktSwYGI4JK+TJA3dMaFyyvHVi/aLarVHpbs8bgCU= k8s.io/client-go v0.17.4/go.mod h1:ouF6o5pz3is8qU0/qYL2RnoxOPqgfuidYLowytyLJmc= @@ -1785,13 +1984,18 @@ k8s.io/client-go v0.20.1 h1:Qquik0xNFbK9aUG92pxHYsyfea5/RPO9o9bSywNor+M= k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= k8s.io/cloud-provider v0.17.4/go.mod h1:XEjKDzfD+b9MTLXQFlDGkk6Ho8SGMpaU8Uugx/KNK9U= k8s.io/code-generator v0.17.2/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s= +k8s.io/code-generator v0.20.1/go.mod h1:UsqdF+VX4PU2g46NC2JRs4gc+IfrctnwHb76RNbWHJg= k8s.io/component-base v0.17.4 h1:H9cdWZyiGVJfWmWIcHd66IsNBWTk1iEgU7D4kJksEnw= k8s.io/component-base v0.17.4/go.mod h1:5BRqHMbbQPm2kKu35v3G+CpVq4K0RJKC7TRioF0I9lE= +k8s.io/component-base v0.20.1 h1:6OQaHr205NSl24t5wOF2IhdrlxZTWEZwuGlLvBgaeIg= +k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= +k8s.io/component-helpers v0.20.1/go.mod h1:Q8trCj1zyLNdeur6pD2QvsF8d/nWVfK71YjN5+qVXy4= k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= k8s.io/csi-translation-lib v0.17.4/go.mod h1:CsxmjwxEI0tTNMzffIAcgR9lX4wOh6AKHdxQrT7L0oo= 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/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= 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.1/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= @@ -1808,11 +2012,15 @@ k8s.io/kube-openapi v0.0.0-20190709113604-33be087ad058/go.mod h1:nfDlWeOsu3pUf4y k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= +k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd h1:sOHNzJIkytDF6qadMNKhhDRpc6ODik8lVC6nOur7B2c= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= +k8s.io/kubectl v0.20.1 h1:7h1vSrL/B3hLrhlCJhbTADElPKDbx+oVUt3+QDSXxBo= +k8s.io/kubectl v0.20.1/go.mod h1:2bE0JLYTRDVKDiTREFsjLAx4R2GvUtL/mGYFXfFFMzY= k8s.io/kubernetes v1.11.10/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/kubernetes v1.13.0 h1:qTfB+u5M92k2fCCCVP2iuhgwwSOv1EkAkvQY1tQODD8= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/legacy-cloud-providers v0.17.4/go.mod h1:FikRNoD64ECjkxO36gkDgJeiQWwyZTuBkhu+yxOc1Js= +k8s.io/metrics v0.20.1/go.mod h1:JhpBE/fad3yRGsgEpiZz5FQQM5wJ18OTLkD7Tv40c0s= k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20200729134348-d5654de09c73 h1:uJmqzgNWG7XyClnU/mLPBWwfKKF1K8Hf8whTseBgJcg= @@ -1832,6 +2040,9 @@ pack.ag/amqp v0.11.2/go.mod h1:4/cbmt4EJXSKlG6LCfWHoqmN0uFdy5i/+YFz+fTfhV4= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= +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/kyaml v0.10.5 h1:PbJcsZsEM7O3hHtUWTR+4WkHVbQRW9crSy75or1gRbI= sigs.k8s.io/kustomize/kyaml v0.10.5/go.mod h1:P6Oy/ah/GZMKzJMIJA2a3/bc8YrBkuL5kJji13PSIzY= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= @@ -1844,6 +2055,7 @@ sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK 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/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= sourcegraph.com/sqs/pbtypes v1.0.0/go.mod h1:3AciMUv4qUuRHRHhOG4TZOB+72GdPVz5k+c648qsFS4= vbom.ml/util v0.0.0-20180919145318-efcd4e0f9787/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI= diff --git a/kube/backend.go b/kube/backend.go new file mode 100644 index 000000000..aa10cd884 --- /dev/null +++ b/kube/backend.go @@ -0,0 +1,84 @@ +// +build kube + +/* + Copyright 2020 Docker Compose CLI authors + + 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. +*/ + +package kube + +import ( + "context" + + "github.com/docker/compose-cli/api/backend" + "github.com/docker/compose-cli/api/cloud" + "github.com/docker/compose-cli/api/compose" + "github.com/docker/compose-cli/api/containers" + apicontext "github.com/docker/compose-cli/api/context" + "github.com/docker/compose-cli/api/context/store" + "github.com/docker/compose-cli/api/resources" + "github.com/docker/compose-cli/api/secrets" + "github.com/docker/compose-cli/api/volumes" +) + +const backendType = store.KubeContextType + +type kubeAPIService struct { + ctx store.KubeContext + composeService compose.Service +} + +func init() { + backend.Register(backendType, backendType, service, cloud.NotImplementedCloudService) +} + +func service(ctx context.Context) (backend.Service, error) { + + contextStore := store.ContextStore(ctx) + currentContext := apicontext.CurrentContext(ctx) + var kubeContext store.KubeContext + + if err := contextStore.GetEndpoint(currentContext, &kubeContext); err != nil { + return nil, err + } + + s, err := NewComposeService(kubeContext) + if err != nil { + return nil, err + } + return &kubeAPIService{ + ctx: kubeContext, + composeService: s, + }, nil +} + +func (s *kubeAPIService) ContainerService() containers.Service { + return nil +} + +func (s *kubeAPIService) ComposeService() compose.Service { + return s.composeService +} + +func (s *kubeAPIService) SecretsService() secrets.Service { + return nil +} + +func (s *kubeAPIService) VolumeService() volumes.Service { + return nil +} + +func (s *kubeAPIService) ResourceService() resources.Service { + return nil +} diff --git a/kube/charts/charts.go b/kube/charts/charts.go new file mode 100644 index 000000000..df035eca7 --- /dev/null +++ b/kube/charts/charts.go @@ -0,0 +1,126 @@ +// +build kube + +/* + Copyright 2020 Docker Compose CLI authors + + 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. +*/ + +package charts + +import ( + "context" + "path/filepath" + "strings" + + "github.com/compose-spec/compose-go/types" + "github.com/docker/compose-cli/api/compose" + "github.com/docker/compose-cli/api/context/store" + "github.com/docker/compose-cli/kube/charts/helm" + "github.com/docker/compose-cli/kube/charts/kubernetes" + kubeutils "github.com/docker/compose-cli/kube/utils" + chart "helm.sh/helm/v3/pkg/chart" + util "helm.sh/helm/v3/pkg/chartutil" + helmenv "helm.sh/helm/v3/pkg/cli" +) + +// API defines management methods for helm charts +type API interface { + GetDefaultEnv() *helmenv.EnvSettings + Connect(ctx context.Context) error + GenerateChart(project *types.Project, dirname string) error + GetChartInMemory(project *types.Project) (*chart.Chart, error) + SaveChart(project *types.Project, dest string) error + + Install(project *types.Project) error + Uninstall(projectName string) error + List(projectName string) ([]compose.Stack, error) +} + +type sdk struct { + h *helm.HelmActions + environment map[string]string +} + +// sdk implement API +var _ API = sdk{} + +func NewSDK(ctx store.KubeContext) (sdk, error) { + return sdk{ + environment: kubeutils.Environment(), + h: helm.NewHelmActions(nil), + }, nil +} + +func (s sdk) Connect(ctx context.Context) error { + return nil +} + +// Install deploys a Compose stack +func (s sdk) Install(project *types.Project) error { + chart, err := s.GetChartInMemory(project) + if err != nil { + return err + } + return s.h.InstallChart(project.Name, chart) +} + +// Uninstall removes a runnign compose stack +func (s sdk) Uninstall(projectName string) error { + return s.h.Uninstall(projectName) +} + +// List returns a list of compose stacks +func (s sdk) List(projectName string) ([]compose.Stack, error) { + return s.h.ListReleases() +} + +// GetDefault initializes Helm EnvSettings +func (s sdk) GetDefaultEnv() *helmenv.EnvSettings { + return helmenv.New() +} + +func (s sdk) GetChartInMemory(project *types.Project) (*chart.Chart, error) { + // replace _ with - in volume names + for k, v := range project.Volumes { + volumeName := strings.ReplaceAll(k, "_", "-") + if volumeName != k { + project.Volumes[volumeName] = v + delete(project.Volumes, k) + } + } + objects, err := kubernetes.MapToKubernetesObjects(project) + if err != nil { + return nil, err + } + //in memory files + return helm.ConvertToChart(project.Name, objects) +} + +func (s sdk) SaveChart(project *types.Project, dest string) error { + chart, err := s.GetChartInMemory(project) + if err != nil { + return err + } + return util.SaveDir(chart, dest) +} + +func (s sdk) GenerateChart(project *types.Project, dirname string) error { + if strings.Contains(dirname, ".") { + splits := strings.SplitN(dirname, ".", 2) + dirname = splits[0] + } + + dirname = filepath.Dir(dirname) + return s.SaveChart(project, dirname) +} diff --git a/kube/charts/helm/chart.go b/kube/charts/helm/chart.go new file mode 100644 index 000000000..d82ed6e9c --- /dev/null +++ b/kube/charts/helm/chart.go @@ -0,0 +1,108 @@ +// +build kube + +/* + Copyright 2020 Docker Compose CLI authors + + 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. +*/ + +package helm + +import ( + "bytes" + "encoding/json" + "html/template" + "path/filepath" + + "gopkg.in/yaml.v3" + + chart "helm.sh/helm/v3/pkg/chart" + loader "helm.sh/helm/v3/pkg/chart/loader" + "k8s.io/apimachinery/pkg/runtime" +) + +func ConvertToChart(name string, objects map[string]runtime.Object) (*chart.Chart, error) { + + files := []*loader.BufferedFile{ + &loader.BufferedFile{ + Name: "README.md", + Data: []byte("This chart was created by converting a Compose file"), + }} + + chart := `name: {{.Name}} +description: A generated Helm Chart for {{.Name}} from Skippbox Kompose +version: 0.0.1 +apiVersion: v1 +keywords: + - {{.Name}} +sources: +home: +` + + t, err := template.New("ChartTmpl").Parse(chart) + if err != nil { + return nil, err + } + type ChartDetails struct { + Name string + } + var chartData bytes.Buffer + err = t.Execute(&chartData, ChartDetails{Name: name}) + if err != nil { + return nil, err + } + files = append(files, &loader.BufferedFile{ + Name: "Chart.yaml", + Data: chartData.Bytes(), + }) + + for name, o := range objects { + j, err := json.Marshal(o) + if err != nil { + return nil, err + } + buf, err := jsonToYaml(j, 2) + if err != nil { + return nil, err + } + files = append(files, &loader.BufferedFile{ + Name: filepath.Join("templates", name), + Data: buf, + }) + + } + return loader.LoadFiles(files) +} + +// Convert JSON to YAML. +func jsonToYaml(j []byte, spaces int) ([]byte, error) { + // Convert the JSON to an object. + var jsonObj interface{} + // We are using yaml.Unmarshal here (instead of json.Unmarshal) because the + // Go JSON library doesn't try to pick the right number type (int, float, + // etc.) when unmarshling to interface{}, it just picks float64 + // universally. go-yaml does go through the effort of picking the right + // number type, so we can preserve number type throughout this process. + err := yaml.Unmarshal(j, &jsonObj) + if err != nil { + return nil, err + } + + var b bytes.Buffer + encoder := yaml.NewEncoder(&b) + encoder.SetIndent(spaces) + if err := encoder.Encode(jsonObj); err != nil { + return nil, err + } + return b.Bytes(), nil +} diff --git a/kube/charts/helm/helm.go b/kube/charts/helm/helm.go new file mode 100644 index 000000000..e7e7f42fd --- /dev/null +++ b/kube/charts/helm/helm.go @@ -0,0 +1,135 @@ +// +build kube + +/* + Copyright 2020 Docker Compose CLI authors + + 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. +*/ + +package helm + +import ( + "errors" + "log" + + "github.com/docker/compose-cli/api/compose" + action "helm.sh/helm/v3/pkg/action" + chart "helm.sh/helm/v3/pkg/chart" + loader "helm.sh/helm/v3/pkg/chart/loader" + env "helm.sh/helm/v3/pkg/cli" + "helm.sh/helm/v3/pkg/release" +) + +type HelmActions struct { + Config *action.Configuration + Settings *env.EnvSettings + kube_conn_init bool +} + +func NewHelmActions(settings *env.EnvSettings) *HelmActions { + if settings == nil { + settings = env.New() + } + return &HelmActions{ + Config: new(action.Configuration), + Settings: settings, + kube_conn_init: false, + } +} + +func (hc *HelmActions) initKubeClient() error { + if hc.kube_conn_init { + return nil + } + if err := hc.Config.Init( + hc.Settings.RESTClientGetter(), + hc.Settings.Namespace(), + "configmap", + log.Printf, + ); err != nil { + log.Fatal(err) + } + if err := hc.Config.KubeClient.IsReachable(); err != nil { + return err + } + hc.kube_conn_init = true + return nil +} + +func (hc *HelmActions) InstallChartFromDir(name string, chartpath string) error { + chart, err := loader.Load(chartpath) + if err != nil { + return err + } + return hc.InstallChart(name, chart) +} + +func (hc *HelmActions) InstallChart(name string, chart *chart.Chart) error { + hc.initKubeClient() + + actInstall := action.NewInstall(hc.Config) + actInstall.ReleaseName = name + actInstall.Namespace = hc.Settings.Namespace() + + release, err := actInstall.Run(chart, map[string]interface{}{}) + if err != nil { + return err + } + log.Println("Release status: ", release.Info.Status) + log.Println(release.Info.Description) + return nil +} + +func (hc *HelmActions) Uninstall(name string) error { + hc.initKubeClient() + release, err := hc.Get(name) + if err != nil { + return err + } + if release == nil { + return errors.New("No release found with the name provided.") + } + actUninstall := action.NewUninstall(hc.Config) + response, err := actUninstall.Run(name) + if err != nil { + return err + } + log.Println(response.Release.Info.Description) + return nil +} + +func (hc *HelmActions) Get(name string) (*release.Release, error) { + hc.initKubeClient() + + actGet := action.NewGet(hc.Config) + return actGet.Run(name) +} + +func (hc *HelmActions) ListReleases() ([]compose.Stack, error) { + hc.initKubeClient() + + actList := action.NewList(hc.Config) + releases, err := actList.Run() + if err != nil { + return nil, err + } + result := []compose.Stack{} + for _, rel := range releases { + result = append(result, compose.Stack{ + ID: rel.Name, + Name: rel.Name, + Status: string(rel.Info.Status), + }) + } + return result, nil +} diff --git a/kube/charts/kubernetes/kube.go b/kube/charts/kubernetes/kube.go new file mode 100644 index 000000000..ded7419b8 --- /dev/null +++ b/kube/charts/kubernetes/kube.go @@ -0,0 +1,232 @@ +// +build kube + +/* + Copyright 2020 Docker Compose CLI authors + + 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. +*/ + +package kubernetes + +import ( + "fmt" + "log" + "strings" + "time" + + "github.com/compose-spec/compose-go/types" + apps "k8s.io/api/apps/v1" + core "k8s.io/api/core/v1" + resource "k8s.io/apimachinery/pkg/api/resource" + meta "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/intstr" +) + +func MapToKubernetesObjects(project *types.Project) (map[string]runtime.Object, error) { + objects := map[string]runtime.Object{} + + for _, service := range project.Services { + svcObject := mapToService(project, service) + if svcObject != nil { + objects[fmt.Sprintf("%s-service.yaml", service.Name)] = svcObject + } else { + log.Println("Missing port mapping from service config.") + } + + if service.Deploy != nil && service.Deploy.Mode == "global" { + daemonset, err := mapToDaemonset(project, service, project.Name) + if err != nil { + return nil, err + } + objects[fmt.Sprintf("%s-daemonset.yaml", service.Name)] = daemonset + } else { + deployment, err := mapToDeployment(project, service, project.Name) + if err != nil { + return nil, err + } + objects[fmt.Sprintf("%s-deployment.yaml", service.Name)] = deployment + } + for _, vol := range service.Volumes { + if vol.Type == "volume" { + vol.Source = strings.ReplaceAll(vol.Source, "_", "-") + objects[fmt.Sprintf("%s-persistentvolumeclaim.yaml", vol.Source)] = mapToPVC(service, vol) + } + } + } + return objects, nil +} + +func mapToService(project *types.Project, service types.ServiceConfig) *core.Service { + ports := []core.ServicePort{} + for _, p := range service.Ports { + ports = append(ports, + core.ServicePort{ + Name: fmt.Sprintf("%d-%s", p.Target, strings.ToLower(string(p.Protocol))), + Port: int32(p.Target), + TargetPort: intstr.FromInt(int(p.Target)), + Protocol: toProtocol(p.Protocol), + }) + } + if len(ports) == 0 { + return nil + } + return &core.Service{ + TypeMeta: meta.TypeMeta{ + Kind: "Service", + APIVersion: "v1", + }, + ObjectMeta: meta.ObjectMeta{ + Name: service.Name, + }, + Spec: core.ServiceSpec{ + Selector: map[string]string{"com.docker.compose.service": service.Name}, + Ports: ports, + Type: mapServiceToServiceType(project, service), + }, + } +} + +func mapServiceToServiceType(project *types.Project, service types.ServiceConfig) core.ServiceType { + serviceType := core.ServiceTypeClusterIP + if len(service.Networks) == 0 { + // service is implicitly attached to "default" network + serviceType = core.ServiceTypeLoadBalancer + } + for name := range service.Networks { + if !project.Networks[name].Internal { + serviceType = core.ServiceTypeLoadBalancer + } + } + for _, port := range service.Ports { + if port.Published != 0 { + serviceType = core.ServiceTypeNodePort + } + } + return serviceType +} + +func mapToDeployment(project *types.Project, service types.ServiceConfig, name string) (*apps.Deployment, error) { + labels := map[string]string{ + "com.docker.compose.service": service.Name, + "com.docker.compose.project": name, + } + podTemplate, err := toPodTemplate(project, service, labels) + if err != nil { + return nil, err + } + selector := new(meta.LabelSelector) + selector.MatchLabels = make(map[string]string) + for key, val := range labels { + selector.MatchLabels[key] = val + } + return &apps.Deployment{ + TypeMeta: meta.TypeMeta{ + Kind: "Deployment", + APIVersion: "apps/v1", + }, + ObjectMeta: meta.ObjectMeta{ + Name: service.Name, + Labels: labels, + }, + Spec: apps.DeploymentSpec{ + Selector: selector, + Replicas: toReplicas(service.Deploy), + Strategy: toDeploymentStrategy(service.Deploy), + Template: podTemplate, + }, + }, nil +} + +func mapToDaemonset(project *types.Project, service types.ServiceConfig, name string) (*apps.DaemonSet, error) { + labels := map[string]string{ + "com.docker.compose.service": service.Name, + "com.docker.compose.project": name, + } + podTemplate, err := toPodTemplate(project, service, labels) + if err != nil { + return nil, err + } + + return &apps.DaemonSet{ + ObjectMeta: meta.ObjectMeta{ + Name: service.Name, + Labels: labels, + }, + Spec: apps.DaemonSetSpec{ + Template: podTemplate, + }, + }, nil +} + +func toReplicas(deploy *types.DeployConfig) *int32 { + v := int32(1) + if deploy != nil { + v = int32(*deploy.Replicas) + } + return &v +} + +func toDeploymentStrategy(deploy *types.DeployConfig) apps.DeploymentStrategy { + if deploy == nil || deploy.UpdateConfig == nil { + return apps.DeploymentStrategy{ + Type: apps.RecreateDeploymentStrategyType, + } + } + return apps.DeploymentStrategy{ + Type: apps.RollingUpdateDeploymentStrategyType, + RollingUpdate: &apps.RollingUpdateDeployment{ + MaxUnavailable: &intstr.IntOrString{ + Type: intstr.Int, + IntVal: int32(*deploy.UpdateConfig.Parallelism), + }, + MaxSurge: nil, + }, + } +} + +func mapToPVC(service types.ServiceConfig, vol types.ServiceVolumeConfig) runtime.Object { + rwaccess := core.ReadWriteOnce + if vol.ReadOnly { + rwaccess = core.ReadOnlyMany + } + return &core.PersistentVolumeClaim{ + TypeMeta: meta.TypeMeta{ + Kind: "PersistentVolumeClaim", + APIVersion: "v1", + }, + ObjectMeta: meta.ObjectMeta{ + Name: vol.Source, + Labels: map[string]string{"com.docker.compose.service": service.Name}, + }, + Spec: core.PersistentVolumeClaimSpec{ + VolumeName: vol.Source, + AccessModes: []core.PersistentVolumeAccessMode{rwaccess}, + Resources: core.ResourceRequirements{ + Requests: core.ResourceList{ + core.ResourceStorage: resource.MustParse("100Mi"), + }, + }, + }, + } +} + +// toSecondsOrDefault converts a duration string in seconds and defaults to a +// given value if the duration is nil. +// The supported units are us, ms, s, m and h. +func toSecondsOrDefault(duration *types.Duration, defaultValue int32) int32 { //nolint: unparam + if duration == nil { + return defaultValue + } + return int32(time.Duration(*duration).Seconds()) +} diff --git a/kube/charts/kubernetes/placement.go b/kube/charts/kubernetes/placement.go new file mode 100644 index 000000000..f5480da48 --- /dev/null +++ b/kube/charts/kubernetes/placement.go @@ -0,0 +1,144 @@ +// +build kube + +/* + Copyright 2020 Docker Compose CLI authors + + 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. +*/ + +package kubernetes + +import ( + "regexp" + "strings" + + "github.com/compose-spec/compose-go/types" + "github.com/pkg/errors" + apiv1 "k8s.io/api/core/v1" +) + +var constraintEquals = regexp.MustCompile(`([\w\.]*)\W*(==|!=)\W*([\w\.]*)`) + +const ( + kubernetesOs = "beta.kubernetes.io/os" + kubernetesArch = "beta.kubernetes.io/arch" + kubernetesHostname = "kubernetes.io/hostname" +) + +// node.id Node ID node.id == 2ivku8v2gvtg4 +// node.hostname Node hostname node.hostname != node-2 +// node.role Node role node.role == manager +// node.labels user defined node labels node.labels.security == high +// engine.labels Docker Engine's labels engine.labels.operatingsystem == ubuntu 14.04 +func toNodeAffinity(deploy *types.DeployConfig) (*apiv1.Affinity, error) { + constraints := []string{} + if deploy != nil && deploy.Placement.Constraints != nil { + constraints = deploy.Placement.Constraints + } + requirements := []apiv1.NodeSelectorRequirement{} + for _, constraint := range constraints { + matches := constraintEquals.FindStringSubmatch(constraint) + if len(matches) == 4 { + key := matches[1] + operator, err := toRequirementOperator(matches[2]) + if err != nil { + return nil, err + } + value := matches[3] + + switch { + case key == constraintOs: + requirements = append(requirements, apiv1.NodeSelectorRequirement{ + Key: kubernetesOs, + Operator: operator, + Values: []string{value}, + }) + case key == constraintArch: + requirements = append(requirements, apiv1.NodeSelectorRequirement{ + Key: kubernetesArch, + Operator: operator, + Values: []string{value}, + }) + case key == constraintHostname: + requirements = append(requirements, apiv1.NodeSelectorRequirement{ + Key: kubernetesHostname, + Operator: operator, + Values: []string{value}, + }) + case strings.HasPrefix(key, constraintLabelPrefix): + requirements = append(requirements, apiv1.NodeSelectorRequirement{ + Key: strings.TrimPrefix(key, constraintLabelPrefix), + Operator: operator, + Values: []string{value}, + }) + } + } + } + + if !hasRequirement(requirements, kubernetesOs) { + requirements = append(requirements, apiv1.NodeSelectorRequirement{ + Key: kubernetesOs, + Operator: apiv1.NodeSelectorOpIn, + Values: []string{"linux"}, + }) + } + if !hasRequirement(requirements, kubernetesArch) { + requirements = append(requirements, apiv1.NodeSelectorRequirement{ + Key: kubernetesArch, + Operator: apiv1.NodeSelectorOpIn, + Values: []string{"amd64"}, + }) + } + return &apiv1.Affinity{ + NodeAffinity: &apiv1.NodeAffinity{ + RequiredDuringSchedulingIgnoredDuringExecution: &apiv1.NodeSelector{ + NodeSelectorTerms: []apiv1.NodeSelectorTerm{ + { + MatchExpressions: requirements, + }, + }, + }, + }, + }, nil +} + +const ( + constraintOs = "node.platform.os" + constraintArch = "node.platform.arch" + constraintHostname = "node.hostname" + constraintLabelPrefix = "node.labels." +) + +func hasRequirement(requirements []apiv1.NodeSelectorRequirement, key string) bool { + for _, r := range requirements { + if r.Key == key { + return true + } + } + return false +} + +func toRequirementOperator(sign string) (apiv1.NodeSelectorOperator, error) { + switch sign { + case "==": + return apiv1.NodeSelectorOpIn, nil + case "!=": + return apiv1.NodeSelectorOpNotIn, nil + case ">": + return apiv1.NodeSelectorOpGt, nil + case "<": + return apiv1.NodeSelectorOpLt, nil + default: + return "", errors.Errorf("operator %s not supported", sign) + } +} diff --git a/kube/charts/kubernetes/placement_test.go b/kube/charts/kubernetes/placement_test.go new file mode 100644 index 000000000..24e51ecdf --- /dev/null +++ b/kube/charts/kubernetes/placement_test.go @@ -0,0 +1,181 @@ +// +build kube + +/* + Copyright 2020 Docker Compose CLI authors + + 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. +*/ + +package kubernetes + +import ( + "reflect" + "sort" + "testing" + + "github.com/compose-spec/compose-go/types" + + "github.com/stretchr/testify/assert" + apiv1 "k8s.io/api/core/v1" +) + +func TestToPodWithPlacement(t *testing.T) { + podTemplate := podTemplate(t, ` +version: "3" +services: + redis: + image: redis:alpine + deploy: + placement: + constraints: + - node.platform.os == linux + - node.platform.arch == amd64 + - node.hostname == node01 + - node.labels.label1 == value1 + - node.labels.label2.subpath != value2 +`) + + expectedRequirements := []apiv1.NodeSelectorRequirement{ + {Key: "beta.kubernetes.io/os", Operator: apiv1.NodeSelectorOpIn, Values: []string{"linux"}}, + {Key: "beta.kubernetes.io/arch", Operator: apiv1.NodeSelectorOpIn, Values: []string{"amd64"}}, + {Key: "kubernetes.io/hostname", Operator: apiv1.NodeSelectorOpIn, Values: []string{"node01"}}, + {Key: "label1", Operator: apiv1.NodeSelectorOpIn, Values: []string{"value1"}}, + {Key: "label2.subpath", Operator: apiv1.NodeSelectorOpNotIn, Values: []string{"value2"}}, + } + + requirements := podTemplate.Spec.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms[0].MatchExpressions + + sort.Slice(expectedRequirements, func(i, j int) bool { return expectedRequirements[i].Key < expectedRequirements[j].Key }) + sort.Slice(requirements, func(i, j int) bool { return requirements[i].Key < requirements[j].Key }) + + assert.EqualValues(t, expectedRequirements, requirements) +} + +type keyValue struct { + key string + value string +} + +func kv(key, value string) keyValue { + return keyValue{key: key, value: value} +} + +func makeExpectedAffinity(kvs ...keyValue) *apiv1.Affinity { + + var matchExpressions []apiv1.NodeSelectorRequirement + for _, kv := range kvs { + matchExpressions = append( + matchExpressions, + apiv1.NodeSelectorRequirement{ + Key: kv.key, + Operator: apiv1.NodeSelectorOpIn, + Values: []string{kv.value}, + }, + ) + } + return &apiv1.Affinity{ + NodeAffinity: &apiv1.NodeAffinity{ + RequiredDuringSchedulingIgnoredDuringExecution: &apiv1.NodeSelector{ + NodeSelectorTerms: []apiv1.NodeSelectorTerm{ + { + MatchExpressions: matchExpressions, + }, + }, + }, + }, + } +} + +func TestNodeAfinity(t *testing.T) { + cases := []struct { + name string + source []string + expected *apiv1.Affinity + }{ + { + name: "nil", + expected: makeExpectedAffinity( + kv(kubernetesOs, "linux"), + kv(kubernetesArch, "amd64"), + ), + }, + { + name: "hostname", + source: []string{"node.hostname == test"}, + expected: makeExpectedAffinity( + kv(kubernetesHostname, "test"), + kv(kubernetesOs, "linux"), + kv(kubernetesArch, "amd64"), + ), + }, + { + name: "os", + source: []string{"node.platform.os == windows"}, + expected: makeExpectedAffinity( + kv(kubernetesOs, "windows"), + kv(kubernetesArch, "amd64"), + ), + }, + { + name: "arch", + source: []string{"node.platform.arch == arm64"}, + expected: makeExpectedAffinity( + kv(kubernetesArch, "arm64"), + kv(kubernetesOs, "linux"), + ), + }, + { + name: "custom-labels", + source: []string{"node.platform.os == windows", "node.platform.arch == arm64"}, + expected: makeExpectedAffinity( + kv(kubernetesArch, "arm64"), + kv(kubernetesOs, "windows"), + ), + }, + } + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + result, err := toNodeAffinity(&types.DeployConfig{ + Placement: types.Placement{ + Constraints: c.source, + }, + }) + assert.NoError(t, err) + assert.True(t, nodeAffinityMatch(c.expected, result)) + }) + } +} + +func nodeSelectorRequirementsToMap(source []apiv1.NodeSelectorRequirement, result map[string]apiv1.NodeSelectorRequirement) { + for _, t := range source { + result[t.Key] = t + } +} + +func nodeAffinityMatch(expected, actual *apiv1.Affinity) bool { + expectedTerms := expected.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms + actualTerms := actual.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms + expectedExpressions := make(map[string]apiv1.NodeSelectorRequirement) + expectedFields := make(map[string]apiv1.NodeSelectorRequirement) + actualExpressions := make(map[string]apiv1.NodeSelectorRequirement) + actualFields := make(map[string]apiv1.NodeSelectorRequirement) + for _, v := range expectedTerms { + nodeSelectorRequirementsToMap(v.MatchExpressions, expectedExpressions) + nodeSelectorRequirementsToMap(v.MatchFields, expectedFields) + } + for _, v := range actualTerms { + nodeSelectorRequirementsToMap(v.MatchExpressions, actualExpressions) + nodeSelectorRequirementsToMap(v.MatchFields, actualFields) + } + return reflect.DeepEqual(expectedExpressions, actualExpressions) && reflect.DeepEqual(expectedFields, actualFields) +} diff --git a/kube/charts/kubernetes/pod.go b/kube/charts/kubernetes/pod.go new file mode 100644 index 000000000..632d122e2 --- /dev/null +++ b/kube/charts/kubernetes/pod.go @@ -0,0 +1,367 @@ +// +build kube + +/* + Copyright 2020 Docker Compose CLI authors + + 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. +*/ + +package kubernetes + +import ( + "fmt" + "sort" + "strconv" + "strings" + "time" + + "github.com/compose-spec/compose-go/types" + "github.com/docker/docker/api/types/swarm" + + "github.com/pkg/errors" + apiv1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func toPodTemplate(project *types.Project, serviceConfig types.ServiceConfig, labels map[string]string) (apiv1.PodTemplateSpec, error) { + tpl := apiv1.PodTemplateSpec{} + //nodeAffinity, err := toNodeAffinity(serviceConfig.Deploy) + //if err != nil { + // return apiv1.PodTemplateSpec{}, err + //} + hostAliases, err := toHostAliases(serviceConfig.ExtraHosts) + if err != nil { + return apiv1.PodTemplateSpec{}, err + } + env, err := toEnv(serviceConfig.Environment) + if err != nil { + return apiv1.PodTemplateSpec{}, err + } + restartPolicy, err := toRestartPolicy(serviceConfig) + if err != nil { + return apiv1.PodTemplateSpec{}, err + } + + var limits apiv1.ResourceList + if serviceConfig.Deploy != nil && serviceConfig.Deploy.Resources.Limits != nil { + limits, err = toResource(serviceConfig.Deploy.Resources.Limits) + if err != nil { + return apiv1.PodTemplateSpec{}, err + } + } + var requests apiv1.ResourceList + if serviceConfig.Deploy != nil && serviceConfig.Deploy.Resources.Reservations != nil { + requests, err = toResource(serviceConfig.Deploy.Resources.Reservations) + if err != nil { + return apiv1.PodTemplateSpec{}, err + } + } + + volumes, err := toVolumes(project, serviceConfig) + if err != nil { + return apiv1.PodTemplateSpec{}, err + } + volumeMounts, err := toVolumeMounts(project, serviceConfig) + if err != nil { + return apiv1.PodTemplateSpec{}, err + } + /* pullPolicy, err := toImagePullPolicy(serviceConfig.Image, x-kubernetes-pull-policy) + if err != nil { + return apiv1.PodTemplateSpec{}, err + } */ + tpl.ObjectMeta = metav1.ObjectMeta{ + Labels: labels, + Annotations: serviceConfig.Labels, + } + tpl.Spec.RestartPolicy = restartPolicy + tpl.Spec.Volumes = volumes + tpl.Spec.HostPID = toHostPID(serviceConfig.Pid) + tpl.Spec.HostIPC = toHostIPC(serviceConfig.Ipc) + tpl.Spec.Hostname = serviceConfig.Hostname + tpl.Spec.TerminationGracePeriodSeconds = toTerminationGracePeriodSeconds(serviceConfig.StopGracePeriod) + tpl.Spec.HostAliases = hostAliases + //tpl.Spec.Affinity = nodeAffinity + // we dont want to remove all containers and recreate them because: + // an admission plugin can add sidecar containers + // we for sure want to keep the main container to be additive + if len(tpl.Spec.Containers) == 0 { + tpl.Spec.Containers = []apiv1.Container{{}} + } + + containerIX := 0 + for ix, c := range tpl.Spec.Containers { + if c.Name == serviceConfig.Name { + containerIX = ix + break + } + } + tpl.Spec.Containers[containerIX].Name = serviceConfig.Name + tpl.Spec.Containers[containerIX].Image = serviceConfig.Image + // FIXME tpl.Spec.Containers[containerIX].ImagePullPolicy = pullPolicy + tpl.Spec.Containers[containerIX].Command = serviceConfig.Entrypoint + tpl.Spec.Containers[containerIX].Args = serviceConfig.Command + tpl.Spec.Containers[containerIX].WorkingDir = serviceConfig.WorkingDir + tpl.Spec.Containers[containerIX].TTY = serviceConfig.Tty + tpl.Spec.Containers[containerIX].Stdin = serviceConfig.StdinOpen + tpl.Spec.Containers[containerIX].Ports = toPorts(serviceConfig.Ports) + tpl.Spec.Containers[containerIX].LivenessProbe = toLivenessProbe(serviceConfig.HealthCheck) + tpl.Spec.Containers[containerIX].Env = env + tpl.Spec.Containers[containerIX].VolumeMounts = volumeMounts + tpl.Spec.Containers[containerIX].SecurityContext = toSecurityContext(serviceConfig) + tpl.Spec.Containers[containerIX].Resources = apiv1.ResourceRequirements{ + Limits: limits, + Requests: requests, + } + + /* FIXME + if serviceConfig.PullSecret != "" { + pullSecrets := map[string]struct{}{} + for _, ps := range tpl.Spec.ImagePullSecrets { + pullSecrets[ps.Name] = struct{}{} + } + if _, ok := pullSecrets[serviceConfig.PullSecret]; !ok { + tpl.Spec.ImagePullSecrets = append(tpl.Spec.ImagePullSecrets, apiv1.LocalObjectReference{Name: serviceConfig.PullSecret}) + } + } + */ + return tpl, nil +} + +func toImagePullPolicy(image string, specifiedPolicy string) (apiv1.PullPolicy, error) { + if specifiedPolicy == "" { + if strings.HasSuffix(image, ":latest") { + return apiv1.PullAlways, nil + } + return apiv1.PullIfNotPresent, nil + } + switch apiv1.PullPolicy(specifiedPolicy) { + case apiv1.PullAlways, apiv1.PullIfNotPresent, apiv1.PullNever: + return apiv1.PullPolicy(specifiedPolicy), nil + default: + return "", errors.Errorf("invalid pull policy %q, must be %q, %q or %q", specifiedPolicy, apiv1.PullAlways, apiv1.PullIfNotPresent, apiv1.PullNever) + } +} + +func toHostAliases(extraHosts []string) ([]apiv1.HostAlias, error) { + if extraHosts == nil { + return nil, nil + } + + byHostnames := map[string]string{} + for _, host := range extraHosts { + split := strings.SplitN(host, ":", 2) + if len(split) != 2 { + return nil, errors.Errorf("malformed host %s", host) + } + byHostnames[split[0]] = split[1] + } + + byIPs := map[string][]string{} + for k, v := range byHostnames { + byIPs[v] = append(byIPs[v], k) + } + + aliases := make([]apiv1.HostAlias, len(byIPs)) + i := 0 + for key, hosts := range byIPs { + sort.Strings(hosts) + aliases[i] = apiv1.HostAlias{ + IP: key, + Hostnames: hosts, + } + i++ + } + sort.Slice(aliases, func(i, j int) bool { return aliases[i].IP < aliases[j].IP }) + return aliases, nil +} + +func toHostPID(pid string) bool { + return "host" == pid +} + +func toHostIPC(ipc string) bool { + return "host" == ipc +} + +func toTerminationGracePeriodSeconds(duration *types.Duration) *int64 { + if duration == nil { + return nil + } + gracePeriod := int64(time.Duration(*duration).Seconds()) + return &gracePeriod +} + +func toLivenessProbe(hc *types.HealthCheckConfig) *apiv1.Probe { + if hc == nil || len(hc.Test) < 1 || hc.Test[0] == "NONE" { + return nil + } + + command := hc.Test[1:] + if hc.Test[0] == "CMD-SHELL" { + command = append([]string{"sh", "-c"}, command...) + } + + return &apiv1.Probe{ + TimeoutSeconds: toSecondsOrDefault(hc.Timeout, 1), + PeriodSeconds: toSecondsOrDefault(hc.Interval, 1), + FailureThreshold: int32(defaultUint64(hc.Retries, 3)), + Handler: apiv1.Handler{ + Exec: &apiv1.ExecAction{ + Command: command, + }, + }, + } +} + +func toEnv(env map[string]*string) ([]apiv1.EnvVar, error) { + var envVars []apiv1.EnvVar + + for k, v := range env { + if v == nil { + return nil, errors.Errorf("%s has no value, unsetting an environment variable is not supported", k) + } + envVars = append(envVars, toEnvVar(k, *v)) + } + sort.Slice(envVars, func(i, j int) bool { return envVars[i].Name < envVars[j].Name }) + return envVars, nil +} + +func toEnvVar(key, value string) apiv1.EnvVar { + return apiv1.EnvVar{ + Name: key, + Value: value, + } +} + +func toPorts(list []types.ServicePortConfig) []apiv1.ContainerPort { + var ports []apiv1.ContainerPort + + for _, v := range list { + ports = append(ports, apiv1.ContainerPort{ + ContainerPort: int32(v.Target), + Protocol: toProtocol(v.Protocol), + }) + } + + return ports +} + +func toProtocol(value string) apiv1.Protocol { + if value == "udp" { + return apiv1.ProtocolUDP + } + return apiv1.ProtocolTCP +} + +func toRestartPolicy(s types.ServiceConfig) (apiv1.RestartPolicy, error) { + if s.Deploy == nil || s.Deploy.RestartPolicy == nil { + return apiv1.RestartPolicyAlways, nil + } + policy := s.Deploy.RestartPolicy + + switch policy.Condition { + case string(swarm.RestartPolicyConditionAny): + return apiv1.RestartPolicyAlways, nil + case string(swarm.RestartPolicyConditionNone): + return apiv1.RestartPolicyNever, nil + case string(swarm.RestartPolicyConditionOnFailure): + return apiv1.RestartPolicyOnFailure, nil + default: + return "", errors.Errorf("unsupported restart policy %s", policy.Condition) + } +} + +func toResource(res *types.Resource) (apiv1.ResourceList, error) { + list := make(apiv1.ResourceList) + if res.NanoCPUs != "" { + cpus, err := resource.ParseQuantity(res.NanoCPUs) + if err != nil { + return nil, err + } + list[apiv1.ResourceCPU] = cpus + } + if res.MemoryBytes != 0 { + memory, err := resource.ParseQuantity(fmt.Sprintf("%v", res.MemoryBytes)) + if err != nil { + return nil, err + } + list[apiv1.ResourceMemory] = memory + } + return list, nil +} + +func toSecurityContext(s types.ServiceConfig) *apiv1.SecurityContext { + isPrivileged := toBoolPointer(s.Privileged) + isReadOnly := toBoolPointer(s.ReadOnly) + + var capabilities *apiv1.Capabilities + if s.CapAdd != nil || s.CapDrop != nil { + capabilities = &apiv1.Capabilities{ + Add: toCapabilities(s.CapAdd), + Drop: toCapabilities(s.CapDrop), + } + } + + var userID *int64 + if s.User != "" { + numerical, err := strconv.Atoi(s.User) + if err == nil { + unixUserID := int64(numerical) + userID = &unixUserID + } + } + + if isPrivileged == nil && isReadOnly == nil && capabilities == nil && userID == nil { + return nil + } + + return &apiv1.SecurityContext{ + RunAsUser: userID, + Privileged: isPrivileged, + ReadOnlyRootFilesystem: isReadOnly, + Capabilities: capabilities, + } +} + +func toBoolPointer(value bool) *bool { + if value { + return &value + } + + return nil +} + +func defaultUint64(v *uint64, defaultValue uint64) uint64 { //nolint: unparam + if v == nil { + return defaultValue + } + + return *v +} + +func toCapabilities(list []string) (capabilities []apiv1.Capability) { + for _, c := range list { + capabilities = append(capabilities, apiv1.Capability(c)) + } + return +} + +//nolint: unparam +func forceRestartPolicy(podTemplate apiv1.PodTemplateSpec, forcedRestartPolicy apiv1.RestartPolicy) apiv1.PodTemplateSpec { + if podTemplate.Spec.RestartPolicy != "" { + podTemplate.Spec.RestartPolicy = forcedRestartPolicy + } + + return podTemplate +} diff --git a/kube/charts/kubernetes/pod_test.go b/kube/charts/kubernetes/pod_test.go new file mode 100644 index 000000000..dee5dbb02 --- /dev/null +++ b/kube/charts/kubernetes/pod_test.go @@ -0,0 +1,1005 @@ +// +build kube + +/* + Copyright 2020 Docker Compose CLI authors + + 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. +*/ + +package kubernetes + +import ( + "fmt" + "os" + "runtime" + "testing" + + "github.com/compose-spec/compose-go/loader" + "github.com/compose-spec/compose-go/types" + "github.com/stretchr/testify/assert" + apiv1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" +) + +func loadYAML(yaml string) (*loader.Config, error) { + dict, err := loader.ParseYAML([]byte(yaml)) + if err != nil { + return nil, err + } + workingDir, err := os.Getwd() + if err != nil { + panic(err) + } + configs := []types.ConfigFiles{} + config := types.ConfigDetails{ + WorkingDir: workingDir, + ConfigFiles: configs, + Environment: utils.Environment(), + } + model, err := loader.Load(config) + return model +} + +func podTemplate(t *testing.T, yaml string) apiv1.PodTemplateSpec { + res, err := podTemplateWithError(yaml) + assert.NoError(t, err) + return res +} + +func podTemplateWithError(yaml string) (apiv1.PodTemplateSpec, error) { + model, err := loadYAML(yaml) + if err != nil { + return apiv1.PodTemplateSpec{}, err + } + + return toPodTemplate(model.Services[0], nil, model) +} + +func TestToPodWithDockerSocket(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("on windows, source path validation is broken (and actually, source validation for windows workload is broken too). Skip it for now, as we don't support it yet") + return + } + podTemplate := podTemplate(t, ` +version: "3" +services: + redis: + image: "redis:alpine" + volumes: + - "/var/run/docker.sock:/var/run/docker.sock" +`) + + expectedVolume := apiv1.Volume{ + Name: "mount-0", + VolumeSource: apiv1.VolumeSource{ + HostPath: &apiv1.HostPathVolumeSource{ + Path: "/var/run", + }, + }, + } + + expectedMount := apiv1.VolumeMount{ + Name: "mount-0", + MountPath: "/var/run/docker.sock", + SubPath: "docker.sock", + } + + assert.Len(t, podTemplate.Spec.Volumes, 1) + assert.Len(t, podTemplate.Spec.Containers[0].VolumeMounts, 1) + assert.Equal(t, expectedVolume, podTemplate.Spec.Volumes[0]) + assert.Equal(t, expectedMount, podTemplate.Spec.Containers[0].VolumeMounts[0]) +} + +func TestToPodWithFunkyCommand(t *testing.T) { + podTemplate := podTemplate(t, ` +version: "3" +services: + redis: + image: basi/node-exporter + command: ["-collector.procfs", "/host/proc", "-collector.sysfs", "/host/sys"] +`) + + expectedArgs := []string{ + `-collector.procfs`, + `/host/proc`, // ? + `-collector.sysfs`, + `/host/sys`, // ? + } + assert.Equal(t, expectedArgs, podTemplate.Spec.Containers[0].Args) +} + +func TestToPodWithGlobalVolume(t *testing.T) { + podTemplate := podTemplate(t, ` +version: "3" +services: + db: + image: "postgres:9.4" + volumes: + - dbdata:/var/lib/postgresql/data +`) + + expectedMount := apiv1.VolumeMount{ + Name: "dbdata", + MountPath: "/var/lib/postgresql/data", + } + assert.Len(t, podTemplate.Spec.Volumes, 0) + assert.Len(t, podTemplate.Spec.Containers[0].VolumeMounts, 1) + assert.Equal(t, expectedMount, podTemplate.Spec.Containers[0].VolumeMounts[0]) +} + +func TestToPodWithResources(t *testing.T) { + podTemplate := podTemplate(t, ` +version: "3" +services: + db: + image: "postgres:9.4" + deploy: + resources: + limits: + cpus: "0.001" + memory: 50Mb + reservations: + cpus: "0.0001" + memory: 20Mb +`) + + expectedResourceRequirements := apiv1.ResourceRequirements{ + Limits: map[apiv1.ResourceName]resource.Quantity{ + apiv1.ResourceCPU: resource.MustParse("0.001"), + apiv1.ResourceMemory: resource.MustParse(fmt.Sprintf("%d", 50*1024*1024)), + }, + Requests: map[apiv1.ResourceName]resource.Quantity{ + apiv1.ResourceCPU: resource.MustParse("0.0001"), + apiv1.ResourceMemory: resource.MustParse(fmt.Sprintf("%d", 20*1024*1024)), + }, + } + assert.Equal(t, expectedResourceRequirements, podTemplate.Spec.Containers[0].Resources) +} + +func TestToPodWithCapabilities(t *testing.T) { + podTemplate := podTemplate(t, ` +version: "3" +services: + redis: + image: "redis:alpine" + cap_add: + - ALL + cap_drop: + - NET_ADMIN + - SYS_ADMIN +`) + + expectedSecurityContext := &apiv1.SecurityContext{ + Capabilities: &apiv1.Capabilities{ + Add: []apiv1.Capability{"ALL"}, + Drop: []apiv1.Capability{"NET_ADMIN", "SYS_ADMIN"}, + }, + } + + assert.Equal(t, expectedSecurityContext, podTemplate.Spec.Containers[0].SecurityContext) +} + +func TestToPodWithReadOnly(t *testing.T) { + podTemplate := podTemplate(t, ` +version: "3" +services: + redis: + image: "redis:alpine" + read_only: true +`) + + yes := true + expectedSecurityContext := &apiv1.SecurityContext{ + ReadOnlyRootFilesystem: &yes, + } + assert.Equal(t, expectedSecurityContext, podTemplate.Spec.Containers[0].SecurityContext) +} + +func TestToPodWithPrivileged(t *testing.T) { + podTemplate := podTemplate(t, ` +version: "3" +services: + redis: + image: "redis:alpine" + privileged: true +`) + + yes := true + expectedSecurityContext := &apiv1.SecurityContext{ + Privileged: &yes, + } + assert.Equal(t, expectedSecurityContext, podTemplate.Spec.Containers[0].SecurityContext) +} + +func TestToPodWithEnvNilShouldErrorOut(t *testing.T) { + _, err := podTemplateWithError(` +version: "3" +services: + redis: + image: "redis:alpine" + environment: + - SESSION_SECRET +`) + assert.Error(t, err) +} + +func TestToPodWithEnv(t *testing.T) { + podTemplate := podTemplate(t, ` +version: "3" +services: + redis: + image: "redis:alpine" + environment: + - RACK_ENV=development + - SHOW=true +`) + + expectedEnv := []apiv1.EnvVar{ + { + Name: "RACK_ENV", + Value: "development", + }, + { + Name: "SHOW", + Value: "true", + }, + } + + assert.Equal(t, expectedEnv, podTemplate.Spec.Containers[0].Env) +} + +func TestToPodWithVolume(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("on windows, source path validation is broken (and actually, source validation for windows workload is broken too). Skip it for now, as we don't support it yet") + return + } + podTemplate := podTemplate(t, ` +version: "3" +services: + nginx: + image: nginx + volumes: + - /ignore:/ignore + - /opt/data:/var/lib/mysql:ro +`) + + assert.Len(t, podTemplate.Spec.Volumes, 2) + assert.Len(t, podTemplate.Spec.Containers[0].VolumeMounts, 2) +} + +func /*FIXME Test*/ ToPodWithRelativeVolumes(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("on windows, source path validation is broken (and actually, source validation for windows workload is broken too). Skip it for now, as we don't support it yet") + return + } + _, err := podTemplateWithError(` +version: "3" +services: + nginx: + image: nginx + volumes: + - ./fail:/ignore +`) + + assert.Error(t, err) +} + +func TestToPodWithHealthCheck(t *testing.T) { + podTemplate := podTemplate(t, ` +version: "3" +services: + nginx: + image: nginx + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost"] + interval: 90s + timeout: 10s + retries: 3 +`) + + expectedLivenessProbe := &apiv1.Probe{ + TimeoutSeconds: 10, + PeriodSeconds: 90, + FailureThreshold: 3, + Handler: apiv1.Handler{ + Exec: &apiv1.ExecAction{ + Command: []string{"curl", "-f", "http://localhost"}, + }, + }, + } + + assert.Equal(t, expectedLivenessProbe, podTemplate.Spec.Containers[0].LivenessProbe) +} + +func TestToPodWithShellHealthCheck(t *testing.T) { + podTemplate := podTemplate(t, ` +version: "3" +services: + nginx: + image: nginx + healthcheck: + test: ["CMD-SHELL", "curl -f http://localhost"] +`) + + expectedLivenessProbe := &apiv1.Probe{ + TimeoutSeconds: 1, + PeriodSeconds: 1, + FailureThreshold: 3, + Handler: apiv1.Handler{ + Exec: &apiv1.ExecAction{ + Command: []string{"sh", "-c", "curl -f http://localhost"}, + }, + }, + } + + assert.Equal(t, expectedLivenessProbe, podTemplate.Spec.Containers[0].LivenessProbe) +} + +func TestToPodWithTargetlessExternalSecret(t *testing.T) { + podTemplate := podTemplate(t, ` +version: "3" +services: + nginx: + image: nginx + secrets: + - my_secret +`) + + expectedVolume := apiv1.Volume{ + Name: "secret-0", + VolumeSource: apiv1.VolumeSource{ + Secret: &apiv1.SecretVolumeSource{ + SecretName: "my_secret", + Items: []apiv1.KeyToPath{ + { + Key: "file", // TODO: This is the key we assume external secrets use + Path: "secret-0", + }, + }, + }, + }, + } + + expectedMount := apiv1.VolumeMount{ + Name: "secret-0", + ReadOnly: true, + MountPath: "/run/secrets/my_secret", + SubPath: "secret-0", + } + + assert.Len(t, podTemplate.Spec.Volumes, 1) + assert.Len(t, podTemplate.Spec.Containers[0].VolumeMounts, 1) + assert.Equal(t, expectedVolume, podTemplate.Spec.Volumes[0]) + assert.Equal(t, expectedMount, podTemplate.Spec.Containers[0].VolumeMounts[0]) +} + +func TestToPodWithExternalSecret(t *testing.T) { + podTemplate := podTemplate(t, ` +version: "3" +services: + nginx: + image: nginx + secrets: + - source: my_secret + target: nginx_secret +`) + + expectedVolume := apiv1.Volume{ + Name: "secret-0", + VolumeSource: apiv1.VolumeSource{ + Secret: &apiv1.SecretVolumeSource{ + SecretName: "my_secret", + Items: []apiv1.KeyToPath{ + { + Key: "file", // TODO: This is the key we assume external secrets use + Path: "secret-0", + }, + }, + }, + }, + } + + expectedMount := apiv1.VolumeMount{ + Name: "secret-0", + ReadOnly: true, + MountPath: "/run/secrets/nginx_secret", + SubPath: "secret-0", + } + + assert.Len(t, podTemplate.Spec.Volumes, 1) + assert.Len(t, podTemplate.Spec.Containers[0].VolumeMounts, 1) + assert.Equal(t, expectedVolume, podTemplate.Spec.Volumes[0]) + assert.Equal(t, expectedMount, podTemplate.Spec.Containers[0].VolumeMounts[0]) +} + +func /*FIXME Test*/ ToPodWithFileBasedSecret(t *testing.T) { + podTemplate := podTemplate(t, ` +version: "3" +services: + nginx: + image: nginx + secrets: + - source: my_secret +secrets: + my_secret: + file: ./secret.txt +`) + + expectedVolume := apiv1.Volume{ + Name: "secret-0", + VolumeSource: apiv1.VolumeSource{ + Secret: &apiv1.SecretVolumeSource{ + SecretName: "my_secret", + Items: []apiv1.KeyToPath{ + { + Key: "secret.txt", + Path: "secret-0", + }, + }, + }, + }, + } + + expectedMount := apiv1.VolumeMount{ + Name: "secret-0", + ReadOnly: true, + MountPath: "/run/secrets/my_secret", + SubPath: "secret-0", + } + + assert.Len(t, podTemplate.Spec.Volumes, 1) + assert.Len(t, podTemplate.Spec.Containers[0].VolumeMounts, 1) + assert.Equal(t, expectedVolume, podTemplate.Spec.Volumes[0]) + assert.Equal(t, expectedMount, podTemplate.Spec.Containers[0].VolumeMounts[0]) +} + +func /*FIXME Test*/ ToPodWithTwoFileBasedSecrets(t *testing.T) { + podTemplate := podTemplate(t, ` +version: "3" +services: + nginx: + image: nginx + secrets: + - source: my_secret1 + - source: my_secret2 + target: secret2 +secrets: + my_secret1: + file: ./secret1.txt + my_secret2: + file: ./secret2.txt +`) + + expectedVolumes := []apiv1.Volume{ + { + Name: "secret-0", + VolumeSource: apiv1.VolumeSource{ + Secret: &apiv1.SecretVolumeSource{ + SecretName: "my_secret1", + Items: []apiv1.KeyToPath{ + { + Key: "secret1.txt", + Path: "secret-0", + }, + }, + }, + }, + }, + { + Name: "secret-1", + VolumeSource: apiv1.VolumeSource{ + Secret: &apiv1.SecretVolumeSource{ + SecretName: "my_secret2", + Items: []apiv1.KeyToPath{ + { + Key: "secret2.txt", + Path: "secret-1", + }, + }, + }, + }, + }, + } + + expectedMounts := []apiv1.VolumeMount{ + { + Name: "secret-0", + ReadOnly: true, + MountPath: "/run/secrets/my_secret1", + SubPath: "secret-0", + }, + { + Name: "secret-1", + ReadOnly: true, + MountPath: "/run/secrets/secret2", + SubPath: "secret-1", + }, + } + + assert.Equal(t, expectedVolumes, podTemplate.Spec.Volumes) + assert.Equal(t, expectedMounts, podTemplate.Spec.Containers[0].VolumeMounts) +} + +func TestToPodWithTerminationGracePeriod(t *testing.T) { + podTemplate := podTemplate(t, ` +version: "3" +services: + redis: + image: "redis:alpine" + stop_grace_period: 100s +`) + + expected := int64(100) + assert.Equal(t, &expected, podTemplate.Spec.TerminationGracePeriodSeconds) +} + +func TestToPodWithTmpfs(t *testing.T) { + podTemplate := podTemplate(t, ` +version: "3" +services: + redis: + image: "redis:alpine" + tmpfs: + - /tmp +`) + + expectedVolume := apiv1.Volume{ + Name: "tmp-0", + VolumeSource: apiv1.VolumeSource{ + EmptyDir: &apiv1.EmptyDirVolumeSource{ + Medium: "Memory", + }, + }, + } + + expectedMount := apiv1.VolumeMount{ + Name: "tmp-0", + MountPath: "/tmp", + } + + assert.Len(t, podTemplate.Spec.Volumes, 1) + assert.Len(t, podTemplate.Spec.Containers[0].VolumeMounts, 1) + assert.Equal(t, expectedVolume, podTemplate.Spec.Volumes[0]) + assert.Equal(t, expectedMount, podTemplate.Spec.Containers[0].VolumeMounts[0]) +} + +func TestToPodWithNumericalUser(t *testing.T) { + podTemplate := podTemplate(t, ` +version: "3" +services: + redis: + image: "redis:alpine" + user: "1000" +`) + + userID := int64(1000) + + expectedSecurityContext := &apiv1.SecurityContext{ + RunAsUser: &userID, + } + + assert.Equal(t, expectedSecurityContext, podTemplate.Spec.Containers[0].SecurityContext) +} + +func TestToPodWithGitVolume(t *testing.T) { + podTemplate := podTemplate(t, ` +version: "3" +services: + redis: + image: "redis:alpine" + volumes: + - source: "git@github.com:moby/moby.git" + target: /sources + type: git +`) + + expectedVolume := apiv1.Volume{ + Name: "mount-0", + VolumeSource: apiv1.VolumeSource{ + GitRepo: &apiv1.GitRepoVolumeSource{ + Repository: "git@github.com:moby/moby.git", + }, + }, + } + + expectedMount := apiv1.VolumeMount{ + Name: "mount-0", + ReadOnly: false, + MountPath: "/sources", + } + + assert.Len(t, podTemplate.Spec.Volumes, 1) + assert.Len(t, podTemplate.Spec.Containers[0].VolumeMounts, 1) + assert.Equal(t, expectedVolume, podTemplate.Spec.Volumes[0]) + assert.Equal(t, expectedMount, podTemplate.Spec.Containers[0].VolumeMounts[0]) +} + +func /*FIXME Test*/ ToPodWithFileBasedConfig(t *testing.T) { + podTemplate := podTemplate(t, ` +version: "3" +services: + redis: + image: "redis:alpine" + configs: + - source: my_config + target: /usr/share/nginx/html/index.html + uid: "103" + gid: "103" + mode: 0440 +configs: + my_config: + file: ./file.html +`) + + mode := int32(0440) + + expectedVolume := apiv1.Volume{ + Name: "config-0", + VolumeSource: apiv1.VolumeSource{ + ConfigMap: &apiv1.ConfigMapVolumeSource{ + LocalObjectReference: apiv1.LocalObjectReference{ + Name: "my_config", + }, + Items: []apiv1.KeyToPath{ + { + Key: "file.html", + Path: "config-0", + Mode: &mode, + }, + }, + }, + }, + } + + expectedMount := apiv1.VolumeMount{ + Name: "config-0", + ReadOnly: true, + MountPath: "/usr/share/nginx/html/index.html", + SubPath: "config-0", + } + + assert.Len(t, podTemplate.Spec.Volumes, 1) + assert.Len(t, podTemplate.Spec.Containers[0].VolumeMounts, 1) + assert.Equal(t, expectedVolume, podTemplate.Spec.Volumes[0]) + assert.Equal(t, expectedMount, podTemplate.Spec.Containers[0].VolumeMounts[0]) +} + +func /*FIXME Test*/ ToPodWithTargetlessFileBasedConfig(t *testing.T) { + podTemplate := podTemplate(t, ` +version: "3" +services: + redis: + image: "redis:alpine" + configs: + - my_config +configs: + my_config: + file: ./file.html +`) + + expectedVolume := apiv1.Volume{ + Name: "config-0", + VolumeSource: apiv1.VolumeSource{ + ConfigMap: &apiv1.ConfigMapVolumeSource{ + LocalObjectReference: apiv1.LocalObjectReference{ + Name: "myconfig", + }, + Items: []apiv1.KeyToPath{ + { + Key: "file.html", + Path: "config-0", + }, + }, + }, + }, + } + + expectedMount := apiv1.VolumeMount{ + Name: "config-0", + ReadOnly: true, + MountPath: "/myconfig", + SubPath: "config-0", + } + + assert.Len(t, podTemplate.Spec.Volumes, 1) + assert.Len(t, podTemplate.Spec.Containers[0].VolumeMounts, 1) + assert.Equal(t, expectedVolume, podTemplate.Spec.Volumes[0]) + assert.Equal(t, expectedMount, podTemplate.Spec.Containers[0].VolumeMounts[0]) +} + +func TestToPodWithExternalConfig(t *testing.T) { + podTemplate := podTemplate(t, ` +version: "3" +services: + redis: + image: "redis:alpine" + configs: + - source: my_config + target: /usr/share/nginx/html/index.html + uid: "103" + gid: "103" + mode: 0440 +configs: + my_config: + external: true +`) + + mode := int32(0440) + + expectedVolume := apiv1.Volume{ + Name: "config-0", + VolumeSource: apiv1.VolumeSource{ + ConfigMap: &apiv1.ConfigMapVolumeSource{ + LocalObjectReference: apiv1.LocalObjectReference{ + Name: "my_config", + }, + Items: []apiv1.KeyToPath{ + { + Key: "file", // TODO: This is the key we assume external config use + Path: "config-0", + Mode: &mode, + }, + }, + }, + }, + } + + expectedMount := apiv1.VolumeMount{ + Name: "config-0", + ReadOnly: true, + MountPath: "/usr/share/nginx/html/index.html", + SubPath: "config-0", + } + + assert.Len(t, podTemplate.Spec.Volumes, 1) + assert.Len(t, podTemplate.Spec.Containers[0].VolumeMounts, 1) + assert.Equal(t, expectedVolume, podTemplate.Spec.Volumes[0]) + assert.Equal(t, expectedMount, podTemplate.Spec.Containers[0].VolumeMounts[0]) +} + +func /*FIXME Test*/ ToPodWithTwoConfigsSameMountPoint(t *testing.T) { + podTemplate := podTemplate(t, ` +version: "3" +services: + nginx: + image: nginx + configs: + - source: first + target: /data/first.json + mode: "0440" + - source: second + target: /data/second.json + mode: "0550" +configs: + first: + file: ./file1 + secondv: + file: ./file2 +`) + + mode0440 := int32(0440) + mode0550 := int32(0550) + + expectedVolumes := []apiv1.Volume{ + { + Name: "config-0", + VolumeSource: apiv1.VolumeSource{ + ConfigMap: &apiv1.ConfigMapVolumeSource{ + LocalObjectReference: apiv1.LocalObjectReference{ + Name: "first", + }, + Items: []apiv1.KeyToPath{ + { + Key: "file1", + Path: "config-0", + Mode: &mode0440, + }, + }, + }, + }, + }, + { + Name: "config-1", + VolumeSource: apiv1.VolumeSource{ + ConfigMap: &apiv1.ConfigMapVolumeSource{ + LocalObjectReference: apiv1.LocalObjectReference{ + Name: "second", + }, + Items: []apiv1.KeyToPath{ + { + Key: "file2", + Path: "config-1", + Mode: &mode0550, + }, + }, + }, + }, + }, + } + + expectedMounts := []apiv1.VolumeMount{ + { + Name: "config-0", + ReadOnly: true, + MountPath: "/data/first.json", + SubPath: "config-0", + }, + { + Name: "config-1", + ReadOnly: true, + MountPath: "/data/second.json", + SubPath: "config-1", + }, + } + + assert.Equal(t, expectedVolumes, podTemplate.Spec.Volumes) + assert.Equal(t, expectedMounts, podTemplate.Spec.Containers[0].VolumeMounts) +} + +func TestToPodWithTwoExternalConfigsSameMountPoint(t *testing.T) { + podTemplate := podTemplate(t, ` +version: "3" +services: + nginx: + image: nginx + configs: + - source: first + target: /data/first.json + - source: second + target: /data/second.json +configs: + first: + file: ./file1 + second: + file: ./file2 +`) + + expectedVolumes := []apiv1.Volume{ + { + Name: "config-0", + VolumeSource: apiv1.VolumeSource{ + ConfigMap: &apiv1.ConfigMapVolumeSource{ + LocalObjectReference: apiv1.LocalObjectReference{ + Name: "first", + }, + Items: []apiv1.KeyToPath{ + { + Key: "file", + Path: "config-0", + }, + }, + }, + }, + }, + { + Name: "config-1", + VolumeSource: apiv1.VolumeSource{ + ConfigMap: &apiv1.ConfigMapVolumeSource{ + LocalObjectReference: apiv1.LocalObjectReference{ + Name: "second", + }, + Items: []apiv1.KeyToPath{ + { + Key: "file", + Path: "config-1", + }, + }, + }, + }, + }, + } + + expectedMounts := []apiv1.VolumeMount{ + { + Name: "config-0", + ReadOnly: true, + MountPath: "/data/first.json", + SubPath: "config-0", + }, + { + Name: "config-1", + ReadOnly: true, + MountPath: "/data/second.json", + SubPath: "config-1", + }, + } + + assert.Equal(t, expectedVolumes, podTemplate.Spec.Volumes) + assert.Equal(t, expectedMounts, podTemplate.Spec.Containers[0].VolumeMounts) +} + +func /*FIXME Test*/ ToPodWithPullSecret(t *testing.T) { + podTemplateWithSecret := podTemplate(t, ` +version: "3" +services: + nginx: + image: nginx + x-kubernetes.pull-secret: test-pull-secret +`) + + assert.Equal(t, 1, len(podTemplateWithSecret.Spec.ImagePullSecrets)) + assert.Equal(t, "test-pull-secret", podTemplateWithSecret.Spec.ImagePullSecrets[0].Name) + + podTemplateNoSecret := podTemplate(t, ` +version: "3" +services: + nginx: + image: nginx +`) + + assert.Nil(t, podTemplateNoSecret.Spec.ImagePullSecrets) +} + +func /*FIXME Test*/ ToPodWithPullPolicy(t *testing.T) { + cases := []struct { + name string + stack string + expectedPolicy apiv1.PullPolicy + expectedError string + }{ + { + name: "specific tag", + stack: ` +version: "3" +services: + nginx: + image: nginx:specific +`, + expectedPolicy: apiv1.PullIfNotPresent, + }, + { + name: "latest tag", + stack: ` +version: "3" +services: + nginx: + image: nginx:latest +`, + expectedPolicy: apiv1.PullAlways, + }, + { + name: "explicit policy", + stack: ` +version: "3" +services: + nginx: + image: nginx:specific + x-kubernetes.pull-policy: Never +`, + expectedPolicy: apiv1.PullNever, + }, + { + name: "invalid policy", + stack: ` +version: "3" +services: + nginx: + image: nginx:specific + x-kubernetes.pull-policy: Invalid +`, + expectedError: `invalid pull policy "Invalid", must be "Always", "IfNotPresent" or "Never"`, + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + pod, err := podTemplateWithError(c.stack) + if c.expectedError != "" { + assert.EqualError(t, err, c.expectedError) + } else { + assert.NoError(t, err) + assert.Equal(t, pod.Spec.Containers[0].ImagePullPolicy, c.expectedPolicy) + } + }) + } +} diff --git a/kube/charts/kubernetes/volumes.go b/kube/charts/kubernetes/volumes.go new file mode 100644 index 000000000..6182cf055 --- /dev/null +++ b/kube/charts/kubernetes/volumes.go @@ -0,0 +1,246 @@ +// +build kube + +/* + Copyright 2020 Docker Compose CLI authors + + 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. +*/ + +package kubernetes + +import ( + "fmt" + "path" + "path/filepath" + "strings" + + "github.com/compose-spec/compose-go/types" + + "github.com/pkg/errors" + apiv1 "k8s.io/api/core/v1" +) + +const dockerSock = "/var/run/docker.sock" + +type volumeSpec struct { + mount apiv1.VolumeMount + source *apiv1.VolumeSource +} + +func hasPersistentVolumes(s types.ServiceConfig) bool { + for _, volume := range s.Volumes { + if volume.Type == "volume" { + return true + } + } + + return false +} + +func toVolumeSpecs(project *types.Project, s types.ServiceConfig) ([]volumeSpec, error) { + var specs []volumeSpec + for i, m := range s.Volumes { + var source *apiv1.VolumeSource + name := fmt.Sprintf("mount-%d", i) + subpath := "" + if m.Source == dockerSock && m.Target == dockerSock { + subpath = "docker.sock" + source = hostPathVolume("/var/run") + } else if strings.HasSuffix(m.Source, ".git") { + source = gitVolume(m.Source) + } else if m.Type == "volume" { + if m.Source != "" { + name = strings.ReplaceAll(m.Source, "_", "-") + } + } else { + // bind mount + if !filepath.IsAbs(m.Source) { + return nil, errors.Errorf("%s: only absolute paths can be specified in mount source", m.Source) + } + if m.Source == "/" { + source = hostPathVolume("/") + } else { + parent, file := filepath.Split(m.Source) + if parent != "/" { + parent = strings.TrimSuffix(parent, "/") + } + source = hostPathVolume(parent) + subpath = file + } + } + + specs = append(specs, volumeSpec{ + source: source, + mount: volumeMount(name, m.Target, m.ReadOnly, subpath), + }) + } + + for i, m := range s.Tmpfs { + name := fmt.Sprintf("tmp-%d", i) + + specs = append(specs, volumeSpec{ + source: emptyVolumeInMemory(), + mount: volumeMount(name, m, false, ""), + }) + } + + for i, s := range s.Secrets { + name := fmt.Sprintf("secret-%d", i) + + target := path.Join("/run/secrets", or(s.Target, s.Source)) + subPath := name + readOnly := true + + specs = append(specs, volumeSpec{ + source: secretVolume(s, project.Secrets[name], subPath), + mount: volumeMount(name, target, readOnly, subPath), + }) + } + + for i, c := range s.Configs { + name := fmt.Sprintf("config-%d", i) + + target := or(c.Target, "/"+c.Source) + subPath := name + readOnly := true + + specs = append(specs, volumeSpec{ + source: configVolume(c, project.Configs[name], subPath), + mount: volumeMount(name, target, readOnly, subPath), + }) + } + + return specs, nil +} + +func or(v string, defaultValue string) string { + if v != "" && v != "." { + return v + } + + return defaultValue +} + +func toVolumeMounts(project *types.Project, s types.ServiceConfig) ([]apiv1.VolumeMount, error) { + var mounts []apiv1.VolumeMount + specs, err := toVolumeSpecs(project, s) + if err != nil { + return nil, err + } + for _, spec := range specs { + mounts = append(mounts, spec.mount) + } + return mounts, nil +} + +func toVolumes(project *types.Project, s types.ServiceConfig) ([]apiv1.Volume, error) { + var volumes []apiv1.Volume + specs, err := toVolumeSpecs(project, s) + if err != nil { + return nil, err + } + for _, spec := range specs { + if spec.source == nil { + spec.source = emptyVolumeInMemory() + } + volumes = append(volumes, apiv1.Volume{ + Name: spec.mount.Name, + VolumeSource: *spec.source, + }) + } + return volumes, nil +} + +func gitVolume(path string) *apiv1.VolumeSource { + return &apiv1.VolumeSource{ + GitRepo: &apiv1.GitRepoVolumeSource{ + Repository: filepath.ToSlash(path), + }, + } +} + +func hostPathVolume(path string) *apiv1.VolumeSource { + return &apiv1.VolumeSource{ + HostPath: &apiv1.HostPathVolumeSource{ + Path: path, + }, + } +} + +func defaultMode(mode *uint32) *int32 { + var defaultMode *int32 + + if mode != nil { + signedMode := int32(*mode) + defaultMode = &signedMode + } + + return defaultMode +} + +func secretVolume(config types.ServiceSecretConfig, topLevelConfig types.SecretConfig, subPath string) *apiv1.VolumeSource { + return &apiv1.VolumeSource{ + Secret: &apiv1.SecretVolumeSource{ + SecretName: config.Source, + Items: []apiv1.KeyToPath{ + { + Key: toKey(topLevelConfig.File), + Path: subPath, + Mode: defaultMode(config.Mode), + }, + }, + }, + } +} + +func volumeMount(name, path string, readOnly bool, subPath string) apiv1.VolumeMount { + return apiv1.VolumeMount{ + Name: name, + MountPath: path, + ReadOnly: readOnly, + SubPath: subPath, + } +} + +func configVolume(config types.ServiceConfigObjConfig, topLevelConfig types.ConfigObjConfig, subPath string) *apiv1.VolumeSource { + return &apiv1.VolumeSource{ + ConfigMap: &apiv1.ConfigMapVolumeSource{ + LocalObjectReference: apiv1.LocalObjectReference{ + Name: config.Source, + }, + Items: []apiv1.KeyToPath{ + { + Key: toKey(topLevelConfig.File), + Path: subPath, + Mode: defaultMode(config.Mode), + }, + }, + }, + } +} + +func toKey(file string) string { + if file != "" { + return path.Base(file) + } + + return "file" // TODO: hard-coded key for external configs +} + +func emptyVolumeInMemory() *apiv1.VolumeSource { + return &apiv1.VolumeSource{ + EmptyDir: &apiv1.EmptyDirVolumeSource{ + Medium: apiv1.StorageMediumMemory, + }, + } +} diff --git a/kube/compose.go b/kube/compose.go new file mode 100644 index 000000000..33fb069df --- /dev/null +++ b/kube/compose.go @@ -0,0 +1,106 @@ +// +build kube + +/* + Copyright 2020 Docker Compose CLI authors + + 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. +*/ + +package kube + +import ( + "context" + + "github.com/compose-spec/compose-go/types" + "github.com/docker/compose-cli/api/compose" + "github.com/docker/compose-cli/api/context/store" + "github.com/docker/compose-cli/api/errdefs" + "github.com/docker/compose-cli/kube/charts" +) + +// NewComposeService create a kubernetes implementation of the compose.Service API +func NewComposeService(ctx store.KubeContext) (compose.Service, error) { + apiclient, err := charts.NewSDK(ctx) + if err != nil { + return nil, err + } + return &composeService{ + ctx: ctx, + sdk: apiclient, + }, nil +} + +type composeService struct { + ctx store.KubeContext + sdk charts.API +} + +// Up executes the equivalent to a `compose up` +func (s *composeService) Up(ctx context.Context, project *types.Project, options compose.UpOptions) error { + return s.sdk.Install(project) +} + +// Down executes the equivalent to a `compose down` +func (s *composeService) Down(ctx context.Context, projectName string, options compose.DownOptions) error { + return s.sdk.Uninstall(projectName) +} + +// List executes the equivalent to a `docker stack ls` +func (s *composeService) List(ctx context.Context, projectName string) ([]compose.Stack, error) { + return s.sdk.List(projectName) +} + +// Build executes the equivalent to a `compose build` +func (s *composeService) Build(ctx context.Context, project *types.Project) error { + return errdefs.ErrNotImplemented +} + +// Push executes the equivalent ot a `compose push` +func (s *composeService) Push(ctx context.Context, project *types.Project) error { + return errdefs.ErrNotImplemented +} + +// Pull executes the equivalent of a `compose pull` +func (s *composeService) Pull(ctx context.Context, project *types.Project) error { + return errdefs.ErrNotImplemented +} + +// Create executes the equivalent to a `compose create` +func (s *composeService) Create(ctx context.Context, project *types.Project, opts compose.CreateOptions) error { + return errdefs.ErrNotImplemented +} + +// Start executes the equivalent to a `compose start` +func (s *composeService) Start(ctx context.Context, project *types.Project, consumer compose.LogConsumer) error { + return errdefs.ErrNotImplemented +} + +// Logs executes the equivalent to a `compose logs` +func (s *composeService) Logs(ctx context.Context, projectName string, consumer compose.LogConsumer, options compose.LogOptions) error { + return errdefs.ErrNotImplemented +} + +// Ps executes the equivalent to a `compose ps` +func (s *composeService) Ps(ctx context.Context, projectName string) ([]compose.ContainerSummary, error) { + return nil, errdefs.ErrNotImplemented +} + +// Convert translate compose model into backend's native format +func (s *composeService) Convert(ctx context.Context, project *types.Project, options compose.ConvertOptions) ([]byte, error) { + return nil, errdefs.ErrNotImplemented +} + +// RunOneOffContainer creates a service oneoff container and starts its dependencies +func (s *composeService) RunOneOffContainer(ctx context.Context, project *types.Project, opts compose.RunOptions) error { + return errdefs.ErrNotImplemented +} diff --git a/kube/context.go b/kube/context.go new file mode 100644 index 000000000..a9ce58a74 --- /dev/null +++ b/kube/context.go @@ -0,0 +1,27 @@ +// +build kube + +/* + Copyright 2020 Docker Compose CLI authors + + 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. +*/ + +package kube + +// ContextParams options for creating a Kubernetes context +type ContextParams struct { + Name string + Description string + Endpoint string + FromEnvironment bool +} diff --git a/kube/utils/config.go b/kube/utils/config.go new file mode 100644 index 000000000..408558923 --- /dev/null +++ b/kube/utils/config.go @@ -0,0 +1,142 @@ +// +build kube + +/* + Copyright 2020 Docker Compose CLI authors + + 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. +*/ + +package utils + +import ( + "io/ioutil" + "os" + "path/filepath" + "regexp" + "strings" + + "github.com/compose-spec/compose-go/loader" + "github.com/compose-spec/compose-go/types" + "github.com/prometheus/common/log" +) + +var SupportedFilenames = []string{"compose.yaml", "compose.yml", "docker-compose.yml", "docker-compose.yaml"} + +func GetConfigs(name string, configPaths []string) (string, []types.ConfigFile, error) { + configPath, err := getConfigPaths(configPaths) + if err != nil { + return "", nil, err + } + if configPath == nil { + return "", nil, nil + } + workingDir := filepath.Dir(configPath[0]) + + if name == "" { + name = os.Getenv("COMPOSE_PROJECT_NAME") + } + if name == "" { + r := regexp.MustCompile(`[^a-z0-9\\-_]+`) + name = r.ReplaceAllString(strings.ToLower(filepath.Base(workingDir)), "") + } + + configs, err := parseConfigs(configPath) + if err != nil { + return "", nil, err + } + return workingDir, configs, nil +} + +func getConfigPaths(configPaths []string) ([]string, error) { + paths := []string{} + pwd, err := os.Getwd() + if err != nil { + return nil, err + } + + if len(configPaths) != 0 { + for _, f := range configPaths { + if f == "-" { + paths = append(paths, f) + continue + } + if !filepath.IsAbs(f) { + f = filepath.Join(pwd, f) + } + if _, err := os.Stat(f); err != nil { + return nil, err + } + paths = append(paths, f) + } + return paths, nil + } + + sep := os.Getenv("COMPOSE_FILE_SEPARATOR") + if sep == "" { + sep = string(os.PathListSeparator) + } + f := os.Getenv("COMPOSE_FILE") + if f != "" { + return strings.Split(f, sep), nil + } + + for { + candidates := []string{} + for _, n := range SupportedFilenames { + f := filepath.Join(pwd, n) + if _, err := os.Stat(f); err == nil { + candidates = append(candidates, f) + } + } + if len(candidates) > 0 { + winner := candidates[0] + if len(candidates) > 1 { + log.Warnf("Found multiple config files with supported names: %s", strings.Join(candidates, ", ")) + log.Warnf("Using %s\n", winner) + } + return []string{winner}, nil + } + parent := filepath.Dir(pwd) + if parent == pwd { + return nil, nil + } + pwd = parent + } +} + +func parseConfigs(configPaths []string) ([]types.ConfigFile, error) { + files := []types.ConfigFile{} + for _, f := range configPaths { + var ( + b []byte + err error + ) + if f == "-" { + b, err = ioutil.ReadAll(os.Stdin) + } else { + if _, err := os.Stat(f); err != nil { + return nil, err + } + b, err = ioutil.ReadFile(f) + } + if err != nil { + return nil, err + } + config, err := loader.ParseYAML(b) + if err != nil { + return nil, err + } + files = append(files, types.ConfigFile{Filename: f, Config: config}) + } + return files, nil +} diff --git a/kube/utils/errors.go b/kube/utils/errors.go new file mode 100644 index 000000000..0b58b9a54 --- /dev/null +++ b/kube/utils/errors.go @@ -0,0 +1,56 @@ +// +build kube + +/* + Copyright 2020 Docker Compose CLI authors + + 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. +*/ + +package utils + +import ( + "fmt" + "strings" +) + +func CombineErrors(errors []error) error { + if len(errors) == 0 { + return nil + } + if len(errors) == 1 { + return errors[0] + } + err := combinedError{} + for _, e := range errors { + if c, ok := e.(combinedError); ok { + err.errors = append(err.errors, c.errors...) + } else { + err.errors = append(err.errors, e) + } + } + return combinedError{errors} +} + +type combinedError struct { + errors []error +} + +func (c combinedError) Error() string { + points := make([]string, len(c.errors)) + for i, err := range c.errors { + points[i] = fmt.Sprintf("* %s", err.Error()) + } + return fmt.Sprintf( + "%d errors occurred:\n\t%s", + len(c.errors), strings.Join(points, "\n\t")) +} diff --git a/kube/utils/labels.go b/kube/utils/labels.go new file mode 100644 index 000000000..0ef3ecc07 --- /dev/null +++ b/kube/utils/labels.go @@ -0,0 +1,35 @@ +// +build kube + +/* + Copyright 2020 Docker Compose CLI authors + + 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. +*/ + +package utils + +const ( + LabelDockerComposePrefix = "com.docker.compose" + LabelService = LabelDockerComposePrefix + ".service" + LabelVersion = LabelDockerComposePrefix + ".version" + LabelContainerNumber = LabelDockerComposePrefix + ".container-number" + LabelOneOff = LabelDockerComposePrefix + ".oneoff" + LabelNetwork = LabelDockerComposePrefix + ".network" + LabelSlug = LabelDockerComposePrefix + ".slug" + LabelVolume = LabelDockerComposePrefix + ".volume" + LabelConfigHash = LabelDockerComposePrefix + ".config-hash" + LabelProject = LabelDockerComposePrefix + ".project" + LabelWorkingDir = LabelDockerComposePrefix + ".working_dir" + LabelConfigFiles = LabelDockerComposePrefix + ".config_files" + LabelEnvironmentFile = LabelDockerComposePrefix + ".environment_file" +) diff --git a/kube/utils/utils.go b/kube/utils/utils.go new file mode 100644 index 000000000..dab4cd5fd --- /dev/null +++ b/kube/utils/utils.go @@ -0,0 +1,34 @@ +// +build kube + +/* + Copyright 2020 Docker Compose CLI authors + + 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. +*/ + +package utils + +import ( + "os" + "strings" +) + +func Environment() map[string]string { + vars := make(map[string]string) + env := os.Environ() + for _, v := range env { + k := strings.SplitN(v, "=", 2) + vars[k[0]] = k[1] + } + return vars +}