diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 026b09df0..de7afeeca 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -89,6 +89,11 @@ jobs: with: name: tink-worker path: cmd/tink-worker/tink-worker-* + - name: Upload tink-controller binaries + uses: actions/upload-artifact@v2 + with: + name: tink-controller + path: cmd/tink-controller/tink-controller-* docker-images: runs-on: ubuntu-latest needs: @@ -102,6 +107,8 @@ jobs: binary: tink-server - repository: quay.io/tinkerbell/tink-worker binary: tink-worker + - repository: quay.io/tinkerbell/tink-controller + binary: tink-controller steps: - name: Docker Image Tag for Sha id: docker-image-tag diff --git a/.gitignore b/.gitignore index 85c4eae69..9e665553f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ bin/ certs/ cmd/tink-cli/tink-cli +cmd/tink-controller/tink-controller cmd/tink-server/tink-server cmd/tink-worker/tink-worker doc/ diff --git a/Makefile b/Makefile index 2fbe75630..35d46368b 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ run: crosscompile run-stack ## Builds and runs the Tink stack (tink, db, cli) vi test: ## Run tests go clean -testcache - go test ./... -v + go test -coverprofile=coverage.txt ./... -v verify: lint check-generated # Verify code style, is lint free, freshness ... gofumpt -s -d . diff --git a/cmd/tink-controller/.gitignore b/cmd/tink-controller/.gitignore new file mode 100644 index 000000000..789ea6c77 --- /dev/null +++ b/cmd/tink-controller/.gitignore @@ -0,0 +1,2 @@ +tink-controller +tink-controller-* diff --git a/cmd/tink-controller/Dockerfile b/cmd/tink-controller/Dockerfile new file mode 100644 index 000000000..eea1a771a --- /dev/null +++ b/cmd/tink-controller/Dockerfile @@ -0,0 +1,10 @@ +FROM alpine:3.15 +ENTRYPOINT ["/usr/bin/tink-controller"] +EXPOSE 42113 +EXPOSE 42114 + +ARG TARGETARCH +ARG TARGETVARIANT + +RUN apk add --update ca-certificates +COPY tink-controller-linux-${TARGETARCH:-amd64}${TARGETVARIANT} /usr/bin/tink-controller diff --git a/cmd/tink-controller/main.go b/cmd/tink-controller/main.go new file mode 100644 index 000000000..77294fe67 --- /dev/null +++ b/cmd/tink-controller/main.go @@ -0,0 +1,125 @@ +package main + +import ( + "context" + "fmt" + "os" + "strings" + + "github.com/packethost/pkg/log" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + "github.com/spf13/viper" + "github.com/tinkerbell/tink/pkg/controllers" + wfctrl "github.com/tinkerbell/tink/pkg/controllers/workflow" + "k8s.io/client-go/tools/clientcmd" +) + +// version is set at build time. +var version = "devel" + +// DaemonConfig represents all the values you can configure as part of the tink-server. +// You can change the configuration via environment variable, or file, or command flags. +type DaemonConfig struct { + K8sAPI string + Kubeconfig string // only applies to out of cluster +} + +func (c *DaemonConfig) AddFlags(fs *pflag.FlagSet) { + fs.StringVar(&c.K8sAPI, "kubernetes", "", "The Kubernetes URL") + fs.StringVar(&c.Kubeconfig, "kubeconfig", "", "Absolute path to the kubeconfig file") +} + +func main() { + logger, err := log.Init("github.com/tinkerbell/tink") + if err != nil { + panic(err) + } + defer logger.Close() + + config := &DaemonConfig{} + + cmd := NewRootCommand(config, logger) + if err := cmd.ExecuteContext(context.Background()); err != nil { + defer os.Exit(1) + } +} + +func NewRootCommand(config *DaemonConfig, logger log.Logger) *cobra.Command { + cmd := &cobra.Command{ + Use: "tink-controller", + PreRunE: func(cmd *cobra.Command, args []string) error { + viper, err := createViper(logger) + if err != nil { + return err + } + return applyViper(viper, cmd) + }, + RunE: func(cmd *cobra.Command, args []string) error { + logger.Info("starting controller version " + version) + + config, err := clientcmd.BuildConfigFromFlags(config.K8sAPI, config.Kubeconfig) + if err != nil { + return err + } + + manager, err := controllers.NewManager(config, controllers.GetControllerOptions()) + if err != nil { + return err + } + + return manager.RegisterControllers( + cmd.Context(), + wfctrl.NewController(manager.GetClient()), + ).Start(cmd.Context()) + }, + } + config.AddFlags(cmd.Flags()) + return cmd +} + +func createViper(logger log.Logger) (*viper.Viper, error) { + v := viper.New() + v.AutomaticEnv() + v.SetConfigName("tink-controller") + v.AddConfigPath("/etc/tinkerbell") + v.AddConfigPath(".") + v.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) + + // If a config file is found, read it in. + if err := v.ReadInConfig(); err != nil { + if _, ok := err.(viper.ConfigFileNotFoundError); !ok { + logger.With("configFile", v.ConfigFileUsed()).Error(err, "could not load config file") + return nil, err + } + logger.Info("no config file found") + } else { + logger.With("configFile", v.ConfigFileUsed()).Info("loaded config file") + } + + return v, nil +} + +func applyViper(v *viper.Viper, cmd *cobra.Command) error { + errors := []error{} + + cmd.Flags().VisitAll(func(f *pflag.Flag) { + if !f.Changed && v.IsSet(f.Name) { + val := v.Get(f.Name) + if err := cmd.Flags().Set(f.Name, fmt.Sprintf("%v", val)); err != nil { + errors = append(errors, err) + return + } + } + }) + + if len(errors) > 0 { + errs := []string{} + for _, err := range errors { + errs = append(errs, err.Error()) + } + return fmt.Errorf(strings.Join(errs, ", ")) + } + + return nil +} diff --git a/config/crd/bases/tinkerbell.org_workflowdata.yaml b/config/crd/bases/tinkerbell.org_workflowdata.yaml index 59ff561f6..e5c9d6daa 100644 --- a/config/crd/bases/tinkerbell.org_workflowdata.yaml +++ b/config/crd/bases/tinkerbell.org_workflowdata.yaml @@ -53,9 +53,6 @@ spec: status: description: WorkflowStatus defines the observed state of Workflow. properties: - data: - description: Data is the populated Workflow Data in Tinkerbell. - type: string globalTimeout: description: GlobalTimeout represents the max execution time format: int64 diff --git a/config/crd/bases/tinkerbell.org_workflows.yaml b/config/crd/bases/tinkerbell.org_workflows.yaml index d90efadce..8e966c0f2 100644 --- a/config/crd/bases/tinkerbell.org_workflows.yaml +++ b/config/crd/bases/tinkerbell.org_workflows.yaml @@ -60,9 +60,6 @@ spec: status: description: WorkflowStatus defines the observed state of Workflow. properties: - data: - description: Data is the populated Workflow Data in Tinkerbell. - type: string globalTimeout: description: GlobalTimeout represents the max execution time format: int64 diff --git a/go.mod b/go.mod index 8ccfcecaa..4446a9013 100644 --- a/go.mod +++ b/go.mod @@ -7,10 +7,11 @@ require ( github.com/docker/distribution v2.7.1+incompatible github.com/docker/docker v20.10.7+incompatible github.com/equinix-labs/otel-init-go v0.0.1 + github.com/go-logr/zapr v0.4.0 github.com/go-openapi/strfmt v0.19.3 // indirect github.com/golang/protobuf v1.5.2 github.com/google/go-cmp v0.5.6 - github.com/google/uuid v1.2.0 + github.com/google/uuid v1.3.0 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 github.com/grpc-ecosystem/grpc-gateway v1.16.0 @@ -30,12 +31,15 @@ require ( github.com/testcontainers/testcontainers-go v0.11.1 go.mongodb.org/mongo-driver v1.1.2 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.22.0 - google.golang.org/genproto v0.0.0-20210921142501-181ce0d877f6 - google.golang.org/grpc v1.41.0-dev.0.20210907181116-2f3355d2244e + go.uber.org/multierr v1.7.0 + google.golang.org/genproto v0.0.0-20211021150943-2b146023228c + google.golang.org/grpc v1.42.0 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 google.golang.org/protobuf v1.27.1 gopkg.in/yaml.v2 v2.4.0 k8s.io/apimachinery v0.22.2 + k8s.io/client-go v0.22.2 + knative.dev/pkg v0.0.0-20211119170723-a99300deff34 mvdan.cc/gofumpt v0.1.1 sigs.k8s.io/controller-runtime v0.10.1 sigs.k8s.io/controller-tools v0.7.0 diff --git a/go.sum b/go.sum index 2bd96065a..af74e8108 100644 --- a/go.sum +++ b/go.sum @@ -24,8 +24,15 @@ cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmW cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0 h1:at8Tk2zUz63cLPR0JPWm5vp77pEZmzxEQBEfRKn1VV8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= +cloud.google.com/go v0.97.0 h1:3DXvAyifywvq64LfkKaMOmkWPS1CikIQdMe2lY9vxU8= +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -49,10 +56,14 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.15.0/go.mod h1:mjjQMoxxyGH7Jr8K5qrx6N2O0AHsczI61sMNn03GIZI= +cloud.google.com/go/storage v1.18.2/go.mod h1:AiIj7BWXyhO5gGVmYJ+S8tbkCx3yb0IMjua8Aw4naVM= code.gitea.io/sdk/gitea v0.14.0/go.mod h1:89WiyOX1KEcvjP66sRHdu0RafojGo60bT9UqW17VbWs= contrib.go.opencensus.io/exporter/aws v0.0.0-20200617204711-c478e41e60e9/go.mod h1:uu1P0UCM/6RbsMrgPa98ll8ZcHM858i/AD06a9aLRCA= +contrib.go.opencensus.io/exporter/ocagent v0.7.1-0.20200907061046-05415f1de66d/go.mod h1:IshRmMJBhDfFj5Y67nVhMYTTIze91RUeT73ipWKs/GY= +contrib.go.opencensus.io/exporter/prometheus v0.4.0/go.mod h1:o7cosnyfuPVK0tB8q0QmaQNhGnptITnPQB+z1+qeFB0= contrib.go.opencensus.io/exporter/stackdriver v0.13.4/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc= contrib.go.opencensus.io/exporter/stackdriver v0.13.5/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc= +contrib.go.opencensus.io/exporter/zipkin v0.1.2/go.mod h1:mP5xM3rrgOjpn79MM8fZbj3gsxcuytSqtH0dxSWW1RE= contrib.go.opencensus.io/integrations/ocsql v0.1.7/go.mod h1:8DsSdjz3F+APR+0z0WkU1aRorQCFfRxvqjUUPMbF3fE= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/AlekSi/pointer v1.1.0/go.mod h1:y7BvfRI3wXPWKXEBhU71nbnIEEZX0QTSB2Bj48UJIZE= @@ -74,6 +85,7 @@ github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSW github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= github.com/Azure/go-autorest/autorest v0.11.3/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= +github.com/Azure/go-autorest/autorest v0.11.12/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= github.com/Azure/go-autorest/autorest v0.11.17/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= @@ -134,12 +146,15 @@ github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt 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/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/sarama v1.30.0/go.mod h1:zujlQQx1kzHsh4jfV1USnptCQrHAEZ2Hk8fTKCulPVs= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/Shopify/toxiproxy/v2 v2.1.6-0.20210914104332-15ea381dcdae/go.mod h1:/cvHQkZ1fst0EmZnA5dFtiQdWCNCFYzb+uE2vqVgvx0= github.com/Songmu/gocredits v0.2.0/go.mod h1:JBywHzwOmBMF9uidu1EgS3mwVNqZCKOPLPrFd1h7qQo= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/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/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= +github.com/alecthomas/jsonschema v0.0.0-20180308105923-f2c93856175a/go.mod h1:qpebaTNSsyUn5rPSJMsfqEtDw71TTggXM6stUDI16HA= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 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= @@ -197,7 +212,11 @@ github.com/bkielbasa/cyclop v1.2.0/go.mod h1:qOI0yy6A7dYC4Zgsa72Ppm9kONl0RoIlPbz 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.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/blendle/zapdriver v1.3.1 h1:C3dydBOWYRiOk+B8X9IVZ5IOe+7cl+tGOexN4QqHfpE= +github.com/blendle/zapdriver v1.3.1/go.mod h1:mdXfREi6u5MArG4j9fewC+FGnXaBR+T4Ox4J2u4eHCc= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= +github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b/go.mod h1:ac9efd0D1fsDb3EJvhqgXRbFx7bs2wqZ10HQPeU8U/Q= github.com/bombsimon/wsl/v3 v3.3.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc= github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= github.com/bufbuild/buf v1.0.0-rc2 h1:OXo9Qm/g4nnY+HGS7llFT+QwUIn43ExFMz0Rb5QgSpo= @@ -206,6 +225,7 @@ github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7 github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/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/c2h5oh/datasize v0.0.0-20171227191756-4eba002a5eae/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= github.com/caarlos0/ctrlc v1.0.0/go.mod h1:CdXpj4rmq0q/1Eb44M9zi2nKB0QraNKuRGYGrrHhcQw= github.com/caarlos0/env/v6 v6.6.2/go.mod h1:P0BVSgU9zfkxfSpFUs6KsO3uWR4k3Ac0P66ibAGTybM= github.com/caarlos0/go-shellwords v1.0.12/go.mod h1:bYeeX1GrTLPl5cAMYEzdm272qdsQAZiaHgeF0KTk1Gw= @@ -240,8 +260,11 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= @@ -361,6 +384,9 @@ github.com/dghubble/oauth1 v0.7.0/go.mod h1:8pFdfPkv/jr8mkChVbNVuJ0suiHe278BtWI4 github.com/dghubble/sling v1.3.0/go.mod h1:XXShWaBWKzNLhu2OxikSNFrlsvowtz4kyRuXUG7oQKY= github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-gk v0.0.0-20140819190930-201884a44051/go.mod h1:qm+vckxRlDt0aOla0RYJJVeqHZlWfOm2UIxHaqPB46E= +github.com/dgryski/go-gk v0.0.0-20200319235926-a69029f61654/go.mod h1:qm+vckxRlDt0aOla0RYJJVeqHZlWfOm2UIxHaqPB46E= +github.com/dgryski/go-lttb v0.0.0-20180810165845-318fcdf10a77/go.mod h1:Va5MyIzkU0rAM92tn3hb3Anb7oz7KcnixF49+2wOMe4= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= @@ -385,6 +411,7 @@ github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3 github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-resiliency v1.2.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= @@ -409,7 +436,9 @@ github.com/esimonov/ifshort v1.0.2/go.mod h1:yZqNJUrNn20K8Q9n2CrjTKYyVEmX209Hgu+ github.com/ettle/strcase v0.1.1/go.mod h1:hzDLsPC7/lwKyBOywSHEP89nt2pDgdy+No1NBA9o9VY= github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.11.0+incompatible h1:glyUF9yIYtMHzn8xaKw5rMhdWcwsYV8dZHIq5567/xs= github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= @@ -423,6 +452,7 @@ github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoD 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/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= 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= @@ -459,6 +489,7 @@ github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7 github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc= github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/zapr v0.4.0 h1:uc1uML3hRYL9/ZZPdgHS/n8Nzo+eaYL/Efxkkamf7OM= github.com/go-logr/zapr v0.4.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk= github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= github.com/go-openapi/errors v0.19.2 h1:a2kIyV3w+OS3S97zxUndRVD46+FhGOUBDFY7nmu4CsY= @@ -470,11 +501,13 @@ github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwoh github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= github.com/go-openapi/strfmt v0.19.3 h1:eRfyY5SkaNJCAwmmMcADjY31ow9+N7MCLW7oRkbsINA= github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= @@ -504,8 +537,9 @@ github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b/go.mod h1:aUCEOzz 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/flect v0.2.3 h1:f/ZukRnSNA/DUpSNDadko7Qc0PhGvsew35p/2tu+CRY= github.com/gobuffalo/flect v0.2.3/go.mod h1:vmkQwuZYhN5Pc4ljYQZzP+1sq+NEkK+lh20jmEmX3jc= +github.com/gobuffalo/flect v0.2.4 h1:BSYA8+T60cdyq+vynaSUjqSVI9mDEg9ZfQUXKmfjo4I= +github.com/gobuffalo/flect v0.2.4/go.mod h1:1ZyCLIbg0YD7sDkzvFdPoOydPtD8y9JQnrOROolUcM8= 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= @@ -556,6 +590,7 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -577,6 +612,7 @@ github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4= github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk= github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8= @@ -587,6 +623,15 @@ github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZ github.com/golangci/misspell v0.3.5/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA= github.com/golangci/revgrep v0.0.0-20210208091834-cd28932614b5/go.mod h1:LK+zW4MpyytAWQRz0M4xnzEk50lSvqDQKfx304apFkY= github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ= +github.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac/go.mod h1:P32wAyui1PQ58Oce/KYkOqQv8cVw1zAapXOl+dRFGbc= +github.com/gonum/diff v0.0.0-20181124234638-500114f11e71/go.mod h1:22dM4PLscQl+Nzf64qNBurVJvfyvZELT0iRW2l/NN70= +github.com/gonum/floats v0.0.0-20181209220543-c233463c7e82/go.mod h1:PxC8OnwL11+aosOB5+iEPoV3picfs8tUpkVd0pDo+Kg= +github.com/gonum/integrate v0.0.0-20181209220457-a422b5c0fdf2/go.mod h1:pDgmNM6seYpwvPos3q+zxlXMsbve6mOIPucUnUOrI7Y= +github.com/gonum/internal v0.0.0-20181124074243-f884aa714029/go.mod h1:Pu4dmpkhSyOzRwuXkOgAvijx4o+4YMUJJo9OvPYMkks= +github.com/gonum/lapack v0.0.0-20181123203213-e4cdc5a0bff9/go.mod h1:XA3DeT6rxh2EAE789SSiSJNqxPaC0aE9J8NTOI0Jo/A= +github.com/gonum/mathext v0.0.0-20181121095525-8a4bf007ea55/go.mod h1:fmo8aiSEWkJeiGXUJf+sPvuDgEFgqIoZSs843ePKrGg= +github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9/go.mod h1:0EXg4mc1CNP0HCqCz+K4ts155PXIlUywf0wqN+GfPZw= +github.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b/go.mod h1:Z4GIJBJO3Wa4gD4vbwQxXXZ+WHmW6E9ixmNrwvs0iZs= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= @@ -606,17 +651,21 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= +github.com/google/go-github/v27 v27.0.6/go.mod h1:/0Gr8pJ55COkmv+S/yPKCczSkUPIM/LnFyubufRNIS0= github.com/google/go-github/v35 v35.3.0/go.mod h1:yWB7uCcVWaUbUP74Aq3whuMySRMatyRmq5U9FTNlbio= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-replayers/grpcreplay v1.0.0/go.mod h1:8Ig2Idjpr6gifRd6pNVggX6TC1Zw6Jx74AKp7QNH2QE= github.com/google/go-replayers/httpreplay v0.1.2/go.mod h1:YKZViNhiGgqdBlUbI2MwGpq4pXxNmhJLPHQ7cv2b5no= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/mako v0.0.0-20190821191249-122f8dcef9e3/go.mod h1:YzLcVlL+NqWnmUEPuhS1LxDDwGO9WNbVlEXaF4IH35g= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian v2.1.1-0.20190517191504-25dcb96d9e51+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -630,6 +679,9 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/rpmpack v0.0.0-20210410105602-e20c988a6f5a/go.mod h1:+y9lKiqDhR4zkLl+V9h4q0rdyrYVsWWm6LLCQP33DIk= github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= @@ -638,13 +690,17 @@ github.com/google/uuid v0.0.0-20161128191214-064e2069ce9c/go.mod h1:TIyPZe4Mgqvf github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= +github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= +github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw= github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= @@ -663,6 +719,8 @@ github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2z github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= @@ -687,6 +745,7 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.12.1/go.mod h1:8XEsbTttt/W+VvjtQhLACqCisSPWTxCZ7sBRjU6iH9c= +github.com/grpc-ecosystem/grpc-gateway v1.14.6/go.mod h1:zdiPV4Yse/1gnckTHtghG4GkDEdKCRJduHpTxT3/jcw= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= @@ -710,6 +769,7 @@ github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerX github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= @@ -738,15 +798,24 @@ github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/improbable-eng/grpc-web v0.12.0/go.mod h1:6hRR09jOEG81ADP5wCQju1z71g6OL4eEvELdran/3cs= github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf/go.mod h1:hyb9oH7vZsitZCiBt0ZvifOrB+qc8PS5IiilCIb87rg= 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/influxdata/tdigest v0.0.0-20180711151920-a7d76c6f093a/go.mod h1:9GkyshztGufsdPQWjH+ifgnIr3xNUL5syI70g2dzU1o= +github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mqiSBE6Ffsg94weZZ2c+v/ciT8QRHFOap7EKDrR0= github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= github.com/jarcoal/httpmock v1.0.8/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= +github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= +github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= +github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= +github.com/jcmturner/gokrb5/v8 v8.4.2/go.mod h1:sb+Xq/fTY5yktf/VxLsE3wlfPqQjp0aWNYyvBVK62bc= +github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= github.com/jedib0t/go-pretty v4.3.0+incompatible h1:CGs8AVhEKg/n9YbUenWmNStRW2PHJzaeDodcfvRAbIo= github.com/jedib0t/go-pretty v4.3.0+incompatible/go.mod h1:XemHduiw8R651AF9Pt4FwCTKeG3oo7hrHJAoznj9nag= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= @@ -790,6 +859,8 @@ github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8 github.com/julz/importas v0.0.0-20210419104244-841f0c0fe66d/go.mod h1:oSFU2R4XK/P7kNBrnL/FEQlDGN1/6WoxXEjSSXO0DV0= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kevinburke/ssh_config v1.1.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= @@ -868,6 +939,7 @@ github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/manifoldco/promptui v0.8.0/go.mod h1:n4zTdgP0vr0S3w7/O/g98U+e0gwLScEXGwov2nIKuGQ= github.com/maratori/testpackage v1.0.1/go.mod h1:ddKdw+XG0Phzhx8BFDTKgpWP4i7MpApTE5fXSKAqwDU= github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= @@ -915,6 +987,7 @@ github.com/mgechev/dots v0.0.0-20190921121421-c36f7dcfbb81/go.mod h1:KQ7+USdGKfp github.com/mgechev/revive v1.0.7/go.mod h1:vuE5ox/4L/HDd63MCcCk3H6wTLQ6XXezRphJ8cJJOxY= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.17/go.mod h1:WgzbA6oji13JREwiNsRDNfl7jYdPnmz+VEuLrA+/48M= github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= @@ -1003,8 +1076,9 @@ github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.1/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= -github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -1014,8 +1088,9 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.11.0/go.mod h1:azGKhqFUon9Vuj0YmTfLSmx0FUwqXYSTl5re8lQLTUg= github.com/onsi/gomega v1.14.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= -github.com/onsi/gomega v1.15.0 h1:WjP/FQ/sk43MRmnEcT+MlDw2TFvkrXlprrPST/IudjU= github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= +github.com/onsi/gomega v1.16.0 h1:6gjqkI8iiRHMvdccRJM8rVKjCWk6ZIm6FTm3ddIe4/c= +github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -1048,6 +1123,7 @@ github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxS 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/openzipkin/zipkin-go v0.3.0/go.mod h1:4c3sLeE8xjNqehmF5RpAFLPLJxXscc0R4l6Zg0P1tTQ= github.com/packethost/pkg v0.0.0-20200903155310-0433e0605550 h1:/ojL7LAVjyH1MY+db0+j6rcWU3UWWpzHksYFsHWs9vQ= github.com/packethost/pkg v0.0.0-20200903155310-0433e0605550/go.mod h1:GSv7cTtIjns4yc0pyajaM1RE/KE4djJONoblFIRDrxA= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= @@ -1063,6 +1139,7 @@ github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7 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/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -1106,8 +1183,10 @@ github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.28.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= +github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= @@ -1121,6 +1200,7 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/statsd_exporter v0.21.0/go.mod h1:rbT83sZq2V+p73lHhPZfMc3MLCHmSHelCh9hSGYNLTQ= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/pseudomuto/protoc-gen-doc v1.3.2/go.mod h1:y5+P6n3iGrbKG+9O04V5ld71in3v/bX88wUwgt+U8EA= github.com/pseudomuto/protokit v0.2.0/go.mod h1:2PdH30hxVHsup8KpBTOXTBeMVhJZVio3Q8ViKSAXT0Q= @@ -1132,9 +1212,11 @@ github.com/quasilyte/go-ruleguard/dsl v0.3.2/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQP github.com/quasilyte/go-ruleguard/rules v0.0.0-20201231183845-9e62ed36efe1/go.mod h1:7JTjp89EGyU1d6XfBiXihJNG37wB2VRkd125Q1u7Plc= github.com/quasilyte/go-ruleguard/rules v0.0.0-20210203162857-b223e0831f88/go.mod h1:4cgAphtvu7Ftv7vOT2ZOYhC6CvBxZixcasr8qIOTA50= github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= +github.com/rabbitmq/amqp091-go v1.1.0/go.mod h1:ogQDLSOACsLPsIq0NpbtiifNZi2YOz0VTJ0kHRghqbM= github.com/rakyll/statik v0.1.6/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs= github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= 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/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= @@ -1231,6 +1313,7 @@ github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag 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/streadway/quantile v0.0.0-20150917103942-b0c588724d25/go.mod h1:lbP8tGiBjZ5YWIc2fzuRpTaz0b/53vT6PEs3QuAWzuU= 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= @@ -1273,6 +1356,8 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1 github.com/tomarrell/wrapcheck/v2 v2.1.0/go.mod h1:crK5eI4RGSUrb9duDTQ5GqcukbKZvi85vX6nbhsBAeI= github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4= github.com/tommy-muehle/go-mnd/v2 v2.4.0/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw= +github.com/tsenart/go-tsz v0.0.0-20180814232043-cdeb9e1e981e/go.mod h1:SWZznP1z5Ki7hDT2ioqiFKEse8K9tU2OUvaRI0NeGQo= +github.com/tsenart/vegeta/v12 v12.8.4/go.mod h1:ZiJtwLn/9M4fTPdMY7bdbIeyNeFVE8/AHbWFqCsUuho= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= @@ -1285,6 +1370,7 @@ github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/uudashr/gocognit v1.0.1/go.mod h1:j44Ayx2KW4+oB6SWMv8KsmHzZrOInQav7D3cQMJ5JUM= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.16.0/go.mod h1:YOKImeEosDdBPnxc0gy7INqi3m1zK6A+xl6TwOBhHCA= @@ -1300,6 +1386,9 @@ github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr github.com/xanzy/go-gitlab v0.50.0/go.mod h1:Q+hQhV508bDPoBijv7YjK/Lvlb4PhVhJdKqXVQrUoAE= github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= +github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= @@ -1389,8 +1478,10 @@ 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/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/automaxprocs v1.4.0/go.mod h1:/mTEdr7LvHhs0v7mjdxDreTz1OG5zdZGqgOnhWiR/+Q= go.uber.org/goleak v0.10.0/go.mod h1:VCZuO8V8mFPlL0F5J5GK1rtHV3DrFcQ1R8ryq7FK0aI= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723 h1:sHOAIxRGBp443oHZIPB+HsUGaksVCXVQENPxwTfQdH4= @@ -1427,17 +1518,21 @@ golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8U 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-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf h1:B2n+Zi5QeYRDAEodEu72OS36gmTWjgpXr2+cWcBW90o= golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= +golang.org/x/crypto v0.0.0-20210920023735-84f357641f63 h1:kETrAMYZq6WVGPa8IIixL0CaEcIUNi+1WX7grUoi3y8= +golang.org/x/crypto v0.0.0-20210920023735-84f357641f63/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1475,6 +1570,7 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= @@ -1531,6 +1627,7 @@ golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= @@ -1541,9 +1638,11 @@ golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210505214959-0714010a04ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210917221730-978cfadd31cf h1:R150MpwJIv1MpS0N/pc+NhTM8ajzvlmxlY5OYsrevXQ= golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211101193420-4a448f8816b3 h1:VrJZAjbekhoRn7n5FBujY31gboH+iB3pdLxn3gE9FjU= +golang.org/x/net v0.0.0-20211101193420-4a448f8816b3/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1558,8 +1657,14 @@ golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210413134643-5e61552d6c78/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210427180440-81ed05c6b58c h1:SgVl/sCtkicsS7psKkje4H9YtjdEl3xsYh7N+5TDHqY= golang.org/x/oauth2 v0.0.0-20210427180440-81ed05c6b58c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211028175245-ba495a64dcb5 h1:v79phzBz03tsVCUTbvTBmmC3CUXF5mKYt7DA4ZVldpM= +golang.org/x/oauth2 v0.0.0-20211028175245-ba495a64dcb5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1676,16 +1781,25 @@ golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210503080704-8803ae5d1324/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210921065528-437939a70204 h1:JJhkWtBuTQKyz2bd5WG9H8iUsJRU3En/KRfN8B2RnDs= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210921065528-437939a70204/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 h1:2B5p2L5IfGiD7+b9BOoRMC6DgObAVZV+Fsp050NqXik= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -1736,6 +1850,7 @@ golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190916130336-e45ffcd953cc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1814,17 +1929,23 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.6 h1:SIasE1FVIQOWz2GEAHFOmoW7xchJcqlucjSULTL0Ag4= golang.org/x/tools v0.1.6/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +golang.org/x/tools v0.1.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ= +golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= +gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= +gonum.org/v1/netlib v0.0.0-20181029234149-ec6d1f5cefe6/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= @@ -1842,6 +1963,7 @@ google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/ google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.25.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= @@ -1853,6 +1975,16 @@ google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= google.golang.org/api v0.45.0/go.mod h1:ISLIJCedJolbZvDfAk+Ctuq5hf+aJ33WgtUsfyFoLXA= google.golang.org/api v0.46.0/go.mod h1:ceL4oozhkAiTID8XMmJBsIxID/9wMXJVVFXPg4ylg3I= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= +google.golang.org/api v0.58.0/go.mod h1:cAbP2FsxoGVNwtgNAmmn3y5G1TWAiVYRmg4yku3lv+E= +google.golang.org/api v0.60.0/go.mod h1:d7rl65NZAkEQ90JFzqBjcRq1TVeG5ZoGV3sSpEnnVb4= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1901,6 +2033,7 @@ google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200626011028-ee7919e894b5/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200707001353-8e8330bf89df/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= @@ -1925,9 +2058,27 @@ google.golang.org/genproto v0.0.0-20210420162539-3c870d7478d2/go.mod h1:P3QM42oQ google.golang.org/genproto v0.0.0-20210423144448-3a41ef94ed2b/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210429181445-86c259c2b4ab/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210506142907-4a47615972c2/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210921142501-181ce0d877f6 h1:2ncG/LajxmrclaZH+ppVi02rQxz4eXYJzGHdFN4Y9UA= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210921142501-181ce0d877f6/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211016002631-37fc39342514/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211021150943-2b146023228c h1:FqrtZMB5Wr+/RecOM3uPJNPfWR8Upb5hAPnt7PU6i4k= +google.golang.org/genproto v0.0.0-20211021150943-2b146023228c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= @@ -1961,9 +2112,12 @@ google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQ google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.41.0-dev.0.20210907181116-2f3355d2244e h1:HKKXKZmOaf1UtYn+/ga7+QSLvK7l6K5Mppj9yGgXYCo= google.golang.org/grpc v1.41.0-dev.0.20210907181116-2f3355d2244e/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= +google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= +google.golang.org/grpc v1.42.0 h1:XT2/MFpuPFsEX2fWh3YQtHkZ+WYZFQRfaUgLZYj/p6A= +google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 h1:M1YKkFIboKNieVO5DLUEVzQfGwJD30Nv2jfUgzb5UcE= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -2043,35 +2197,53 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.2.0/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY= k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= +k8s.io/api v0.21.4/go.mod h1:fTVGP+M4D8+00FN2cMnJqk/eb/GH53bvmNs2SVTmpFk= k8s.io/api v0.22.2 h1:M8ZzAD0V6725Fjg53fKeTJxGsJvRbk4TEm/fexHMtfw= k8s.io/api v0.22.2/go.mod h1:y3ydYpLJAaDI+BbSe2xmGcqxiWHmWjkEeIbiwHvnPR8= +k8s.io/apiextensions-apiserver v0.21.4/go.mod h1:OoC8LhI9LnV+wKjZkXIBbLUwtnOGJiTRE33qctH5CIk= k8s.io/apiextensions-apiserver v0.22.2 h1:zK7qI8Ery7j2CaN23UCFaC1hj7dMiI87n01+nKuewd4= k8s.io/apiextensions-apiserver v0.22.2/go.mod h1:2E0Ve/isxNl7tWLSUDgi6+cmwHi5fQRdwGVCxbC+KFA= k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= +k8s.io/apimachinery v0.21.4/go.mod h1:H/IM+5vH9kZRNJ4l3x/fXP/5bOPJaVP/guptnZPeCFI= k8s.io/apimachinery v0.22.2 h1:ejz6y/zNma8clPVfNDLnPbleBo6MpoFy/HBiBqCouVk= k8s.io/apimachinery v0.22.2/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= +k8s.io/apiserver v0.21.4/go.mod h1:SErUuFBBPZUcD2nsUU8hItxoYheqyYr2o/pCINEPW8g= k8s.io/apiserver v0.22.2/go.mod h1:vrpMmbyjWrgdyOvZTSpsusQq5iigKNWv9o9KlDAbBHI= k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= +k8s.io/client-go v0.21.4/go.mod h1:t0/eMKyUAq/DoQ7vW8NVVA00/nomlwC+eInsS8PxSew= +k8s.io/client-go v0.22.2 h1:DaSQgs02aCC1QcwUdkKZWOeaVsQjYvWv8ZazcZ6JcHc= k8s.io/client-go v0.22.2/go.mod h1:sAlhrkVDf50ZHx6z4K0S40wISNTarf1r800F+RlCF6U= +k8s.io/code-generator v0.21.4/go.mod h1:K3y0Bv9Cz2cOW2vXUrNZlFbflhuPvuadW6JdnN6gGKo= k8s.io/code-generator v0.22.2/go.mod h1:eV77Y09IopzeXOJzndrDyCI88UBok2h6WxAlBwpxa+o= k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= +k8s.io/component-base v0.21.4/go.mod h1:ZKG0eHVX+tUDcaoIGpU3Vtk4TIjMddN9uhEWDmW6Nyg= +k8s.io/component-base v0.22.2 h1:vNIvE0AIrLhjX8drH0BgCNJcR4QZxMXcJzBsDplDx9M= k8s.io/component-base v0.22.2/go.mod h1:5Br2QhI9OTe79p+TzPe9JKNQYvEKbq9rTJDWllunGug= k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/gengo v0.0.0-20210915205010-39e73c8a59cd/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= +k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= +k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= k8s.io/klog/v2 v2.9.0 h1:D7HV+n1V57XeZ0m6tdRkfknthUaM06VFbWldOFh8kzM= k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= +k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE= +k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM= k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a h1:8dYfu/Fc9Gz2rNJKB9IQRGgQOh2clmRzNIPPY1xLY5g= k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +knative.dev/hack v0.0.0-20211112192837-128cf0150a69/go.mod h1:PHt8x8yX5Z9pPquBEfIj0X66f8iWkWfR0S/sarACJrI= +knative.dev/pkg v0.0.0-20211119170723-a99300deff34 h1:3PTfphK/gjxA+XJIwyDZ1g1KxRaADsI/BdhFlRAtdEE= +knative.dev/pkg v0.0.0-20211119170723-a99300deff34/go.mod h1:VqUp1KWJqpTDNoiSI/heaX3uMdubImslJE2tBkP+Bbw= mvdan.cc/gofumpt v0.1.1 h1:bi/1aS/5W00E2ny5q65w9SnKpWEF/UIOqDYBILpo9rA= mvdan.cc/gofumpt v0.1.1/go.mod h1:yXG1r1WqZVKWbVRtBWKWX9+CxGYfA51nSomhM0woR48= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= @@ -2079,6 +2251,7 @@ mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jC mvdan.cc/unparam v0.0.0-20210104141923-aac4ce9116a7/go.mod h1:hBpJkZE8H/sb+VRFvw2+rBpHNsTBcvSpk61hr8mzXZE= nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= +pgregory.net/rapid v0.3.3/go.mod h1:UYpPVyjFHzYBGHIxLFoupi8vwk6rXNzRY9OMvVxFIOU= 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= @@ -2092,6 +2265,7 @@ sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno= sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= 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= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/pkg/apis/core/v1alpha1/hardware_methods.go b/pkg/apis/core/v1alpha1/hardware_methods.go new file mode 100644 index 000000000..c12e18dbf --- /dev/null +++ b/pkg/apis/core/v1alpha1/hardware_methods.go @@ -0,0 +1,20 @@ +package v1alpha1 + +const ( + // HardwareIDAnnotation is used by the controller to store the + // ID assigned to the hardware by Tinkerbell for migrated hardware. + HardwareIDAnnotation = "hardware.tinkerbell.org/id" +) + +// TinkID returns the Tinkerbell ID associated with this Hardware. +func (h *Hardware) TinkID() string { + return h.Annotations[HardwareIDAnnotation] +} + +// SetTinkID sets the Tinkerbell ID associated with this Hardware. +func (h *Hardware) SetTinkID(id string) { + if h.Annotations == nil { + h.Annotations = make(map[string]string) + } + h.Annotations[HardwareIDAnnotation] = id +} diff --git a/pkg/apis/core/v1alpha1/hardware_test.go b/pkg/apis/core/v1alpha1/hardware_test.go new file mode 100644 index 000000000..8eca50ad5 --- /dev/null +++ b/pkg/apis/core/v1alpha1/hardware_test.go @@ -0,0 +1,57 @@ +package v1alpha1 + +import ( + "testing" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestHardwareTinkID(t *testing.T) { + id := "d2c26e20-97e0-449c-b665-61efa7373f47" + cases := []struct { + name string + input *Hardware + want string + overwrite string + }{ + { + "Already set", + &Hardware{ + ObjectMeta: metav1.ObjectMeta{ + Name: "debian", + Namespace: "default", + Annotations: map[string]string{ + HardwareIDAnnotation: id, + }, + }, + }, + id, + "", + }, + { + "nil annotations", + &Hardware{ + ObjectMeta: metav1.ObjectMeta{ + Name: "debian", + Namespace: "default", + Annotations: nil, + }, + }, + "", + "abc", + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + if tc.input.TinkID() != tc.want { + t.Errorf("Got unexpected ID: got %v, wanted %v", tc.input.TinkID(), tc.want) + } + + tc.input.SetTinkID(tc.overwrite) + + if tc.input.TinkID() != tc.overwrite { + t.Errorf("Got unexpected ID: got %v, wanted %v", tc.input.TinkID(), tc.overwrite) + } + }) + } +} diff --git a/pkg/apis/core/v1alpha1/template_methods.go b/pkg/apis/core/v1alpha1/template_methods.go new file mode 100644 index 000000000..afce6bb9a --- /dev/null +++ b/pkg/apis/core/v1alpha1/template_methods.go @@ -0,0 +1,20 @@ +package v1alpha1 + +const ( + // TemplateIDAnnotation is used by the controller to store the + // ID assigned to the template by Tinkerbell for migrated templates. + TemplateIDAnnotation = "template.tinkerbell.org/id" +) + +// TinkID returns the Tinkerbell ID associated with this Template. +func (t *Template) TinkID() string { + return t.Annotations[TemplateIDAnnotation] +} + +// SetTinkID sets the Tinkerbell ID associated with this Template. +func (t *Template) SetTinkID(id string) { + if t.Annotations == nil { + t.Annotations = make(map[string]string) + } + t.Annotations[TemplateIDAnnotation] = id +} diff --git a/pkg/apis/core/v1alpha1/template_test.go b/pkg/apis/core/v1alpha1/template_test.go new file mode 100644 index 000000000..c7f22ec3f --- /dev/null +++ b/pkg/apis/core/v1alpha1/template_test.go @@ -0,0 +1,57 @@ +package v1alpha1 + +import ( + "testing" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestTemplateTinkID(t *testing.T) { + id := "d2c26e20-97e0-449c-b665-61efa7373f47" + cases := []struct { + name string + input *Template + want string + overwrite string + }{ + { + "Already set", + &Template{ + ObjectMeta: metav1.ObjectMeta{ + Name: "debian", + Namespace: "default", + Annotations: map[string]string{ + TemplateIDAnnotation: id, + }, + }, + }, + id, + "", + }, + { + "nil annotations", + &Template{ + ObjectMeta: metav1.ObjectMeta{ + Name: "debian", + Namespace: "default", + Annotations: nil, + }, + }, + "", + "abc", + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + if tc.input.TinkID() != tc.want { + t.Errorf("Got unexpected ID: got %v, wanted %v", tc.input.TinkID(), tc.want) + } + + tc.input.SetTinkID(tc.overwrite) + + if tc.input.TinkID() != tc.overwrite { + t.Errorf("Got unexpected ID: got %v, wanted %v", tc.input.TinkID(), tc.overwrite) + } + }) + } +} diff --git a/pkg/apis/core/v1alpha1/workflow_methods.go b/pkg/apis/core/v1alpha1/workflow_methods.go index cdc488861..b2cad6045 100644 --- a/pkg/apis/core/v1alpha1/workflow_methods.go +++ b/pkg/apis/core/v1alpha1/workflow_methods.go @@ -2,6 +2,25 @@ package v1alpha1 import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +const ( + // WorkflowIDAnnotation is used by the controller to store the + // ID assigned to the workflow by Tinkerbell for migrated workflows. + WorkflowIDAnnotation = "workflow.tinkerbell.org/id" +) + +// TinkID returns the Tinkerbell ID associated with this Workflow. +func (w *Workflow) TinkID() string { + return w.Annotations[WorkflowIDAnnotation] +} + +// SetTinkID sets the Tinkerbell ID associated with this Workflow. +func (w *Workflow) SetTinkID(id string) { + if w.Annotations == nil { + w.Annotations = make(map[string]string) + } + w.Annotations[WorkflowIDAnnotation] = id +} + // GetStartTime returns the start time, for the first action of the first task. func (w *Workflow) GetStartTime() *metav1.Time { if len(w.Status.Tasks) > 0 { diff --git a/pkg/apis/core/v1alpha1/workflow_test.go b/pkg/apis/core/v1alpha1/workflow_test.go new file mode 100644 index 000000000..95af57dce --- /dev/null +++ b/pkg/apis/core/v1alpha1/workflow_test.go @@ -0,0 +1,182 @@ +package v1alpha1 + +import ( + "testing" + "time" + + "github.com/tinkerbell/tink/pkg/internal/tests" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var TestNow = tests.NewFrozenTimeUnix(1637361793) + +func TestWorkflowTinkID(t *testing.T) { + id := "d2c26e20-97e0-449c-b665-61efa7373f47" + cases := []struct { + name string + input *Workflow + want string + overwrite string + }{ + { + "Already set", + &Workflow{ + ObjectMeta: metav1.ObjectMeta{ + Name: "debian", + Namespace: "default", + Annotations: map[string]string{ + WorkflowIDAnnotation: id, + }, + }, + }, + id, + "", + }, + { + "nil annotations", + &Workflow{ + ObjectMeta: metav1.ObjectMeta{ + Name: "debian", + Namespace: "default", + Annotations: nil, + }, + }, + "", + "abc", + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + if tc.input.TinkID() != tc.want { + t.Errorf("Got unexpected ID: got %v, wanted %v", tc.input.TinkID(), tc.want) + } + + tc.input.SetTinkID(tc.overwrite) + + if tc.input.TinkID() != tc.overwrite { + t.Errorf("Got unexpected ID: got %v, wanted %v", tc.input.TinkID(), tc.overwrite) + } + }) + } +} + +func TestGetStartTime(t *testing.T) { + cases := []struct { + name string + input *Workflow + want *metav1.Time + }{ + { + "Empty wflow", + &Workflow{ + ObjectMeta: metav1.ObjectMeta{ + Name: "debian", + Namespace: "default", + }, + }, + nil, + }, + { + "Running workflow", + &Workflow{ + TypeMeta: metav1.TypeMeta{ + Kind: "Workflow", + APIVersion: "tinkerbell.org/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "debian", + Namespace: "default", + }, + Spec: WorkflowSpec{}, + Status: WorkflowStatus{ + State: "STATE_RUNNING", + GlobalTimeout: 600, + Tasks: []Task{ + { + Name: "os-installation", + WorkerAddr: "3c:ec:ef:4c:4f:54", + Actions: []Action{ + { + Name: "stream-debian-image", + Image: "quay.io/tinkerbell-actions/image2disk:v1.0.0", + Timeout: 60, + Environment: map[string]string{ + "COMPRESSED": "true", + "DEST_DISK": "/dev/nvme0n1", + "IMG_URL": "http://10.1.1.11:8080/debian-10-openstack-amd64.raw.gz", + }, + Status: "STATE_SUCCESS", + StartedAt: TestNow.MetaV1Now(), + Seconds: 20, + }, + { + Name: "stream-debian-image", + Image: "quay.io/tinkerbell-actions/image2disk:v1.0.0", + Timeout: 60, + Environment: map[string]string{ + "COMPRESSED": "true", + "DEST_DISK": "/dev/nvme0n1", + "IMG_URL": "http://10.1.1.11:8080/debian-10-openstack-amd64.raw.gz", + }, + Status: "STATE_RUNNING", + StartedAt: TestNow.MetaV1AfterSec(21), + }, + }, + }, + }, + }, + }, + TestNow.MetaV1Now(), + }, + { + "pending without a start time", + &Workflow{ + TypeMeta: metav1.TypeMeta{ + Kind: "Workflow", + APIVersion: "tinkerbell.org/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "debian", + Namespace: "default", + }, + Spec: WorkflowSpec{}, + Status: WorkflowStatus{ + State: "STATE_PENDING", + GlobalTimeout: 600, + Tasks: []Task{ + { + Name: "os-installation", + WorkerAddr: "3c:ec:ef:4c:4f:54", + Actions: []Action{ + { + Name: "stream-debian-image", + Image: "quay.io/tinkerbell-actions/image2disk:v1.0.0", + Timeout: 60, + Environment: map[string]string{ + "COMPRESSED": "true", + "DEST_DISK": "/dev/nvme0n1", + "IMG_URL": "http://10.1.1.11:8080/debian-10-openstack-amd64.raw.gz", + }, + Status: "STATE_PENDING", + StartedAt: nil, + }, + }, + }, + }, + }, + }, + nil, + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + got := tc.input.GetStartTime() + if got == nil && tc.want == nil { + return + } + if !got.Time.Equal(tc.want.Time) { + t.Errorf("Got time %s, wanted %s", got.Format(time.RFC1123), tc.want.Time.Format(time.RFC1123)) + } + }) + } +} diff --git a/pkg/apis/core/v1alpha1/workflow_types.go b/pkg/apis/core/v1alpha1/workflow_types.go index cbf06c115..bfcb311c1 100644 --- a/pkg/apis/core/v1alpha1/workflow_types.go +++ b/pkg/apis/core/v1alpha1/workflow_types.go @@ -18,9 +18,6 @@ type WorkflowStatus struct { // State is the state of the workflow in Tinkerbell. State string `json:"state,omitempty"` - // Data is the populated Workflow Data in Tinkerbell. - Data string `json:"data,omitempty"` - // GlobalTimeout represents the max execution time GlobalTimeout int64 `json:"globalTimeout,omitempty"` diff --git a/pkg/controllers/manager.go b/pkg/controllers/manager.go new file mode 100644 index 000000000..9571c4c17 --- /dev/null +++ b/pkg/controllers/manager.go @@ -0,0 +1,134 @@ +package controllers + +import ( + "context" + "fmt" + + "github.com/go-logr/zapr" + "github.com/tinkerbell/tink/pkg/apis/core/v1alpha1" + "k8s.io/apimachinery/pkg/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "knative.dev/pkg/logging" + controllerruntime "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/healthz" + "sigs.k8s.io/controller-runtime/pkg/manager" +) + +const ( + WorkerAddr = "status.tasks.workeraddr" +) + +var ( + runtimescheme = runtime.NewScheme() + options = Options{} +) + +func init() { + _ = clientgoscheme.AddToScheme(runtimescheme) + _ = v1alpha1.AddToScheme(runtimescheme) +} + +// Options for running this binary. +type Options struct { + MetricsPort int + HealthProbePort int +} + +// GetControllerOptions returns a set of options used by the Tink controller. +// These options include leader election enabled. +func GetControllerOptions() controllerruntime.Options { + return controllerruntime.Options{ + Logger: zapr.NewLogger(logging.FromContext(context.Background()).Desugar()), + LeaderElection: true, + LeaderElectionID: "tink-leader-election", + Scheme: runtimescheme, + MetricsBindAddress: fmt.Sprintf(":%d", options.MetricsPort), + HealthProbeBindAddress: fmt.Sprintf(":%d", options.HealthProbePort), + } +} + +// GetServerOptions returns a set of options used by the Tink API. +// These options include leader election disabled. +func GetServerOptions() controllerruntime.Options { + return controllerruntime.Options{ + Logger: zapr.NewLogger(logging.FromContext(context.Background()).Desugar()), + LeaderElection: false, + Scheme: runtimescheme, + MetricsBindAddress: fmt.Sprintf(":%d", options.MetricsPort), + HealthProbeBindAddress: fmt.Sprintf(":%d", options.HealthProbePort), + } +} + +// NewManagerOrDie instantiates a controller manager. +func NewManagerOrDie(config *rest.Config, options controllerruntime.Options) Manager { + m, err := NewManager(config, options) + if err != nil { + panic(err) + } + return m +} + +// NewManager instantiates a controller manager. +func NewManager(config *rest.Config, options controllerruntime.Options) (Manager, error) { + m, err := controllerruntime.NewManager(config, options) + if err != nil { + return nil, err + } + indexers := []struct { + obj client.Object + field string + extractValue client.IndexerFunc + }{ + { + &v1alpha1.Workflow{}, + WorkerAddr, + wokerIndexFunc, + }, + } + for _, indexer := range indexers { + if err := m.GetFieldIndexer().IndexField( + context.Background(), + indexer.obj, + indexer.field, + indexer.extractValue, + ); err != nil { + return nil, fmt.Errorf("failed to setup %s indexer, %w", indexer.field, err) + } + } + return &GenericControllerManager{Manager: m}, nil +} + +// GenericControllerManager is a manager.Manager that allows for registering of controllers. +type GenericControllerManager struct { + manager.Manager +} + +// RegisterControllers registers a set of controllers to the controller manager. +func (m *GenericControllerManager) RegisterControllers(ctx context.Context, controllers ...Controller) Manager { + for _, c := range controllers { + if err := c.Register(ctx, m); err != nil { + panic(err) + } + } + if err := m.AddHealthzCheck("healthz", healthz.Ping); err != nil { + panic(fmt.Sprintf("Failed to add readiness probe, %s", err.Error())) + } + return m +} + +// workerIndex func returns a list of worker addresses from a workflow. +func wokerIndexFunc(obj client.Object) []string { + wf, ok := obj.(*v1alpha1.Workflow) + if !ok { + return nil + } + resp := []string{} + for _, task := range wf.Status.Tasks { + if task.WorkerAddr != "" { + resp = append(resp, task.WorkerAddr) + } + } + return resp +} diff --git a/pkg/controllers/retry.go b/pkg/controllers/retry.go new file mode 100644 index 000000000..0c2e93900 --- /dev/null +++ b/pkg/controllers/retry.go @@ -0,0 +1,17 @@ +package controllers + +import ( + "context" + + "go.uber.org/multierr" + "knative.dev/pkg/logging" + "sigs.k8s.io/controller-runtime/pkg/reconcile" +) + +// RetryIfError logs any errors and requeues if not nil. Supports multierr unwrapping. +func RetryIfError(ctx context.Context, err error) (reconcile.Result, error) { + for _, err := range multierr.Errors(err) { + logging.FromContext(ctx).Errorf("Failed reconciliation, %s", err.Error()) + } + return reconcile.Result{Requeue: err != nil}, nil +} diff --git a/pkg/controllers/template/controller.go b/pkg/controllers/template/controller.go new file mode 100644 index 000000000..7b5f4040e --- /dev/null +++ b/pkg/controllers/template/controller.go @@ -0,0 +1,3 @@ +package template + +// TODO: Create a controller that watches for Templates, validates them, and patches the .spec diff --git a/pkg/controllers/types.go b/pkg/controllers/types.go new file mode 100644 index 000000000..47f1555c7 --- /dev/null +++ b/pkg/controllers/types.go @@ -0,0 +1,25 @@ +package controllers + +import ( + "context" + + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/reconcile" +) + +// Controller is an interface implemented by Karpenter custom resources. +type Controller interface { + // Reconcile hands a hydrated kubernetes resource to the controller for + // reconciliation. Any changes made to the resource's status are persisted + // after Reconcile returns, even if it returns an error. + reconcile.Reconciler + + // Register will register the controller with the manager + Register(context.Context, manager.Manager) error +} + +// Manager manages a set of controllers and webhooks. +type Manager interface { + manager.Manager + RegisterControllers(context.Context, ...Controller) Manager +} diff --git a/pkg/controllers/workflow/controller.go b/pkg/controllers/workflow/controller.go new file mode 100644 index 000000000..f9d21ac6d --- /dev/null +++ b/pkg/controllers/workflow/controller.go @@ -0,0 +1,119 @@ +package workflow + +import ( + "context" + "fmt" + "time" + + "github.com/tinkerbell/tink/pkg/apis/core/v1alpha1" + "github.com/tinkerbell/tink/pkg/controllers" + "github.com/tinkerbell/tink/pkg/convert" + protoworkflow "github.com/tinkerbell/tink/protos/workflow" + tinkworkflow "github.com/tinkerbell/tink/workflow" + "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/api/errors" + "knative.dev/pkg/ptr" + controllerruntime "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/reconcile" +) + +// Controller is a type for managing Workflows. +type Controller struct { + kubeClient client.Client + nowFunc func() time.Time +} + +func NewController(kubeClient client.Client) *Controller { + return &Controller{ + kubeClient: kubeClient, + nowFunc: time.Now, + } +} + +func (c *Controller) Reconcile(ctx context.Context, req reconcile.Request) (reconcile.Result, error) { + stored := &v1alpha1.Workflow{} + if err := c.kubeClient.Get(ctx, req.NamespacedName, stored); err != nil { + if errors.IsNotFound(err) { + return reconcile.Result{}, nil + } + return controllers.RetryIfError(ctx, err) + } + if !stored.DeletionTimestamp.IsZero() { + return reconcile.Result{}, nil + } + wflow := stored.DeepCopy() + + var ( + resp reconcile.Result + err error + ) + switch wflow.Status.State { + case "": + resp, err = c.processNewWorkflow(ctx, wflow) + case protoworkflow.State_name[int32(protoworkflow.State_STATE_RUNNING)]: + resp = c.processRunningWorkflow(ctx, wflow) + } + + // Patch any changes, regardless of errors + if !equality.Semantic.DeepEqual(wflow, stored) { + if perr := c.kubeClient.Status().Patch(ctx, wflow, client.MergeFrom(stored)); perr != nil { + err = fmt.Errorf("error patching workflow %s, %w", wflow.Name, perr) + } + } + return resp, err +} + +func (c *Controller) processNewWorkflow(ctx context.Context, stored *v1alpha1.Workflow) (reconcile.Result, error) { + tpl := &v1alpha1.Template{} + if err := c.kubeClient.Get(ctx, client.ObjectKey{Name: stored.Spec.TemplateRef, Namespace: stored.Namespace}, tpl); err != nil { + if errors.IsNotFound(err) { + return reconcile.Result{}, nil + } + return controllers.RetryIfError(ctx, err) + } + + tinkWf, _, err := tinkworkflow.RenderTemplateHardware(stored.Name, ptr.StringValue(tpl.Spec.Data), stored.Spec.HardwareMap) + if err != nil { + return reconcile.Result{}, err + } + + // populate Task and Action data + convert.WorkflowYAMLToCRD(tinkWf, stored) + stored.Status.State = protoworkflow.State_name[int32(protoworkflow.State_STATE_PENDING)] + return reconcile.Result{}, nil +} + +func (c *Controller) processRunningWorkflow(_ context.Context, stored *v1alpha1.Workflow) reconcile.Result { + // Check for global timeout expiration + if c.nowFunc().After(stored.GetStartTime().Add(time.Duration(stored.Status.GlobalTimeout) * time.Second)) { + stored.Status.State = protoworkflow.State_name[int32(protoworkflow.State_STATE_TIMEOUT)] + } + + // check for any running actions that may have timed out + for ti, task := range stored.Status.Tasks { + for ai, action := range task.Actions { + // A running workflow task action has timed out + if action.Status == protoworkflow.State_name[int32(protoworkflow.State_STATE_RUNNING)] && + action.StartedAt != nil && + c.nowFunc().After(action.StartedAt.Add(time.Duration(action.Timeout)*time.Second)) { + // Set fields on the timed out action + stored.Status.Tasks[ti].Actions[ai].Status = protoworkflow.State_name[int32(protoworkflow.State_STATE_TIMEOUT)] + stored.Status.Tasks[ti].Actions[ai].Message = "Action timed out" + stored.Status.Tasks[ti].Actions[ai].Seconds = int64(c.nowFunc().Sub(action.StartedAt.Time).Seconds()) + // Mark the workflow as timed out + stored.Status.State = protoworkflow.State_name[int32(protoworkflow.State_STATE_TIMEOUT)] + } + } + } + + return reconcile.Result{} +} + +func (c *Controller) Register(_ context.Context, m manager.Manager) error { + return controllerruntime. + NewControllerManagedBy(m). + For(&v1alpha1.Workflow{}). + Complete(c) +} diff --git a/pkg/controllers/workflow/controller_test.go b/pkg/controllers/workflow/controller_test.go new file mode 100644 index 000000000..688a6151e --- /dev/null +++ b/pkg/controllers/workflow/controller_test.go @@ -0,0 +1,711 @@ +package workflow + +import ( + "context" + "errors" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/tinkerbell/tink/pkg/apis/core/v1alpha1" + "github.com/tinkerbell/tink/pkg/internal/tests" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/controller-runtime/pkg/reconcile" +) + +var runtimescheme = runtime.NewScheme() + +// TestTime is a static time that can be used for testing. +var TestTime = tests.NewFrozenTimeUnix(1637361793) + +func init() { + _ = clientgoscheme.AddToScheme(runtimescheme) + _ = v1alpha1.AddToScheme(runtimescheme) +} + +func GetFakeClientBuilder() *fake.ClientBuilder { + return fake.NewClientBuilder().WithScheme( + runtimescheme, + ).WithRuntimeObjects( + &v1alpha1.Hardware{}, &v1alpha1.Template{}, &v1alpha1.Workflow{}, + ) +} + +var minimalTemplate = `version: "0.1" +name: debian +global_timeout: 1800 +tasks: + - name: "os-installation" + worker: "{{.device_1}}" + volumes: + - /dev:/dev + - /dev/console:/dev/console + - /lib/firmware:/lib/firmware:ro + actions: + - name: "stream-debian-image" + image: quay.io/tinkerbell-actions/image2disk:v1.0.0 + timeout: 600 + environment: + DEST_DISK: /dev/nvme0n1 + # Hegel IP + IMG_URL: "http://10.1.1.11:8080/debian-10-openstack-amd64.raw.gz" + COMPRESSED: true` + +func TestReconcile(t *testing.T) { + cases := []struct { + name string + seedTemplate *v1alpha1.Template + seedWorkflow *v1alpha1.Workflow + seedHardware *v1alpha1.Hardware + req reconcile.Request + want reconcile.Result + wantWflow *v1alpha1.Workflow + wantErr error + }{ + { + name: "DoesNotExist", + req: reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: "notreal", + Namespace: "default", + }, + }, + want: reconcile.Result{}, + wantWflow: &v1alpha1.Workflow{ + TypeMeta: metav1.TypeMeta{ + Kind: "Workflow", + APIVersion: "tinkerbell.org/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + ResourceVersion: "999", + }, + }, + wantErr: nil, + }, + // ************** + { + name: "NewWorkflow", + seedTemplate: &v1alpha1.Template{ + TypeMeta: metav1.TypeMeta{ + Kind: "Template", + APIVersion: "tinkerbell.org/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "debian", + Namespace: "default", + }, + Spec: v1alpha1.TemplateSpec{ + Data: &minimalTemplate, + }, + Status: v1alpha1.TemplateStatus{}, + }, + seedWorkflow: &v1alpha1.Workflow{ + TypeMeta: metav1.TypeMeta{ + Kind: "Workflow", + APIVersion: "tinkerbell.org/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "debian", + Namespace: "default", + }, + Spec: v1alpha1.WorkflowSpec{ + TemplateRef: "debian", + HardwareMap: map[string]string{ + "device_1": "3c:ec:ef:4c:4f:54", + }, + }, + Status: v1alpha1.WorkflowStatus{}, + }, + seedHardware: &v1alpha1.Hardware{ + TypeMeta: metav1.TypeMeta{ + Kind: "Hardware", + APIVersion: "tinkerbell.org/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "machine1", + Namespace: "default", + }, + Spec: v1alpha1.HardwareSpec{ + Interfaces: []v1alpha1.Interface{ + { + Netboot: &v1alpha1.Netboot{ + AllowPXE: &[]bool{true}[0], + AllowWorkflow: &[]bool{true}[0], + }, + DHCP: &v1alpha1.DHCP{ + Arch: "x86_64", + Hostname: "sm01", + IP: &v1alpha1.IP{ + Address: "172.16.10.100", + Gateway: "172.16.10.1", + Netmask: "255.255.255.0", + }, + LeaseTime: 86400, + MAC: "3c:ec:ef:4c:4f:54", + NameServers: []string{}, + UEFI: true, + }, + }, + }, + }, + }, + req: reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: "debian", + Namespace: "default", + }, + }, + want: reconcile.Result{}, + wantWflow: &v1alpha1.Workflow{ + TypeMeta: metav1.TypeMeta{ + Kind: "Workflow", + APIVersion: "tinkerbell.org/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + ResourceVersion: "1000", + Name: "debian", + Namespace: "default", + }, + Spec: v1alpha1.WorkflowSpec{ + TemplateRef: "debian", + HardwareMap: map[string]string{ + "device_1": "3c:ec:ef:4c:4f:54", + }, + }, + Status: v1alpha1.WorkflowStatus{ + State: "STATE_PENDING", + GlobalTimeout: 1800, + Tasks: []v1alpha1.Task{ + { + Name: "os-installation", + + WorkerAddr: "3c:ec:ef:4c:4f:54", + Volumes: []string{ + "/dev:/dev", + "/dev/console:/dev/console", + "/lib/firmware:/lib/firmware:ro", + }, + Actions: []v1alpha1.Action{ + { + Name: "stream-debian-image", + Image: "quay.io/tinkerbell-actions/image2disk:v1.0.0", + Timeout: 600, + Environment: map[string]string{ + "COMPRESSED": "true", + "DEST_DISK": "/dev/nvme0n1", + "IMG_URL": "http://10.1.1.11:8080/debian-10-openstack-amd64.raw.gz", + }, + Status: "STATE_PENDING", + }, + }, + }, + }, + }, + }, + wantErr: nil, + }, + // ***** + { + name: "MalformedWorkflow", + seedTemplate: &v1alpha1.Template{ + TypeMeta: metav1.TypeMeta{ + Kind: "Template", + APIVersion: "tinkerbell.org/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "debian", + Namespace: "default", + }, + Spec: v1alpha1.TemplateSpec{ + Data: &[]string{`version: "0.1" + name: debian +global_timeout: 1800 +tasks: + - name: "os-installation" + worker: "{{.device_1}}"`}[0], + }, + Status: v1alpha1.TemplateStatus{}, + }, + seedWorkflow: &v1alpha1.Workflow{ + TypeMeta: metav1.TypeMeta{ + Kind: "Workflow", + APIVersion: "tinkerbell.org/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "debian", + Namespace: "default", + }, + Spec: v1alpha1.WorkflowSpec{ + TemplateRef: "debian", + HardwareMap: map[string]string{ + "device_1": "3c:ec:ef:4c:4f:54", + }, + }, + Status: v1alpha1.WorkflowStatus{}, + }, + seedHardware: &v1alpha1.Hardware{ + TypeMeta: metav1.TypeMeta{ + Kind: "Hardware", + APIVersion: "tinkerbell.org/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "machine1", + Namespace: "default", + }, + Spec: v1alpha1.HardwareSpec{ + Interfaces: []v1alpha1.Interface{ + { + Netboot: &v1alpha1.Netboot{ + AllowPXE: &[]bool{true}[0], + AllowWorkflow: &[]bool{true}[0], + }, + DHCP: &v1alpha1.DHCP{ + Arch: "x86_64", + Hostname: "sm01", + IP: &v1alpha1.IP{ + Address: "172.16.10.100", + Gateway: "172.16.10.1", + Netmask: "255.255.255.0", + }, + LeaseTime: 86400, + MAC: "3c:ec:ef:4c:4f:54", + NameServers: []string{}, + UEFI: true, + }, + }, + }, + }, + }, + req: reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: "debian", + Namespace: "default", + }, + }, + want: reconcile.Result{}, + wantWflow: &v1alpha1.Workflow{ + TypeMeta: metav1.TypeMeta{ + Kind: "Workflow", + APIVersion: "tinkerbell.org/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + ResourceVersion: "1000", + Name: "debian", + Namespace: "default", + }, + Spec: v1alpha1.WorkflowSpec{ + TemplateRef: "debian", + HardwareMap: map[string]string{ + "device_1": "3c:ec:ef:4c:4f:54", + }, + }, + Status: v1alpha1.WorkflowStatus{ + State: "STATE_PENDING", + GlobalTimeout: 1800, + Tasks: []v1alpha1.Task{ + { + Name: "os-installation", + + WorkerAddr: "3c:ec:ef:4c:4f:54", + Volumes: []string{ + "/dev:/dev", + "/dev/console:/dev/console", + "/lib/firmware:/lib/firmware:ro", + }, + Actions: []v1alpha1.Action{ + { + Name: "stream-debian-image", + Image: "quay.io/tinkerbell-actions/image2disk:v1.0.0", + Timeout: 600, + Environment: map[string]string{ + "COMPRESSED": "true", + "DEST_DISK": "/dev/nvme0n1", + "IMG_URL": "http://10.1.1.11:8080/debian-10-openstack-amd64.raw.gz", + }, + Status: "STATE_PENDING", + }, + }, + }, + }, + }, + }, + wantErr: errors.New("parsing yaml data: yaml: line 2: found character that cannot start any token"), + }, + // ***** + { + name: "Missing Template", + seedTemplate: &v1alpha1.Template{ + TypeMeta: metav1.TypeMeta{ + Kind: "Template", + APIVersion: "tinkerbell.org/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "dummy", + Namespace: "default", + }, + Spec: v1alpha1.TemplateSpec{}, + Status: v1alpha1.TemplateStatus{}, + }, + seedWorkflow: &v1alpha1.Workflow{ + TypeMeta: metav1.TypeMeta{ + Kind: "Workflow", + APIVersion: "tinkerbell.org/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "debian", + Namespace: "default", + }, + Spec: v1alpha1.WorkflowSpec{ + TemplateRef: "debian", // doesn't exist + HardwareMap: map[string]string{ + "device_1": "3c:ec:ef:4c:4f:54", + }, + }, + Status: v1alpha1.WorkflowStatus{}, + }, + seedHardware: &v1alpha1.Hardware{ + TypeMeta: metav1.TypeMeta{ + Kind: "Hardware", + APIVersion: "tinkerbell.org/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "machine1", + Namespace: "default", + }, + Spec: v1alpha1.HardwareSpec{ + Interfaces: []v1alpha1.Interface{ + { + Netboot: &v1alpha1.Netboot{ + AllowPXE: &[]bool{true}[0], + AllowWorkflow: &[]bool{true}[0], + }, + DHCP: &v1alpha1.DHCP{ + Arch: "x86_64", + Hostname: "sm01", + IP: &v1alpha1.IP{ + Address: "172.16.10.100", + Gateway: "172.16.10.1", + Netmask: "255.255.255.0", + }, + LeaseTime: 86400, + MAC: "3c:ec:ef:4c:4f:54", + NameServers: []string{}, + UEFI: true, + }, + }, + }, + }, + }, + req: reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: "debian", + Namespace: "default", + }, + }, + want: reconcile.Result{}, + wantWflow: &v1alpha1.Workflow{ + TypeMeta: metav1.TypeMeta{ + Kind: "Workflow", + APIVersion: "tinkerbell.org/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + ResourceVersion: "999", + }, + }, + wantErr: nil, + }, + // ***** + { + name: "Deleted Workflow", + seedTemplate: &v1alpha1.Template{ + TypeMeta: metav1.TypeMeta{ + Kind: "Template", + APIVersion: "tinkerbell.org/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "debian", + Namespace: "default", + }, + Spec: v1alpha1.TemplateSpec{}, + Status: v1alpha1.TemplateStatus{}, + }, + seedWorkflow: &v1alpha1.Workflow{ + TypeMeta: metav1.TypeMeta{ + Kind: "Workflow", + APIVersion: "tinkerbell.org/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "debian", + Namespace: "default", + DeletionTimestamp: func() *metav1.Time { + t := metav1.NewTime(TestTime.BeforeSec(5)) + return &t + }(), + }, + Spec: v1alpha1.WorkflowSpec{ + TemplateRef: "debian", // doesn't exist + HardwareMap: map[string]string{ + "device_1": "3c:ec:ef:4c:4f:54", + }, + }, + Status: v1alpha1.WorkflowStatus{}, + }, + seedHardware: &v1alpha1.Hardware{ + TypeMeta: metav1.TypeMeta{ + Kind: "Hardware", + APIVersion: "tinkerbell.org/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "machine1", + Namespace: "default", + }, + Spec: v1alpha1.HardwareSpec{ + Interfaces: []v1alpha1.Interface{ + { + Netboot: &v1alpha1.Netboot{ + AllowPXE: &[]bool{true}[0], + AllowWorkflow: &[]bool{true}[0], + }, + DHCP: &v1alpha1.DHCP{ + Arch: "x86_64", + Hostname: "sm01", + IP: &v1alpha1.IP{ + Address: "172.16.10.100", + Gateway: "172.16.10.1", + Netmask: "255.255.255.0", + }, + LeaseTime: 86400, + MAC: "3c:ec:ef:4c:4f:54", + NameServers: []string{}, + UEFI: true, + }, + }, + }, + }, + }, + req: reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: "debian", + Namespace: "default", + }, + }, + want: reconcile.Result{}, + wantWflow: &v1alpha1.Workflow{ + TypeMeta: metav1.TypeMeta{ + Kind: "Workflow", + APIVersion: "tinkerbell.org/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + ResourceVersion: "999", + }, + }, + wantErr: nil, + }, + // ************** + { + name: "Timed Out workflow Workflow", + seedTemplate: &v1alpha1.Template{ + TypeMeta: metav1.TypeMeta{ + Kind: "Template", + APIVersion: "tinkerbell.org/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "debian", + Namespace: "default", + }, + Spec: v1alpha1.TemplateSpec{ + Data: &minimalTemplate, + }, + Status: v1alpha1.TemplateStatus{}, + }, + seedWorkflow: &v1alpha1.Workflow{ + TypeMeta: metav1.TypeMeta{ + Kind: "Workflow", + APIVersion: "tinkerbell.org/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "debian", + Namespace: "default", + }, + Spec: v1alpha1.WorkflowSpec{ + TemplateRef: "debian", + HardwareMap: map[string]string{ + "device_1": "3c:ec:ef:4c:4f:54", + }, + }, + Status: v1alpha1.WorkflowStatus{ + State: "STATE_RUNNING", + GlobalTimeout: 600, + Tasks: []v1alpha1.Task{ + { + Name: "os-installation", + WorkerAddr: "3c:ec:ef:4c:4f:54", + Volumes: []string{ + "/dev:/dev", + "/dev/console:/dev/console", + "/lib/firmware:/lib/firmware:ro", + }, + Actions: []v1alpha1.Action{ + { + Name: "stream-debian-image", + Image: "quay.io/tinkerbell-actions/image2disk:v1.0.0", + Timeout: 60, + Environment: map[string]string{ + "COMPRESSED": "true", + "DEST_DISK": "/dev/nvme0n1", + "IMG_URL": "http://10.1.1.11:8080/debian-10-openstack-amd64.raw.gz", + }, + Status: "STATE_RUNNING", + StartedAt: TestTime.MetaV1BeforeSec(601), + }, + }, + }, + }, + }, + }, + seedHardware: &v1alpha1.Hardware{ + TypeMeta: metav1.TypeMeta{ + Kind: "Hardware", + APIVersion: "tinkerbell.org/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "machine1", + Namespace: "default", + }, + Spec: v1alpha1.HardwareSpec{ + Interfaces: []v1alpha1.Interface{ + { + Netboot: &v1alpha1.Netboot{ + AllowPXE: &[]bool{true}[0], + AllowWorkflow: &[]bool{true}[0], + }, + DHCP: &v1alpha1.DHCP{ + Arch: "x86_64", + Hostname: "sm01", + IP: &v1alpha1.IP{ + Address: "172.16.10.100", + Gateway: "172.16.10.1", + Netmask: "255.255.255.0", + }, + LeaseTime: 86400, + MAC: "3c:ec:ef:4c:4f:54", + NameServers: []string{}, + UEFI: true, + }, + }, + }, + }, + }, + req: reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: "debian", + Namespace: "default", + }, + }, + want: reconcile.Result{}, + wantWflow: &v1alpha1.Workflow{ + TypeMeta: metav1.TypeMeta{ + Kind: "Workflow", + APIVersion: "tinkerbell.org/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + ResourceVersion: "1000", + Name: "debian", + Namespace: "default", + }, + Spec: v1alpha1.WorkflowSpec{ + TemplateRef: "debian", + HardwareMap: map[string]string{ + "device_1": "3c:ec:ef:4c:4f:54", + }, + }, + Status: v1alpha1.WorkflowStatus{ + State: "STATE_TIMEOUT", + GlobalTimeout: 600, + Tasks: []v1alpha1.Task{ + { + Name: "os-installation", + WorkerAddr: "3c:ec:ef:4c:4f:54", + Volumes: []string{ + "/dev:/dev", + "/dev/console:/dev/console", + "/lib/firmware:/lib/firmware:ro", + }, + Actions: []v1alpha1.Action{ + { + Name: "stream-debian-image", + Image: "quay.io/tinkerbell-actions/image2disk:v1.0.0", + Timeout: 60, + Environment: map[string]string{ + "COMPRESSED": "true", + "DEST_DISK": "/dev/nvme0n1", + "IMG_URL": "http://10.1.1.11:8080/debian-10-openstack-amd64.raw.gz", + }, + Status: "STATE_TIMEOUT", + StartedAt: TestTime.MetaV1BeforeSec(601), + Seconds: 601, + Message: "Action timed out", + }, + }, + }, + }, + }, + }, + wantErr: nil, + }, + } + + for _, tc := range cases { + kc := GetFakeClientBuilder() + if tc.seedHardware != nil { + kc = kc.WithObjects(tc.seedHardware) + } + if tc.seedTemplate != nil { + kc = kc.WithObjects(tc.seedTemplate) + } + if tc.seedWorkflow != nil { + kc = kc.WithObjects(tc.seedWorkflow) + } + controller := &Controller{ + kubeClient: kc.Build(), + nowFunc: TestTime.Now, + } + + t.Run(tc.name, func(t *testing.T) { + got, gotErr := controller.Reconcile(context.Background(), tc.req) + if gotErr != nil { + if tc.wantErr == nil { + t.Errorf(`Got unexpected error: %v"`, gotErr) + } else if gotErr.Error() != tc.wantErr.Error() { + t.Errorf(`Got unexpected error: got "%v" wanted "%v"`, gotErr, tc.wantErr) + } + return + } + if gotErr == nil && tc.wantErr != nil { + t.Errorf("Missing expected error: %v", tc.wantErr) + return + } + if tc.want != got { + t.Errorf("Got unexpected result. Wanted %v, got %v", tc.want, got) + // Don't return, also check the modified object + } + wflow := &v1alpha1.Workflow{} + err := controller.kubeClient.Get( + context.Background(), + client.ObjectKey{Name: tc.wantWflow.Name, Namespace: tc.wantWflow.Namespace}, + wflow) + if err != nil { + t.Errorf("Error finding desired workflow: %v", err) + return + } + + if diff := cmp.Diff(tc.wantWflow, wflow); diff != "" { + t.Errorf("unexpected difference:\n%v", diff) + } + }) + } +} diff --git a/pkg/convert/template.go b/pkg/convert/template.go new file mode 100644 index 000000000..79b29f8b2 --- /dev/null +++ b/pkg/convert/template.go @@ -0,0 +1,63 @@ +package convert + +import ( + "github.com/tinkerbell/tink/pkg/apis/core/v1alpha1" + prototemplate "github.com/tinkerbell/tink/protos/template" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func metav1ToTimestamppb(in *metav1.Time) *timestamppb.Timestamp { + if in == nil { + return nil + } + return timestamppb.New(in.Time) +} + +// TemplateCRDToProto converts a K8s Template to a tinkerbell WorkflowTemplate. +func TemplateCRDToProto(t *v1alpha1.Template) *prototemplate.WorkflowTemplate { + if t == nil { + return nil + } + data := "" + if t.Spec.Data != nil { + data = *t.Spec.Data + } + return &prototemplate.WorkflowTemplate{ + Id: t.TinkID(), + Name: t.Name, + CreatedAt: timestamppb.New(t.CreationTimestamp.Time), + DeletedAt: metav1ToTimestamppb(t.DeletionTimestamp), + Data: data, + } +} + +// TemplateProtoToCRD converts a tinkerbell WorkflowTemplate to a K8s Template. +func TemplateProtoToCRD(t *prototemplate.WorkflowTemplate) *v1alpha1.Template { + if t == nil { + return nil + } + return &v1alpha1.Template{ + TypeMeta: metav1.TypeMeta{ + Kind: "Template", + APIVersion: "tinkerbell.org/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: t.Name, + Annotations: map[string]string{ + "template.tinkerbell.org/id": t.Id, + }, + CreationTimestamp: metav1.NewTime(t.CreatedAt.AsTime()), + DeletionTimestamp: func() *metav1.Time { + if t.DeletedAt != nil { + resp := metav1.NewTime(t.DeletedAt.AsTime()) + return &resp + } + return nil + }(), + }, + Spec: v1alpha1.TemplateSpec{ + Data: &t.Data, + }, + } +} diff --git a/pkg/convert/template_test.go b/pkg/convert/template_test.go new file mode 100644 index 000000000..74f016216 --- /dev/null +++ b/pkg/convert/template_test.go @@ -0,0 +1,215 @@ +package convert + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/tinkerbell/tink/pkg/apis/core/v1alpha1" + "github.com/tinkerbell/tink/pkg/internal/tests" + prototemplate "github.com/tinkerbell/tink/protos/template" + "google.golang.org/protobuf/testing/protocmp" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// TestTime is a static time that can be used for testing. +var TestTime = tests.NewFrozenTimeUnix(1637361794) + +func TestTemplateCRDToProto(t *testing.T) { + cases := []struct { + name string + input *v1alpha1.Template + want *prototemplate.WorkflowTemplate + }{ + { + "Full Example", + &v1alpha1.Template{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: "template1", + Annotations: map[string]string{ + "template.tinkerbell.org/id": "7d9031ee-18d4-4ba4-b934-c3a78a1330f6", + }, + CreationTimestamp: *TestTime.MetaV1Now(), + DeletionTimestamp: TestTime.MetaV1AfterSec(600), + }, + Spec: v1alpha1.TemplateSpec{ + Data: func() *string { + resp := `version: "0.1"` + return &resp + }(), + }, + }, + &prototemplate.WorkflowTemplate{ + Id: "7d9031ee-18d4-4ba4-b934-c3a78a1330f6", + Name: "template1", + CreatedAt: TestTime.PbNow(), + DeletedAt: TestTime.PbAfterSec(600), + Data: `version: "0.1"`, + }, + }, + { + "No DeletionTime", + &v1alpha1.Template{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: "template1", + Annotations: map[string]string{ + "template.tinkerbell.org/id": "7d9031ee-18d4-4ba4-b934-c3a78a1330f6", + }, + CreationTimestamp: *TestTime.MetaV1Now(), + }, + Spec: v1alpha1.TemplateSpec{ + Data: func() *string { + resp := `version: "0.1"` + return &resp + }(), + }, + }, + &prototemplate.WorkflowTemplate{ + Id: "7d9031ee-18d4-4ba4-b934-c3a78a1330f6", + Name: "template1", + CreatedAt: TestTime.PbNow(), + Data: `version: "0.1"`, + }, + }, + { + "No Annotation or data", + &v1alpha1.Template{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: "template1", + CreationTimestamp: *TestTime.MetaV1Now(), + }, + Spec: v1alpha1.TemplateSpec{ + Data: nil, + }, + }, + &prototemplate.WorkflowTemplate{ + Id: "", + Name: "template1", + CreatedAt: TestTime.PbNow(), + Data: "", + }, + }, + { + "Empty", + nil, + nil, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + got := TemplateCRDToProto(tc.input) + if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" { + t.Errorf("unexpected difference:\n%v", diff) + } + }) + } +} + +func TestTemplateProtoToCRD(t *testing.T) { + cases := []struct { + name string + input *prototemplate.WorkflowTemplate + want *v1alpha1.Template + }{ + { + "Full Example", + &prototemplate.WorkflowTemplate{ + Id: "7d9031ee-18d4-4ba4-b934-c3a78a1330f6", + Name: "template1", + CreatedAt: TestTime.PbNow(), + DeletedAt: TestTime.PbAfterSec(600), + Data: `version: "0.1"`, + }, + &v1alpha1.Template{ + TypeMeta: metav1.TypeMeta{ + Kind: "Template", + APIVersion: "tinkerbell.org/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "template1", + Annotations: map[string]string{ + "template.tinkerbell.org/id": "7d9031ee-18d4-4ba4-b934-c3a78a1330f6", + }, + CreationTimestamp: *TestTime.MetaV1Now(), + DeletionTimestamp: TestTime.MetaV1AfterSec(600), + }, + Spec: v1alpha1.TemplateSpec{ + Data: func() *string { + resp := `version: "0.1"` + return &resp + }(), + }, + }, + }, + { + "No DeletionTime", + &prototemplate.WorkflowTemplate{ + Id: "7d9031ee-18d4-4ba4-b934-c3a78a1330f6", + Name: "template1", + CreatedAt: TestTime.PbNow(), + Data: `version: "0.1"`, + }, + &v1alpha1.Template{ + TypeMeta: metav1.TypeMeta{ + Kind: "Template", + APIVersion: "tinkerbell.org/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "template1", + Annotations: map[string]string{ + "template.tinkerbell.org/id": "7d9031ee-18d4-4ba4-b934-c3a78a1330f6", + }, + CreationTimestamp: *TestTime.MetaV1Now(), + }, + Spec: v1alpha1.TemplateSpec{ + Data: func() *string { + resp := `version: "0.1"` + return &resp + }(), + }, + }, + }, + { + "No id or data", + &prototemplate.WorkflowTemplate{ + Id: "", + Name: "template1", + CreatedAt: TestTime.PbNow(), + Data: "", + }, + &v1alpha1.Template{ + TypeMeta: metav1.TypeMeta{ + Kind: "Template", + APIVersion: "tinkerbell.org/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "template1", + Annotations: map[string]string{ + "template.tinkerbell.org/id": "", + }, + CreationTimestamp: *TestTime.MetaV1Now(), + }, + Spec: v1alpha1.TemplateSpec{ + Data: func(s string) *string { return &s }(""), + }, + }, + }, + { + "Empty", + nil, + nil, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + got := TemplateProtoToCRD(tc.input) + if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" { + t.Errorf("unexpected difference:\n%v", diff) + } + }) + } +} diff --git a/pkg/convert/workflow.go b/pkg/convert/workflow.go new file mode 100644 index 000000000..b211d491b --- /dev/null +++ b/pkg/convert/workflow.go @@ -0,0 +1,128 @@ +package convert + +import ( + "fmt" + "sort" + + "github.com/tinkerbell/tink/pkg/apis/core/v1alpha1" + protoworkflow "github.com/tinkerbell/tink/protos/workflow" + "github.com/tinkerbell/tink/workflow" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func WorkflowYAMLToCRD(wf *workflow.Workflow, crd *v1alpha1.Workflow) { + if wf == nil || crd == nil { + return + } + + tasks := []v1alpha1.Task{} + for _, task := range wf.Tasks { + actions := []v1alpha1.Action{} + for _, action := range task.Actions { + actions = append(actions, v1alpha1.Action{ + Name: action.Name, + Image: action.Image, + Timeout: action.Timeout, + Command: action.Command, + Volumes: action.Volumes, + Status: protoworkflow.State_name[int32(protoworkflow.State_STATE_PENDING)], + Environment: action.Environment, + Pid: action.Pid, + }) + } + tasks = append(tasks, v1alpha1.Task{ + Name: task.Name, + WorkerAddr: task.WorkerAddr, + Volumes: task.Volumes, + Environment: task.Environment, + Actions: actions, + }) + } + crd.Status.GlobalTimeout = int64(wf.GlobalTimeout) + crd.Status.Tasks = tasks +} + +func WorkflowCRDToProto(w *v1alpha1.Workflow) *protoworkflow.Workflow { + if w == nil { + return nil + } + v, ok := protoworkflow.State_value[w.Status.State] + state := protoworkflow.State(v) + if !ok { + state = protoworkflow.State_STATE_PENDING + } + return &protoworkflow.Workflow{ + Id: w.TinkID(), + Template: w.Spec.TemplateRef, + State: state, + CreatedAt: timestamppb.New(w.CreationTimestamp.Time), + DeletedAt: metav1ToTimestamppb(w.DeletionTimestamp), + } +} + +func WorkflowActionListCRDToProto(wf *v1alpha1.Workflow) *protoworkflow.WorkflowActionList { + if wf == nil { + return nil + } + resp := &protoworkflow.WorkflowActionList{ + ActionList: []*protoworkflow.WorkflowAction{}, + } + for _, task := range wf.Status.Tasks { + for _, action := range task.Actions { + resp.ActionList = append(resp.ActionList, &protoworkflow.WorkflowAction{ + TaskName: task.Name, + Name: action.Name, + Image: action.Image, + Timeout: action.Timeout, + Command: action.Command, + WorkerId: task.WorkerAddr, + Volumes: append(task.Volumes, action.Volumes...), + // TODO: (micahhausler) Dedupe task volume targets overridden in the action volumes? + // Also not sure how Docker handles nested mounts (ex: "/foo:/foo" and "/bar:/foo/bar") + Environment: func(env map[string]string) []string { + resp := []string{} + merged := map[string]string{} + for k, v := range env { + merged[k] = v + } + for k, v := range action.Environment { + merged[k] = v + } + for k, v := range merged { + resp = append(resp, fmt.Sprintf("%s=%s", k, v)) + } + sort.Strings(resp) + return resp + }(task.Environment), + Pid: action.Pid, + }) + } + } + return resp +} + +func WorkflowProtoToCRD(w *protoworkflow.Workflow) *v1alpha1.Workflow { + if w == nil { + return nil + } + resp := &v1alpha1.Workflow{ + TypeMeta: metav1.TypeMeta{ + Kind: "Workflow", + APIVersion: "tinkerbell.org/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + v1alpha1.WorkflowIDAnnotation: w.Id, + }, + CreationTimestamp: metav1.NewTime(w.CreatedAt.AsTime()), + }, + Spec: v1alpha1.WorkflowSpec{}, + Status: v1alpha1.WorkflowStatus{}, + } + + if v, ok := protoworkflow.State_name[int32(w.State)]; ok { + resp.Status.State = v + } + return resp +} diff --git a/pkg/convert/workflow_test.go b/pkg/convert/workflow_test.go new file mode 100644 index 000000000..d712791a4 --- /dev/null +++ b/pkg/convert/workflow_test.go @@ -0,0 +1,434 @@ +package convert + +import ( + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/tinkerbell/tink/pkg/apis/core/v1alpha1" + protoworkflow "github.com/tinkerbell/tink/protos/workflow" + "github.com/tinkerbell/tink/workflow" + "google.golang.org/protobuf/testing/protocmp" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestWorkflowCRDToProto(t *testing.T) { + cases := []struct { + name string + input *v1alpha1.Workflow + want *protoworkflow.Workflow + }{ + { + "nil arg", + nil, + nil, + }, + { + "empty workflow", + &v1alpha1.Workflow{ + TypeMeta: metav1.TypeMeta{ + Kind: "Workflow", + APIVersion: "tinkerbell.org/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "wf1", + CreationTimestamp: *TestTime.MetaV1Now(), + }, + Spec: v1alpha1.WorkflowSpec{}, + Status: v1alpha1.WorkflowStatus{}, + }, + &protoworkflow.Workflow{ + Id: "", + Template: "", + State: protoworkflow.State_STATE_PENDING, + CreatedAt: TestTime.PbNow(), + }, + }, + { + "full workflow", + &v1alpha1.Workflow{ + TypeMeta: metav1.TypeMeta{ + Kind: "Workflow", + APIVersion: "tinkerbell.org/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "wf1", + Annotations: map[string]string{ + "workflow.tinkerbell.org/id": "7d9031ee-18d4-4ba4-b934-c3a78a1330f6", + }, + CreationTimestamp: *TestTime.MetaV1Now(), + }, + Spec: v1alpha1.WorkflowSpec{ + TemplateRef: "MyCoolWorkflow", + }, + Status: v1alpha1.WorkflowStatus{ + State: "STATE_SUCCESS", + }, + }, + &protoworkflow.Workflow{ + Id: "7d9031ee-18d4-4ba4-b934-c3a78a1330f6", + Template: "MyCoolWorkflow", + State: protoworkflow.State_STATE_SUCCESS, + CreatedAt: TestTime.PbNow(), + }, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + got := WorkflowCRDToProto(tc.input) + if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" { + t.Errorf("unexpected difference:\n%v", diff) + } + }) + } +} + +func TestWorkflowActionListCRDToProto(t *testing.T) { + cases := []struct { + name string + input *v1alpha1.Workflow + want *protoworkflow.WorkflowActionList + }{ + { + "nil arg", + nil, + nil, + }, + { + "empty workflow", + &v1alpha1.Workflow{ + TypeMeta: metav1.TypeMeta{ + Kind: "Workflow", + APIVersion: "tinkerbell.org/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "wf1", + CreationTimestamp: *TestTime.MetaV1Now(), + }, + Spec: v1alpha1.WorkflowSpec{}, + Status: v1alpha1.WorkflowStatus{}, + }, + &protoworkflow.WorkflowActionList{ + ActionList: []*protoworkflow.WorkflowAction{}, + }, + }, + { + "full workflow", + &v1alpha1.Workflow{ + TypeMeta: metav1.TypeMeta{ + Kind: "Workflow", + APIVersion: "tinkerbell.org/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "wf1", + Annotations: map[string]string{ + "workflow.tinkerbell.org/id": "7d9031ee-18d4-4ba4-b934-c3a78a1330f6", + }, + CreationTimestamp: *TestTime.MetaV1Now(), + }, + Spec: v1alpha1.WorkflowSpec{ + TemplateRef: "MyCoolWorkflow", + }, + Status: v1alpha1.WorkflowStatus{ + State: "STATE_SUCCESS", + Tasks: []v1alpha1.Task{ + { + Name: "worker1", + WorkerAddr: "00:00:53:00:53:F4", + Actions: []v1alpha1.Action{ + { + Name: "stream-debian-image", + Timeout: 600, + Image: "quay.io/tinkerbell-actions/image2disk:v1.0.0", + Environment: map[string]string{ + "DEST_DISK": "/dev/nvme0n1", + "IMG_URL": "http://10.1.1.11:8080/debian-10-openstack-amd64.raw.gz", + "COMPRESSED": "true", + "GODEBUG": "", + }, + Volumes: []string{ + "/tmp/debug:/tmp/debug", + }, + }, + { + Name: "kexec", + Image: "quay.io/tinkerbell-actions/kexec:v1.0.1", + Timeout: 90, + Pid: "host", + Environment: map[string]string{ + "FS_TYPE": "ext4", + "BLOCK_DEVICE": "/dev/nvme0n1p1", + }, + }, + }, + Volumes: []string{ + "/dev:/dev", + "/dev/console:/dev/console", + "/lib/firmware:/lib/firmware:ro", + }, + Environment: map[string]string{ + "GODEBUG": "http2debug=1", + "GOGC": "100", + }, + }, + }, + }, + }, + &protoworkflow.WorkflowActionList{ + ActionList: []*protoworkflow.WorkflowAction{ + { + TaskName: "worker1", + Name: "stream-debian-image", + Image: "quay.io/tinkerbell-actions/image2disk:v1.0.0", + Timeout: 600, + WorkerId: "00:00:53:00:53:F4", + Environment: []string{ + "COMPRESSED=true", + "DEST_DISK=/dev/nvme0n1", + "GODEBUG=", + "GOGC=100", + "IMG_URL=http://10.1.1.11:8080/debian-10-openstack-amd64.raw.gz", + }, + Volumes: []string{ + "/dev:/dev", + "/dev/console:/dev/console", + "/lib/firmware:/lib/firmware:ro", + "/tmp/debug:/tmp/debug", + }, + }, + { + TaskName: "worker1", + Name: "kexec", + Image: "quay.io/tinkerbell-actions/kexec:v1.0.1", + Timeout: 90, + WorkerId: "00:00:53:00:53:F4", + Environment: []string{ + "BLOCK_DEVICE=/dev/nvme0n1p1", + "FS_TYPE=ext4", + "GODEBUG=http2debug=1", + "GOGC=100", + }, + Pid: "host", + Volumes: []string{ + "/dev:/dev", + "/dev/console:/dev/console", + "/lib/firmware:/lib/firmware:ro", + }, + }, + }, + }, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + got := WorkflowActionListCRDToProto(tc.input) + if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" { + t.Errorf("unexpected difference:\n%v", diff) + } + }) + } +} + +func TestWorkflowProtoToCRD(t *testing.T) { + cases := []struct { + name string + input *protoworkflow.Workflow + want *v1alpha1.Workflow + }{ + { + "nil arg", + nil, + nil, + }, + { + "empty workflow", + &protoworkflow.Workflow{ + Id: "", + Template: "", + // State: protoworkflow.State_STATE_PENDING, + CreatedAt: timestamppb.New(time.Unix(1637361794, 0)), + }, + &v1alpha1.Workflow{ + TypeMeta: metav1.TypeMeta{ + Kind: "Workflow", + APIVersion: "tinkerbell.org/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{"workflow.tinkerbell.org/id": ""}, + CreationTimestamp: metav1.NewTime(time.Unix(1637361794, 0)), + }, + Spec: v1alpha1.WorkflowSpec{}, + Status: v1alpha1.WorkflowStatus{ + State: "STATE_PENDING", + }, + }, + }, + { + "full workflow", + &protoworkflow.Workflow{ + Id: "7d9031ee-18d4-4ba4-b934-c3a78a1330f6", + Template: "MyCoolWorkflow", + State: protoworkflow.State_STATE_SUCCESS, + CreatedAt: timestamppb.New(time.Unix(1637361794, 0)), + }, + &v1alpha1.Workflow{ + TypeMeta: metav1.TypeMeta{ + Kind: "Workflow", + APIVersion: "tinkerbell.org/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + + Annotations: map[string]string{ + "workflow.tinkerbell.org/id": "7d9031ee-18d4-4ba4-b934-c3a78a1330f6", + }, + CreationTimestamp: metav1.NewTime(time.Unix(1637361794, 0)), + }, + Spec: v1alpha1.WorkflowSpec{}, + Status: v1alpha1.WorkflowStatus{ + State: "STATE_SUCCESS", + }, + }, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + got := WorkflowProtoToCRD(tc.input) + if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" { + t.Errorf("unexpected difference:\n%v", diff) + } + }) + } +} + +func TestWorkflowYAMLToCRD(t *testing.T) { + cases := []struct { + name string + inputWf *workflow.Workflow + inputCrd *v1alpha1.Workflow + want *v1alpha1.Workflow + }{ + { + "Nil workflow", + nil, + nil, + nil, + }, + { + "Nil crd", + &workflow.Workflow{ + Version: "1", + Name: "debian-provision", + ID: "0a90fac9-b509-4aa5-b294-5944128ece81", + GlobalTimeout: 600, + Tasks: []workflow.Task{ + { + Name: "do-or-do-not-there-is-no-try", + WorkerAddr: "00:00:53:00:53:F4", + Actions: []workflow.Action{ + { + Name: "stream-image-to-disk", + Image: "quay.io/tinkerbell-actions/image2disk:v1.0.0", + Timeout: 300, + Volumes: []string{ + "/dev:/dev", + "/dev/console:/dev/console", + "/lib/firmware:/lib/firmware:ro", + "/tmp/debug:/tmp/debug", + }, + Environment: map[string]string{ + "COMPRESSED": "true", + "DEST_DISK": "/dev/nvme0n1", + "IMG_URL": "http://10.1.1.11:8080/debian-10-openstack-amd64.raw.gz", + }, + Pid: "host", + }, + }, + }, + }, + }, + nil, + nil, + }, + { + "Full crd", + &workflow.Workflow{ + Version: "1", + Name: "debian-provision", + ID: "0a90fac9-b509-4aa5-b294-5944128ece81", + GlobalTimeout: 600, + Tasks: []workflow.Task{ + { + Name: "do-or-do-not-there-is-no-try", + WorkerAddr: "00:00:53:00:53:F4", + Actions: []workflow.Action{ + { + Name: "stream-image-to-disk", + Image: "quay.io/tinkerbell-actions/image2disk:v1.0.0", + Timeout: 300, + Volumes: []string{ + "/dev:/dev", + "/dev/console:/dev/console", + "/lib/firmware:/lib/firmware:ro", + "/tmp/debug:/tmp/debug", + }, + Environment: map[string]string{ + "COMPRESSED": "true", + "DEST_DISK": "/dev/nvme0n1", + "IMG_URL": "http://10.1.1.11:8080/debian-10-openstack-amd64.raw.gz", + }, + Pid: "host", + }, + }, + }, + }, + }, + &v1alpha1.Workflow{ + Status: v1alpha1.WorkflowStatus{}, + }, + &v1alpha1.Workflow{ + Status: v1alpha1.WorkflowStatus{ + GlobalTimeout: 600, + Tasks: []v1alpha1.Task{ + { + Name: "do-or-do-not-there-is-no-try", + WorkerAddr: "00:00:53:00:53:F4", + Actions: []v1alpha1.Action{ + { + Name: "stream-image-to-disk", + Image: "quay.io/tinkerbell-actions/image2disk:v1.0.0", + Timeout: 300, + Volumes: []string{ + "/dev:/dev", + "/dev/console:/dev/console", + "/lib/firmware:/lib/firmware:ro", + "/tmp/debug:/tmp/debug", + }, + Pid: "host", + Environment: map[string]string{ + "COMPRESSED": "true", + "DEST_DISK": "/dev/nvme0n1", + "IMG_URL": "http://10.1.1.11:8080/debian-10-openstack-amd64.raw.gz", + }, + Status: "STATE_PENDING", + }, + }, + }, + }, + }, + }, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + WorkflowYAMLToCRD(tc.inputWf, tc.inputCrd) + if diff := cmp.Diff(tc.inputCrd, tc.want, protocmp.Transform()); diff != "" { + t.Errorf("unexpected difference:\n%v", diff) + } + }) + } +} diff --git a/pkg/internal/tests/frozen_time.go b/pkg/internal/tests/frozen_time.go new file mode 100644 index 000000000..391bfa61b --- /dev/null +++ b/pkg/internal/tests/frozen_time.go @@ -0,0 +1,64 @@ +package tests + +import ( + "time" + + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// FrozenTime is an interface for testing out fake times with additional +// methods for protobuf and Kubernetes time formats. +type FrozenTime interface { + // Now never changes + Now() time.Time + Before(time.Duration) time.Time + After(time.Duration) time.Time + + // Before Now() by int64 seconds + BeforeSec(int64) time.Time + // After Now() by int64 seconds + AfterSec(int64) time.Time + + MetaV1Now() *metav1.Time + MetaV1Before(time.Duration) *metav1.Time + MetaV1After(time.Duration) *metav1.Time + MetaV1BeforeSec(int64) *metav1.Time + MetaV1AfterSec(int64) *metav1.Time + + PbNow() *timestamppb.Timestamp + PbBefore(time.Duration) *timestamppb.Timestamp + PbAfter(time.Duration) *timestamppb.Timestamp + PbBeforeSec(int64) *timestamppb.Timestamp + PbAfterSec(int64) *timestamppb.Timestamp +} + +func NewFrozenTimeUnix(unix int64) FrozenTime { + return &ft{t: time.Unix(unix, 0)} +} + +func NewFrozenTime(t time.Time) FrozenTime { + return &ft{t} +} + +type ft struct { + t time.Time +} + +func (f *ft) Now() time.Time { return f.t } +func (f *ft) Before(d time.Duration) time.Time { return f.Now().Add(d) } +func (f *ft) After(d time.Duration) time.Time { return f.Now().Add(-d) } +func (f *ft) BeforeSec(s int64) time.Time { return f.Now().Add(time.Duration(-s) * time.Second) } +func (f *ft) AfterSec(s int64) time.Time { return f.Now().Add(time.Duration(s) * time.Second) } + +func (f *ft) MetaV1Now() *metav1.Time { t := metav1.NewTime(f.Now()); return &t } +func (f *ft) MetaV1Before(d time.Duration) *metav1.Time { t := metav1.NewTime(f.Before(d)); return &t } +func (f *ft) MetaV1After(d time.Duration) *metav1.Time { t := metav1.NewTime(f.After(d)); return &t } +func (f *ft) MetaV1BeforeSec(s int64) *metav1.Time { t := metav1.NewTime(f.BeforeSec(s)); return &t } +func (f *ft) MetaV1AfterSec(s int64) *metav1.Time { t := metav1.NewTime(f.AfterSec(s)); return &t } + +func (f *ft) PbNow() *timestamppb.Timestamp { return timestamppb.New(f.Now()) } +func (f *ft) PbBefore(d time.Duration) *timestamppb.Timestamp { return timestamppb.New(f.Before(d)) } +func (f *ft) PbAfter(d time.Duration) *timestamppb.Timestamp { return timestamppb.New(f.After(d)) } +func (f *ft) PbBeforeSec(s int64) *timestamppb.Timestamp { return timestamppb.New(f.BeforeSec(s)) } +func (f *ft) PbAfterSec(s int64) *timestamppb.Timestamp { return timestamppb.New(f.AfterSec(s)) } diff --git a/rules.mk b/rules.mk index c70bf8e55..24e318d37 100644 --- a/rules.mk +++ b/rules.mk @@ -11,7 +11,7 @@ MAKEFLAGS += --no-builtin-rules SHELL := bash .SHELLFLAGS := -o pipefail -euc -binaries := cmd/tink-cli/tink-cli cmd/tink-server/tink-server cmd/tink-worker/tink-worker +binaries := cmd/tink-cli/tink-cli cmd/tink-controller/tink-controller cmd/tink-server/tink-server cmd/tink-worker/tink-worker version := $(shell git rev-parse --short HEAD) tag := $(shell git tag --points-at HEAD) ifneq (,$(tag)) @@ -22,6 +22,7 @@ export CGO_ENABLED := 0 .PHONY: server cli worker test $(binaries) cli: cmd/tink-cli/tink-cli +controller: cmd/tink-controller/tink-controller server: cmd/tink-server/tink-server worker : cmd/tink-worker/tink-worker @@ -37,9 +38,11 @@ crossbinaries := $(crossbinaries:=386) $(crossbinaries:=amd64) $(crossbinaries:= $(binaries) $(crossbinaries): $(FLAGS) go build $(LDFLAGS) -o $@ ./$(@D) -.PHONY: tink-cli-image tink-server-image tink-worker-image +.PHONY: tink-cli-image tink-controller-image tink-server-image tink-worker-image tink-cli-image: cmd/tink-cli/tink-cli-linux-amd64 docker build -t tink-cli cmd/tink-cli/ +tink-controller-image: cmd/tink-controller/tink-controller-linux-amd64 + docker build -t tink-controller cmd/tink-controller/ tink-server-image: cmd/tink-server/tink-server-linux-amd64 docker build -t tink-server cmd/tink-server/ tink-worker-image: cmd/tink-worker/tink-worker-linux-amd64 @@ -72,10 +75,10 @@ protomocks: bin/moq .PHONY: check-protomocks check-protomocks: @git diff --no-ext-diff --quiet --exit-code -- protos/*/mock.go || ( - echo "Mock files need to be regenerated!"; + echo "Mock files need to be regenerated!"; git diff --no-ext-diff --exit-code --stat -- protos/*/mock.go ) - + .PHONY: pbfiles pbfiles: buf.gen.yaml buf.lock $(shell git ls-files 'protos/*/*.proto') $(toolsBins) buf generate @@ -84,6 +87,6 @@ pbfiles: buf.gen.yaml buf.lock $(shell git ls-files 'protos/*/*.proto') $(toolsB .PHONY: check-pbfiles check-pbfiles: pbfiles @git diff --no-ext-diff --quiet --exit-code -- protos/*/*.pb.* || ( - echo "Protobuf files need to be regenerated!"; + echo "Protobuf files need to be regenerated!"; git diff --no-ext-diff --exit-code --stat -- protos/*/*.pb.* ) diff --git a/workflow/template_validator.go b/workflow/template_validator.go index 342a7261b..6eae3bb87 100644 --- a/workflow/template_validator.go +++ b/workflow/template_validator.go @@ -21,7 +21,7 @@ const ( errActionDuplicateName = "two actions in a task cannot have same name: %s" errActionInvalidImage = "invalid action image: %s" errTemplateParsing = "failed to parse template with ID %s" - errInvalidHardwareAddress = "failed to render template, invalid hardware address: %s" + errInvalidHardwareAddress = "failed to render template, invalid hardware address: %v" ) // Parse parses the template yaml content into a Workflow. @@ -60,36 +60,48 @@ func MustParseFromFile(path string) *Workflow { return MustParse(content) } -// RenderTemplate renders the workflow template wrt given hardware details. +// RenderTemplate renders the workflow template with regard to the given hardware details. func RenderTemplate(templateID, templateData string, devices []byte) (string, error) { - var hardware map[string]interface{} + var hardware map[string]string err := json.Unmarshal(devices, &hardware) if err != nil { err = errors.Wrapf(err, errTemplateParsing, templateID) return "", err } + _, buf, err := RenderTemplateHardware(templateID, templateData, hardware) + if err != nil { + return "", err + } + return buf.String(), nil +} + +// RenderTemplateHardware renders the workflow template and returns the Workflow and the interpolated bytes. +func RenderTemplateHardware(templateID, templateData string, hardware map[string]string) (*Workflow, *bytes.Buffer, error) { t := template.New("workflow-template").Option("missingkey=error") - _, err = t.Parse(templateData) + _, err := t.Parse(templateData) if err != nil { err = errors.Wrapf(err, errTemplateParsing, templateID) - return "", err + return nil, nil, err } buf := new(bytes.Buffer) err = t.Execute(buf, hardware) if err != nil { err = errors.Wrapf(err, errTemplateParsing, templateID) - return "", err + return nil, nil, err } - wf := MustParse(buf.Bytes()) + wf, err := Parse(buf.Bytes()) + if err != nil { + return nil, nil, err + } for _, task := range wf.Tasks { if task.WorkerAddr == "" { - return "", fmt.Errorf(errInvalidHardwareAddress, string(devices)) + return nil, nil, fmt.Errorf(errInvalidHardwareAddress, hardware) } } - return buf.String(), nil + return wf, buf, nil } // validate validates a workflow template against certain requirements. diff --git a/workflow/template_validator_test.go b/workflow/template_validator_test.go index ee93221b8..9d4b52c07 100644 --- a/workflow/template_validator_test.go +++ b/workflow/template_validator_test.go @@ -301,6 +301,110 @@ tasks: timeout: 60 `, }, + { + name: "malformed template", + hwAddress: []byte("{\"device_1\":\"08:00:27:00:00:01\"}"), + templateID: "98788301-d0d9-4ee9-84df-b64e6e1ef1cc", + templateData: ` +version: "0.1" +name: hello_world_workflow +global_timeout: 600 +tasks: + - name: "hello world" + worker: "{{.device_1}" + actions: + - name: "hello_world" + image: hello-world + timeout: 60 +`, + expectedError: func(t *testing.T, err error) { + t.Helper() + if err == nil { + t.Error("expected error, got nil") + } + if !strings.Contains(err.Error(), `template: workflow-template:7: unexpected "}" in operand`) { + t.Errorf("\nexpected err: '%s'\ngot: '%s'", `failed to parse template with ID 98788301-d0d9-4ee9-84df-b64e6e1ef1cc: template: workflow-template:7: unexpected "}" in operand`, err) + } + }, + }, + { + name: "invalid yaml in template", + hwAddress: []byte("{\"device_1\":\"08:00:27:00:00:01\"}"), + templateID: "98788301-d0d9-4ee9-84df-b64e6e1ef1cc", + templateData: ` +version: "0.1" + name: hello_world_workflow +global_timeout: 600 +tasks: + - name: "hello world" + worker: "{{.device_1}}" + actions: + - name: "hello_world" + image: hello-world + timeout: 60 +`, + expectedError: func(t *testing.T, err error) { + t.Helper() + if err == nil { + t.Error("expected error, got nil") + } + if !strings.Contains(err.Error(), `parsing yaml data: yaml: line 2: did not find expected key`) { + t.Errorf("\nexpected err: '%s'\ngot: '%s'", `parsing yaml data: yaml: line 2: did not find expected key`, err) + } + }, + }, + { + name: "Empty worker address", + hwAddress: []byte("{\"device_1\":\"\"}"), + templateID: "98788301-d0d9-4ee9-84df-b64e6e1ef1cc", + templateData: ` +version: "0.1" +name: hello_world_workflow +global_timeout: 600 +tasks: + - name: "hello world" + worker: "{{.device_1}}" + actions: + - name: "hello_world" + image: hello-world + timeout: 60 +`, + expectedError: func(t *testing.T, err error) { + t.Helper() + if err == nil { + t.Error("expected error, got nil") + } + if !strings.Contains(err.Error(), `failed to render template, invalid hardware address: map[device_1:]`) { + t.Errorf("\nexpected err: '%s'\ngot: '%s'", `failed to render template, invalid hardware address: map[device_1:]`, err) + } + }, + }, + { + name: "Invalid hardware json", + hwAddress: []byte(`{"device_1":"abc"}}`), + templateID: "98788301-d0d9-4ee9-84df-b64e6e1ef1cc", + templateData: ` +version: "0.1" +name: hello_world_workflow +global_timeout: 600 +tasks: + - name: "hello world" + worker: "{{.device_1}}" + actions: + - name: "hello_world" + image: hello-world + timeout: 60 +`, + expectedError: func(t *testing.T, err error) { + t.Helper() + if err == nil { + t.Error("expected error, got nil") + } + if !strings.Contains(err.Error(), `failed to parse template with ID 98788301-d0d9-4ee9-84df-b64e6e1ef1cc: invalid character '}' after top-level value`) { + t.Errorf("\nexpected err: '%s'\ngot: '%s'", `failed to parse template with ID 98788301-d0d9-4ee9-84df-b64e6e1ef1cc: invalid character '}' after top-level value`, err) + } + }, + }, } for _, test := range tests {