diff --git a/go.mod b/go.mod index 8de5c26831..030f8f312b 100644 --- a/go.mod +++ b/go.mod @@ -10,17 +10,22 @@ replace github.com/chzyer/logex => github.com/chzyer/logex v1.1.11-0.20170329064 // Avoid pulling in incompatible libraries replace github.com/docker/distribution => github.com/docker/distribution v0.0.0-20191216044856-a8371794149d -replace github.com/docker/docker => github.com/moby/moby v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible - // Client-go does not handle different versions of mergo due to some breaking changes - use the matching version replace github.com/imdario/mergo => github.com/imdario/mergo v0.3.5 require ( + github.com/AlecAivazis/survey/v2 v2.3.6 github.com/agiledragon/gomonkey/v2 v2.9.0 github.com/avast/retry-go/v4 v4.3.4 + github.com/compose-spec/compose-go v1.8.2 + github.com/docker/cli v20.10.20+incompatible + github.com/docker/compose/v2 v2.0.0-00010101000000-000000000000 + github.com/docker/docker v20.10.20+incompatible github.com/dubbogo/go-zookeeper v1.0.4-0.20211212162352-f9d2183d89d5 github.com/dubbogo/gost v1.13.1 github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1 + github.com/fatih/color v1.14.1 + github.com/fatih/structtag v1.2.0 github.com/gogo/protobuf v1.3.2 github.com/golang/protobuf v1.5.2 github.com/google/go-cmp v0.5.9 @@ -28,25 +33,31 @@ require ( github.com/hashicorp/consul/api v1.23.0 github.com/hashicorp/go-multierror v1.1.1 github.com/hudl/fargo v1.4.0 + github.com/iancoleman/orderedmap v0.3.0 + github.com/mitchellh/go-homedir v1.1.0 + github.com/mitchellh/mapstructure v1.5.0 github.com/nacos-group/nacos-sdk-go v1.0.8 github.com/nacos-group/nacos-sdk-go/v2 v2.1.2 github.com/pkg/errors v0.9.1 - github.com/spf13/cobra v1.2.1 + github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 + github.com/spf13/cobra v1.6.1 github.com/spf13/pflag v1.0.5 + github.com/spf13/viper v1.8.1 github.com/stretchr/testify v1.8.3 go.uber.org/atomic v1.9.0 google.golang.org/grpc v1.48.0 - google.golang.org/protobuf v1.28.0 + google.golang.org/protobuf v1.28.1 gopkg.in/yaml.v2 v2.4.0 + gopkg.in/yaml.v3 v3.0.1 istio.io/api v0.0.0-20211122181927-8da52c66ff23 istio.io/client-go v1.12.0-rc.1.0.20211118171212-b744b6f111e4 istio.io/gogo-genproto v0.0.0-20211115195057-0e34bdd2be67 istio.io/istio v0.0.0 istio.io/pkg v0.0.0-20211115195056-e379f31ee62a - k8s.io/api v0.22.5 - k8s.io/apimachinery v0.22.5 + k8s.io/api v0.24.1 + k8s.io/apimachinery v0.24.1 k8s.io/cli-runtime v0.22.2 - k8s.io/client-go v0.22.5 + k8s.io/client-go v0.24.1 k8s.io/kubectl v0.22.2 sigs.k8s.io/controller-runtime v0.10.2 sigs.k8s.io/yaml v1.3.0 @@ -69,56 +80,63 @@ require ( github.com/Masterminds/semver/v3 v3.1.1 // indirect github.com/Masterminds/sprig/v3 v3.2.2 // indirect github.com/Masterminds/squirrel v1.5.0 // indirect - github.com/Microsoft/go-winio v0.5.0 // indirect - github.com/Microsoft/hcsshim v0.8.21 // indirect + github.com/Microsoft/go-winio v0.5.2 // indirect + github.com/Microsoft/hcsshim v0.9.6 // indirect github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/RageCage64/multilinediff v0.2.0 // indirect github.com/aliyun/alibaba-cloud-sdk-go v1.61.1704 // indirect github.com/armon/go-metrics v0.4.1 // indirect github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 // indirect - github.com/aws/aws-sdk-go v1.41.7 // indirect + github.com/aws/aws-sdk-go v1.43.16 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bmatcuk/doublestar/v4 v4.6.0 // indirect github.com/braydonk/yaml v0.7.0 // indirect + github.com/buger/goterm v1.0.4 // indirect github.com/buger/jsonparser v1.1.1 // indirect - github.com/cenkalti/backoff/v4 v4.1.1 // indirect + github.com/cenkalti/backoff/v4 v4.1.2 // indirect github.com/census-instrumentation/opencensus-proto v0.3.0 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 // indirect github.com/clbanning/mxj v1.8.4 // indirect github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4 // indirect github.com/cncf/xds/go v0.0.0-20220520190051-1e77728a1eaa // indirect - github.com/containerd/containerd v1.5.7 // indirect - github.com/containerd/continuity v0.1.0 // indirect + github.com/compose-spec/godotenv v1.1.1 // indirect + github.com/containerd/cgroups v1.0.4 // indirect + github.com/containerd/console v1.0.3 // indirect + github.com/containerd/containerd v1.6.14 // indirect + github.com/containerd/continuity v0.3.0 // indirect + github.com/containerd/typeurl v1.0.2 // indirect github.com/coreos/go-oidc/v3 v3.1.0 // indirect github.com/cyphar/filepath-securejoin v0.2.3 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0 // indirect - github.com/docker/cli v20.10.7+incompatible // indirect - github.com/docker/distribution v2.7.1+incompatible // indirect - github.com/docker/docker v20.10.7+incompatible // indirect - github.com/docker/docker-credential-helpers v0.6.3 // indirect + github.com/distribution/distribution/v3 v3.0.0-20221201083218-92d136e113cf // indirect + github.com/docker/buildx v0.9.1 // indirect + github.com/docker/distribution v2.8.1+incompatible // indirect + github.com/docker/docker-credential-helpers v0.7.0 // indirect + github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-metrics v0.0.1 // indirect - github.com/docker/go-units v0.4.0 // indirect + github.com/docker/go-units v0.5.0 // indirect github.com/envoyproxy/protoc-gen-validate v0.1.0 // indirect github.com/evanphx/json-patch v4.12.0+incompatible // indirect github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect - github.com/fatih/color v1.14.1 // indirect github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8 // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect - github.com/fvbommel/sortorder v1.0.1 // indirect + github.com/fvbommel/sortorder v1.0.2 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-errors/errors v1.0.1 // indirect github.com/go-kit/log v0.1.0 // indirect github.com/go-logfmt/logfmt v0.5.0 // indirect - github.com/go-logr/logr v1.2.2 // indirect + github.com/go-logr/logr v1.2.3 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.19.5 // indirect github.com/go-openapi/swag v0.19.15 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/goccy/go-json v0.4.8 // indirect + github.com/gofrs/flock v0.8.0 // indirect + github.com/gogo/googleapis v1.4.1 // indirect github.com/golang-jwt/jwt/v4 v4.2.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/mock v1.6.0 // indirect @@ -133,23 +151,27 @@ require ( github.com/gosuri/uitable v0.0.4 // indirect github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect + github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-hclog v1.5.0 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect - github.com/hashicorp/go-version v1.3.0 // indirect + github.com/hashicorp/go-version v1.6.0 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/serf v0.10.1 // indirect github.com/huandu/xstrings v1.3.2 // indirect - github.com/imdario/mergo v0.3.12 // indirect - github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/imdario/mergo v0.3.13 // indirect + github.com/inconshreveable/mousetrap v1.0.1 // indirect + github.com/jaguilar/vt100 v0.0.0-20150826170717-2703a27b14ea // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmoiron/sqlx v1.3.1 // indirect github.com/jonboulle/clockwork v0.2.2 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.13.6 // indirect + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect + github.com/klauspost/compress v1.15.9 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/lestrrat-go/backoff/v2 v2.0.7 // indirect @@ -162,20 +184,27 @@ require ( github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042 // indirect github.com/lib/pq v1.10.0 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect + github.com/magiconair/properties v1.8.5 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.17 // indirect github.com/mattn/go-runewidth v0.0.12 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect + github.com/mattn/go-shellwords v1.0.12 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect github.com/miekg/dns v1.1.43 // indirect + github.com/miekg/pkcs11 v1.1.1 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect - github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-wordwrap v1.0.0 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/moby/buildkit v0.10.4 // indirect github.com/moby/locker v1.0.1 // indirect github.com/moby/spdystream v0.2.0 // indirect - github.com/moby/term v0.0.0-20210610120745-9d4ed1856297 // indirect + github.com/moby/sys/mount v0.3.0 // indirect + github.com/moby/sys/mountinfo v0.6.0 // indirect + github.com/moby/sys/signal v0.7.0 // indirect + github.com/moby/sys/symlink v0.2.0 // indirect + github.com/moby/term v0.0.0-20221128092401-c43b287e0e0f // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect @@ -183,9 +212,11 @@ require ( github.com/natefinch/lumberjack v2.0.0+incompatible // indirect github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.0.1 // indirect - github.com/opencontainers/runc v1.0.2 // indirect + github.com/opencontainers/image-spec v1.1.0-rc2 // indirect + github.com/opencontainers/runc v1.1.3 // indirect github.com/openshift/api v0.0.0-20200713203337-b2494ecb17dd // indirect + github.com/opentracing/opentracing-go v1.2.0 // indirect + github.com/pelletier/go-toml v1.9.4 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_golang v1.12.2 // indirect @@ -195,11 +226,17 @@ require ( github.com/prometheus/statsd_exporter v0.21.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/rubenv/sql-migrate v0.0.0-20210614095031-55d5740dbbcc // indirect - github.com/russross/blackfriday v1.5.2 // indirect + github.com/russross/blackfriday v1.6.0 // indirect + github.com/sanathkr/go-yaml v0.0.0-20170819195128-ed9d249f429b // indirect github.com/shopspring/decimal v1.2.0 // indirect - github.com/sirupsen/logrus v1.8.1 // indirect + github.com/sirupsen/logrus v1.9.0 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect + github.com/spf13/afero v1.2.2 // indirect github.com/spf13/cast v1.3.1 // indirect + github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/theupdateframework/notary v0.7.0 // indirect + github.com/tonistiigi/fsutil v0.0.0-20220930225714-4638ad635be5 // indirect + github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect github.com/toolkits/concurrent v0.0.0-20150624120057-a4371d70e3e3 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect @@ -207,14 +244,14 @@ require ( github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca // indirect github.com/yl2chen/cidranger v1.0.2 // indirect go.opencensus.io v0.23.0 // indirect - go.opentelemetry.io/proto/otlp v0.7.0 // indirect + go.opentelemetry.io/proto/otlp v0.12.0 // indirect go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect go.uber.org/multierr v1.7.0 // indirect go.uber.org/zap v1.21.0 // indirect golang.org/x/crypto v0.11.0 // indirect golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect golang.org/x/net v0.12.0 // indirect - golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect + golang.org/x/oauth2 v0.6.0 // indirect golang.org/x/sync v0.2.0 // indirect golang.org/x/sys v0.10.0 // indirect golang.org/x/term v0.10.0 // indirect @@ -225,7 +262,7 @@ require ( gomodules.xyz/orderedmap v0.1.0 // indirect google.golang.org/api v0.61.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12 // indirect + google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 // indirect gopkg.in/gcfg.v1 v1.2.3 // indirect gopkg.in/gorp.v1 v1.7.2 // indirect gopkg.in/inf.v0 v0.9.1 // indirect @@ -233,10 +270,9 @@ require ( gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiserver v0.22.2 // indirect - k8s.io/component-base v0.22.2 // indirect - k8s.io/klog/v2 v2.40.1 // indirect + k8s.io/apiserver v0.22.5 // indirect + k8s.io/component-base v0.22.5 // indirect + k8s.io/klog/v2 v2.60.1 // indirect k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c // indirect k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect oras.land/oras-go v0.4.0 // indirect @@ -259,29 +295,58 @@ replace istio.io/client-go => ./external/client-go replace istio.io/istio => ./external/istio +require ( + github.com/evanphx/json-patch/v5 v5.6.0 + github.com/google/yamlfmt v0.10.0 + github.com/kylelemons/godebug v1.1.0 + helm.sh/helm/v3 v3.7.1 + k8s.io/apiextensions-apiserver v0.25.4 + knative.dev/networking v0.0.0-20220302134042-e8b2eb995165 + knative.dev/pkg v0.0.0-20220301181942-2fdd5f232e77 +) + replace ( + github.com/Sirupsen/logrus => github.com/sirupsen/logrus v1.9.3 github.com/go-logr/logr => github.com/go-logr/logr v0.4.0 + k8s.io/api => k8s.io/api v0.22.2 k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.22.2 k8s.io/apimachinery => k8s.io/apimachinery v0.22.2 k8s.io/cli-runtime => k8s.io/cli-runtime v0.22.2 k8s.io/client-go => k8s.io/client-go v0.22.2 + k8s.io/code-generator => k8s.io/code-generator v0.22.2 k8s.io/component-base => k8s.io/component-base v0.22.2 + k8s.io/component-helpers => k8s.io/component-helpers v0.22.2 k8s.io/klog/v2 => k8s.io/klog/v2 v2.10.0 - k8s.io/kube-openapi => k8s.io/kube-openapi v0.0.0-20211020163157-7327e2aaee2b // indirect + k8s.io/kube-openapi => k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e k8s.io/kubectl => k8s.io/kubectl v0.22.2 + k8s.io/metrics => k8s.io/metrics v0.22.2 + k8s.io/utils => k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect sigs.k8s.io/gateway-api => sigs.k8s.io/gateway-api v0.4.0 // indirect sigs.k8s.io/kustomize/api => sigs.k8s.io/kustomize/api v0.8.11 // indirect sigs.k8s.io/kustomize/kyaml => sigs.k8s.io/kustomize/kyaml v0.11.0 // indirect ) -require ( - github.com/evanphx/json-patch/v5 v5.6.0 - github.com/google/yamlfmt v0.10.0 - github.com/kylelemons/godebug v1.1.0 - helm.sh/helm/v3 v3.7.1 - k8s.io/apiextensions-apiserver v0.25.4 - knative.dev/networking v0.0.0-20220302134042-e8b2eb995165 - knative.dev/pkg v0.0.0-20220301181942-2fdd5f232e77 +// for pkg/cmd/hgctl/docker/compose.go +// TODO(WeixinX): Wait for the dependency library to upgrade, such as github.com/go-logr/logr from v0.4.0 to v1.2+ +// replace ( +// github.com/compose-spec/compose-go => github.com/compose-spec/compose-go v1.8.2 +// github.com/cucumber/godog => github.com/laurazard/godog v0.0.0-20220922095256-4c4b17abdae7 +// github.com/docker/buildx => github.com/docker/buildx v0.9.1 +// github.com/docker/cli => github.com/docker/cli v20.10.3-0.20221013132413-1d6c6e2367e2+incompatible +// github.com/docker/compose/v2 => github.com/docker/compose/v2 v2.15.1 +// github.com/docker/docker => github.com/moby/moby v20.10.3-0.20221021173910-5aac513617f0+incompatible +// github.com/moby/buildkit => github.com/moby/buildkit v0.10.1-0.20220816171719-55ba9d14360a +// ) + +replace ( + github.com/compose-spec/compose-go => github.com/compose-spec/compose-go v1.0.8 + github.com/docker/buildx => github.com/docker/buildx v0.5.2-0.20210422185057-908a856079fc + github.com/docker/cli => github.com/docker/cli v20.10.7+incompatible + github.com/docker/compose/v2 => github.com/docker/compose/v2 v2.2.0 + github.com/docker/docker => github.com/docker/docker v20.10.3+incompatible + github.com/jaguilar/vt100 => github.com/tonistiigi/vt100 v0.0.0-20190402012908-ad4c4a574305 + github.com/moby/buildkit => github.com/moby/buildkit v0.8.2-0.20210401015549-df49b648c8bf + github.com/tonistiigi/fsutil => github.com/tonistiigi/fsutil v0.0.0-20201103201449-0834f99b7b85 ) diff --git a/go.sum b/go.sum index 354f1dee00..25a6a93936 100644 --- a/go.sum +++ b/go.sum @@ -62,8 +62,11 @@ dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1 dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/AdaLogics/go-fuzz-headers v0.0.0-20210929163055-e81b3f25be97/go.mod h1:WpB7kf89yJUETZxQnP1kgYPNwlT2jjdDYUCoxVggM3g= +github.com/AlecAivazis/survey/v2 v2.3.6 h1:NvTuVHISgTHEHeBFqt6BHOe4Ny/NwGZr7w+F8S9ziyw= +github.com/AlecAivazis/survey/v2 v2.3.6/go.mod h1:4AuI9b7RjAR+G7v9+C4YSlX/YL3K3cWNXgWXOhllqvI= github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go v56.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= @@ -118,8 +121,9 @@ github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugX github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.5.0 h1:Elr9Wn+sGKPlkaBvwu4mTrxtmOp3F3yV9qhaHbXGjwU= github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= +github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= @@ -128,12 +132,15 @@ github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2 github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600= github.com/Microsoft/hcsshim v0.8.20/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= -github.com/Microsoft/hcsshim v0.8.21 h1:btRfUDThBE5IKcvI8O8jOiIkujUsAMBSRsYDYmEi6oM= github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= +github.com/Microsoft/hcsshim v0.9.6 h1:VwnDOgLeoi2du6dAznfmspNqTiwczvjv4K7NxuY9jsY= +github.com/Microsoft/hcsshim v0.9.6/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfyFRBqxEbCc= github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= +github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= +github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= @@ -141,6 +148,7 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/RageCage64/multilinediff v0.2.0 h1:yNSpSF5NXIrmo6bRIgO4Q0g7SXqFD4j/WEcBE+BdCFY= github.com/RageCage64/multilinediff v0.2.0/go.mod h1:pKr+KLgP0gvRzA+yv0/IUaYQuBYN1ucWysvsL58aMP0= +github.com/Shopify/logrus-bugsnag v0.0.0-20170309145241-6dbc35f2c30d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= @@ -151,8 +159,10 @@ github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrU github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/agiledragon/gomonkey/v2 v2.9.0 h1:PDiKKybR596O6FHW+RVSG0Z7uGCBNbmbUXh3uCNQ7Hc= github.com/agiledragon/gomonkey/v2 v2.9.0/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY= +github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0= github.com/ahmetb/gen-crd-api-reference-docs v0.3.0/go.mod h1:TdjdkYhlOifCQWPs1UdTma97kQQMozf5h26hTuG70u8= 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= @@ -170,6 +180,10 @@ github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYU github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apparentlymart/go-cidr v1.0.1/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc= +github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM= +github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= +github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= @@ -187,24 +201,29 @@ github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQ github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.34.9/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= -github.com/aws/aws-sdk-go v1.41.7 h1:vlpR8Cky3ZxUVNINgeRZS6N0p6zmFvu/ZqRRwrTI25U= github.com/aws/aws-sdk-go v1.41.7/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= +github.com/aws/aws-sdk-go v1.43.16 h1:Y7wBby44f+tINqJjw5fLH3vA+gFq4uMITIKqditwM14= +github.com/aws/aws-sdk-go v1.43.16/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/beorn7/perks v0.0.0-20150223135152-b965b613227f/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= +github.com/bitly/go-hostpool v0.1.0/go.mod h1:4gOCgp6+NZnVqlKyZ/iBZFTAJKembaVENUpMkpg42fw= github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= 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/go.mod h1:mdXfREi6u5MArG4j9fewC+FGnXaBR+T4Ox4J2u4eHCc= +github.com/bmatcuk/doublestar v1.1.5/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE= github.com/bmatcuk/doublestar/v4 v4.6.0 h1:HTuxyug8GyFbRkrffIpzNCSK4luc0TY3wzXvzIZhEXc= github.com/bmatcuk/doublestar/v4 v4.6.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= @@ -215,21 +234,27 @@ github.com/braydonk/yaml v0.7.0/go.mod h1:hcm3h581tudlirk8XEUPDBAimBPbmnL0Y45hCR github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70= github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= +github.com/buger/goterm v1.0.4 h1:Z9YvGmOih81P0FbVtEYTFF6YsSgxSUKEhf/f9bTMXbY= +github.com/buger/goterm v1.0.4/go.mod h1:HiFWV3xnkolgrBV3mY8m0X0Pumt4zg4QhbdOzQtB8tE= github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= -github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd h1:rFt+Y/IK1aEZkEHchZRSq9OQbsSzIT/OrI8YFFmRIng= github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= -github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ= +github.com/bugsnag/bugsnag-go v1.0.5-0.20150529004307-13fd6b8acda0/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= +github.com/bugsnag/bugsnag-go v1.4.1 h1:TT3P9AX69w8mbSGE8L7IJOO2KBlPN0iQtYD0dUlrWHc= +github.com/bugsnag/bugsnag-go v1.4.1/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 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o= github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= +github.com/bugsnag/panicwrap v1.2.0 h1:OzrKrRvXis8qEvOkfcxNcYbOd2O7xXS2nnKMEMABFQA= +github.com/bugsnag/panicwrap v1.2.0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= github.com/c2h5oh/datasize v0.0.0-20171227191756-4eba002a5eae/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/cenkalti/backoff/v4 v4.1.1 h1:G2HAfAmvm/GcKan2oOQpBXOd2tT2G57ZnZGWa1PxPBQ= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuDPIFo= +github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0 h1:t/LhUZLVitR1Ow2YOnduCsavhwFUklBMoGVYUCqmCqk= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -244,6 +269,7 @@ github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 h1:7aWHqerlJ41 github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= +github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= github.com/cheggaaa/pb/v3 v3.0.8/go.mod h1:UICbiLec/XO6Hw6k+BHEtHeQFzzBH4i2/qk/ow1EJTA= @@ -256,12 +282,16 @@ github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/cilium/ebpf v0.5.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= +github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I= github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA= +github.com/cloudflare/cfssl v0.0.0-20181213083726-b94e044bb51e h1:Qux+lbuMaRzkQyTdzgtz8MgzPtzmaPQy6DXmxpdxT3U= +github.com/cloudflare/cfssl v0.0.0-20181213083726-b94e044bb51e/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA= 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= @@ -275,7 +305,12 @@ github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:z 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= github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= +github.com/codahale/hdrhistogram v0.0.0-20160425231609-f8ad88b59a58/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/compose-spec/compose-go v1.0.8 h1:fgT7mYYu5Sp37i2lUIAAvwJpkAHk6dP5ITHy/LlutUk= +github.com/compose-spec/compose-go v1.0.8/go.mod h1:REnCbBugoIdHB7S1sfkN/aJ7AJpNApGNjNiVjA9L8x4= +github.com/compose-spec/godotenv v1.1.1 h1:lp+WpAInnw06YN9sV/XLUOV/9z4C+6wjJdWlrdVac7o= +github.com/compose-spec/godotenv v1.1.1/go.mod h1:zF/3BOa18Z24tts5qnO/E9YURQanJTBUf7nlcCTNsyc= github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU= github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= @@ -289,13 +324,17 @@ github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1 github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= -github.com/containerd/cgroups v1.0.1 h1:iJnMvco9XGvKUvNQkv88bE4uJXxRQH18efbKo9w5vHQ= github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU= +github.com/containerd/cgroups v1.0.4 h1:jN/mbWBEaz+T1pi5OFtnkQ+8qnmEbAr1Oo1FRm5B0dA= +github.com/containerd/cgroups v1.0.4/go.mod h1:nLNQtsF7Sl2HxNebu77i1R0oDlhiTG+kO4JTrUzo6IA= github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= +github.com/containerd/console v1.0.0/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= +github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= +github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= @@ -304,28 +343,33 @@ github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMX github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.5.0-beta.0.0.20210122062454-5a66c2ae5cec/go.mod h1:p9z+oqCID32tZ7LKgej316N9pJf1iIkQ/7nK1VvEFZE= github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ= github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU= github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI= github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s= github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= github.com/containerd/containerd v1.5.2/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= -github.com/containerd/containerd v1.5.7 h1:rQyoYtj4KddB3bxG6SAqd4+08gePNyJjRqvOIfV3rkM= github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c= +github.com/containerd/containerd v1.6.14 h1:W+d0AJKVG3ioTZZyQwcw1Y3vvo6ZDYzAcjDcY4tkgGI= +github.com/containerd/containerd v1.6.14/go.mod h1:U2NnBPIhzJDm59xF7xB2MMHnKtggpZ+phKg8o2TKj2c= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y= github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ= -github.com/containerd/continuity v0.1.0 h1:UFRRY5JemiAhPZrr/uE0n8fMTLcZsUvySPr1+D7pgr8= github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM= +github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= +github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM= github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= +github.com/containerd/fifo v1.0.0 h1:6PirWBr9/L7GDamKr+XM0IeUFXu5mf3M/BPpH9gaLBU= github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= +github.com/containerd/fuse-overlayfs-snapshotter v1.0.2/go.mod h1:nRZceC8a7dRm3Ao6cJAwuJWPFiBPaibHiFntRUnzhwU= github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU= github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk= github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= @@ -340,6 +384,9 @@ github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJ github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c= github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= +github.com/containerd/stargz-snapshotter v0.4.1 h1:YS3ZC4M+doc5NKXdnGdd8LlcYCC0s8o/GsCTJTYk7Ew= +github.com/containerd/stargz-snapshotter v0.4.1/go.mod h1:H59SY9Rw+vkIfWtuuyjeLeAc7uuALmuvUdAS6w+xmEw= +github.com/containerd/stargz-snapshotter/estargz v0.4.1/go.mod h1:x7Q9dg9QYb4+ELgxmo4gBUeJB0tl5dqH1Sdz0nJU1QM= github.com/containerd/stargz-snapshotter/estargz v0.7.0 h1:1d/rydzTywc76lnjJb6qbPCiTiCwts49AzKps/Ecblw= github.com/containerd/stargz-snapshotter/estargz v0.7.0/go.mod h1:83VWDqHnurTKliEB0YvWMiCfLDwv4Cjj1X9Vk98GJZw= github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= @@ -347,9 +394,12 @@ github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDG github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8= github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= +github.com/containerd/ttrpc v1.1.0 h1:GbtyLRxb0gOLR0TYQWt3O6B0NvT8tMdorEHqIQo/lWI= +github.com/containerd/ttrpc v1.1.0/go.mod h1:XX4ZTnoOId4HklF4edwc4DcqskFZuvXB1Evzy5KFQpQ= github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk= github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg= +github.com/containerd/typeurl v1.0.2 h1:Chlt8zIieDbzQFzXzAeBEF92KhExuE4p9p92/QmY7aY= github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s= github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw= github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y= @@ -388,10 +438,12 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI= +github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI= github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= @@ -399,6 +451,7 @@ github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1S github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= +github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -408,6 +461,8 @@ github.com/decred/dcrd/chaincfg/chainhash v1.0.2/go.mod h1:BpbrGgrPTr3YJYRN3Bm+D github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0 h1:sgNeV1VRMDzs6rzyPpxyM0jp317hnwiq58Filgag2xw= github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0/go.mod h1:J70FGZSbzsjecRTiTzER+3f1KZLNaXkuv+yeFTKoxM8= +github.com/denisenkom/go-mssqldb v0.0.0-20190315220205-a8ed825ac853/go.mod h1:xN/JuLBIz4bjkxNmByTiV1IbhfnYb6oo99phBn4Eqhc= +github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= @@ -415,16 +470,29 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm 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/distribution/distribution/v3 v3.0.0-20210316161203-a01c71e2477e/go.mod h1:xpWTC2KnJMiDLkoawhsPQcXjvwATEBcbq0xevG2YR9M= github.com/distribution/distribution/v3 v3.0.0-20210804104954-38ab4c606ee3/go.mod h1:gt38b7cvVKazi5XkHvINNytZXgTEntyhtyM3HQz46Nk= -github.com/distribution/distribution/v3 v3.0.0-20210926092439-1563384b69df h1:zafDqOsnugdrReF9Pe0wybnfFtEIaegSyHNIvnwKPVk= github.com/distribution/distribution/v3 v3.0.0-20210926092439-1563384b69df/go.mod h1:ZDZib/BOniVWcXcsy0voU8gR00znhe5VJm47d3H2Y5g= +github.com/distribution/distribution/v3 v3.0.0-20221201083218-92d136e113cf h1:q0uyPLfHgAu2Mke31RRXErLcAbuBguRpJugWxjMNRnQ= +github.com/distribution/distribution/v3 v3.0.0-20221201083218-92d136e113cf/go.mod h1:4x0IxAMsdeCSTr9UopCvp6MnryD2nyRLycsOrgvveAs= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= +github.com/docker/buildx v0.5.2-0.20210422185057-908a856079fc h1:oqPGOy23wxFCyOMSfdZTk02F7qvPi7kUEEeKrExKtfw= +github.com/docker/buildx v0.5.2-0.20210422185057-908a856079fc/go.mod h1:T5sa7xGu8G7dLXwaLLj6dRbJ5mxugPFDfELGnO2B5lQ= github.com/docker/cli v20.10.7+incompatible h1:pv/3NqibQKphWZiAskMzdz8w0PRbtTaEB+f6NwdU7Is= github.com/docker/cli v20.10.7+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/compose-on-kubernetes v0.4.19-0.20190128150448-356b2919c496/go.mod h1:iT2pYfi580XlpaV4KmK0T6+4/9+XoKmk/fhoDod1emE= +github.com/docker/compose/v2 v2.2.0 h1:tkkB4MCl1E+268dd6VpAwvUc8DQqGPgesbMb0Qngbc4= +github.com/docker/compose/v2 v2.2.0/go.mod h1:gxNxC8jKXZHD0P9LC4TH981aggWeKMzb89RuRoBnEmY= github.com/docker/distribution v0.0.0-20191216044856-a8371794149d h1:jC8tT/S0OGx2cswpeUTn4gOIea8P08lD3VFQT0cOZ50= github.com/docker/distribution v0.0.0-20191216044856-a8371794149d/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= -github.com/docker/docker-credential-helpers v0.6.3 h1:zI2p9+1NQYdnG6sMU26EX4aVGlqbInSQxQXLvzJ4RPQ= +github.com/docker/docker v20.10.3+incompatible h1:+HS4XO73J41FpA260ztGujJ+0WibrA2TPJEnWNSyGNE= +github.com/docker/docker v20.10.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= +github.com/docker/docker-credential-helpers v0.6.4-0.20210125172408-38bea2ce277a/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c= +github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= +github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= +github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0= +github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c/go.mod h1:CADgU4DSXK5QUlFslkQu2yW2TKzFZcXq/leZfM0UH5Q= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= @@ -433,27 +501,37 @@ github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6Uezg github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= -github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/libnetwork v0.8.0-dev.2.0.20201215162534-fa125a3512ee h1:VQGPaek8TO9sRNRVNXmjzrya1SmleN0cMf/vvyjjJHo= +github.com/docker/libnetwork v0.8.0-dev.2.0.20201215162534-fa125a3512ee/go.mod h1:93m0aTqz6z+g32wla4l4WxTrdtvBRmVzYRkYvasA5Z8= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= +github.com/docker/libtrust v0.0.0-20150526203908-9cbd2a1374f4/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= +github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4= +github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= +github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dubbogo/go-zookeeper v1.0.4-0.20211212162352-f9d2183d89d5 h1:XoR8SSVziXe698dt4uZYDfsmHpKLemqAgFyndQsq5Kw= github.com/dubbogo/go-zookeeper v1.0.4-0.20211212162352-f9d2183d89d5/go.mod h1:fn6n2CAEer3novYgk9ULLwAjuV8/g4DdC2ENwRb6E+c= 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/dvsekhvalnov/jose2go v0.0.0-20170216131308-f21a8cedbbae/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM= github.com/dvyukov/go-fuzz v0.0.0-20210914135545-4980593459a1/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= 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= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/elazarl/goproxy v0.0.0-20191011121108-aa519ddbe484 h1:pEtiCjIXx3RvGjlUJuCNxNOw0MNblyR9Wi+vJGBFh+8= +github.com/elazarl/goproxy v0.0.0-20191011121108-aa519ddbe484/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= +github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= @@ -475,6 +553,8 @@ github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGE github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= +github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= +github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/florianl/go-nflog/v2 v2.0.1/go.mod h1:g+SOgM/SuePn9bvS/eo3Ild7J71nSb29OzbxR+7cln0= @@ -494,8 +574,9 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4 github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= -github.com/fvbommel/sortorder v1.0.1 h1:dSnXLt4mJYH25uDDGa3biZNQsozaUWDSWeKJ0qqFfzE= github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= +github.com/fvbommel/sortorder v1.0.2 h1:mV4o8B2hKboCdkJm+a7uX/SIpZob4JzUpc5GGnM45eo= +github.com/fvbommel/sortorder v1.0.2/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7 h1:LofdAjjjqCSXMwLGgOgnE+rdPuvX9DxCqaHwKy7i/ko= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= @@ -537,6 +618,7 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= @@ -544,6 +626,7 @@ github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/gobuffalo/flect v0.2.0/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80= github.com/gobuffalo/flect v0.2.3/go.mod h1:vmkQwuZYhN5Pc4ljYQZzP+1sq+NEkK+lh20jmEmX3jc= github.com/gobuffalo/flect v0.2.4/go.mod h1:1ZyCLIbg0YD7sDkzvFdPoOydPtD8y9JQnrOROolUcM8= @@ -562,12 +645,20 @@ github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblf github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godror/godror v0.24.2/go.mod h1:wZv/9vPiUib6tkoDl+AZ/QLf5YZgMravZ7jxH2eQWAE= +github.com/gofrs/flock v0.7.3/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gofrs/flock v0.8.0 h1:MSdYClljsF3PbENUUEx85nkWfJSGfzYI9yEBZOJz6CY= github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= +github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0= +github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4= +github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= @@ -601,6 +692,7 @@ github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71 github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= 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= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -638,6 +730,9 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/certificate-transparency-go v1.0.10-0.20180222191210-5ab67e519c93/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg= +github.com/google/certificate-transparency-go v1.0.21 h1:Yf1aXowfZ2nuboBsg7iYGLmwsOARdV86pfH3g95wXmE= +github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -652,6 +747,7 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0= github.com/google/go-containerregistry v0.6.0 h1:niQ+8XD//kKgArIFwDVBXsWVWbde16LPdHMyNwSC8h4= github.com/google/go-containerregistry v0.6.0/go.mod h1:euCCtNbZ6tKqi1E72vwDj2xZcN5ttKpZLfa/wSo5iLw= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= @@ -710,6 +806,7 @@ github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= @@ -724,6 +821,7 @@ github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16 github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.2.0/go.mod h1:mJzapYve32yjrKlk9GbyCZHuPgZsrbyIbyKhSzOpg6s= github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= @@ -733,6 +831,12 @@ github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpg github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.14.6/go.mod h1:zdiPV4Yse/1gnckTHtghG4GkDEdKCRJduHpTxT3/jcw= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1:MJG/KsmcqMwFAkh8mTnAwhyKoB+sTAnY4CACC110tbU= +github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw= +github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= +github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= +github.com/hanwen/go-fuse v1.0.0/go.mod h1:unqXarDXqzAk0rt98O2tVndEPIpUgLD9+rwFisZH3Ok= +github.com/hanwen/go-fuse/v2 v2.0.4-0.20201208195215-4a458845028b/go.mod h1:0EQM6aH2ctVpvZ6a+onrQ/vaykxh2GH7hy3e13vzTUY= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/api v1.23.0 h1:L6e4v1AfoumqAHq/Rrsmuulev+nd7vltM3k8H329tyI= github.com/hashicorp/consul/api v1.23.0/go.mod h1:SfvUIT74b0EplDuNgAJQ/FVqSO6KyK2ia80UI39/Ye8= @@ -746,6 +850,7 @@ github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-cty-funcs v0.0.0-20200930094925-2721b1e36840/go.mod h1:Abjk0jbRkDaNCzsRhOv2iDCofYpX1eVsjozoiK63qLA= github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= @@ -770,14 +875,18 @@ github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go-version v1.3.0 h1:McDWVJIU/y+u1BRV06dPaLfLCaT7fUTJLp5r04x7iNw= github.com/hashicorp/go-version v1.3.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= +github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/hcl/v2 v2.8.2/go.mod h1:bQTN5mpo+jewjJgh8jr0JUguIi7qPHUF6yIfAEN3jqY= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= @@ -787,6 +896,9 @@ github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4 github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= +github.com/hashicorp/uuid v0.0.0-20160311170451-ebb0a03e909c/go.mod h1:fHzc09UnyJyqyW+bFuq864eh+wC7dj65aXmXLRe5to0= +github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= +github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= @@ -794,14 +906,19 @@ github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/hudl/fargo v1.4.0 h1:ZDDILMbB37UlAVLlWcJ2Iz1XuahZZTDZfdCKeclfq2s= github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo= +github.com/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJn+Ichc= +github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= +github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 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/ishidawataru/sctp v0.0.0-20191218070446-00ab2ac2db07/go.mod h1:co9pwDoBCm1kGxawmb4sPq0cSIOOWNPT4KnHotMP1Zg= +github.com/istio/viper v1.3.3-0.20190515210538-2789fed3109c h1:EFWADU43GY2T7NIYYbIHWdrG2hRiWyGSHeON57ZADBE= github.com/istio/viper v1.3.3-0.20190515210538-2789fed3109c/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= github.com/j-keck/arping v1.0.2/go.mod h1:aJbELhR92bSk7tp79AWM/ftfc90EfEi2bQJrbBFOsPw= @@ -815,6 +932,14 @@ github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 h1:IPJ3dvxmJ4uc github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jinzhu/gorm v0.0.0-20170222002820-5409931a1bb8/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo= +github.com/jinzhu/gorm v1.9.2 h1:lCvgEaqe/HVE+tjAR2mt4HbbHAZsQOv3XAZiEZV37iw= +github.com/jinzhu/gorm v1.9.2/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo= +github.com/jinzhu/inflection v0.0.0-20170102125226-1c35d901db3d/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a h1:eeaG9XMUvRBYXJi4pg1ZKM7nxc5AfXfojeLLW7O5J3k= +github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.0.0/go.mod h1:oHTiXerJ20+SfYcrdlBO7rzZRJWGwSTQ0iUY2jI6Gfc= +github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= @@ -825,6 +950,7 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGw github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmoiron/sqlx v1.3.1 h1:aLN7YINNZ7cYOPK3QC83dbM6KT0NMqVMw961TqrejlE= github.com/jmoiron/sqlx v1.3.1/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ= +github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8= github.com/johnlanni/gost v1.11.23-0.20220713132522-0967a24036c6 h1:i9IP6menkNYRAOJQ27+81deRmcyyirLZRXR5+BIilV0= github.com/johnlanni/gost v1.11.23-0.20220713132522-0967a24036c6/go.mod h1:PhJ8+qZJx+Txjx1KthNPuVkCvUca0jRLgKWj/noGgeI= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= @@ -855,12 +981,17 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1 github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= 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/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA= +github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= github.com/karrick/godirwalk v1.15.8 h1:7+rWAZPn9zuRxaIqqT8Ohs2Q2Ac0msBqwRdxNCr2VVs= github.com/karrick/godirwalk v1.15.8/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= @@ -870,8 +1001,9 @@ github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYs github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.0/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY= +github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -888,6 +1020,7 @@ github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= @@ -916,6 +1049,7 @@ github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f h1:sgU github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f/go.mod h1:UGmTpUd3rjbtfIpwAPrcfmGf/Z1HS95TATB+m57TPB8= github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042 h1:Bvq8AziQ5jFF4BHGAEDSqwPW1NJS3XshxbRCxtjFAZc= github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042/go.mod h1:TPpsiPUEh0zFL1Snz4crhMlBe60PYxRHr5oFF3rRYg0= +github.com/lib/pq v0.0.0-20150723085316-0dad96c0b94f/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.0 h1:Zx5DJFEYQXio93kgXnQ09fXNiUKsqv4OUEu2UtGcB1E= github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= @@ -923,13 +1057,16 @@ github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhn github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= +github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo= github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= github.com/lucas-clemente/quic-go v0.24.0 h1:ToR7SIIEdrgOhgVTHvPgdVRJfgVy+N0wQAagH7L4d5g= github.com/lucas-clemente/quic-go v0.24.0/go.mod h1:paZuzjXCE5mj6sikVLMvqXk8lJV2AsqtJ6bDhjEfxx0= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= +github.com/magiconair/properties v1.5.3/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -978,13 +1115,19 @@ github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m github.com/mattn/go-runewidth v0.0.12 h1:Y41i/hVW3Pgwr8gV+J23B9YEY0zxjptBuCWEaxmAOow= github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= +github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= github.com/mattn/go-shellwords v1.0.11/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= +github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= +github.com/mattn/go-sqlite3 v1.6.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY= github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43/go.mod h1:+t7E0lkKfbBsebllff1xdTmyJt8lH37niI6kwFk9OTo= github.com/mdlayher/genetlink v1.0.0/go.mod h1:0rJ0h4itni50A86M2kHcgS85ttZazNt7a8H2a2cw0Gc= github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= @@ -998,6 +1141,8 @@ github.com/mdlayher/netlink v1.3.0/go.mod h1:xK/BssKuwcRXHrtN04UBkwQ6dY9VviGGuri github.com/mdlayher/netlink v1.4.0/go.mod h1:dRJi5IABcZpBD2A3D0Mv/AiX8I9uDEu5oGkAVrekmf8= github.com/mdlayher/netlink v1.4.1/go.mod h1:e4/KuJ+s8UhfUpO9z00/fDZZmhSrs+oxyqAS9cNgn6Q= github.com/mdlayher/socket v0.0.0-20210307095302-262dc9984e00/go.mod h1:GAFlyu4/XV68LkQKYzKhIo/WW7j3Zi0YRAz/BOoanUc= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= 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= @@ -1005,7 +1150,10 @@ github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKju github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg= github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= +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= +github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU= +github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= @@ -1018,12 +1166,16 @@ github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20150613213606-2caf8efc9366/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= @@ -1031,18 +1183,32 @@ github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/moby/buildkit v0.8.2-0.20210401015549-df49b648c8bf h1:dHwWBX8OhYb69qVcT27rFSwzKsn4CRbg0HInQyVh33c= +github.com/moby/buildkit v0.8.2-0.20210401015549-df49b648c8bf/go.mod h1:GJcrUlTGFAPlEmPQtbrTsJYn+cy+Jwl7vTZS7jYAoow= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= -github.com/moby/moby v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible h1:NT0cwArZg/wGdvY8pzej4tPr+9WGmDdkF8Suj+mkz2g= -github.com/moby/moby v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +github.com/moby/sys/mount v0.1.0/go.mod h1:FVQFLDRWwyBjDTBNQXDlWnSFREqOo3OKX9aqhmeoo74= +github.com/moby/sys/mount v0.2.0/go.mod h1:aAivFE2LB3W4bACsUXChRHQ0qKWsetY4Y9V7sxOougM= +github.com/moby/sys/mount v0.3.0 h1:bXZYMmq7DBQPwHRxH/MG+u9+XF90ZOwoXpHTOznMGp0= +github.com/moby/sys/mount v0.3.0/go.mod h1:U2Z3ur2rXPFrFmy4q6WMwWrBOAQGYtYTRVM8BIvzbwk= +github.com/moby/sys/mountinfo v0.1.0/go.mod h1:w2t2Avltqx8vE7gX5l+QiBKxODu2TX0+Syr3h52Tw4o= +github.com/moby/sys/mountinfo v0.1.3/go.mod h1:w2t2Avltqx8vE7gX5l+QiBKxODu2TX0+Syr3h52Tw4o= github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= -github.com/moby/sys/mountinfo v0.4.1 h1:1O+1cHA1aujwEwwVMa2Xm2l+gIpUHyd3+D+d7LZh1kM= github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= +github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= +github.com/moby/sys/mountinfo v0.6.0 h1:gUDhXQx58YNrpHlK4nSL+7y2pxFZkUcXqzFDKWdC0Oo= +github.com/moby/sys/mountinfo v0.6.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= +github.com/moby/sys/signal v0.7.0 h1:25RW3d5TnQEoKvRbEKUGay6DCQ46IxAVTT9CUMgmsSI= +github.com/moby/sys/signal v0.7.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg= github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= -github.com/moby/term v0.0.0-20210610120745-9d4ed1856297 h1:yH0SvLzcbZxcJXho2yh7CqdENGMQe73Cw3woZBpPli0= +github.com/moby/sys/symlink v0.2.0 h1:tk1rOM+Ljp0nFmfOIBtlV3rTDlWOwFRhjEeAhZB0nZc= +github.com/moby/sys/symlink v0.2.0/go.mod h1:7uZVF2dqJjG/NsClqul95CqKOBRQyYSNnJ6BMgR/gFs= +github.com/moby/term v0.0.0-20201110203204-bea5bbe245bf/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= +github.com/moby/term v0.0.0-20221128092401-c43b287e0e0f h1:J/7hjLaHLD7epG0m6TBMGmp4NQ+ibBYLfeyJWdAIFLA= +github.com/moby/term v0.0.0-20221128092401-c43b287e0e0f/go.mod h1:15ce4BGCFxt7I5NQKT+HV0yEDxmf6fSysfEDiVo3zFM= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -1054,6 +1220,7 @@ github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/ github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/mrunalp/fileutils v0.0.0-20200520151820-abd8a0e76976/go.mod h1:x8F1gnqOkIEiO4rqoeEEEqQbo7HjGMTvyoq3gej4iT0= github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= @@ -1089,9 +1256,11 @@ github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= @@ -1102,9 +1271,11 @@ github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042 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= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= +github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= @@ -1121,32 +1292,43 @@ github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go. github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= +github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc10/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc92/go.mod h1:X1zlU4p7wOlX4+WRCz+hvlRv8phdL7UqbYD+vQwNMmE= github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= -github.com/opencontainers/runc v1.0.2 h1:opHZMaswlyxz1OuGpBE53Dwe4/xF7EZTY0A2L/FpCOg= github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= +github.com/opencontainers/runc v1.1.3 h1:vIXrkId+0/J2Ymu2m7VjGvbSlAId9XNRPhn2p4b+d8w= +github.com/opencontainers/runc v1.1.3/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg= github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.3-0.20200728170252-4d89ac9fbff6/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 h1:3snG66yBm59tKhhSPQrQ/0bCrv1LQbKt40LnUPiUxdc= github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= +github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= +github.com/opencontainers/selinux v1.10.1 h1:09LIPVRP3uuZGQvgR+SgMSNBd1Eb3vlRbGqQpoHsF8w= github.com/openshift/api v0.0.0-20200713203337-b2494ecb17dd h1:MV2FH/cm1wqoVCIL98GT46CMnXZw9faUoIzdZ4nfZw0= github.com/openshift/api v0.0.0-20200713203337-b2494ecb17dd/go.mod h1:vWmWTm4y7XR3wkLR+bDDjRbvkBfx2yP7yve6kfb7+Ts= github.com/openshift/build-machinery-go v0.0.0-20200713135615-1f43d26dccc7/go.mod h1:b1BuldmJlbA/xYtdZvKi+7j5YGB44qJUJDZ9zwiNCfE= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= +github.com/opentracing-contrib/go-stdlib v1.0.0/go.mod h1:qtI1ogk+2JhVPIXVc6q+NHziSmy2W5GbdQZFUHADCBU= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= @@ -1161,6 +1343,8 @@ github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtP github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= +github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM= +github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= @@ -1175,6 +1359,7 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +github.com/pkg/profile v1.5.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -1183,6 +1368,7 @@ github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSg github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.0-pre1.0.20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= @@ -1244,15 +1430,18 @@ github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rs/dnscache v0.0.0-20211102005908-e0241e321417/go.mod h1:qe5TWALJ8/a1Lqznoc5BDHpYX/8HU60Hm2AwRmqzxqA= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rubenv/sql-migrate v0.0.0-20210614095031-55d5740dbbcc h1:BD7uZqkN8CpjJtN/tScAKiccBikU4dlqe/gNrkRaPY4= github.com/rubenv/sql-migrate v0.0.0-20210614095031-55d5740dbbcc/go.mod h1:HFLT6i9iR4QBOF5rdCyjddC9t59ArqWJV2xx+jwcCMo= -github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= +github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= @@ -1260,14 +1449,21 @@ github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIH github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= github.com/safchain/ethtool v0.0.0-20210803160452-9aa261dae9b1/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +github.com/sanathkr/go-yaml v0.0.0-20170819195128-ed9d249f429b h1:jUK33OXuZP/l6babJtnLo1qsGvq6G9so9KMflGAm4YA= +github.com/sanathkr/go-yaml v0.0.0-20170819195128-ed9d249f429b/go.mod h1:8458kAagoME2+LN5//WxE71ysZ3B7r22fdgb7qVmXSY= +github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4= +github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= +github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= +github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/serialx/hashring v0.0.0-20190422032157-8b2912629002/go.mod h1:/yeG0My1xr/u+HZrFQ1tOQQQQrOawfyMUH13ai5brBc= github.com/shirou/gopsutil v3.20.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= @@ -1301,8 +1497,9 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= @@ -1318,10 +1515,13 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/cast v0.0.0-20150508191742-4d07383ffe94/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.1/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= @@ -1329,11 +1529,18 @@ github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHN github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= -github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= +github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= +github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= +github.com/spf13/jwalterweatherman v0.0.0-20141219030609-3d60171a6431/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.0/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -1370,15 +1577,26 @@ github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cb github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= github.com/tebeka/strftime v0.1.3 h1:5HQXOqWKYRFfNyBMNVc9z5+QzuBtIXy03psIhtdJYto= github.com/tebeka/strftime v0.1.3/go.mod h1:7wJm3dZlpr4l/oVK0t1HYIc4rMzQ2XJlOMIUJUJH6XQ= +github.com/theupdateframework/notary v0.6.1/go.mod h1:MOfgIfmox8s7/7fduvB2xyPPMJCrjRLRizA8OFwpnKY= +github.com/theupdateframework/notary v0.7.0 h1:QyagRZ7wlSpjT5N2qQAh/pN+DVqgekv4DzbAiAiEL3c= +github.com/theupdateframework/notary v0.7.0/go.mod h1:c9DRxcmhHmVLDay4/2fUYdISnHqbFDGRSlXPO0AhYWw= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tonistiigi/fsutil v0.0.0-20201103201449-0834f99b7b85 h1:014iQD8i8EabPWK2XgUuOTxg5s2nhfDmq6GupskfUO8= +github.com/tonistiigi/fsutil v0.0.0-20201103201449-0834f99b7b85/go.mod h1:a7cilN64dG941IOXfhJhlH0qB92hxJ9A1ewrdUmJ6xo= +github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea h1:SXhTLE6pb6eld/v/cCndK0AMpt1wiVFb/YYmqB3/QG0= +github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea/go.mod h1:WPnis/6cRcDZSUvVmezrxJPkiO87ThFYsoUiMwWNDJk= +github.com/tonistiigi/vt100 v0.0.0-20190402012908-ad4c4a574305 h1:y/1cL5AL2oRcfzz8CAHHhR6kDDfIOT0WEyH5k40sccM= +github.com/tonistiigi/vt100 v0.0.0-20190402012908-ad4c4a574305/go.mod h1:gXOLibKqQTRAVuVZ9gX7G9Ykky8ll8yb4slxsEMoY0c= github.com/toolkits/concurrent v0.0.0-20150624120057-a4371d70e3e3 h1:kF/7m/ZU+0D4Jj5eZ41Zm3IH/J8OElK1Qtd7tVKAwLk= github.com/toolkits/concurrent v0.0.0-20150624120057-a4371d70e3e3/go.mod h1:QDlpd3qS71vYtakd2hmdpqhJ9nwv6mD6A30bQ1BPBFE= 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/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/uber/jaeger-client-go v2.25.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= +github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= @@ -1395,6 +1613,9 @@ github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmF github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= +github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= +github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= +github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= @@ -1426,6 +1647,9 @@ github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMzt github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= +github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= +github.com/zclconf/go-cty v1.4.0/go.mod h1:nHzOclRkoj++EU9ZjSrZvRG0BXIWt8c7loYc0qXAFGQ= +github.com/zclconf/go-cty v1.7.1/go.mod h1:VDR4+I79ubFBGm1uJac1226K5yANQFHeauxPBoP54+o= github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= @@ -1469,8 +1693,9 @@ go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= -go.opentelemetry.io/proto/otlp v0.7.0 h1:rwOQPCuKAKmwGKq2aVNnYIibI6wnV7EvzgfTCzcdGg8= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.opentelemetry.io/proto/otlp v0.12.0 h1:CMJ/3Wp7iOWES+CYLfnBv+DVmPbB+kmy9PJ92XvlR6c= +go.opentelemetry.io/proto/otlp v0.12.0/go.mod h1:TsIjwGWIx5VFYv9KGVlOpxoBl5Dy+63SUguV7GGvlSQ= go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc= go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -1483,8 +1708,8 @@ 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 v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= @@ -1513,6 +1738,7 @@ golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -1523,12 +1749,15 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200422194213-44a606286825/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-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20201217014255-9d1352758620/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= @@ -1582,6 +1811,7 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1648,11 +1878,13 @@ golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qx 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-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= @@ -1675,8 +1907,9 @@ golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ 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-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw= +golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= 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= @@ -1710,6 +1943,7 @@ golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1718,6 +1952,7 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1767,6 +2002,7 @@ golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1794,6 +2030,7 @@ golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1810,17 +2047,22 @@ golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBc 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-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/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-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211020174200-9d6173849985/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= @@ -1828,6 +2070,7 @@ golang.org/x/sys v0.10.0/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= +golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= @@ -1846,6 +2089,7 @@ golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1872,6 +2116,7 @@ golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 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-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1911,6 +2156,7 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20200916195026-c9a70fc28ce3/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20200918232735-d647fc253266/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20201014170642-d1624618ad65/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= @@ -2066,9 +2312,11 @@ google.golang.org/genproto v0.0.0-20211016002631-37fc39342514/go.mod h1:5CzLGKJ6 google.golang.org/genproto v0.0.0-20211018162055-cf77aa76bad2/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211020151524-b7c3a969101a/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12 h1:DN5b3HU13J4sMd/QjDx34U6afpaexKTDdop+26pdjdk= google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 h1:hrbNEivu7Zn1pxvHk6MBrq9iE22woVILTHqexqBxe6I= +google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.0.5/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= @@ -2105,6 +2353,8 @@ google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnD google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w= google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= @@ -2121,10 +2371,13 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/cenkalti/backoff.v2 v2.2.1 h1:eJ9UAg01/HIHG987TwxvnzK2MgxXq97YY6rYDpY9aII= +gopkg.in/cenkalti/backoff.v2 v2.2.1/go.mod h1:S0QdOvT2AlerfSBkp0O+dk+bbIMaNbEmVk876gPCthU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -2133,11 +2386,14 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/dancannon/gorethink.v3 v3.0.5/go.mod h1:GXsi1e3N2OcKhcP6nsYABTiUejbWMFO4GY5a4pEaeEc= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fatih/pool.v2 v2.0.0/go.mod h1:8xVGeu1/2jr2wm5V9SPuMht2H5AEmf5aFMGSQixtjTY= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3 h1:m8OOJ4ccYHnx2f4gQwpno8nAX5OGOh7RLaaz0pj3Ogs= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +gopkg.in/gorethink/gorethink.v3 v3.0.5/go.mod h1:+3yIIHJUGMBK+wyPH+iN5TP+88ikFDfZdqTlK3Y9q8I= gopkg.in/gorp.v1 v1.7.2 h1:j3DWlAyGVv8whO7AcIWznQ2Yj7yJkn34B8s63GViAAw= gopkg.in/gorp.v1 v1.7.2/go.mod h1:Wo3h+DBQZIxATwftsglhdD/62zRFPhGhTiu5jUJmCaw= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= @@ -2148,6 +2404,8 @@ gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/rethinkdb/rethinkdb-go.v6 v6.2.1 h1:d4KQkxAaAiRY2h5Zqis161Pv91A37uZyJOx73duwUwM= +gopkg.in/rethinkdb/rethinkdb-go.v6 v6.2.1/go.mod h1:WbjuEoo1oadwzQ4apSDU+JTvmllEHtsNHS6y7vFc7iw= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= @@ -2203,16 +2461,14 @@ k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= k8s.io/apiserver v0.22.1/go.mod h1:2mcM6dzSt+XndzVQJX21Gx0/Klo7Aen7i0Ai6tIa400= -k8s.io/apiserver v0.22.2 h1:TdIfZJc6YNhu2WxeAOWq1TvukHF0Sfx0+ln4XK9qnL4= k8s.io/apiserver v0.22.2/go.mod h1:vrpMmbyjWrgdyOvZTSpsusQq5iigKNWv9o9KlDAbBHI= +k8s.io/apiserver v0.22.5 h1:71krQxCUz218ecb+nPhfDsNB6QgP1/4EMvi1a2uYBlg= +k8s.io/apiserver v0.22.5/go.mod h1:s2WbtgZAkTKt679sYtSudEQrTGWUSQAPe6MupLnlmaQ= k8s.io/cli-runtime v0.22.2 h1:fsd9rFk9FSaVq4SUq1fM27c8CFGsYZUJ/3BkgmjYWuY= k8s.io/cli-runtime v0.22.2/go.mod h1:tkm2YeORFpbgQHEK/igqttvPTRIHFRz5kATlw53zlMI= 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.18.3/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c= -k8s.io/code-generator v0.22.0/go.mod h1:eV77Y09IopzeXOJzndrDyCI88UBok2h6WxAlBwpxa+o= k8s.io/code-generator v0.22.2/go.mod h1:eV77Y09IopzeXOJzndrDyCI88UBok2h6WxAlBwpxa+o= -k8s.io/code-generator v0.22.5/go.mod h1:sbdWCOVob+KaQ5O7xs8PNNaCTpbWVqNgA6EPwLOmRNk= k8s.io/component-base v0.22.2 h1:vNIvE0AIrLhjX8drH0BgCNJcR4QZxMXcJzBsDplDx9M= k8s.io/component-base v0.22.2/go.mod h1:5Br2QhI9OTe79p+TzPe9JKNQYvEKbq9rTJDWllunGug= k8s.io/component-helpers v0.22.2/go.mod h1:+N61JAR9aKYSWbnLA88YcFr9K/6ISYvRNybX7QW7Rs8= @@ -2220,17 +2476,17 @@ 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/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc= -k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/gengo v0.0.0-20201203183100-97869a43a9d9/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/gengo v0.0.0-20211129171323-c02415ce4185/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/klog v0.2.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/klog/v2 v2.10.0 h1:R2HDMDJsHVTHA2n4RjwbeYXdOcBymXdX/JRb1v0VGhE= k8s.io/klog/v2 v2.10.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= -k8s.io/kube-openapi v0.0.0-20211020163157-7327e2aaee2b h1:RQzI0hL8LPCNd9eAZvuG8orAUrxXFiVqBKSU8fs6ygc= -k8s.io/kube-openapi v0.0.0-20211020163157-7327e2aaee2b/go.mod h1:gULf0pSS32YtLlO7+li/EV8/0y5mG/sO32H4OUsHIQg= +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/kubectl v0.22.2 h1:KMyYNZoBshaL3XKx04X07DtpoD4vMrdkfiN/G2Qx/PU= k8s.io/kubectl v0.22.2/go.mod h1:BApg2j0edxLArCOfO0ievI27EeTQqBDMNU9VQH734iQ= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= diff --git a/pkg/cmd/hgctl/docker/compose.go b/pkg/cmd/hgctl/docker/compose.go new file mode 100644 index 0000000000..33b6ad0997 --- /dev/null +++ b/pkg/cmd/hgctl/docker/compose.go @@ -0,0 +1,111 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package docker + +import ( + "context" + "io" + "strings" + + "github.com/compose-spec/compose-go/cli" + "github.com/docker/cli/cli/command" + "github.com/docker/cli/cli/flags" + "github.com/docker/compose/v2/cmd/formatter" + "github.com/docker/compose/v2/pkg/api" + "github.com/docker/compose/v2/pkg/compose" +) + +type Compose struct { + client *api.ServiceProxy + w io.Writer +} + +func NewCompose(w io.Writer) (*Compose, error) { + c := &Compose{w: w} + + dockerCli, err := command.NewDockerCli( + command.WithCombinedStreams(c.w), + // command.WithDefaultContextStoreConfig(), Deprecated, set during NewDockerCli + ) + if err != nil { + return nil, err + } + + opts := flags.NewClientOptions() + err = dockerCli.Initialize(opts) + if err != nil { + return nil, err + } + c.client = api.NewServiceProxy().WithService(compose.NewComposeService(dockerCli.Client(), dockerCli.ConfigFile())) + + return c, nil +} + +func (c Compose) Up(ctx context.Context, name string, configs []string, source string, detach bool) error { + pOpts, err := cli.NewProjectOptions( + configs, + cli.WithWorkingDirectory(source), + cli.WithDefaultConfigPath, + cli.WithName(name), + ) + if err != nil { + return err + } + + project, err := cli.ProjectFromOptions(pOpts) + if err != nil { + return err + } + + for i, s := range project.Services { + // TODO(WeixinX): Change from `Label` to `CustomLabels` after upgrading the dependency library github.com/compose-spec/compose-go + s.Labels = map[string]string{ + api.ProjectLabel: project.Name, + api.ServiceLabel: s.Name, + api.VersionLabel: api.ComposeVersion, + api.WorkingDirLabel: project.WorkingDir, + api.ConfigFilesLabel: strings.Join(project.ComposeFiles, ","), + api.OneoffLabel: "False", + } + project.Services[i] = s + } + project.WithoutUnnecessaryResources() + + // for log + var consumer api.LogConsumer + if !detach { + // TODO(WeixinX): Change to `formatter.NewLogConsumer(ctx, c.w, c.w, true, true, false)` after upgrading the dependency library github.com/compose-spec/compose-go + consumer = formatter.NewLogConsumer(ctx, c.w, true, true) + } + attachTo := make([]string, 0) + for _, svc := range project.Services { + attachTo = append(attachTo, svc.Name) + } + + return c.client.Up(ctx, project, api.UpOptions{ + Start: api.StartOptions{ + Attach: consumer, + AttachTo: attachTo, + }, + }) +} + +func (c Compose) List(ctx context.Context) ([]api.Stack, error) { + return c.client.List(ctx, api.ListOptions{}) +} + +func (c Compose) Down(ctx context.Context, name string) error { + return c.client.Down(ctx, name, api.DownOptions{}) +} diff --git a/pkg/cmd/hgctl/kubernetes/wasmplugin.go b/pkg/cmd/hgctl/kubernetes/wasmplugin.go new file mode 100644 index 0000000000..7305c698ce --- /dev/null +++ b/pkg/cmd/hgctl/kubernetes/wasmplugin.go @@ -0,0 +1,130 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package kubernetes + +import ( + "context" + + "github.com/spf13/pflag" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" +) + +const ( + DefaultHigressNamespace = "higress-system" + HigressExtGroup = "extensions.higress.io" + HigressExtVersion = "v1alpha1" + HigressExtAPIVersion = HigressExtGroup + "/" + HigressExtVersion + + WasmPluginKind = "WasmPlugin" + WasmPluginResource = "wasmplugins" +) + +var ( + HigressNamespace = DefaultHigressNamespace + WasmPluginGVK = schema.GroupVersionKind{Group: HigressExtGroup, Version: HigressExtVersion, Kind: WasmPluginKind} + WasmPluginGVR = schema.GroupVersionResource{Group: HigressExtGroup, Version: HigressExtVersion, Resource: WasmPluginResource} +) + +func AddHigressNamespaceFlags(flags *pflag.FlagSet) { + flags.StringVarP(&HigressNamespace, "namespace", "n", + DefaultHigressNamespace, "Namespace where Higress was installed") +} + +type WasmPluginClient struct { + dyn *DynamicClient +} + +func NewWasmPluginClient(dynClient *DynamicClient) *WasmPluginClient { + return &WasmPluginClient{dynClient} +} + +func (c WasmPluginClient) Get(ctx context.Context, name string) (*unstructured.Unstructured, error) { + return c.dyn.Get(ctx, WasmPluginGVR, HigressNamespace, name) +} + +func (c WasmPluginClient) List(ctx context.Context) (*unstructured.UnstructuredList, error) { + return c.dyn.List(ctx, WasmPluginGVR, HigressNamespace) +} + +func (c WasmPluginClient) Create(ctx context.Context, obj *unstructured.Unstructured) (*unstructured.Unstructured, error) { + return c.dyn.Create(ctx, WasmPluginGVR, HigressNamespace, obj) +} + +func (c WasmPluginClient) Delete(ctx context.Context, name string) (*unstructured.Unstructured, error) { + return c.dyn.Delete(ctx, WasmPluginGVR, HigressNamespace, name) +} + +func (c WasmPluginClient) Update(ctx context.Context, obj *unstructured.Unstructured) (*unstructured.Unstructured, error) { + return c.dyn.Update(ctx, WasmPluginGVR, HigressNamespace, obj) +} + +// TODO(WeixinX): Will be changed to WasmPlugin specific Client instead of Unstructured +type DynamicClient struct { + config *rest.Config + client dynamic.Interface +} + +func NewDynamicClient(clientConfig clientcmd.ClientConfig) (*DynamicClient, error) { + var ( + c DynamicClient + err error + ) + + c.config, err = clientConfig.ClientConfig() + if err != nil { + return nil, err + } + + c.client, err = dynamic.NewForConfig(c.config) + if err != nil { + return nil, err + } + + return &c, nil +} + +func (c DynamicClient) Get(ctx context.Context, gvr schema.GroupVersionResource, namespace, name string) (*unstructured.Unstructured, error) { + return c.client.Resource(gvr).Namespace(namespace).Get(ctx, name, metav1.GetOptions{}) +} + +func (c DynamicClient) List(ctx context.Context, gvr schema.GroupVersionResource, namespace string) (*unstructured.UnstructuredList, error) { + return c.client.Resource(gvr).Namespace(namespace).List(ctx, metav1.ListOptions{}) +} + +func (c DynamicClient) Create(ctx context.Context, gvr schema.GroupVersionResource, namespace string, obj *unstructured.Unstructured) (*unstructured.Unstructured, error) { + return c.client.Resource(gvr).Namespace(namespace).Create(ctx, obj, metav1.CreateOptions{}) +} + +func (c DynamicClient) Delete(ctx context.Context, gvr schema.GroupVersionResource, namespace, name string) (*unstructured.Unstructured, error) { + result, err := c.client.Resource(gvr).Namespace(namespace).Get(ctx, name, metav1.GetOptions{}) + if err != nil { + return nil, err + } + err = c.client.Resource(gvr).Namespace(namespace).Delete(ctx, name, metav1.DeleteOptions{}) + if err != nil { + return nil, err + } + return result, nil +} + +func (c DynamicClient) Update(ctx context.Context, gvr schema.GroupVersionResource, namespace string, + obj *unstructured.Unstructured) (*unstructured.Unstructured, error) { + return c.client.Resource(gvr).Namespace(namespace).Update(ctx, obj, metav1.UpdateOptions{}) +} diff --git a/pkg/cmd/hgctl/plugin/build/build.go b/pkg/cmd/hgctl/plugin/build/build.go new file mode 100644 index 0000000000..aae2fbe921 --- /dev/null +++ b/pkg/cmd/hgctl/plugin/build/build.go @@ -0,0 +1,778 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package build + +import ( + "context" + "encoding/json" + "fmt" + "io" + "os" + "os/signal" + "os/user" + "strings" + "syscall" + + "github.com/alibaba/higress/pkg/cmd/hgctl/plugin/option" + ptypes "github.com/alibaba/higress/pkg/cmd/hgctl/plugin/types" + "github.com/alibaba/higress/pkg/cmd/hgctl/plugin/utils" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/mount" + "github.com/docker/docker/client" + "github.com/docker/docker/pkg/stdcopy" + "github.com/pkg/errors" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + "github.com/spf13/viper" + cmdutil "k8s.io/kubectl/pkg/cmd/util" +) + +const ( + DefaultBuilderRepository = "higress-registry.cn-hangzhou.cr.aliyuncs.com/plugins/wasm-go-builder" + DefaultBuilderGo = "1.19" + DefaultBuilderTinyGo = "0.28.1" + DefaultBuilderOras = "1.0.0" + + MediaTypeSpec = "application/vnd.module.wasm.spec.v1+yaml" + MediaTypeREADME = "application/vnd.module.wasm.doc.v1+markdown" + MediaTypeREADME_ZH = "application/vnd.module.wasm.doc.v1.zh+markdown" + MediaTypeREADME_EN = "application/vnd.module.wasm.doc.v1.en+markdown" + MediaTypeIcon = "application/vnd.module.wasm.icon.v1+png" + MediaTypePlugin = "application/vnd.oci.image.layer.v1.tar+gzip" + + HostTempDirPattern = "higress-wasm-go-build-*" + HostDockerEntryPattern = "higress-wasm-go-build-docker-entrypoint-*.sh" + + ContainerWorkDir = "/workspace" + ContainerTempDir = "/higress_temp" // the directory to temporarily store the build products + ContainerOutDir = "/output" + ContainerDockerAuth = "/root/.docker/config.json" + ContainerEntryFile = "docker-entrypoint.sh" + ContainerEntryFilePath = "/" + ContainerEntryFile +) + +type Builder struct { + OptionFile string + option.BuildOptions + Username, Password string + + repository string + tempDir string + dockerEntrypoint string + uid, gid string + manualClean bool + + containerID string + containerConf types.ContainerCreateConfig + dockerCli *client.Client + w io.Writer + sig chan os.Signal // watch interrupt + stop chan struct{} // stop the build process when an interruption occurs + done chan struct{} // signal that the build process is finished + + utils.Debugger + *utils.YesOrNoPrinter +} + +func NewBuilder(f ConfigFunc) (*Builder, error) { + b := new(Builder) + if err := b.config(f); err != nil { + return nil, err + } + + return b, nil +} + +func NewCommand() *cobra.Command { + var bld Builder + v := viper.New() + + buildCmd := &cobra.Command{ + Use: "build", + Aliases: []string{"bld", "b"}, + Short: "Build Golang WASM plugin", + Example: ` # If the option.yaml file exists in the current path, do the following: + hgctl plugin build + + # Using "--model(-s)" to specify the WASM plugin configuration structure name, e.g. "HelloWorldConfig" + hgctl plugin build --model HelloWorldConfig + + # Using "--output-type(-t)" and "--output-dest(-d)" to push the build products as an OCI image to the specified repository + docker login + hgctl plugin build -s BasicAuthConfig -t image -d docker.io// + `, + PreRun: func(cmd *cobra.Command, args []string) { + cmdutil.CheckErr(bld.config(func(b *Builder) error { + return b.parseOptions(v, cmd) + })) + }, + + Run: func(cmd *cobra.Command, args []string) { + cmdutil.CheckErr(bld.Build()) + }, + } + + bld.bindFlags(v, buildCmd.PersistentFlags()) + + return buildCmd +} + +func (b *Builder) bindFlags(v *viper.Viper, flags *pflag.FlagSet) { + option.AddOptionFileFlag(&b.OptionFile, flags) + flags.StringVarP(&b.Username, "username", "u", "", "Username for pushing image to the docker repository") + flags.StringVarP(&b.Password, "password", "p", "", "Password for pushing image to the docker repository") + v.BindPFlags(flags) + + // this binding ensures that flags explicitly set on the command line have the + // highest priority, and if they are not set, they are read from the configuration file. + flags.StringP("builder-go", "g", DefaultBuilderGo, "Golang version in the official builder image") + v.BindPFlag("build.builder.go", flags.Lookup("builder-go")) + v.SetDefault("build.builder.go", DefaultBuilderGo) + + flags.StringP("builder-tinygo", "n", DefaultBuilderTinyGo, "TinyGo version in the official builder image") + v.BindPFlag("build.builder.tinygo", flags.Lookup("builder-tinygo")) + v.SetDefault("build.builder.tinygo", DefaultBuilderTinyGo) + + flags.StringP("builder-oras", "r", DefaultBuilderOras, "ORAS version in official the builder image") + v.BindPFlag("build.builder.oras", flags.Lookup("builder-oras")) + v.SetDefault("build.builder.oras", DefaultBuilderOras) + + flags.StringP("input", "i", "./", "Directory of the WASM plugin project to be built") + v.BindPFlag("build.input", flags.Lookup("input")) + v.SetDefault("build.input", "./") + + flags.StringP("output-type", "t", "files", "Output type of the build products. [files, image]") + v.BindPFlag("build.output.type", flags.Lookup("output-type")) + v.SetDefault("build.output.type", "files") + + flags.StringP("output-dest", "d", "./out", "Output destination of the build products") + v.BindPFlag("build.output.dest", flags.Lookup("output-dest")) + v.SetDefault("build.output.dest", "./out") + + flags.StringP("docker-auth", "a", "~/.docker/config.json", "Authentication configuration for pushing image to the docker repository") + v.BindPFlag("build.docker-auth", flags.Lookup("docker-auth")) + v.SetDefault("build.docker-auth", "~/.docker/config.json") + + flags.StringP("model-dir", "m", "./", "Directory of the WASM plugin configuration structure") + v.BindPFlag("build.model-dir", flags.Lookup("model-dir")) + v.SetDefault("build.model-dir", "./") + + flags.StringP("model", "s", "", "Structure name of the WASM plugin configuration") + v.BindPFlag("build.model", flags.Lookup("model")) + v.SetDefault("build.model", "PluginConfig") + + flags.BoolP("debug", "", false, "Enable debug mode") + v.BindPFlag("build.debug", flags.Lookup("debug")) + v.SetDefault("build.debug", false) +} + +func (b *Builder) Build() (err error) { + b.Debugf("build options: \n%s\n", b.String()) + + go func() { + err = b.doBuild() + }() + + // wait for an interruption to occur or finishing the build + select { + case <-b.sig: + b.interrupt() + b.Nof("\nInterrupt ...\n") + // wait for the doBuild process to exit, otherwise there will be unexpected bugs + b.waitForFinished() + // if the build process is interrupted, then we ignore the flag `manualClean` and clean up + // TODO(WeixinX): How do we clean up uploaded image when an interruption occurs? + b.Debugln("clean up for interrupting ...") + b.CleanupForError() + os.Exit(0) + + case <-b.done: + if err != nil { + if !b.manualClean { + b.Debugln("clean up for error ...") + b.CleanupForError() + } + return + } + if !b.manualClean { + b.Debugln("clean up for normal ...") + b.Cleanup() + } + } + + return +} + +var ( + waitIcon = "[-]" + successfulIcon = "[√]" +) + +func (b *Builder) doBuild() (err error) { + // finish here does not mean that the build was successful, + // but that the doBuild process is complete + defer b.finish() + + if err = b.generateMetadata(); err != nil { + return errors.Wrap(err, "failed to generate wasm plugin metadata files") + } + + b.Printf("%s pull the builder image ...\n", waitIcon) + ctx := context.TODO() + if err = b.imagePull(ctx); err != nil { + return errors.Wrapf(err, "failed to pull the builder image %s", b.builderImageRef()) + } + b.Yesf("%s pull the builder image: %s\n", successfulIcon, b.builderImageRef()) + + if err = b.addContainerConfByOutType(); err != nil { + return errors.Wrapf(err, "failed to add the additional container configuration for output type %q", b.Output.Type) + } + + b.Printf("%s create the builder container ...\n", waitIcon) + if err = b.containerCreate(ctx); err != nil { + return errors.Wrap(err, "failed to create the builder container") + } + b.Yesf("%s create the builder container: %s\n", successfulIcon, b.containerID) + + b.Printf("%s start the builder container ...\n", waitIcon) + if err = b.containerStart(ctx); err != nil { + return errors.Wrap(err, "failed to start the builder container") + } + + if b.Output.Type == "files" { + b.Yesf("%s finish building!\n", successfulIcon) + } else if b.Output.Type == "image" { + b.Yesf("%s finish building and pushing!\n", successfulIcon) + } + + return nil +} + +var errBuildAbort = errors.New("build aborted") + +func (b *Builder) generateMetadata() error { + // spec.yaml + if b.isInterrupted() { + return errBuildAbort + } + spec, err := os.Create(b.SpecYAMLPath()) + if err != nil { + return err + } + defer spec.Close() + meta, err := ptypes.ParseGoSrc(b.ModelDir, b.Model) + if err != nil { + return err + } + if err = utils.MarshalYamlWithIndentTo(spec, meta, 2); err != nil { + return err + } + + // TODO(WeixinX): More languages need to be supported + // README.md is required, README_{lang}.md is optional + if b.isInterrupted() { + return errBuildAbort + } + usages, err := meta.GetUsages() + if err != nil { + return errors.Wrap(err, "failed to get wasm usage") + } + for i, u := range usages { + // since `usages` are ordered by `I18nType` and currently only `en-US` and + // `zh-CN` are available, en-US is the default README.md language when en-US is + // present (because after sorting it is in the first place) + suffix := true + if i == 0 { + suffix = false + } + if err = genMarkdownUsage(&u, b.tempDir, suffix); err != nil { + return err + } + } + + return nil +} + +func (b *Builder) imagePull(ctx context.Context) error { + if b.isInterrupted() { + return errBuildAbort + } + r, err := b.dockerCli.ImagePull(ctx, b.builderImageRef(), types.ImagePullOptions{}) + if err != nil { + return err + } + + if b.isInterrupted() { + return errBuildAbort + } + io.Copy(b.w, r) + + return nil +} + +func (b *Builder) addContainerConfByOutType() error { + if b.isInterrupted() { + return errBuildAbort + } + + var err error + switch b.Output.Type { + case "files": + err = b.filesHandler() + case "image": + err = b.imageHandler() + default: + return errors.New("invalid output option, output type is unknown") + } + if err != nil { + return err + } + + return nil +} + +func (b *Builder) containerCreate(ctx context.Context) error { + if b.isInterrupted() { + return errBuildAbort + } + + resp, err := b.dockerCli.ContainerCreate(ctx, b.containerConf.Config, b.containerConf.HostConfig, + b.containerConf.NetworkingConfig, b.containerConf.Platform, b.containerConf.Name) + if err != nil { + return err + } + b.containerID = resp.ID + + return nil +} + +func (b *Builder) containerStart(ctx context.Context) error { + if b.isInterrupted() { + return errBuildAbort + } + if err := b.dockerCli.ContainerStart(ctx, b.containerID, types.ContainerStartOptions{}); err != nil { + return err + } + + if b.isInterrupted() { + return errBuildAbort + } + statusCh, errCh := b.dockerCli.ContainerWait(ctx, b.containerID, container.WaitConditionNotRunning) + select { + case err := <-errCh: + if err != nil { + return err + } + case <-statusCh: + } + + if b.isInterrupted() { + return errBuildAbort + } + logs, err := b.dockerCli.ContainerLogs(ctx, b.containerID, types.ContainerLogsOptions{ShowStdout: true, ShowStderr: true}) + if err != nil { + return err + } + + if b.isInterrupted() { + return errBuildAbort + } + _, err = stdcopy.StdCopy(b.w, b.w, logs) + if err != nil { + return err + } + + return nil +} + +var errWriteDockerEntrypoint = errors.New("failed to write docker entrypoint") + +func (b *Builder) filesHandler() error { + b.containerConf.HostConfig.Mounts = append(b.containerConf.HostConfig.Mounts, mount.Mount{ + // output dir for the build products + Type: mount.TypeBind, + Source: b.Output.Dest, + Target: ContainerOutDir, + }) + + ft := &FilesTmplFields{ + BuildSrcDir: ContainerWorkDir, + BuildDestDir: ContainerTempDir, + Output: ContainerOutDir, + UID: b.uid, + GID: b.uid, + Debug: b.Debug, + } + + if err := genFilesDockerEntrypoint(ft, b.dockerEntrypoint); err != nil { + return errors.Wrap(err, errWriteDockerEntrypoint.Error()) + } + + return nil +} + +var ( + optionalProducts = [][2]string{ + {"README_ZH.md", MediaTypeREADME_ZH}, + {"README_EN.md", MediaTypeREADME_EN}, + {"icon.png", MediaTypeIcon}, + } +) + +// TODO(WeixinX): If the image exists, no push is performed +func (b *Builder) imageHandler() error { + products := "" + for i, p := range optionalProducts { + fileName := p[0] + mediaType := p[1] + if i == 0 { + products = fmt.Sprintf("%s %s", fileName, mediaType) + } else { + products = fmt.Sprintf("%s %s %s", products, fileName, mediaType) + } + } + + // spec.yaml, README.md and plugin.tar.gz are required + basicCmd := fmt.Sprintf("oras push %s -u %s -p %s ./spec.yaml:%s ./README.md:%s", + b.Output.Dest, b.Username, b.Password, MediaTypeSpec, MediaTypeREADME) + + if b.Username == "" || b.Password == "" { + basicCmd = fmt.Sprintf("oras push %s ./spec.yaml:%s ./README.md:%s", + b.Output.Dest, MediaTypeSpec, MediaTypeREADME) + + b.containerConf.HostConfig.Mounts = append(b.containerConf.HostConfig.Mounts, mount.Mount{ + // docker auth + Type: mount.TypeBind, + Source: b.DockerAuth, + Target: ContainerDockerAuth, + }) + } + + it := &ImageTmplFields{ + BuildSrcDir: ContainerWorkDir, + BuildDestDir: ContainerTempDir, + Output: ContainerOutDir, + Username: b.Username, + Password: b.Password, + BasicCmd: basicCmd, + Products: products, + MediaTypePlugin: MediaTypePlugin, + Debug: b.Debug, + } + + if err := genImageDockerEntrypoint(it, b.dockerEntrypoint); err != nil { + return errors.Wrap(err, errWriteDockerEntrypoint.Error()) + } + + return nil +} + +// ConfigFunc is customized to set the fields of Builder +type ConfigFunc func(b *Builder) error + +func (b *Builder) config(f ConfigFunc) (err error) { + if err = f(b); err != nil { + return err + } + + // builder-go + b.Builder.Go = strings.TrimSpace(b.Builder.Go) + if b.Builder.Go == "" { + b.Builder.Go = DefaultBuilderGo + } + + // builder-tinygo + b.Builder.TinyGo = strings.TrimSpace(b.Builder.TinyGo) + if b.Builder.TinyGo == "" { + b.Builder.TinyGo = DefaultBuilderTinyGo + } + + // builder-oras + b.Builder.Oras = strings.TrimSpace(b.Builder.Oras) + if b.Builder.Oras == "" { + b.Builder.Oras = DefaultBuilderOras + } + + // input + b.Input = strings.TrimSpace(b.Input) + if b.Input == "" { + b.Input = "./" + } + inp, err := utils.GetAbsolutePath(b.Input) + if err != nil { + return errors.Wrapf(err, "failed to parse input option %q", b.Input) + } + b.Input = inp + + // output-type + b.Output.Type = strings.ToLower(strings.TrimSpace(b.Output.Type)) + if b.Output.Type == "" { + b.Output.Type = "files" + } + if b.Output.Type != "files" && b.Output.Type != "image" { + return errors.Errorf("invalid output type: %q, must be `files` or `image`", b.Output.Type) + } + + // output-dest + b.Output.Dest = strings.TrimSpace(b.Output.Dest) + if b.Output.Dest == "" { + b.Output.Dest = "./out" + } + out := b.Output.Dest + if b.Output.Type == "files" { + out, err = utils.GetAbsolutePath(b.Output.Dest) + if err != nil { + return errors.Wrapf(err, "failed to parse output destination %q", b.Output.Dest) + } + err = os.MkdirAll(b.Output.Dest, 0755) + if err != nil && !os.IsExist(err) { + return errors.Wrapf(err, "failed to create output destination %q", b.Output.Dest) + } + } + b.Output.Dest = out + + // docker-auth + b.DockerAuth = strings.TrimSpace(b.DockerAuth) + if b.DockerAuth == "" { + b.DockerAuth = "~/.docker/config.json" + } + auth, err := utils.GetAbsolutePath(b.DockerAuth) + if err != nil { + return errors.Wrapf(err, "failed to parse docker authentication %q", b.DockerAuth) + } + b.DockerAuth = auth + + // model-dir + b.ModelDir = strings.TrimSpace(b.ModelDir) + if b.ModelDir == "" { + b.ModelDir = "./" + } + + // option-file/username/password/model/debug: nothing to deal with + + // the unexported fields that users do not need to care about are as follows: + b.repository = DefaultBuilderRepository + + b.tempDir, err = os.MkdirTemp("", HostTempDirPattern) + if err != nil && !os.IsExist(err) { + return errors.Wrap(err, "failed to create the host temporary dir") + } + + dockerEp, err := os.CreateTemp("", HostDockerEntryPattern) + if err != nil && !os.IsExist(err) { + return errors.Wrap(err, "failed to create the docker entrypoint file") + } + err = dockerEp.Chmod(0777) + if err != nil { + return err + } + b.dockerEntrypoint = dockerEp.Name() + dockerEp.Close() + + u, err := user.Current() + if err != nil { + return errors.Wrap(err, "failed to get the current user information") + } + b.uid, b.gid = u.Uid, u.Gid + + b.containerConf = types.ContainerCreateConfig{ + Name: "higress-wasm-go-builder", + Config: &container.Config{ + Image: b.builderImageRef(), + Env: []string{ + "GO111MODULE=on", + "GOPROXY=https://goproxy.cn,direct", + }, + WorkingDir: ContainerWorkDir, + Entrypoint: []string{ContainerEntryFilePath}, + }, + HostConfig: &container.HostConfig{ + NetworkMode: "host", + Mounts: []mount.Mount{ + { // input dir that includes the wasm plugin source: main.go ... + Type: mount.TypeBind, + Source: b.Input, + Target: ContainerWorkDir, + }, + { // temp dir that includes the wasm plugin metadata: spec.yaml and README.md ... + Type: mount.TypeBind, + Source: b.tempDir, + Target: ContainerTempDir, + }, + { // entrypoint + Type: mount.TypeBind, + Source: b.dockerEntrypoint, + Target: ContainerEntryFilePath, + }, + }, + }, + } + + if b.dockerCli == nil { + b.dockerCli, err = client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) + if err != nil { + return errors.Wrap(err, "failed to initialize the docker client") + } + } + + if b.w == nil { + b.w = os.Stdout + } + + b.sig = make(chan os.Signal, 1) + b.stop = make(chan struct{}, 1) + b.done = make(chan struct{}, 1) + signal.Notify(b.sig, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, + syscall.SIGUSR1, syscall.SIGUSR2, syscall.SIGQUIT, syscall.SIGTSTP) + + if b.Debugger == nil { + b.Debugger = utils.NewDefaultDebugger(b.Debug, b.w) + } + + if b.YesOrNoPrinter == nil { + b.YesOrNoPrinter = utils.NewPrinter(b.w, utils.DefaultIdent, utils.DefaultYes, utils.DefaultNo) + } + + return nil +} + +func (b *Builder) parseOptions(v *viper.Viper, cmd *cobra.Command) error { + allOpt, err := option.ParseOptions(b.OptionFile, v, cmd.PersistentFlags()) + if err != nil { + return err + } + b.BuildOptions = allOpt.Build + + b.w = cmd.OutOrStdout() + + return nil +} + +func (b *Builder) finish() { + select { + case <-b.done: + default: + close(b.done) + } +} + +func (b *Builder) waitForFinished() { + <-b.done +} + +func (b *Builder) interrupt() { + select { + case <-b.stop: + default: + close(b.stop) + } +} + +func (b *Builder) isInterrupted() bool { + if b.stop == nil { + return true + } + select { + case <-b.stop: + return true + default: + return false + } +} + +// WithManualClean if set this option, then the temporary files and the container +// will not be cleaned up automatically, and you need to clean up manually +func (b *Builder) WithManualClean() { + b.manualClean = true +} + +func (b *Builder) WithWriter(w io.Writer) { + b.w = w +} + +// CleanupForError cleans up the temporary files and the container when an error occurs +func (b *Builder) CleanupForError() { + b.Cleanup() + b.removeOutputDest() +} + +// Cleanup cleans up the temporary files and the container +func (b *Builder) Cleanup() { + b.removeTempDir() + b.removeDockerEntrypoint() + b.removeBuilderContainer() + b.closeDockerCli() +} + +func (b *Builder) removeOutputDest() { + if b.BuildOptions.Output.Type == "files" { + b.Debugf("remove output destination %q\n", b.BuildOptions.Output.Dest) + os.RemoveAll(b.BuildOptions.Output.Dest) + } +} + +func (b *Builder) removeTempDir() { + if b.tempDir != "" { + b.Debugf("remove temporary directory %q\n", b.tempDir) + os.RemoveAll(b.tempDir) + } +} + +func (b *Builder) removeDockerEntrypoint() { + if b.dockerEntrypoint != "" { + b.Debugf("delete docker entrypoint %q\n", b.dockerEntrypoint) + os.Remove(b.dockerEntrypoint) + } +} + +func (b *Builder) removeBuilderContainer() { + if b.containerID != "" { + err := b.dockerCli.ContainerRemove(context.TODO(), b.containerID, types.ContainerRemoveOptions{Force: true}) + if err != nil { + b.Debugf("failed to remove container (%s): %s\n", b.containerConf.Name, b.containerID) + } else { + b.Debugf("remove container (%s): %s\n", b.containerConf.Name, b.containerID) + } + } +} + +func (b *Builder) closeDockerCli() { + if b.dockerCli != nil { + b.Debugln("close the docker client") + b.dockerCli.Close() + } +} + +func (b *Builder) builderImageRef() string { + return fmt.Sprintf("%s:go%s-tinygo%s-oras%s", b.repository, b.Builder.Go, b.Builder.TinyGo, b.Builder.Oras) +} + +func (b *Builder) SpecYAMLPath() string { + return fmt.Sprintf("%s/spec.yaml", b.tempDir) +} + +func (b *Builder) TempDir() string { + return b.tempDir +} + +func (b *Builder) String() string { + by, err := json.MarshalIndent(b, "", " ") + if err != nil { + return "" + } + return string(by) +} diff --git a/pkg/cmd/hgctl/plugin/build/templates.go b/pkg/cmd/hgctl/plugin/build/templates.go new file mode 100644 index 0000000000..b1e06ce68b --- /dev/null +++ b/pkg/cmd/hgctl/plugin/build/templates.go @@ -0,0 +1,194 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package build + +import ( + "os" + "text/template" + + "github.com/alibaba/higress/pkg/cmd/hgctl/plugin/types" +) + +const ( + filesDockerEntrypoint = `#!/bin/bash +set -e +{{- if eq .Debug true }} +set -x +{{- end }} + +go mod tidy +tinygo build -o {{ .BuildDestDir }}/plugin.wasm -scheduler=none -gc=custom -tags='custommalloc nottinygc_finalizer' -target=wasi {{ .BuildSrcDir }} + +mv {{ .BuildDestDir }}/* {{ .Output }}/ +chown -R {{ .UID }}:{{ .GID }} {{ .Output }} +` + imageDockerEntrypoint = `#!/bin/bash +set -e +{{- if eq .Debug true }} +set -x +{{- end }} + +go mod tidy +tinygo build -o {{ .BuildDestDir }}/plugin.wasm -scheduler=none -gc=custom -tags='custommalloc nottinygc_finalizer' -target=wasi {{ .BuildSrcDir }} + +cd {{ .BuildDestDir }} +tar czf plugin.tar.gz plugin.wasm +cmd="{{ .BasicCmd }}" +products=({{ .Products }}) +for ((i=0; i<${#products[*]}; i=i+2)); do + f=${products[i]} + typ=${products[i+1]} + if [ -e ${f} ]; then + cmd="${cmd} ./${f}:${typ}" + fi +done +cmd="${cmd} ./plugin.tar.gz:{{ .MediaTypePlugin }}" +eval ${cmd} +` +) + +type FilesTmplFields struct { + BuildSrcDir string + BuildDestDir string + Output string + UID, GID string + Debug bool +} + +type ImageTmplFields struct { + BuildSrcDir string + BuildDestDir string + Output string + Username, Password string + BasicCmd string + Products string + MediaTypePlugin string + Debug bool +} + +func genFilesDockerEntrypoint(ft *FilesTmplFields, target string) error { + f, err := os.OpenFile(target, os.O_CREATE|os.O_WRONLY, 0777) + if err != nil { + return err + } + defer f.Close() + + if err = template.Must(template.New("FilesDockerEntrypoint").Parse(filesDockerEntrypoint)).Execute(f, ft); err != nil { + return err + } + + return nil +} + +func genImageDockerEntrypoint(it *ImageTmplFields, target string) error { + f, err := os.OpenFile(target, os.O_CREATE|os.O_WRONLY, 0777) + if err != nil { + return err + } + defer f.Close() + + if err = template.Must(template.New("ImageDockerEntrypoint").Parse(imageDockerEntrypoint)).Execute(f, it); err != nil { + return err + } + + return nil +} + +const ( + readme_zh_CN = `> 该插件用法文件根据源代码自动生成,请根据需求自行修改! + +# 功能说明 + +{{ .Description }} + +# 配置字段 + +| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 | +| -------- | -------- | -------- | -------- | -------- | +{{- range .ConfigEntries }} +| {{ .Name }} | {{ .Type }} | {{ .Requirement }} | {{ .Default }} | {{ .Description }} | +{{- end }} + +# 配置示例 + +` + "```yaml" + ` +{{ .Example }} +` + "```" + ` +` + + readme_en_US = `> THIS PLUGIN USAGE FILE IS AUTOMATICALLY GENERATED BASED ON THE SOURCE CODE. MODIFY IT AS REQUIRED! + +# Description + +{{ .Description }} + +# Configuration + +| Name | Type | Requirement | Default | Description | +| -------- | -------- | -------- | -------- | -------- | +{{- range .ConfigEntries }} +| {{ .Name }} | {{ .Type }} | {{ .Requirement }} | {{ .Default }} | {{ .Description }} | +{{- end }} + +# Examples + +` + "```yaml" + ` +{{ .Example }} +` + "```" + ` +` +) + +func genMarkdownUsage(u *types.WasmUsage, dir string, suffix bool) error { + md, err := os.Create(i18n2MDTitle(u.I18nType, dir, suffix)) + if err != nil { + return err + } + defer md.Close() + + if err = template.Must(template.New("MD_Usage").Parse(i18n2MD(u.I18nType))).Execute(md, u); err != nil { + return err + } + + return nil +} + +func i18n2MD(i18n types.I18nType) string { + switch i18n { + case types.I18nEN_US: + return readme_en_US + case types.I18nZH_CN: + return readme_zh_CN + default: + return readme_zh_CN + } +} + +func i18n2MDTitle(i18n types.I18nType, dir string, suffix bool) string { + var file string + if !suffix { + file = "README.md" + } else { + switch i18n { + case types.I18nEN_US: + file = "README_EN.md" + case types.I18nZH_CN: + file = "README_ZH.md" + default: + file = "README_ZH.md" + } + } + + return dir + "/" + file +} diff --git a/pkg/cmd/hgctl/plugin/config/config.go b/pkg/cmd/hgctl/plugin/config/config.go new file mode 100644 index 0000000000..7f32495ca4 --- /dev/null +++ b/pkg/cmd/hgctl/plugin/config/config.go @@ -0,0 +1,30 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package config + +import "github.com/spf13/cobra" + +func NewCommand() *cobra.Command { + configCmd := &cobra.Command{ + Use: "config", + Aliases: []string{"conf", "cnf"}, + Short: "Configure the WasmPlugin manifest", + } + + configCmd.AddCommand(newCreateCommand()) + configCmd.AddCommand(newEditCommand()) + + return configCmd +} diff --git a/pkg/cmd/hgctl/plugin/config/create.go b/pkg/cmd/hgctl/plugin/config/create.go new file mode 100644 index 0000000000..b2561bd19f --- /dev/null +++ b/pkg/cmd/hgctl/plugin/config/create.go @@ -0,0 +1,76 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package config + +import ( + "fmt" + "io" + "os" + + "github.com/alibaba/higress/pkg/cmd/hgctl/plugin/utils" + + "github.com/pkg/errors" + "github.com/spf13/cobra" + cmdutil "k8s.io/kubectl/pkg/cmd/util" +) + +func newCreateCommand() *cobra.Command { + var target string + + createCmd := &cobra.Command{ + Use: "create", + Aliases: []string{"c"}, + Short: "Create the WASM plugin configuration template file", + Example: ` hgctl plugin config create`, + Run: func(cmd *cobra.Command, args []string) { + cmdutil.CheckErr(create(cmd.OutOrStdout(), target)) + }, + } + + createCmd.PersistentFlags().StringVarP(&target, "target", "t", "./", "Directory where the configuration is generated") + + return createCmd +} + +func create(w io.Writer, target string) error { + target, err := utils.GetAbsolutePath(target) + if err != nil { + return errors.Wrap(err, "invalid target path") + } + if err = os.MkdirAll(target, 0755); err != nil { + return err + } + if err = GenPluginConfYAML(configHelpTmpl, target); err != nil { + return errors.Wrap(err, "failed to create configuration template") + } + + fmt.Fprintf(w, "Created configuration template %q\n", fmt.Sprintf("%s/%s", target, "plugin-conf.yaml")) + + return nil +} + +var configHelpTmpl = &PluginConf{ + Name: "Plugin Name", + Namespace: "higress-system", + Title: "Display Name", + Description: "Plugin Description", + IconUrl: "Plugin Icon", + Version: "0.1.0", + Category: "auth | security | protocol | flow-control | flow-monitor | custom", + Phase: "UNSPECIFIED_PHASE | AUTHN | AUTHZ | STATS", + Priority: 0, + Config: " Plugin Configuration", + Url: "Plugin Image URL", +} diff --git a/pkg/cmd/hgctl/plugin/config/edit.go b/pkg/cmd/hgctl/plugin/config/edit.go new file mode 100644 index 0000000000..07b49feb6d --- /dev/null +++ b/pkg/cmd/hgctl/plugin/config/edit.go @@ -0,0 +1,143 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package config + +import ( + "bytes" + "context" + "fmt" + "io" + "os" + + k8s "github.com/alibaba/higress/pkg/cmd/hgctl/kubernetes" + "github.com/alibaba/higress/pkg/cmd/options" + + "github.com/pkg/errors" + "github.com/spf13/cobra" + k8serr "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/util/yaml" + "k8s.io/cli-runtime/pkg/printers" + cmdutil "k8s.io/kubectl/pkg/cmd/util" + "k8s.io/kubectl/pkg/cmd/util/editor" +) + +func newEditCommand() *cobra.Command { + var name string + + editCmd := &cobra.Command{ + Use: "edit", + Aliases: []string{"e"}, + Short: "Edit the installed WASM plugin configuration", + Example: ` # Edit the installed WASM plugin 'request-block' + hgctl plugin config edit -p request-block + `, + Run: func(cmd *cobra.Command, args []string) { + cmdutil.CheckErr(edit(cmd.OutOrStdout(), name)) + }, + } + + flags := editCmd.PersistentFlags() + options.AddKubeConfigFlags(flags) + k8s.AddHigressNamespaceFlags(flags) + flags.StringVarP(&name, "name", "p", "", "Name of the WASM plugin that needs to be edited") + + return editCmd +} + +func edit(w io.Writer, name string) error { + // TODO(WeixinX): Use WasmPlugin Object type instead of Unstructured + dynCli, err := k8s.NewDynamicClient(options.DefaultConfigFlags.ToRawKubeConfigLoader()) + if err != nil { + return errors.Wrap(err, "failed to build kubernetes dynamic client") + } + cli := k8s.NewWasmPluginClient(dynCli) + + originalObj, err := cli.Get(context.TODO(), name) + if err != nil { + if k8serr.IsNotFound(err) { + return errors.Errorf("wasm plugin %q is not found", fmt.Sprintf("%s/%s", k8s.HigressNamespace, name)) + } + return errors.Wrapf(err, "failed to get wasm plugin %q", fmt.Sprintf("%s/%s", k8s.HigressNamespace, name)) + } + + originalObj.SetGroupVersionKind(k8s.WasmPluginGVK) + originalObj.SetManagedFields(nil) // TODO(WeixinX): Managed Fields should be written back + + buf := &bytes.Buffer{} + var wObj io.Writer = buf + printer := printers.YAMLPrinter{} + if err = printer.PrintObj(originalObj.DeepCopyObject(), wObj); err != nil { + return err + } + original := buf.Bytes() + e := editor.NewDefaultEditor(editorEnvs()) + edited, file, err := e.LaunchTempFile("higress-wasm-edit-", ".yaml", buf) + if err != nil { + return errors.Wrap(err, "failed to launch editor") + } + defer os.Remove(file) + + if bytes.Equal(cmdutil.StripComments(original), cmdutil.StripComments(edited)) { // no change + fmt.Fprintf(w, "edit %q canceled, no change\n", + fmt.Sprintf("%s/%s", originalObj.GetNamespace(), originalObj.GetName())) + return nil + } + + var editedObj unstructured.Unstructured + eBuf := bytes.NewReader(edited) + dc := yaml.NewYAMLOrJSONDecoder(eBuf, 4096) + if err = dc.Decode(&editedObj); err != nil { + return err + } + if !keepSameMeta(&editedObj, originalObj) { + fmt.Fprintln(w, "Warning: ensure that the apiVersion, kind, namespace, and name are the same as the original and are automatically corrected") + } + + ret, err := cli.Update(context.TODO(), &editedObj) + if err != nil { + return errors.Wrapf(err, "failed to update wasm plugin %q", + fmt.Sprintf("%s/%s", originalObj.GetNamespace(), originalObj.GetName())) + } + + fmt.Fprintf(w, "Edited wasm plugin %q\n", fmt.Sprintf("%s/%s", ret.GetNamespace(), ret.GetName())) + + return nil +} + +func editorEnvs() []string { + return []string{ + "KUBE_EDITOR", + "EDITOR", + } +} + +// to avoid changing the apiVersion, kind, namespace and name, keep them the same as the original +func keepSameMeta(edited, original *unstructured.Unstructured) bool { + same := true + if edited.GroupVersionKind().String() != original.GroupVersionKind().String() { + edited.SetGroupVersionKind(original.GroupVersionKind()) + same = false + } + if edited.GetNamespace() != original.GetNamespace() { + edited.SetNamespace(original.GetNamespace()) + same = false + } + if edited.GetName() != original.GetName() { + edited.SetName(original.GetName()) + same = false + } + return same +} diff --git a/pkg/cmd/hgctl/plugin/config/templates.go b/pkg/cmd/hgctl/plugin/config/templates.go new file mode 100644 index 0000000000..dfbdc26dc3 --- /dev/null +++ b/pkg/cmd/hgctl/plugin/config/templates.go @@ -0,0 +1,160 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package config + +import ( + "encoding/json" + "fmt" + "os" + "strings" + "text/template" + + "github.com/alibaba/higress/pkg/cmd/hgctl/plugin/types" + "github.com/alibaba/higress/pkg/cmd/hgctl/plugin/utils" + + "gopkg.in/yaml.v3" +) + +// TODO(WeixinX): Use 'hgctl plugin push' command to fill the image url automatically +const pluginConfYAML = `# File generated by hgctl. Modify as required. +# See: https://higress.io/zh-cn/docs/plugins/intro + +apiVersion: extensions.higress.io/v1alpha1 +kind: WasmPlugin +metadata: + name: {{ .Name }} + namespace: {{ .Namespace }} + annotations: + higress.io/wasm-plugin-title: {{ .Title }} + higress.io/wasm-plugin-description: {{ .Description }} + higress.io/wasm-plugin-icon: {{ .IconUrl }} + labels: + higress.io/wasm-plugin-name: {{ .Name }} + higress.io/wasm-plugin-category: {{ .Category }} + higress.io/wasm-plugin-version: {{ .Version }} + higress.io/resource-definer: higress + higress.io/wasm-plugin-built-in: "false" +spec: + phase: {{ .Phase }} + priority: {{ .Priority }} +{{ .Config }} + # Please fill the image url in according to your needs + url: {{ .Url }} +` + +type PluginConf struct { + Name string + Namespace string + Title string + Description string + IconUrl string + Version string + Category string + Phase string + Priority int64 + Config string + Url string +} + +func (pc *PluginConf) String() string { + b, err := json.MarshalIndent(pc, "", " ") + if err != nil { + return "" + } + return string(b) +} + +// GenPluginConfYAML generates plugin-conf.yaml based on the template +func GenPluginConfYAML(p *PluginConf, dir string) error { + path := fmt.Sprintf("%s/plugin-conf.yaml", dir) + f, err := os.Create(path) + if err != nil { + return err + } + defer f.Close() + + if err = template.Must(template.New("PluginConfYAML").Parse(pluginConfYAML)).Execute(f, p); err != nil { + return err + } + + return nil +} + +// ExtractPluginConfFrom extracts the params of plugin-conf.yaml from spec.yaml. +// input params `config`, `url` are only used to implement the command `hgctl plugin install -g ` +func ExtractPluginConfFrom(spec *types.WasmPluginMeta, config, url string) (*PluginConf, error) { + if config == "" { + // by default, Example from spec.yaml is used as the defaultConfig for the wasm plugin + var obj map[string]interface{} + example := spec.GetConfigExample() + if err := yaml.Unmarshal([]byte(example), &obj); err != nil { + return nil, err + } + conf := struct { + DefaultConfig map[string]interface{} `yaml:"defaultConfig,omitempty"` + }{DefaultConfig: obj} + b, err := utils.MarshalYamlWithIndent(conf, 2) + if err != nil { + return nil, err + } + + config = string(b) + } + + pc := &PluginConf{ + Name: spec.Info.Name, + Namespace: "higress-system", + Title: spec.Info.Title, + Description: spec.Info.Description, + IconUrl: spec.Info.IconUrl, + Version: spec.Info.Version, + Category: string(spec.Info.Category), + Phase: string(spec.Spec.Phase), + Priority: spec.Spec.Priority, + Config: utils.AddIndent(config, strings.Repeat(" ", 2)), + Url: url, + } + pc.withDefaultValue() + + return pc, nil +} + +func (pc *PluginConf) withDefaultValue() { + if pc.Name == "" { + pc.Name = "Unnamed" + } + if pc.Namespace == "" { + pc.Namespace = "higress-system" + } + if pc.Title == "" { + pc.Title = "Untitled" + } + if pc.Description == "" { + pc.Description = "No description" + } + if pc.IconUrl == "" { + pc.IconUrl = types.Category2IconUrl(types.Category(pc.Category)) + } + if pc.Version == "" { + pc.Version = "0.1.0" + } + if pc.Category == "" { + pc.Category = string(types.CategoryDefault) + } + if pc.Phase == "" { + pc.Phase = string(types.PhaseDefault) + } + +} diff --git a/pkg/cmd/hgctl/plugin/init/init.go b/pkg/cmd/hgctl/plugin/init/init.go new file mode 100644 index 0000000000..b54dcb2d9e --- /dev/null +++ b/pkg/cmd/hgctl/plugin/init/init.go @@ -0,0 +1,92 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package plugininit + +import ( + "fmt" + "io" + "os" + + "github.com/alibaba/higress/pkg/cmd/hgctl/plugin/option" + "github.com/alibaba/higress/pkg/cmd/hgctl/plugin/utils" + + "github.com/AlecAivazis/survey/v2/terminal" + "github.com/pkg/errors" + "github.com/spf13/cobra" + cmdutil "k8s.io/kubectl/pkg/cmd/util" +) + +func NewCommand() *cobra.Command { + var target string + + initCmd := &cobra.Command{ + Use: "init", + Aliases: []string{"ini", "i"}, + Short: "Initialize a Golang WASM plugin project", + Example: ` hgctl plugin init`, + Run: func(cmd *cobra.Command, args []string) { + cmdutil.CheckErr(runInit(cmd.OutOrStdout(), target)) + }, + } + + initCmd.PersistentFlags().StringVarP(&target, "target", "t", "./", "Directory where the project is initialized") + + return initCmd +} + +func runInit(w io.Writer, target string) (err error) { + ans := answer{} + err = utils.Ask(questions, &ans) + if err != nil { + if errors.Is(err, terminal.InterruptErr) { + fmt.Fprintf(w, "Interrupted\n") + return nil + } + return errors.Wrap(err, "failed to initialize the project") + } + + target, err = utils.GetAbsolutePath(target) + if err != nil { + return errors.Wrap(err, "invalid target directory") + } + dir := fmt.Sprintf("%s/%s", target, ans.Name) + err = os.MkdirAll(dir, 0755) + defer func() { + if err != nil { + os.RemoveAll(dir) + err = errors.Wrap(err, "failed to initialize the project") + + } + }() + if err != nil { + return + } + if err = genGoMain(&ans, dir); err != nil { + return errors.Wrap(err, "failed to create main.go") + } + if err = genGoMod(&ans, dir); err != nil { + return errors.Wrap(err, "failed to create go.mod") + } + if err = genGitIgnore(dir); err != nil { + return errors.Wrap(err, "failed to create .gitignore") + } + if err = option.GenOptionYAML(dir); err != nil { + return errors.Wrap(err, "failed to create option.yaml") + } + + fmt.Fprintf(w, "Initialized the project in %q\n", dir) + + return nil +} diff --git a/pkg/cmd/hgctl/plugin/init/templates.go b/pkg/cmd/hgctl/plugin/init/templates.go new file mode 100644 index 0000000000..9957e527b2 --- /dev/null +++ b/pkg/cmd/hgctl/plugin/init/templates.go @@ -0,0 +1,296 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package plugininit + +import ( + "fmt" + "os" + "text/template" + + "github.com/AlecAivazis/survey/v2" + "github.com/alibaba/higress/pkg/cmd/hgctl/plugin/types" +) + +const ( + goMain = `// File generated by hgctl. Modify as required. +// See: https://higress.io/zh-cn/docs/user/wasm-go#2-%E7%BC%96%E5%86%99-maingo-%E6%96%87%E4%BB%B6 + +package main + +import ( + "github.com/tidwall/gjson" + "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm" + "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types" + "github.com/alibaba/higress/plugins/wasm-go/pkg/wrapper" +) + +func main() { + wrapper.SetCtx( + "{{ .Name }}", + wrapper.ParseConfigBy(parseConfig), + wrapper.ProcessRequestHeadersBy(onHttpRequestHeaders), + ) +} + +// @Name {{ .Name }} +// @Category {{ .Category }} +// @Phase {{ .Phase }} +// @Priority {{ .Priority }} +// @Title {{ .I18nType }} {{ .Title }} +// @Description {{ .I18nType }} {{ .Description }} +// @IconUrl {{ .IconUrl }} +// @Version {{ .Version }} +// +// @Contact.name {{ .ContactName }} +// @Contact.url {{ .ContactUrl }} +// @Contact.email {{ .ContactEmail }} +// +// @Example +// firstField: hello +// secondField: world +// @End +// +type PluginConfig struct { + // @Title 第一个字段,注解格式为 @Title [语言] [标题],语言缺省值为 en-US + // @Description 字符串的前半部分,注解格式为 @Description [语言] [描述],语言缺省值为 en-US + firstField string ` + "`required:\"true\"`" + ` + + // @Title en-US Second Field, annotation format is @Title [language] [title], language defaults to en-US + // @Description en-US The second half of the string, annotation format is @Description [language] [description], language defaults to en-US + secondField string ` + "`required:\"true\"`" + ` +} + +func parseConfig(json gjson.Result, config *PluginConfig, log wrapper.Log) error { + config.firstField = json.Get("firstField").String() + config.secondField = json.Get("secondField").String() + return nil +} + +func onHttpRequestHeaders(ctx wrapper.HttpContext, config PluginConfig, log wrapper.Log) types.Action { + err := proxywasm.AddHttpRequestHeader(config.firstField, config.secondField) + if err != nil { + log.Critical("failed to set request header") + } + return types.ActionContinue +} +` + goMod = `// File generated by hgctl. Modify as required. + +module {{ .Name }} + +go 1.19 + +require ( + github.com/alibaba/higress/plugins/wasm-go v0.0.0-20231019123123-86b223bc75f1 + github.com/tetratelabs/proxy-wasm-go-sdk v0.22.0 + github.com/tidwall/gjson v1.14.3 +) +` + + gitIgnore = `# File generated by hgctl. Modify as required. + +* + +!/.gitignore + +!*.go +!go.sum +!go.mod + +!LICENSE +!*.md +!*.yaml +!*.yml + +!*/ + +/out +/test +` +) + +func genGoMain(ans *answer, dir string) error { + path := fmt.Sprintf("%s/main.go", dir) + f, err := os.Create(path) + if err != nil { + return err + } + defer f.Close() + + if err = template.Must(template.New("GoMain").Parse(goMain)).Execute(f, ans); err != nil { + return err + } + + return nil +} + +func genGoMod(ans *answer, dir string) error { + path := fmt.Sprintf("%s/go.mod", dir) + f, err := os.Create(path) + if err != nil { + return err + } + defer f.Close() + + if err = template.Must(template.New("GoMod").Parse(goMod)).Execute(f, ans); err != nil { + return err + } + + return nil +} + +func genGitIgnore(dir string) error { + path := fmt.Sprintf("%s/.gitignore", dir) + f, err := os.Create(path) + if err != nil { + return err + } + defer f.Close() + + if _, err = f.WriteString(gitIgnore); err != nil { + return err + } + + return nil +} + +// obtain parameters through command line interaction +type answer struct { + Name string + Category string + Phase string + Priority int64 + I18nType string + Title string + Description string + IconUrl string + Version string + + ContactName string + ContactUrl string + ContactEmail string +} + +var questions = []*survey.Question{ + { + Name: "Name", + Prompt: &survey.Input{ + Message: "Plugin name:", + Default: "hello-world", + }, + Validate: survey.Required, + }, + { + Name: "Category", + Prompt: &survey.Select{ + Message: "Choose a plugin category:", + Options: []string{ + string(types.CategoryCustom), + string(types.CategoryAuth), + string(types.CategorySecurity), + string(types.CategoryProtocol), + string(types.CategoryFlowControl), + string(types.CategoryFlowMonitor), + }, + Default: string(types.CategoryCustom), + }, + Validate: survey.Required, + }, + { + Name: "Phase", + Prompt: &survey.Select{ + Message: "Choose a execution phase:", + Options: []string{ + string(types.PhaseUnspecified), + string(types.PhaseAuthn), + string(types.PhaseAuthz), + string(types.PhaseStats), + }, + Default: string(types.PhaseUnspecified), + }, + Validate: survey.Required, + }, + { + Name: "Priority", + Prompt: &survey.Input{ + Message: "Execution priority:", + Default: "0", + }, + Validate: survey.Required, + }, + { + Name: "I18nType", + Prompt: &survey.Select{ + Message: "Choose a language:", + Options: []string{ + string(types.I18nEN_US), + string(types.I18nZH_CN), + }, + Default: string(types.I18nDefault), + }, + Validate: survey.Required, + }, + { + Name: "Title", + Prompt: &survey.Input{ + Message: "Display name in the plugin market:", + Default: "Hello World", + }, + Validate: survey.Required, + }, + { + Name: "Description", + Prompt: &survey.Input{ + Message: "Description of the plugin functionality:", + Default: "This is a demo plugin", + }, + }, + { + Name: "IconUrl", + Prompt: &survey.Input{ + Message: "Display icon in the plugin market:", + Default: "", + }, + }, + { + Name: "Version", + Prompt: &survey.Input{ + Message: "Plugin version:", + Default: "0.1.0", + }, + Validate: survey.Required, + }, + { + Name: "ContactName", + Prompt: &survey.Input{ + Message: "Name of developer:", + Default: "", + }, + }, + { + Name: "ContactUrl", + Prompt: &survey.Input{ + Message: "Homepage of developer:", + Default: "", + }, + }, + { + Name: "ContactEmail", + Prompt: &survey.Input{ + Message: "Email of developer:", + Default: "", + }, + }, +} diff --git a/pkg/cmd/hgctl/plugin/install/asker.go b/pkg/cmd/hgctl/plugin/install/asker.go new file mode 100644 index 0000000000..c8c7a73557 --- /dev/null +++ b/pkg/cmd/hgctl/plugin/install/asker.go @@ -0,0 +1,744 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package install + +import ( + "fmt" + "strconv" + "strings" + + "github.com/alibaba/higress/pkg/cmd/hgctl/plugin/types" + "github.com/alibaba/higress/pkg/cmd/hgctl/plugin/utils" + + "github.com/AlecAivazis/survey/v2" + "github.com/mitchellh/mapstructure" + "github.com/pkg/errors" + "github.com/santhosh-tekuri/jsonschema/v5" +) + +const ( + askInterrupted = "X Interrupted." + invalidSyntax = "X Invalid syntax." + failedToValidate = "X Failed to validate: not satisfied with schema." + addConfSuccessful = "√ Successful to add configuration." +) + +var iconIdent = strings.Repeat(" ", 2) + +type Asker interface { + Ask() error +} + +type WasmPluginSpecConfAsker struct { + resp *WasmPluginSpecConf + + ingAsk *IngressAsker + domAsk *DomainAsker + glcAsk *GlobalConfAsker + + printer *utils.YesOrNoPrinter +} + +func NewWasmPluginSpecConfAsker(ingAsk *IngressAsker, domAsk *DomainAsker, glcAsk *GlobalConfAsker, printer *utils.YesOrNoPrinter) *WasmPluginSpecConfAsker { + return &WasmPluginSpecConfAsker{ + ingAsk: ingAsk, + domAsk: domAsk, + glcAsk: glcAsk, + printer: printer, + } +} + +func (p *WasmPluginSpecConfAsker) Ask() error { + var ( + wpc = NewPluginSpecConf() + + globalConf map[string]interface{} + ingressRule *IngressMatchRule + domainRule *DomainMatchRule + + scopeA = newScopeAsker(p.printer) + rewriteA = newRewriteAsker(p.printer) + ruleA = newRuleAsker(p.printer) + + complete = false + ) + + for { + err := scopeA.Ask() + if err != nil { + return err + } + scope := scopeA.resp + + switch scope { + case types.ScopeInstance: + err = ruleA.Ask() + if err != nil { + return err + } + rule := ruleA.resp + + switch rule { + case ruleIngress: + if ingressRule != nil { + p.printer.Yesf("\n%s\n", ingressRule) + err = rewriteA.Ask() + if err != nil { + return err + } + if !rewriteA.resp { + continue + } + } + + p.ingAsk.scope = scope + err = p.ingAsk.Ask() + if err != nil { + return err + } + ingressRule = p.ingAsk.resp + + case ruleDomain: + if domainRule != nil { + p.printer.Yesf("\n%s\n", domainRule) + err = rewriteA.Ask() + if err != nil { + return err + } + if !rewriteA.resp { + continue + } + } + + p.domAsk.scope = scope + err = p.domAsk.Ask() + if err != nil { + return err + } + domainRule = p.domAsk.resp + } + + case types.ScopeGlobal: + if globalConf != nil { + b, _ := utils.MarshalYamlWithIndent(globalConf, 2) + p.printer.Yesf("\n%s\n", string(b)) + err = rewriteA.Ask() + if err != nil { + return err + } + if !rewriteA.resp { + continue + } + } + + p.glcAsk.scope = scope + err = p.glcAsk.Ask() + if err != nil { + return err + } + globalConf = p.glcAsk.resp + + case "Complete": + complete = true + break + } + + if complete { + break + } + } + + if globalConf != nil { + wpc.DefaultConfig = globalConf + } + if ingressRule != nil { + wpc.MatchRules = append(wpc.MatchRules, ingressRule) + } + if domainRule != nil { + wpc.MatchRules = append(wpc.MatchRules, domainRule) + } + + p.printer.Yesln("The complete configuration is as follows:") + p.printer.Yesf("\n%s\n", wpc) + p.resp = wpc + return nil +} + +type IngressAsker struct { + resp *IngressMatchRule + + structName string + schema *types.JSONSchemaProps + scope types.Scope + + vld *jsonschema.Schema // for validation + printer *utils.YesOrNoPrinter +} + +func NewIngressAsker(structName string, schema *types.JSONSchemaProps, vld *jsonschema.Schema, printer *utils.YesOrNoPrinter) *IngressAsker { + return &IngressAsker{ + structName: structName, + schema: schema, + vld: vld, + printer: printer, + } +} + +func (i *IngressAsker) Ask() error { + continueA := newContinueAsker(i.printer) + ings := make([]string, 0) + for { + var ing string + err := utils.AskOne(&survey.Input{ + Message: "Enter the matched ingress:", + Help: "Matching ingress resource object, the matching format is: namespace/ingress name", + }, &ing) + if err != nil { + return err + } + + ing = strings.TrimSpace(ing) + if ing != "" { + ings = append(ings, ing) + } + + err = continueA.Ask() + if err != nil { + return err + } + if !continueA.resp { + break + } + } + + i.printer.Yesln(iconIdent + "Ingress:") + as, err := recursivePrompt(i.structName, i.schema, i.scope, i.printer) + if err != nil { + return err + } + if ok, ve := validate(i.vld, as); !ok { + i.printer.Noln(failedToValidate) + i.printer.Noln(ve) + return nil + } + + i.resp = &IngressMatchRule{ + Ingress: ings, + Config: as, + } + i.printer.Yesln(addConfSuccessful) + + return nil +} + +type DomainAsker struct { + resp *DomainMatchRule + + structName string + schema *types.JSONSchemaProps + scope types.Scope + + vld *jsonschema.Schema // for validation + printer *utils.YesOrNoPrinter +} + +func NewDomainAsker(structName string, schema *types.JSONSchemaProps, vld *jsonschema.Schema, printer *utils.YesOrNoPrinter) *DomainAsker { + return &DomainAsker{ + structName: structName, + schema: schema, + vld: vld, + printer: printer, + } +} + +func (d *DomainAsker) Ask() error { + continueA := newContinueAsker(d.printer) + doms := make([]string, 0) + for { + var dom string + err := utils.AskOne(&survey.Input{ + Message: "Enter the matched domain:", + Help: "match domain name, support generic domain name", + }, &dom) + if err != nil { + return err + } + + dom = strings.TrimSpace(dom) + if dom != "" { + doms = append(doms, dom) + } + + err = continueA.Ask() + if err != nil { + return err + } + if !continueA.resp { + break + } + } + + d.printer.Yesln(iconIdent + "Domain:") + as, err := recursivePrompt(d.structName, d.schema, d.scope, d.printer) + if err != nil { + return err + } + if ok, ve := validate(d.vld, as); !ok { + d.printer.Noln(failedToValidate) + d.printer.Noln(ve) + return nil + } + + d.resp = &DomainMatchRule{ + Domain: doms, + Config: as, + } + d.printer.Yesln(addConfSuccessful) + + return nil +} + +type GlobalConfAsker struct { + resp map[string]interface{} + + structName string + schema *types.JSONSchemaProps + scope types.Scope + + vld *jsonschema.Schema // for validation + printer *utils.YesOrNoPrinter +} + +func NewGlobalConfAsker(structName string, schema *types.JSONSchemaProps, vld *jsonschema.Schema, printer *utils.YesOrNoPrinter) *GlobalConfAsker { + return &GlobalConfAsker{ + structName: structName, + schema: schema, + vld: vld, + printer: printer, + } +} + +func (g *GlobalConfAsker) Ask() error { + g.printer.Yesln(iconIdent + "Global:") + as, err := recursivePrompt(g.structName, g.schema, g.scope, g.printer) + if err != nil { + return err + } + if ok, ve := validate(g.vld, as); !ok { + g.printer.Noln(failedToValidate) + g.printer.Noln(ve) + return nil + } + + g.resp = as.(map[string]interface{}) + g.printer.Yesln(addConfSuccessful) + + return nil +} + +type continueAsker struct { + resp bool + + printer *utils.YesOrNoPrinter +} + +func newContinueAsker(printer *utils.YesOrNoPrinter) *continueAsker { + return &continueAsker{printer: printer} +} + +func (c *continueAsker) Ask() error { + resp := true + err := utils.AskOne(&survey.Confirm{ + Message: fmt.Sprintf("%scontinue?", c.printer.Ident()), + Default: true, + }, &resp) + if err != nil { + return err + } + + c.resp = resp + return nil +} + +type rewriteAsker struct { + resp bool + + printer *utils.YesOrNoPrinter +} + +func newRewriteAsker(printer *utils.YesOrNoPrinter) *rewriteAsker { + return &rewriteAsker{printer: printer} +} + +func (r *rewriteAsker) Ask() error { + resp := false + err := utils.AskOne(&survey.Confirm{ + Message: fmt.Sprintf("%sThe configuration already exists as shown above. Do you want to rewrite it?", r.printer.Ident()), + Default: false, + }, &resp) + if err != nil { + return err + } + + r.resp = resp + return nil +} + +type scopeAsker struct { + resp types.Scope + + printer *utils.YesOrNoPrinter +} + +func newScopeAsker(printer *utils.YesOrNoPrinter) *scopeAsker { + return &scopeAsker{printer: printer} +} + +func (s *scopeAsker) Ask() error { + var resp string + err := utils.AskOne(&survey.Select{ + Message: fmt.Sprintf("%sChoose a configuration effective scope or complete:", s.printer.Ident()), + Options: []string{ + // TODO(WeixinX): Not visible to the user, instead Global, Ingress, and Domain are asked in ruleAsker + string(types.ScopeInstance), + string(types.ScopeGlobal), + "Complete", + }, + Default: string(types.ScopeInstance), + }, &resp) + if err != nil { + return err + } + + s.resp = types.Scope(resp) + return nil +} + +type ruleAsker struct { + resp Rule + + printer *utils.YesOrNoPrinter +} + +func newRuleAsker(printer *utils.YesOrNoPrinter) *ruleAsker { + return &ruleAsker{printer: printer} +} + +func (r *ruleAsker) Ask() error { + var resp string + err := utils.AskOne(&survey.Select{ + Message: fmt.Sprintf("%sChoose Ingress or Domain:", r.printer.Ident()), + Options: []string{ + string(ruleIngress), + string(ruleDomain), + }, + Default: string(ruleIngress), + }, &resp) + if err != nil { + return err + } + + r.resp = Rule(resp) + return nil +} + +type WasmPluginSpecConf struct { + DefaultConfig map[string]interface{} `yaml:"defaultConfig,omitempty"` + MatchRules []MatchRule `yaml:"matchRules,omitempty"` +} + +func NewPluginSpecConf() *WasmPluginSpecConf { + return &WasmPluginSpecConf{ + MatchRules: make([]MatchRule, 0), + } +} + +func (p *WasmPluginSpecConf) String() string { + if len(p.DefaultConfig) == 0 && len(p.MatchRules) == 0 { + return " " + } + + b, _ := utils.MarshalYamlWithIndent(p, 2) + return string(b) +} + +type MatchRule interface { + String() string +} + +type IngressMatchRule struct { + Ingress []string `json:"ingress" yaml:"ingress" mapstructure:"ingress"` + Config interface{} `json:"config" yaml:"config" mapstructure:"config"` +} + +func (i IngressMatchRule) String() string { + b, _ := utils.MarshalYamlWithIndent(i, 2) + return string(b) +} + +func decodeIngressMatchRule(obj map[string]interface{}) (*IngressMatchRule, error) { + var ing IngressMatchRule + if err := mapstructure.Decode(obj, &ing); err != nil { + return nil, err + } + + return &ing, nil +} + +type DomainMatchRule struct { + Domain []string `json:"domain" yaml:"domain" mapstructure:"domain"` + Config interface{} `json:"config" yaml:"config" mapstructure:"config"` +} + +func (d DomainMatchRule) String() string { + b, _ := utils.MarshalYamlWithIndent(d, 2) + return string(b) +} + +func decodeDomainMatchRule(obj map[string]interface{}) (*DomainMatchRule, error) { + var dom DomainMatchRule + if err := mapstructure.Decode(obj, &dom); err != nil { + return nil, err + } + + return &dom, nil +} + +type Rule string + +const ( + ruleIngress Rule = "Ingress" + ruleDomain Rule = "Domain" +) + +func recursivePrompt(structName string, schema *types.JSONSchemaProps, selScope types.Scope, printer *utils.YesOrNoPrinter) (interface{}, error) { + printer.IncIdentRepeat() + defer printer.DecIndentRepeat() + return doPrompt(structName, nil, schema, types.ScopeAll, selScope, printer) +} + +func doPrompt(fieldName string, parent, schema *types.JSONSchemaProps, oriScope, selScope types.Scope, printer *utils.YesOrNoPrinter) (interface{}, error) { + if schema.Title == "" { + schema.Title = fieldName + } + if schema.Description == "" { + schema.Description = fieldName + } + required := true + if parent != nil { + required = isRequired(fieldName, parent.Required) + } + msg, help := fieldTips(fieldName, parent, schema, required, printer) + + switch types.JsonType(schema.Type) { + case types.JsonTypeObject: + printer.Println(iconIdent + msg) + obj := make(map[string]interface{}) + m := schema.GetPropertiesOrderMap() + for _, name := range m.Keys() { + propI, _ := m.Get(name) + prop := propI.(types.JSONSchemaProps) + + if parent == nil { // keep topmost scope + if prop.Scope == types.ScopeGlobal { + oriScope = types.ScopeGlobal + } else if prop.Scope == types.ScopeInstance || prop.Scope == "" { + oriScope = types.ScopeInstance + } + } + + if !matchesScope(oriScope, selScope, prop.Scope) { + continue + } + + printer.IncIdentRepeat() + v, err := doPrompt(name, schema, &prop, oriScope, selScope, printer) + printer.DecIndentRepeat() + if err != nil { + return nil, err + } + if v != nil { + obj[name] = v + } + } + + if len(obj) == 0 { + return nil, nil + } + return obj, nil + + case types.JsonTypeArray: + printer.Println(iconIdent + msg) + continueA := newContinueAsker(printer) + arr := make([]interface{}, 0) + for { + printer.IncIdentRepeat() + v, err := doPrompt("item", schema, schema.Items.Schema, oriScope, selScope, printer) + if err != nil { + printer.DecIndentRepeat() + return nil, err + } + if v != nil { + arr = append(arr, v) + } + + err = continueA.Ask() + printer.DecIndentRepeat() + if err != nil { + return nil, err + } + + if !continueA.resp { + break + } + } + + if len(arr) == 0 { + return nil, nil + } + return arr, nil + + case types.JsonTypeInteger, types.JsonTypeNumber, types.JsonTypeBoolean, types.JsonTypeString: + for { + var inp string + if err := utils.AskOne(&survey.Input{ + Message: msg, + Help: help, + }, &inp); err != nil { + return nil, err + } + if inp == "" && !required { + return nil, nil + } + + switch types.JsonType(schema.Type) { + case types.JsonTypeInteger: + v, err := strconv.ParseInt(inp, 10, 64) + if err != nil { + if errors.Is(err, strconv.ErrSyntax) { + printer.Nof("%s %q type is invalid.\n", invalidSyntax, inp) + continue + } + return nil, err + } + return v, nil + case types.JsonTypeNumber: + v, err := strconv.ParseFloat(inp, 64) + if err != nil { + if errors.Is(err, strconv.ErrSyntax) { + printer.Nof("%s %q type is invalid.\n", invalidSyntax, inp) + continue + } + return nil, err + } + return v, nil + case types.JsonTypeBoolean: + v, err := strconv.ParseBool(inp) + if err != nil { + if errors.Is(err, strconv.ErrSyntax) { + printer.Nof("%s %q type is invalid.\n", invalidSyntax, inp) + continue + } + return nil, err + } + return v, nil + case types.JsonTypeString: + return inp, nil + default: + return inp, nil + } + } + + default: + return nil, fmt.Errorf("unsupported type: %s", schema.Type) + } +} + +func matchesScope(oriScope, selScope, scope types.Scope) bool { + return (oriScope == selScope) || + (selScope == types.ScopeInstance && (scope == selScope || scope == "" || scope == types.ScopeAll)) || + (selScope == types.ScopeGlobal && (scope == selScope || scope == types.ScopeAll)) +} + +func fieldTips(fieldName string, parent, schema *types.JSONSchemaProps, required bool, printer *utils.YesOrNoPrinter) (string, string) { + var msg, help string + if fieldName == "item" { + msg = fmt.Sprintf("%s%s(%s)", printer.Ident(), fieldName, schema.Type) + help = fmt.Sprintf("%s%s: %s", printer.Ident(), parent.Title, parent.Description) + } else { + req := schema.JoinRequirementsBy(types.I18nEN_US, required) + msg = fmt.Sprintf("%s%s(%s, %s)", printer.Ident(), fieldName, schema.Type, req) + help = fmt.Sprintf("%s%s: %s", printer.Ident(), schema.Title, schema.Description) + } + + return msg, help +} + +func isRequired(name string, required []string) bool { + req := false + for _, n := range required { + if name == n { + req = true + break + } + } + return req +} + +func validate(schema *jsonschema.Schema, v interface{}) (bool, error) { + if err := schema.Validate(v); err != nil { + err = convertValidationError(err.(*jsonschema.ValidationError)) + return false, err + } + return true, nil +} + +func convertValidationError(ve *jsonschema.ValidationError) error { + de := ve.DetailedOutput() + if de.Valid { + return nil + } + + errs := make([]error, 0) + if de.Error != "" { + errs = append(errs, errors.New(de.Error)) + } + errs = append(errs, doConvertValidationError(de.Errors, errs)...) + if len(errs) == 0 { + return nil + } + + var ret error + for i, err := range errs { + if i == 0 { + ret = fmt.Errorf("%w", err) + } else { + ret = fmt.Errorf("%s\n%w", ret.Error(), err) + } + } + return ret +} + +func doConvertValidationError(de []jsonschema.Detailed, errs []error) []error { + for _, e := range de { + if e.Error != "" { + errs = append(errs, errors.New(e.Error)) + } + if len(e.Errors) > 0 { + errs = append(errs, doConvertValidationError(e.Errors, errs)...) + } + } + return errs +} diff --git a/pkg/cmd/hgctl/plugin/install/install.go b/pkg/cmd/hgctl/plugin/install/install.go new file mode 100644 index 0000000000..6ff87a51b1 --- /dev/null +++ b/pkg/cmd/hgctl/plugin/install/install.go @@ -0,0 +1,383 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package install + +import ( + "context" + "encoding/json" + "fmt" + "io" + "os" + "strings" + + k8s "github.com/alibaba/higress/pkg/cmd/hgctl/kubernetes" + "github.com/alibaba/higress/pkg/cmd/hgctl/plugin/build" + "github.com/alibaba/higress/pkg/cmd/hgctl/plugin/config" + "github.com/alibaba/higress/pkg/cmd/hgctl/plugin/option" + "github.com/alibaba/higress/pkg/cmd/hgctl/plugin/types" + "github.com/alibaba/higress/pkg/cmd/hgctl/plugin/utils" + "github.com/alibaba/higress/pkg/cmd/options" + + "github.com/AlecAivazis/survey/v2/terminal" + "github.com/pkg/errors" + "github.com/santhosh-tekuri/jsonschema/v5" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + "github.com/spf13/viper" + k8serr "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + k8syaml "k8s.io/apimachinery/pkg/util/yaml" + cmdutil "k8s.io/kubectl/pkg/cmd/util" +) + +type installer struct { + optionFile string + bldOpts option.BuildOptions + insOpts option.InstallOptions + + cli *k8s.WasmPluginClient + w io.Writer + utils.Debugger +} + +func NewCommand() *cobra.Command { + var ins installer + v := viper.New() + + installCmd := &cobra.Command{ + Use: "install", + Aliases: []string{"ins", "i"}, + Short: "Install WASM plugin", + Example: ` # Install WASM plugin using a WasmPlugin manifest + hgctl plugin install -y plugin-conf.yaml + + # Install WASM plugin through the Golang WASM plugin project (do it by relying on option.yaml now) + docker login + hgctl plugin install -g ./ + `, + + PreRun: func(cmd *cobra.Command, args []string) { + cmdutil.CheckErr(ins.config(v, cmd)) + }, + + Run: func(cmd *cobra.Command, args []string) { + cmdutil.CheckErr(ins.install(cmd.PersistentFlags())) + }, + } + + flags := installCmd.PersistentFlags() + options.AddKubeConfigFlags(flags) + option.AddOptionFileFlag(&ins.optionFile, flags) + v.BindPFlags(flags) + + flags.StringP("namespace", "n", k8s.HigressNamespace, "Namespace where Higress was installed") + v.BindPFlag("install.namespace", flags.Lookup("namespace")) + v.SetDefault("install.namespace", k8s.DefaultHigressNamespace) + + flags.StringP("spec-yaml", "s", "./out/spec.yaml", "Use to validate WASM plugin configuration") + v.BindPFlag("install.spec-yaml", flags.Lookup("spec-yaml")) + v.SetDefault("install.spec-yaml", "./test/plugin-spec-yaml") + + // TODO(WeixinX): + // - Change "--from-yaml (-y)" to "--from-oci (-o)" and implement command line interaction like "--from-go-src" + // - Add "--from-jar (-j)" + flags.StringP("from-yaml", "y", "./test/plugin-conf.yaml", "Install WASM plugin using a WasmPlugin manifest") + v.BindPFlag("install.from-yaml", flags.Lookup("from-yaml")) + v.SetDefault("install.from-yaml", "./test/plugin-conf.yaml") + + flags.StringP("from-go-src", "g", "", "Install WASM plugin through the Golang WASM plugin project") + v.BindPFlag("install.from-go-src", flags.Lookup("from-go-src")) + v.SetDefault("install.from-go-src", "") + + flags.BoolP("debug", "", false, "Enable debug mode") + v.BindPFlag("install.debug", flags.Lookup("debug")) + v.SetDefault("install.debug", false) + + return installCmd +} + +func (ins *installer) config(v *viper.Viper, cmd *cobra.Command) error { + allOpt, err := option.ParseOptions(ins.optionFile, v, cmd.PersistentFlags()) + if err != nil { + return err + } + // TODO(WeixinX): Avoid relying on build options, add a new option "--push/--image" for installing from go src + ins.bldOpts = allOpt.Build + ins.insOpts = allOpt.Install + + dynCli, err := k8s.NewDynamicClient(options.DefaultConfigFlags.ToRawKubeConfigLoader()) + if err != nil { + return errors.Wrap(err, "failed to build kubernetes dynamic client") + } + ins.cli = k8s.NewWasmPluginClient(dynCli) + ins.w = cmd.OutOrStdout() + ins.Debugger = utils.NewDefaultDebugger(ins.insOpts.Debug, ins.w) + + return nil +} + +func (ins *installer) install(flags *pflag.FlagSet) (err error) { + ins.Debugf("install option:\n%s\n", ins.String()) + + if ins.insOpts.FromGoSrc == "" || flags.Changed("from-yaml") { + err = ins.yamlHandler() + } else { + err = ins.goHandler() + } + return +} + +func (ins *installer) yamlHandler() error { + return ins.doInstall(true) +} + +func (ins *installer) goHandler() error { + // 0. ensure output.type == image + if ins.bldOpts.Output.Type != "image" { + return errors.New("output type must be image") + } + + // 1. build the WASM plugin project and push the image to the registry + bld, err := build.NewBuilder(func(b *build.Builder) error { + b.BuildOptions = ins.bldOpts + b.Debug = ins.insOpts.Debug + b.WithManualClean() // keep spec.yaml + b.WithWriter(ins.w) + return nil + }) + if err != nil { + return errors.Wrap(err, "failed to initialize builder") + } + err = bld.Build() + if err != nil { + bld.Debugln("clean up for error ...") + bld.CleanupForError() + return errors.Wrap(err, "failed to build and push wasm plugin") + } + defer bld.Cleanup() + + // 2. command-line interaction lets the user enter the wasm plugin configuration + specPath := bld.SpecYAMLPath() + spec, err := types.ParseSpecYAML(specPath) + if err != nil { + return errors.Wrapf(err, "failed to parse spec.yaml: %s", specPath) + } + vld, err := buildSchemaValidator(spec) + if err != nil { + return err + } + + example := spec.GetConfigExample() + schema := spec.Spec.ConfigSchema.OpenAPIV3Schema + printer := utils.DefaultPrinter() + asker := NewWasmPluginSpecConfAsker( + NewIngressAsker(bld.Model, schema, vld, printer), + NewDomainAsker(bld.Model, schema, vld, printer), + NewGlobalConfAsker(bld.Model, schema, vld, printer), + printer, + ) + + printer.Yesln("Please enter the configurations for the WASM plugin you want to install:") + printer.Yesln("Configuration example:") + printer.Yesf("\n%s\n", example) + + err = asker.Ask() + if err != nil { + if errors.Is(err, terminal.InterruptErr) { + printer.Noln(askInterrupted) + return nil + } + panic(err) + } + + // 3. generate the WasmPlugin manifest + wpc := asker.resp + if err != nil { + return errors.Wrap(err, "failed to marshal wasm plugin config") + } + // get the parameters of plugin-conf.yaml from spec.yaml + pc, err := config.ExtractPluginConfFrom(spec, wpc.String(), bld.Output.Dest) + if err != nil { + return errors.Wrapf(err, "failed to get the parameters of plugin-conf.yaml from %s", specPath) + } + ins.Debugf("plugin-conf.yaml params:\n%s\n", pc.String()) + if err = config.GenPluginConfYAML(pc, bld.TempDir()); err != nil { + return errors.Wrap(err, "failed to generate plugin-conf.yaml") + } + + // 4. install by the manifest + ins.insOpts.FromYaml = bld.TempDir() + "/plugin-conf.yaml" + if err = ins.doInstall(false); err != nil { + return err + } + return nil +} + +func (ins *installer) doInstall(validate bool) error { + f, err := os.Open(ins.insOpts.FromYaml) + if err != nil { + return err + } + defer f.Close() + + // multiple WASM plugins are separated by '---' in yaml, but we only handle first one + // TODO(WeixinX): Use WasmPlugin Object type instead of Unstructured + obj := &unstructured.Unstructured{} + dc := k8syaml.NewYAMLOrJSONDecoder(f, 4096) + if err = dc.Decode(obj); err != nil { + return errors.Wrapf(err, "failed to parse wasm plugin from manifest %q", ins.insOpts.FromYaml) + } + + if !isValidAPIVersion(obj) { + fmt.Fprintf(ins.w, "Warning: wasm plugin %q has invalid apiVersion, automatically modified: %q -> %q\n", + obj.GetName(), obj.GetAPIVersion(), k8s.HigressExtAPIVersion) + obj.SetAPIVersion(k8s.HigressExtAPIVersion) + } + if !isValidKind(obj) { + fmt.Fprintf(ins.w, "Warning: wasm plugin %q has invalid kind, automatically modified: %q -> %q\n", + obj.GetName(), obj.GetKind(), k8s.WasmPluginKind) + obj.SetKind(k8s.WasmPluginKind) + } + if !isValidNamespace(obj) { + fmt.Fprintf(ins.w, "Warning: wasm plugin %q has invalid namespace, automatically modified: %q -> %q\n", + obj.GetName(), obj.GetNamespace(), k8s.HigressNamespace) + obj.SetNamespace(k8s.HigressNamespace) + } + + // validate wasm plugin config + if validate { + if wps, ok := obj.Object["spec"].(map[string]interface{}); ok { + if err = ins.validateWasmPluginConfig(wps); err != nil { + return err + } + } else { + return errors.New("failed to get the spec filed of wasm plugin") + } + ins.Debugln("successfully validated wasm plugin config") + } + + result, err := ins.cli.Create(context.TODO(), obj) + if err != nil { + if k8serr.IsAlreadyExists(err) { + fmt.Fprintf(ins.w, "wasm plugin %q already exists\n", + fmt.Sprintf("%s/%s", obj.GetNamespace(), obj.GetName())) + return nil + } + return errors.Wrapf(err, "failed to install wasm plugin %q", + fmt.Sprintf("%s/%s", obj.GetNamespace(), obj.GetName())) + } + + fmt.Fprintf(ins.w, "Installed wasm plugin %q\n", fmt.Sprintf("%s/%s", result.GetNamespace(), result.GetName())) + + return nil +} + +func isValidAPIVersion(obj *unstructured.Unstructured) bool { + return obj.GetAPIVersion() == k8s.HigressExtAPIVersion +} + +func isValidKind(obj *unstructured.Unstructured) bool { + return obj.GetKind() == k8s.WasmPluginKind +} + +func isValidNamespace(obj *unstructured.Unstructured) bool { + return obj.GetNamespace() == k8s.HigressNamespace +} + +func (ins *installer) validateWasmPluginConfig(wps map[string]interface{}) error { + spec, err := types.ParseSpecYAML(ins.insOpts.SpecYaml) + if err != nil { + return errors.Wrapf(err, "failed to parse %s", ins.insOpts.SpecYaml) + } + vld, err := buildSchemaValidator(spec) + if err != nil { + return errors.Wrapf(err, "failed to build schema validator") + } + + if dc, ok := wps["defaultConfig"].(map[string]interface{}); ok { + if ok, err = validate(vld, dc); !ok { + return errors.Wrap(err, "failed to validate default config") + } + + // debug + b, _ := utils.MarshalYamlWithIndent(dc, 2) + ins.Debugf("default config:\n%s\n", string(b)) + } + + if mrs, ok := wps["matchRules"].([]interface{}); ok { + for _, mr := range mrs { + if r, ok := mr.(map[string]interface{}); ok { + if _, ok = r["ingress"]; ok { + ing, err := decodeIngressMatchRule(r) + if err != nil { + return errors.Wrap(err, "failed to parse ingress match rule") + } + if ok, err = validate(vld, ing.Config); !ok { + return errors.Wrap(err, "failed to validate ingress match rule") + } + + ins.Debugf("ingress match rule:\n%s\n", ing.String()) + + } else if _, ok = r["domain"]; ok { + dom, err := decodeDomainMatchRule(r) + if err != nil { + return errors.Wrap(err, "failed to parse domain match rule") + } + if ok, err = validate(vld, dom.Config); !ok { + return errors.Wrap(err, "failed to validate ingress match rule") + } + + ins.Debugf("domain match rule:\n%s\n", dom.String()) + } + } + } + } + + return nil +} + +func buildSchemaValidator(spec *types.WasmPluginMeta) (*jsonschema.Schema, error) { + if spec == nil { + return nil, errors.New("spec is nil") + } + + schema := spec.Spec.ConfigSchema.OpenAPIV3Schema + if schema == nil { + return nil, errors.New("spec has no config schema") + } + + b, err := json.Marshal(schema) + if err != nil { + return nil, err + } + + c := jsonschema.NewCompiler() + c.Draft = jsonschema.Draft4 + err = c.AddResource("schema.json", strings.NewReader(string(b))) + vld, err := c.Compile("schema.json") + if err != nil { + errors.Wrap(err, "failed to compile schema") + } + + return vld, nil +} + +func (ins *installer) String() string { + b, err := json.MarshalIndent(ins.insOpts, "", " ") + if err != nil { + return "" + } + return fmt.Sprintf("OptionFile: %s\n%s", ins.optionFile, string(b)) +} diff --git a/pkg/cmd/hgctl/plugin/ls/ls.go b/pkg/cmd/hgctl/plugin/ls/ls.go new file mode 100644 index 0000000000..3bae009b1e --- /dev/null +++ b/pkg/cmd/hgctl/plugin/ls/ls.go @@ -0,0 +1,78 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ls + +import ( + "context" + "fmt" + "io" + "time" + + k8s "github.com/alibaba/higress/pkg/cmd/hgctl/kubernetes" + "github.com/alibaba/higress/pkg/cmd/options" + + "github.com/pkg/errors" + "github.com/spf13/cobra" + "k8s.io/apimachinery/pkg/util/duration" + "k8s.io/cli-runtime/pkg/printers" + cmdutil "k8s.io/kubectl/pkg/cmd/util" +) + +func NewCommand() *cobra.Command { + lsCmd := &cobra.Command{ + Use: "ls", + Aliases: []string{"l"}, + Short: "List all installed WASM plugins", + Example: ` hgctl plugin ls`, + Run: func(cmd *cobra.Command, args []string) { + cmdutil.CheckErr(runLs(cmd.OutOrStdout())) + }, + } + + flags := lsCmd.PersistentFlags() + options.AddKubeConfigFlags(flags) + k8s.AddHigressNamespaceFlags(flags) + + return lsCmd +} + +func runLs(w io.Writer) error { + dynCli, err := k8s.NewDynamicClient(options.DefaultConfigFlags.ToRawKubeConfigLoader()) + if err != nil { + return errors.Wrap(err, "failed to build kubernetes client") + } + cli := k8s.NewWasmPluginClient(dynCli) + + list, err := cli.List(context.TODO()) + if err != nil { + return errors.Wrap(err, "failed to list all wasm plugins") + } + + printer := printers.GetNewTabWriter(w) + now := time.Now() + fmt.Fprintf(printer, "NAME\tAGE\n") + for _, item := range list.Items { + fmt.Fprintf(printer, "%s\t%s\n", item.GetName(), getAge(now, item.GetCreationTimestamp().Time)) + } + if err = printer.Flush(); err != nil { + return errors.Wrap(err, "failed to flush output") + } + + return nil +} + +func getAge(now time.Time, create time.Time) string { + return duration.ShortHumanDuration(now.Sub(create)) +} diff --git a/pkg/cmd/hgctl/plugin/option/option.go b/pkg/cmd/hgctl/plugin/option/option.go new file mode 100644 index 0000000000..5a178eb3b5 --- /dev/null +++ b/pkg/cmd/hgctl/plugin/option/option.go @@ -0,0 +1,96 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package option + +import ( + "os" + + "github.com/pkg/errors" + "github.com/spf13/pflag" + "github.com/spf13/viper" +) + +type Option struct { + Version string `json:"version" yaml:"version" mapstructure:"version"` + Build BuildOptions `json:"build" yaml:"build" mapstructure:"build"` + Test TestOptions `json:"test" yaml:"test" mapstructure:"test"` + Install InstallOptions `json:"install" yaml:"install" mapstructure:"install"` +} + +type BuildOptions struct { + Builder BuilderVersion `json:"builder" yaml:"builder" mapstructure:"builder"` + Input string `json:"input" yaml:"input" mapstructure:"input"` + Output Output `json:"output" yaml:"output" mapstructure:"output"` + DockerAuth string `json:"docker-auth" yaml:"docker-auth" mapstructure:"docker-auth"` + ModelDir string `json:"model-dir" yaml:"model-dir" mapstructure:"model-dir"` + Model string `json:"model" yaml:"model" mapstructure:"model"` + Debug bool `json:"debug" yaml:"debug" mapstructure:"debug"` +} + +type TestOptions struct { + Name string `json:"name" yaml:"name" mapstructure:"name"` + FromPath string `json:"from-path" yaml:"from-path" mapstructure:"from-path"` + TestPath string `json:"test-path" yaml:"test-path" mapstructure:"test-path"` + ComposeFile string `json:"compose-file" yaml:"compose-file" mapstructure:"compose-file"` + Detach bool `json:"detach" yaml:"detach" mapstructure:"detach"` +} + +type InstallOptions struct { + Namespace string `json:"namespace" yaml:"namespace" mapstructure:"namespace"` + SpecYaml string `json:"spec-yaml" yaml:"spec-yaml" mapstructure:"spec-yaml"` + FromYaml string `json:"from-yaml" yaml:"from-yaml" mapstructure:"from-yaml"` + FromGoSrc string `json:"from-go-src" yaml:"from-go-src" mapstructure:"from-go-src"` + Debug bool `json:"debug" yaml:"debug" mapstructure:"debug"` +} + +type BuilderVersion struct { + Go string `json:"go" yaml:"go" mpastructure:"go"` + TinyGo string `json:"tinygo" yaml:"tinygo" mapstructure:"tinygo"` + Oras string `json:"oras" yaml:"oras" mapstructure:"oras"` +} + +type Output struct { + Type string `json:"type" yaml:"type" mapstructure:"type"` + Dest string `json:"dest" yaml:"dest" mapstructure:"dest"` +} + +// ParseOptions reads `option.yaml` and parses it into Option struct +func ParseOptions(optionFile string, v *viper.Viper, flags *pflag.FlagSet) (*Option, error) { + _, err := os.Stat(optionFile) + if err != nil { + // `option-file` is explicitly specified, but the given file does not exist + if errors.Is(err, os.ErrNotExist) && flags.Changed("option-file") { + return nil, errors.Errorf("option file does not exist: %q", optionFile) + } + } else { + v.SetConfigFile(optionFile) + if err = v.ReadInConfig(); err != nil { + return nil, errors.Wrapf(err, "failed to read option file %q", optionFile) + } + } + + var opt Option + if err = v.Unmarshal(&opt); err != nil { + return nil, errors.Wrapf(err, "failed to unmarshal option file %q", optionFile) + } + + return &opt, nil +} + +// AddOptionFileFlag adds `option-file` flag +func AddOptionFileFlag(optionFile *string, flags *pflag.FlagSet) { + flags.StringVarP(optionFile, "option-file", "f", "./option.yaml", + "Option file for build, test and install") +} diff --git a/pkg/cmd/hgctl/plugin/option/template.go b/pkg/cmd/hgctl/plugin/option/template.go new file mode 100644 index 0000000000..d29fc6727a --- /dev/null +++ b/pkg/cmd/hgctl/plugin/option/template.go @@ -0,0 +1,89 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package option + +import ( + "fmt" + "os" +) + +const optionYAML = `# File generated by hgctl. Modify as required. + +version: 1.0.0 + +build: + # The official builder image version + builder: + go: 1.19 + tinygo: 0.28.1 + oras: 1.0.0 + # The WASM plugin project directory + input: ./ + # The output of the build products + output: + # Choose between 'files' and 'image' + type: files + # Destination address: when type=files, specify the local directory path, e.g., './out' or + # type=image, specify the remote docker repository, e.g., 'docker.io//' + dest: ./out + # The authentication configuration for pushing image to the docker repository + docker-auth: ~/.docker/config.json + # The directory for the WASM plugin configuration structure + model-dir: ./ + # The WASM plugin configuration structure name + model: PluginConfig + # Enable debug mode + debug: false + +test: + # Test environment name, that is a docker compose project name + name: wasm-test + # The output path to build products, that is the source of test configuration parameters + from-path: ./out + # The test configuration source + test-path: ./test + # Docker compose configuration, which is empty, looks for the following files from 'test-path': + # compose.yaml, compose.yml, docker-compose.yml, docker-compose.yaml + compose-file: + # Detached mode: Run containers in the background + detach: false + +install: + # The namespace of the installation + namespace: higress-system + # Use to validate WASM plugin configuration when install by yaml + spec-yaml: ./out/spec.yaml + # Installation source. Choose between 'from-yaml' and 'from-go-project' + from-yaml: ./test/plugin-conf.yaml + # If 'from-go-src' is non-empty, the output type of the build option must be 'image' + from-go-src: + # Enable debug mode + debug: false +` + +func GenOptionYAML(dir string) error { + path := fmt.Sprintf("%s/option.yaml", dir) + f, err := os.Create(path) + if err != nil { + return err + } + defer f.Close() + + if _, err = f.WriteString(optionYAML); err != nil { + return err + } + + return nil +} diff --git a/pkg/cmd/hgctl/plugin/plugin.go b/pkg/cmd/hgctl/plugin/plugin.go new file mode 100644 index 0000000000..9a45612aef --- /dev/null +++ b/pkg/cmd/hgctl/plugin/plugin.go @@ -0,0 +1,45 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package plugin + +import ( + "github.com/alibaba/higress/pkg/cmd/hgctl/plugin/build" + "github.com/alibaba/higress/pkg/cmd/hgctl/plugin/config" + plugininit "github.com/alibaba/higress/pkg/cmd/hgctl/plugin/init" + "github.com/alibaba/higress/pkg/cmd/hgctl/plugin/install" + "github.com/alibaba/higress/pkg/cmd/hgctl/plugin/ls" + "github.com/alibaba/higress/pkg/cmd/hgctl/plugin/test" + "github.com/alibaba/higress/pkg/cmd/hgctl/plugin/uninstall" + + "github.com/spf13/cobra" +) + +func NewCommand() *cobra.Command { + pluginCommand := &cobra.Command{ + Use: "plugin", + Aliases: []string{"plg", "p"}, + Short: "For the Golang WASM plugin", + } + + pluginCommand.AddCommand(build.NewCommand()) + pluginCommand.AddCommand(install.NewCommand()) + pluginCommand.AddCommand(uninstall.NewCommand()) + pluginCommand.AddCommand(ls.NewCommand()) + pluginCommand.AddCommand(test.NewCommand()) + pluginCommand.AddCommand(config.NewCommand()) + pluginCommand.AddCommand(plugininit.NewCommand()) + + return pluginCommand +} diff --git a/pkg/cmd/hgctl/plugin/test/clean.go b/pkg/cmd/hgctl/plugin/test/clean.go new file mode 100644 index 0000000000..3750479d99 --- /dev/null +++ b/pkg/cmd/hgctl/plugin/test/clean.go @@ -0,0 +1,108 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package test + +import ( + "context" + "fmt" + "io" + "os" + + "github.com/alibaba/higress/pkg/cmd/hgctl/docker" + "github.com/alibaba/higress/pkg/cmd/hgctl/plugin/option" + "github.com/alibaba/higress/pkg/cmd/hgctl/plugin/utils" + + "github.com/pkg/errors" + "github.com/spf13/cobra" + "github.com/spf13/viper" + cmdutil "k8s.io/kubectl/pkg/cmd/util" +) + +type cleaner struct { + optionFile string + option.TestOptions + + w io.Writer +} + +func newCleanCommand() *cobra.Command { + var c cleaner + v := viper.New() + + cleanCmd := &cobra.Command{ + Use: "clean", + Aliases: []string{"cl"}, + Short: "Clean the test environment, that is remove the source of test configuration", + Example: ` hgctl plugin test clean`, + PreRun: func(cmd *cobra.Command, args []string) { + cmdutil.CheckErr(c.config(v, cmd)) + }, + Run: func(cmd *cobra.Command, args []string) { + cmdutil.CheckErr(c.clean()) + }, + } + + flags := cleanCmd.PersistentFlags() + option.AddOptionFileFlag(&c.optionFile, flags) + v.BindPFlags(flags) + + flags.StringP("name", "p", "wasm-test", "Test environment name") + v.BindPFlag("test.name", flags.Lookup("name")) + v.SetDefault("test.name", "wasm-test") + + // TODO(WeixinX): Obtain the test configuration source directory based on the test environment name (hgctl plugin test ls) + flags.StringP("test-path", "t", "./test", "Test configuration source") + v.BindPFlag("test.test-path", flags.Lookup("test-path")) + v.SetDefault("test.test-path", "./test") + + return cleanCmd +} + +func (c *cleaner) config(v *viper.Viper, cmd *cobra.Command) error { + allOpt, err := option.ParseOptions(c.optionFile, v, cmd.PersistentFlags()) + if err != nil { + return err + } + c.TestOptions = allOpt.Test + + c.w = cmd.OutOrStdout() + + return nil +} + +func (c *cleaner) clean() error { + cli, err := docker.NewCompose(c.w) + if err != nil { + return errors.Wrap(err, "failed to build the docker compose client") + } + + err = cli.Down(context.TODO(), c.Name) + if err != nil { + return errors.Wrapf(err, "failed to stop the test environment %q", c.Name) + } + fmt.Fprintf(c.w, "Stopped the test environment %q\n", c.Name) + + source, err := utils.GetAbsolutePath(c.TestPath) + if err != nil { + return errors.Wrapf(err, "invalid test configuration source %q", c.TestPath) + } + err = os.RemoveAll(source) + if err != nil { + return errors.Wrapf(err, "failed to remove the test configuration source %q", source) + } + fmt.Fprintf(c.w, "Removed the source %q\n", source) + + return nil +} diff --git a/pkg/cmd/hgctl/plugin/test/create.go b/pkg/cmd/hgctl/plugin/test/create.go new file mode 100644 index 0000000000..9e5aa77441 --- /dev/null +++ b/pkg/cmd/hgctl/plugin/test/create.go @@ -0,0 +1,175 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package test + +import ( + "encoding/json" + "fmt" + "io" + "os" + "strings" + + "github.com/alibaba/higress/pkg/cmd/hgctl/plugin/config" + "github.com/alibaba/higress/pkg/cmd/hgctl/plugin/option" + "github.com/alibaba/higress/pkg/cmd/hgctl/plugin/types" + "github.com/alibaba/higress/pkg/cmd/hgctl/plugin/utils" + + "github.com/pkg/errors" + "github.com/spf13/cobra" + "github.com/spf13/viper" + "gopkg.in/yaml.v3" + cmdutil "k8s.io/kubectl/pkg/cmd/util" +) + +type creator struct { + optionFile string + option.TestOptions + + w io.Writer +} + +func newCreateCommand() *cobra.Command { + var c creator + v := viper.New() + + createCmd := &cobra.Command{ + Use: "create", + Aliases: []string{"c"}, + Short: "Create the test environment", + Example: ` # If the option.yaml file exists in the current path, do the following: + hgctl plugin test create + + # Explicitly specify the source of the parameters (directory of the build + products) and the directory where the test configuration files is stored + hgctl plugin test create -d ./out -t ./test + `, + + PreRun: func(cmd *cobra.Command, args []string) { + cmdutil.CheckErr(c.config(v, cmd)) + }, + + Run: func(cmd *cobra.Command, args []string) { + cmdutil.CheckErr(c.create()) + }, + } + + flags := createCmd.PersistentFlags() + option.AddOptionFileFlag(&c.optionFile, flags) + v.BindPFlags(flags) + + flags.StringP("from-path", "d", "./out", "Path of storing the build products") + v.BindPFlag("test.from-path", flags.Lookup("from-path")) + v.SetDefault("test.from-path", "./out") + + flags.StringP("test-path", "t", "./test", "Path for storing the test configuration") + v.BindPFlag("test.test-path", flags.Lookup("test-path")) + v.SetDefault("test.test-path", "./test") + + return createCmd +} + +func (c *creator) config(v *viper.Viper, cmd *cobra.Command) error { + allOpt, err := option.ParseOptions(c.optionFile, v, cmd.PersistentFlags()) + if err != nil { + return err + } + c.TestOptions = allOpt.Test + + c.w = cmd.OutOrStdout() + + return nil +} + +func (c *creator) create() (err error) { + source, err := utils.GetAbsolutePath(c.FromPath) + if err != nil { + return errors.Wrapf(err, "invalid build products path %q", c.FromPath) + } + c.FromPath = source + + target, err := utils.GetAbsolutePath(c.TestPath) + if err != nil { + return errors.Wrapf(err, "invalid test path %q", c.TestPath) + } + c.TestPath = target + + fields := testTmplFields{} + + // 1. extract the parameters from spec.yaml and convert them to PluginConf + path := fmt.Sprintf("%s/spec.yaml", c.FromPath) + spec, err := types.ParseSpecYAML(path) + if err != nil { + return errors.Wrapf(err, "failed to parse %s", path) + } + fields.PluginConf, err = config.ExtractPluginConfFrom(spec, "", "") + if err != nil { + return errors.Wrapf(err, "failed to get the parameters of plugin-conf.yaml from %s", path) + } + + // 2. get DockerCompose instance + fields.DockerCompose = &DockerCompose{ + TestPath: c.TestPath, + ProductPath: c.FromPath, + } + + // 3. get Envoy instance + var obj interface{} + conf := spec.GetConfigExample() + err = yaml.Unmarshal([]byte(conf), &obj) + if err != nil { + return errors.Wrap(err, "failed to get the example of wasm plugin") + } + b, err := json.MarshalIndent(obj, "", strings.Repeat(" ", 2)) + if err != nil { + return errors.Wrap(err, "failed to marshal example to json") + } + jsExample := utils.AddIndent(string(b), strings.Repeat(" ", 30)) + fields.Envoy = &Envoy{JSONExample: jsExample} + + // 4. generate corresponding test files + if err = os.MkdirAll(target, 0755); err != nil { + return errors.Wrap(err, "failed to create the test environment") + + } + if err = c.genTestConfFiles(fields); err != nil { + return errors.Wrap(err, "failed to create the test environment") + } + + fmt.Fprintf(c.w, "Created the test environment in %q\n", target) + + return nil +} + +type testTmplFields struct { + PluginConf *config.PluginConf // for plugin-conf.yaml + DockerCompose *DockerCompose // for docker-compose.yaml + Envoy *Envoy // for envoy.yaml +} + +func (c *creator) genTestConfFiles(fields testTmplFields) (err error) { + if err = config.GenPluginConfYAML(fields.PluginConf, c.TestPath); err != nil { + return err + } + + if err = genDockerComposeYAML(fields.DockerCompose, c.TestPath); err != nil { + return err + } + + if err = genEnvoyYAML(fields.Envoy, c.TestPath); err != nil { + return err + } + + return nil +} diff --git a/pkg/cmd/hgctl/plugin/test/ls.go b/pkg/cmd/hgctl/plugin/test/ls.go new file mode 100644 index 0000000000..5ec7d845b6 --- /dev/null +++ b/pkg/cmd/hgctl/plugin/test/ls.go @@ -0,0 +1,64 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package test + +import ( + "context" + "fmt" + "io" + + "github.com/alibaba/higress/pkg/cmd/hgctl/docker" + + "github.com/pkg/errors" + "github.com/spf13/cobra" + "k8s.io/cli-runtime/pkg/printers" + cmdutil "k8s.io/kubectl/pkg/cmd/util" +) + +func newLsCommand() *cobra.Command { + lsCmd := &cobra.Command{ + Use: "ls", + Aliases: []string{"l"}, + Short: "List all test environments", + Example: ` hgctl plugin test ls`, + Run: func(cmd *cobra.Command, args []string) { + cmdutil.CheckErr(runLs(cmd.OutOrStdout())) + }, + } + + return lsCmd +} + +func runLs(w io.Writer) error { + cli, err := docker.NewCompose(w) + if err != nil { + return errors.Wrap(err, "failed to build the docker compose client") + } + + list, err := cli.List(context.TODO()) + if err != nil { + return errors.Wrap(err, "failed to list all test environments") + } + + printer := printers.GetNewTabWriter(w) + // fmt.Fprintf(printer, "NAME\tSTATUS\tCONFIG FILES\n") // compose v2.3.0+ + fmt.Fprintf(printer, "NAME\tSTATUS\n") + for _, stack := range list { + fmt.Fprintf(printer, "%s\t%s\n", stack.Name, stack.Status) + } + printer.Flush() + + return nil +} diff --git a/pkg/cmd/hgctl/plugin/test/start.go b/pkg/cmd/hgctl/plugin/test/start.go new file mode 100644 index 0000000000..1aa5f4d719 --- /dev/null +++ b/pkg/cmd/hgctl/plugin/test/start.go @@ -0,0 +1,115 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package test + +import ( + "context" + "fmt" + "io" + + "github.com/alibaba/higress/pkg/cmd/hgctl/docker" + "github.com/alibaba/higress/pkg/cmd/hgctl/plugin/option" + + "github.com/pkg/errors" + "github.com/spf13/cobra" + "github.com/spf13/viper" + cmdutil "k8s.io/kubectl/pkg/cmd/util" +) + +// TODO(WeixinX): If no test environment exists, create one first and then start +type starter struct { + optionFile string + option.TestOptions + + w io.Writer +} + +func newStartCommand() *cobra.Command { + var s starter + v := viper.New() + + startCmd := &cobra.Command{ + Use: "start", + Aliases: []string{"s"}, + Short: "Start the test environment", + Example: ` # If the option.yaml file exists in the current path, do the following: + hgctl plugin test start + + # Run containers in the background with the option --detach(-d) + hgctl plugin test start -d + `, + PreRun: func(cmd *cobra.Command, args []string) { + cmdutil.CheckErr(s.config(v, cmd)) + }, + + Run: func(cmd *cobra.Command, args []string) { + cmdutil.CheckErr(s.start()) + }, + } + + flags := startCmd.PersistentFlags() + option.AddOptionFileFlag(&s.optionFile, flags) + v.BindPFlags(flags) + + flags.StringP("name", "p", "wasm-test", "Test environment name") + v.BindPFlag("test.name", flags.Lookup("name")) + v.SetDefault("test.name", "wasm-test") + + flags.StringP("test-path", "t", "./test", "Test configuration source") + v.BindPFlag("test.test-path", flags.Lookup("test-path")) + v.SetDefault("test.test-path", "./test") + + flags.StringP("compose-file", "c", "", "Docker compose configuration file") + v.BindPFlag("test.compose-file", flags.Lookup("compose-file")) + v.SetDefault("test.compose-file", "") + + flags.BoolP("detach", "d", false, "Detached mode: Run containers in the background") + v.BindPFlag("test.detach", flags.Lookup("detach")) + v.SetDefault("test.detach", false) + + return startCmd +} + +func (s *starter) config(v *viper.Viper, cmd *cobra.Command) error { + allOpt, err := option.ParseOptions(s.optionFile, v, cmd.PersistentFlags()) + if err != nil { + return err + } + s.TestOptions = allOpt.Test + + s.w = cmd.OutOrStdout() + + return nil +} + +func (s *starter) start() error { + cli, err := docker.NewCompose(s.w) + if err != nil { + return errors.Wrap(err, "failed to build the docker compose client") + } + + var configs []string + if s.ComposeFile != "" { + configs = []string{s.ComposeFile} + } + + err = cli.Up(context.TODO(), s.Name, configs, s.TestPath, s.Detach) + if err != nil { + return errors.Wrap(err, "failed to start the test environment") + } + fmt.Fprintf(s.w, "Started the test environment %q\n", s.Name) + + return nil +} diff --git a/pkg/cmd/hgctl/plugin/test/stop.go b/pkg/cmd/hgctl/plugin/test/stop.go new file mode 100644 index 0000000000..a253525540 --- /dev/null +++ b/pkg/cmd/hgctl/plugin/test/stop.go @@ -0,0 +1,95 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package test + +import ( + "context" + "fmt" + "io" + + "github.com/alibaba/higress/pkg/cmd/hgctl/docker" + "github.com/alibaba/higress/pkg/cmd/hgctl/plugin/option" + + "github.com/pkg/errors" + "github.com/spf13/cobra" + "github.com/spf13/viper" + cmdutil "k8s.io/kubectl/pkg/cmd/util" +) + +type stopper struct { + optionFile string + option.TestOptions + + w io.Writer +} + +func newStopCommand() *cobra.Command { + var s stopper + v := viper.New() + + stopCmd := &cobra.Command{ + Use: "stop", + Aliases: []string{"st"}, + Short: "Stop the test environment", + Example: ` # Stop responding to the compose containers with the option --name(-p) + hgctl plugin test stop -p wasm-test + `, + + PreRun: func(cmd *cobra.Command, args []string) { + cmdutil.CheckErr(s.config(v, cmd)) + }, + + Run: func(cmd *cobra.Command, args []string) { + cmdutil.CheckErr(s.stop()) + }, + } + + flags := stopCmd.PersistentFlags() + option.AddOptionFileFlag(&s.optionFile, flags) + v.BindPFlags(flags) + + flags.StringP("name", "p", "wasm-test", "Test environment name") + v.BindPFlag("test.name", flags.Lookup("name")) + v.SetDefault("test.name", "wasm-test") + + return stopCmd +} + +func (s *stopper) config(v *viper.Viper, cmd *cobra.Command) error { + allOpt, err := option.ParseOptions(s.optionFile, v, cmd.PersistentFlags()) + if err != nil { + return err + } + s.TestOptions = allOpt.Test + + s.w = cmd.OutOrStdout() + + return nil +} + +func (s *stopper) stop() error { + cli, err := docker.NewCompose(s.w) + if err != nil { + return errors.Wrap(err, "failed to build the docker compose client") + } + + err = cli.Down(context.TODO(), s.Name) + if err != nil { + return errors.Wrapf(err, "failed to stop the test environment %q", s.Name) + } + fmt.Fprintf(s.w, "Stopped the test environment %q\n", s.Name) + + return nil +} diff --git a/pkg/cmd/hgctl/plugin/test/templates.go b/pkg/cmd/hgctl/plugin/test/templates.go new file mode 100644 index 0000000000..6b3c319c28 --- /dev/null +++ b/pkg/cmd/hgctl/plugin/test/templates.go @@ -0,0 +1,167 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package test + +import ( + "fmt" + "os" + "text/template" +) + +const ( + dockerComposeYAML = `# File generated by hgctl. Modify as required. + +version: '3.7' +services: + envoy: + image: higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/envoy:1.20 + command: envoy -c /etc/envoy/envoy.yaml --component-log-level wasm:debug + depends_on: + - httpbin + networks: + - wasmtest + ports: + - "10000:10000" + volumes: + - {{ .TestPath }}/envoy.yaml:/etc/envoy/envoy.yaml + - {{ .ProductPath }}/plugin.wasm:/etc/envoy/plugin.wasm + + httpbin: + image: kennethreitz/httpbin:latest + networks: + - wasmtest + ports: + - "12345:80" + +networks: + wasmtest: {} +` + + envoyYAML = `# File generated by hgctl. Modify as required. + +admin: + address: + socket_address: + protocol: TCP + address: 0.0.0.0 + port_value: 9901 +static_resources: + listeners: + - name: listener_0 + address: + socket_address: + protocol: TCP + address: 0.0.0.0 + port_value: 10000 + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + scheme_header_transformation: + scheme_to_overwrite: https + stat_prefix: ingress_http + # Output envoy logs to stdout + access_log: + - name: envoy.access_loggers.stdout + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog + # Modify as required + route_config: + name: local_route + virtual_hosts: + - name: local_service + domains: ["*"] + routes: + - match: + prefix: "/" + route: + cluster: httpbin + http_filters: + - name: wasmtest + typed_config: + "@type": type.googleapis.com/udpa.type.v1.TypedStruct + type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm + value: + config: + name: wasmtest + vm_config: + runtime: envoy.wasm.runtime.v8 + code: + local: + filename: /etc/envoy/plugin.wasm + configuration: + "@type": "type.googleapis.com/google.protobuf.StringValue" + # Modify as required + value: | +{{ .JSONExample }} + - name: envoy.filters.http.router + clusters: + - name: httpbin + connect_timeout: 30s + type: LOGICAL_DNS + # Comment out the following line to test on v6 networks + dns_lookup_family: V4_ONLY + lb_policy: ROUND_ROBIN + load_assignment: + cluster_name: httpbin + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: httpbin + port_value: 80 +` +) + +type DockerCompose struct { + TestPath string + ProductPath string +} + +type Envoy struct { + JSONExample string +} + +func genDockerComposeYAML(d *DockerCompose, dir string) error { + path := fmt.Sprintf("%s/docker-compose.yaml", dir) + f, err := os.Create(path) + if err != nil { + return err + } + defer f.Close() + + if err = template.Must(template.New("DockerComposeYAML").Parse(dockerComposeYAML)).Execute(f, d); err != nil { + return err + } + + return nil +} + +func genEnvoyYAML(e *Envoy, dir string) error { + path := fmt.Sprintf("%s/envoy.yaml", dir) + f, err := os.Create(path) + if err != nil { + panic(fmt.Sprintf("failed to create %q: %v\n", path, err)) + } + defer f.Close() + + if err = template.Must(template.New("EnvoyYAML").Parse(envoyYAML)).Execute(f, e); err != nil { + return err + } + + return nil +} diff --git a/pkg/cmd/hgctl/plugin/test/test.go b/pkg/cmd/hgctl/plugin/test/test.go new file mode 100644 index 0000000000..352aea9b8f --- /dev/null +++ b/pkg/cmd/hgctl/plugin/test/test.go @@ -0,0 +1,35 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package test + +import ( + "github.com/spf13/cobra" +) + +func NewCommand() *cobra.Command { + testCmd := &cobra.Command{ + Use: "test", + Aliases: []string{"t"}, + Short: "Test WASM plugin locally", + } + + testCmd.AddCommand(newCreateCommand()) + testCmd.AddCommand(newStartCommand()) + testCmd.AddCommand(newStopCommand()) + testCmd.AddCommand(newCleanCommand()) + testCmd.AddCommand(newLsCommand()) + + return testCmd +} diff --git a/pkg/cmd/hgctl/plugin/types/annotation.go b/pkg/cmd/hgctl/plugin/types/annotation.go new file mode 100644 index 0000000000..a52d414b84 --- /dev/null +++ b/pkg/cmd/hgctl/plugin/types/annotation.go @@ -0,0 +1,163 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "fmt" + "strings" + + "github.com/pkg/errors" +) + +type Annotation struct { + Type AnnotationType + I18nType I18nType + Text string +} + +type AnnotationType int + +const ( + // Info + ACategory AnnotationType = iota + AName + ATitle + ADescription + AIconUrl + AVersion + AContactName + AContactUrl + AContactEmail + + // Spec + APhase + APriority + + // Schema + AScope + AExample + AEnd + + AUnknown +) + +func str2AnnotationType(typ string) AnnotationType { + switch strings.ToLower(typ) { + case "@category": + return ACategory + case "@name": + return AName + case "@title": + return ATitle + case "@description": + return ADescription + case "@iconurl": + return AIconUrl + case "@version": + return AVersion + case "@contact.name": + return AContactName + case "@contact.url": + return AContactUrl + case "@contact.email": + return AContactEmail + case "@phase": + return APhase + case "@priority": + return APriority + case "@scope": + return AScope + case "@example": + return AExample + case "@end": + return AEnd + default: + return AUnknown + } +} + +// GetAnnotations returns all annotations in the comment +func GetAnnotations(comment string) []Annotation { + as := make([]Annotation, 0) + cs := strings.Split(comment, "\n") + for i := 0; i < len(cs); i++ { + a, err := getAnnotationFrom(cs[i]) + if err != nil { + continue + } + + if a.Type == AExample { + for j := i + 1; j < len(cs); j++ { + if str2AnnotationType(strings.TrimSpace(cs[j])) == AEnd { + break + } + if j == i+1 { + a.Text = fmt.Sprintf("%s", cs[j]) + } else { + a.Text = fmt.Sprintf("%s\n%s", a.Text, cs[j]) + } + } + } + as = append(as, a) + } + return as +} + +func getAnnotationFrom(c string) (Annotation, error) { + // the annotation is like `@AnnotationType [I18nType] Text` + + c = strings.TrimSpace(c) + if !strings.HasPrefix(c, "@") { + return Annotation{}, errors.New("invalid annotation") + } + + // first param: AnnotationType + idx := strings.Index(c, " ") + if idx == -1 && str2AnnotationType(c) == AUnknown { // only an invalid annotation type + return Annotation{}, errors.New("invalid annotation") + } + // idx != -1 or type != unknown + var typ AnnotationType + if idx == -1 { + typ = str2AnnotationType(c) + } else { + typ = str2AnnotationType(strings.TrimSpace(c[0:idx])) + } + c = strings.TrimSpace(c[idx+1:]) + a := Annotation{ + Type: typ, + I18nType: I18nDefault, + Text: c, + } + if a.Type != ATitle && a.Type != ADescription { // other annotation types do not define i18n + a.I18nType = I18nUndefined + } + if idx == -1 && typ != AUnknown { // only a valid annotation type + a.Text = "" + } + + // second or/and third param: I18nType and Text + idx = strings.Index(c, " ") + if idx == -1 { + return a, nil + } + i18n := str2I18nType(strings.TrimSpace(c[0:idx])) + if i18n == I18nUnknown { + return a, nil + } + a.I18nType = i18n + a.Text = strings.TrimSpace(c[idx+1:]) + return a, nil +} diff --git a/pkg/cmd/hgctl/plugin/types/marshal.go b/pkg/cmd/hgctl/plugin/types/marshal.go new file mode 100644 index 0000000000..e950254e49 --- /dev/null +++ b/pkg/cmd/hgctl/plugin/types/marshal.go @@ -0,0 +1,176 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "bytes" + + "github.com/pkg/errors" + "gopkg.in/yaml.v3" + "k8s.io/apimachinery/pkg/util/json" +) + +func (s JSON) MarshalJSON() ([]byte, error) { + if len(s.Raw) > 0 { + var obj interface{} + err := json.Unmarshal(s.Raw, &obj) + if err != nil { + return []byte("null"), err + } + return json.Marshal(obj) + } + return []byte("null"), nil +} + +func (s *JSON) UnmarshalJSON(data []byte) error { + if len(data) > 0 && !bytes.Equal(data, []byte("null")) { + s.Raw = data + } + return nil +} + +func (s JSON) MarshalYAML() (interface{}, error) { + if len(s.Raw) > 0 { + var obj interface{} + err := yaml.Unmarshal(s.Raw, &obj) + if err != nil { + return "null", err + } + return obj, nil + } + return "null", nil +} + +func (s JSONSchemaPropsOrArray) MarshalJSON() ([]byte, error) { + if len(s.JSONSchemas) > 0 { + return json.Marshal(s.JSONSchemas) + } + return json.Marshal(s.Schema) +} + +func (s *JSONSchemaPropsOrArray) UnmarshalJSON(data []byte) error { + var nw JSONSchemaPropsOrArray + var first byte + if len(data) > 1 { + first = data[0] + } + if first == '{' { + var sch JSONSchemaProps + if err := json.Unmarshal(data, &sch); err != nil { + return err + } + nw.Schema = &sch + } + if first == '[' { + if err := json.Unmarshal(data, &nw.JSONSchemas); err != nil { + return err + } + } + *s = nw + return nil +} + +func (s JSONSchemaPropsOrArray) MarshalYAML() (interface{}, error) { + if len(s.JSONSchemas) > 0 { + return s.JSONSchemas, nil + } + return s.Schema, nil +} + +func (s JSONSchemaPropsOrBool) MarshalJSON() ([]byte, error) { + if s.Schema != nil { + return json.Marshal(s.Schema) + } + + if s.Schema == nil && !s.Allows { + return []byte("false"), nil + } + return []byte("true"), nil +} + +func (s *JSONSchemaPropsOrBool) UnmarshalJSON(data []byte) error { + var nw JSONSchemaPropsOrBool + switch { + case len(data) == 0: + case data[0] == '{': + var sch JSONSchemaProps + if err := json.Unmarshal(data, &sch); err != nil { + return err + } + nw.Allows = true + nw.Schema = &sch + case len(data) == 4 && string(data) == "true": + nw.Allows = true + case len(data) == 5 && string(data) == "false": + nw.Allows = false + default: + return errors.New("boolean or JSON schema expected") + } + *s = nw + return nil +} + +func (s JSONSchemaPropsOrBool) MarshalYAML() (interface{}, error) { + if s.Schema != nil { + return yaml.Marshal(s.Schema) + } + + if s.Schema == nil && !s.Allows { + return false, nil + } + return true, nil +} + +func (s JSONSchemaPropsOrStringArray) MarshalJSON() ([]byte, error) { + if len(s.Property) > 0 { + return json.Marshal(s.Property) + } + if s.Schema != nil { + return json.Marshal(s.Schema) + } + return []byte("null"), nil +} + +func (s *JSONSchemaPropsOrStringArray) UnmarshalJSON(data []byte) error { + var first byte + if len(data) > 1 { + first = data[0] + } + var nw JSONSchemaPropsOrStringArray + if first == '{' { + var sch JSONSchemaProps + if err := json.Unmarshal(data, &sch); err != nil { + return err + } + nw.Schema = &sch + } + if first == '[' { + if err := json.Unmarshal(data, &nw.Property); err != nil { + return err + } + } + *s = nw + return nil +} + +func (s JSONSchemaPropsOrStringArray) MarshalYAML() (interface{}, error) { + if len(s.Property) > 0 { + return s.Property, nil + } + if s.Schema != nil { + return s.Schema, nil + } + return "null", nil +} diff --git a/pkg/cmd/hgctl/plugin/types/meta.go b/pkg/cmd/hgctl/plugin/types/meta.go new file mode 100644 index 0000000000..03a57cd26c --- /dev/null +++ b/pkg/cmd/hgctl/plugin/types/meta.go @@ -0,0 +1,393 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "fmt" + "os" + "sort" + "strconv" + "strings" + + "github.com/iancoleman/orderedmap" + k8syaml "k8s.io/apimachinery/pkg/util/yaml" +) + +// WasmPluginMeta is used to describe WASM plugin metadata, +// see https://higress.io/en-us/docs/user/wasm-image-spec/ +type WasmPluginMeta struct { + APIVersion string `json:"apiVersion" yaml:"apiVersion"` + Info WasmPluginInfo `json:"info" yaml:"info"` + Spec WasmPluginSpec `json:"spec" yaml:"spec"` +} + +func defaultWsamPluginMeta() *WasmPluginMeta { + return &WasmPluginMeta{ + APIVersion: "1.0.0", + Info: WasmPluginInfo{ + Category: CategoryCustom, + Name: "Unnamed", + XTitleI18n: make(map[I18nType]string), + XDescriptionI18n: make(map[I18nType]string), + Version: "0.1.0", + }, + Spec: WasmPluginSpec{ + Phase: PhaseUnspecified, + Priority: 0, + }, + } +} + +// ParseSpecYAML parses the `spec.yaml` to WasmPluginMeta +func ParseSpecYAML(spec string) (*WasmPluginMeta, error) { + f, err := os.Open(spec) + if err != nil { + return nil, err + } + defer f.Close() + + var m WasmPluginMeta + dc := k8syaml.NewYAMLOrJSONDecoder(f, 4096) + if err = dc.Decode(&m); err != nil { + return nil, err + } + + return &m, nil +} + +// ParseGoSrc parses the config model of the golang WASM plugin project to WasmPluginMeta +func ParseGoSrc(dir, model string) (*WasmPluginMeta, error) { + mp, err := NewModelParser(dir) + if err != nil { + return nil, err + } + m, err := mp.GetModel(model) + if err != nil { + return nil, err + } + meta := defaultWsamPluginMeta() + meta.setByConfigModel(m) + return meta, nil +} + +func (meta *WasmPluginMeta) setByConfigModel(model *Model) { + _, schema := recursiveSetSchema(model, nil) + meta.Spec.ConfigSchema.OpenAPIV3Schema = schema + meta.setModelAnnotations(model.Doc) +} + +func recursiveSetSchema(model *Model, parent *JSONSchemaProps) (string, *JSONSchemaProps) { + cur := NewJSONSchemaProps() + cur.Type = model.Type + if parent != nil { + cur.HandleFieldAnnotations(model.Doc) + } + newName := cur.HandleFieldTags(model.Tag, parent, model.Name) + if IsArray(model.Type) { + item := NewJSONSchemaProps() + item.Type = GetItemType(cur.Type) + cur.Type = "array" + if IsObject(item.Type) { + item.Properties = make(map[string]JSONSchemaProps) + for _, field := range model.Fields { + name, child := recursiveSetSchema(&field, cur) + item.Properties[name] = *child + } + } + cur.Items = &JSONSchemaPropsOrArray{Schema: item} + } else if IsObject(model.Type) { // type may be `array of object`, and it is handled in the first branch + for _, field := range model.Fields { + name, child := recursiveSetSchema(&field, cur) + cur.Properties[name] = *child + } + } + return newName, cur +} + +func (meta *WasmPluginMeta) setModelAnnotations(comment string) { + as := GetAnnotations(comment) + for _, a := range as { + switch a.Type { + // Info + case ACategory: + meta.Info.Category = Category(a.Text) + case AName: + meta.Info.Name = a.Text + case ATitle: + if meta.Info.Title == "" { + meta.Info.Title = a.Text + } + meta.Info.XTitleI18n[a.I18nType] = a.Text + case ADescription: + if meta.Info.Description == "" { + meta.Info.Description = a.Text + } + meta.Info.XDescriptionI18n[a.I18nType] = a.Text + case AIconUrl: + meta.Info.IconUrl = a.Text + case AVersion: + meta.Info.Version = a.Text + case AContactName: + meta.Info.Contact.Name = a.Text + case AContactUrl: + meta.Info.Contact.Url = a.Text + case AContactEmail: + meta.Info.Contact.Email = a.Text + + // Spec + case APhase: + meta.Spec.Phase = Phase(a.Text) + case APriority: + priority, err := strconv.ParseInt(a.Text, 10, 64) + if err != nil { + priority = 0 + } + meta.Spec.Priority = priority + + // Schema + case AExample: + meta.Spec.ConfigSchema.OpenAPIV3Schema.Example = &JSON{Raw: []byte(a.Text)} + case AScope: + meta.Spec.ConfigSchema.OpenAPIV3Schema.Scope = Scope(a.Text) + } + } +} + +type WasmPluginInfo struct { + Category Category `json:"category" yaml:"category"` + Name string `json:"name" yaml:"name"` + Title string `json:"title,omitempty" yaml:"title,omitempty"` + XTitleI18n map[I18nType]string `json:"x-title-i18n,omitempty" yaml:"x-title-i18n,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + XDescriptionI18n map[I18nType]string `json:"x-description-i18n,omitempty" yaml:"x-description-i18n,omitempty"` + IconUrl string `json:"iconUrl,omitempty" yaml:"iconUrl,omitempty"` + Version string `json:"version" yaml:"version"` + Contact Contact `json:"contact,omitempty" yaml:"contact,omitempty"` +} + +type Category string + +const ( + CategoryAuth Category = "auth" + CategorySecurity Category = "security" + CategoryProtocol Category = "protocol" + CategoryFlowControl Category = "flow-control" + CategoryFlowMonitor Category = "flow-monitor" + CategoryCustom Category = "custom" + CategoryDefault = CategoryCustom +) + +const ( + IconAuth = "https://img.alicdn.com/imgextra/i4/O1CN01BPFGlT1pGZ2VDLgaH_!!6000000005333-2-tps-42-42.png" + IconSecurity = "https://img.alicdn.com/imgextra/i1/O1CN01jKT9vC1O059vNaq5u_!!6000000001642-2-tps-42-42.png" + IconProtocol = "https://img.alicdn.com/imgextra/i2/O1CN01xIywow1mVGuRUjbhe_!!6000000004959-2-tps-42-42.png" + IconFlowControl = "https://img.alicdn.com/imgextra/i3/O1CN01bAFa9k1t1gdQcVTH0_!!6000000005842-2-tps-42-42.png" + IconFlowMonitor = "https://img.alicdn.com/imgextra/i4/O1CN01aet3s61MoLOEEhRIo_!!6000000001481-2-tps-42-42.png" + IconCustom = "https://img.alicdn.com/imgextra/i1/O1CN018iKKih1iVx287RltL_!!6000000004419-2-tps-42-42.png" + IconDefault = IconCustom +) + +func Category2IconUrl(category Category) string { + switch category { + case CategoryAuth: + return IconAuth + case CategorySecurity: + return IconSecurity + case CategoryProtocol: + return IconProtocol + case CategoryFlowControl: + return IconFlowControl + case CategoryFlowMonitor: + return IconFlowMonitor + case CategoryCustom: + return IconCustom + default: + return IconDefault + } +} + +type I18nType string + +const ( + I18nZH_CN I18nType = "zh-CN" // default + I18nEN_US I18nType = "en-US" + I18nUndefined I18nType = "undefined" // i18n type is empty in the annotation + I18nUnknown I18nType = "unknown" + I18nDefault = I18nEN_US +) + +func str2I18nType(typ string) I18nType { + switch strings.ToLower(typ) { + case "zh-cn": + return I18nZH_CN + case "en-us": + return I18nEN_US + default: + return I18nUnknown + } +} + +type Contact struct { + Name string `json:"name,omitempty" yaml:"name,omitempty"` + Url string `json:"url,omitempty" yaml:"url,omitempty"` + Email string `json:"email,omitempty" yaml:"email,omitempty"` +} + +type WasmPluginSpec struct { + // Phase refers to https://istio.io/latest/docs/reference/config/proxy_extensions/wasm-plugin/#PluginPhase + Phase Phase `json:"phase" yaml:"phase"` + + // Priority refers to https://istio.io/latest/docs/reference/config/proxy_extensions/wasm-plugin/#WasmPlugin + Priority int64 `json:"priority" yaml:"priority"` + + ConfigSchema ConfigSchema `json:"configSchema" yaml:"configSchema"` +} + +type Phase string + +const ( + PhaseUnspecified Phase = "UNSPECIFIED_PHASE" + PhaseAuthn Phase = "AUTHN" + PhaseAuthz Phase = "AUTHZ" + PhaseStats Phase = "STATS" + PhaseDefault = PhaseUnspecified +) + +type ConfigSchema struct { + OpenAPIV3Schema *JSONSchemaProps `json:"openAPIV3Schema" yaml:"openAPIV3Schema"` +} + +// GetConfigExample returns a pretty WASM plugin config example +func (meta *WasmPluginMeta) GetConfigExample() string { + s := meta.Spec.ConfigSchema.OpenAPIV3Schema + if s != nil { + return s.GetExample() + } + return "" +} + +// getLanguageUnionOrderMap returns a ordered map of language union of title and description. +// If there is a language type in title that description does not have, the value is "No description" +func (meta *WasmPluginMeta) getLanguageUnionOrderMap() *orderedmap.OrderedMap { + m := orderedmap.New() + for i18n, desc := range meta.Info.XDescriptionI18n { + m.Set(string(i18n), desc) + } + for i18n := range meta.Info.XTitleI18n { + if _, ok := m.Get(string(i18n)); !ok { + m.Set(string(i18n), "No description") + } + } + if len(m.Keys()) == 0 { + m.Set(string(I18nEN_US), "No description") + } + m.SortKeys(sort.Strings) + return m +} + +// WasmUsage is used to describe WASM plugin usage in the Markdown document +type WasmUsage struct { + I18nType I18nType + Description string + ConfigEntries []ConfigEntry + Example string +} + +type ConfigEntry struct { + Name string + Type string + Requirement string + Default string + Description string +} + +// GetUsages returns WASM plugin usages in different languages +func (meta *WasmPluginMeta) GetUsages() ([]WasmUsage, error) { + usages := make([]WasmUsage, 0) + example := meta.GetConfigExample() + m := meta.getLanguageUnionOrderMap() + for _, i18n := range m.Keys() { + desc, ok := m.Get(i18n) + if !ok { + continue + } + + u := WasmUsage{ + I18nType: I18nType(i18n), + Description: desc.(string), + ConfigEntries: make([]ConfigEntry, 0), + Example: example, + } + getConfigEntries(meta.Spec.ConfigSchema.OpenAPIV3Schema, &u.ConfigEntries, I18nType(i18n)) + usages = append(usages, u) + } + + return usages, nil +} + +func getConfigEntries(schema *JSONSchemaProps, entries *[]ConfigEntry, i18n I18nType) { + doGetConfigEntries(schema, entries, "", "", i18n, false) +} + +func doGetConfigEntries(schema *JSONSchemaProps, entries *[]ConfigEntry, parentName, name string, i18n I18nType, required bool) { + newName := constructName(parentName, name) + switch schema.Type { + case "object": + m := schema.GetPropertiesOrderMap() + for _, fieldName := range m.Keys() { + val, ok := m.Get(fieldName) + if !ok { + continue + } + props := val.(JSONSchemaProps) + required = schema.IsRequired(fieldName) + doGetConfigEntries(&props, entries, newName, fieldName, i18n, required) + } + case "array": + itemType := schema.Items.Schema.Type + e := ConfigEntry{ + Name: newName, + Type: ArrayPrefix + itemType, + Requirement: schema.JoinRequirementsBy(i18n, required), + Default: schema.GetDefaultValue(), + Description: schema.XDescriptionI18n[i18n], + } + *entries = append(*entries, e) + if itemType == "object" { + doGetConfigEntries(schema.Items.Schema, entries, newName+"[*]", "", i18n, false) + } + default: + e := ConfigEntry{ + Name: newName, + Type: schema.Type, + Requirement: schema.JoinRequirementsBy(i18n, required), + Default: schema.GetDefaultValue(), + Description: schema.XDescriptionI18n[i18n], + } + *entries = append(*entries, e) + } +} + +func constructName(parent, name string) string { + newName := name + if parent != "" { + if name != "" { + newName = fmt.Sprintf("%s.%s", parent, name) + } else { + newName = parent + } + } + return newName +} diff --git a/pkg/cmd/hgctl/plugin/types/model_parser.go b/pkg/cmd/hgctl/plugin/types/model_parser.go new file mode 100644 index 0000000000..c88ea06d90 --- /dev/null +++ b/pkg/cmd/hgctl/plugin/types/model_parser.go @@ -0,0 +1,391 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "fmt" + "go/ast" + "go/parser" + "go/token" + "io/fs" + "os" + "path/filepath" + "strings" + + "github.com/fatih/structtag" + "github.com/pkg/errors" +) + +const ( + ArrayPrefix = "array of " + ObjectSuffix = "object" +) + +// IsArray returns true if the given type is an `array of ` +func IsArray(typ string) bool { + return strings.HasPrefix(typ, ArrayPrefix) +} + +// GetItemType returns the item type of array, e.g.: array of int -> int +func GetItemType(typ string) string { + return strings.TrimPrefix(typ, ArrayPrefix) +} + +// IsObject returns true if the given type is an `object` or an `array of object` +func IsObject(typ string) bool { + return strings.HasSuffix(typ, ObjectSuffix) +} + +var ( + ErrInvalidModel = errors.New("invalid model") + ErrInvalidFiledType = errors.New("invalid field type") +) + +type ModelParser struct { + structs map[string]*astNode + + // alias for a basic type, such as type MyInt int: MyInt -> int + // TODO(WeixinX): Support alias for package name + alias map[string]*astNode +} + +type Model struct { + Name string + Type string + Doc string + Tag string + Fields []Model +} + +type astNode struct { + name string + doc string + expr ast.Expr +} + +func (m *Model) Inspect(f func(model *Model) bool) { + ctn := f(m) + if !ctn { + return + } + + for _, field := range m.Fields { + field.Inspect(f) + } +} + +// NewModelParser new a model parser based on the dir where the given model exists +func NewModelParser(dir string) (*ModelParser, error) { + pkgs, err := walkGoSrc(dir) + if err != nil { + return nil, err + } + p := &ModelParser{ + structs: make(map[string]*astNode), + alias: make(map[string]*astNode), + } + for _, pkg := range pkgs { + for _, f := range pkg.Files { + for _, decl := range f.Decls { + x, ok := decl.(*ast.GenDecl) + if !ok || x.Tok != token.TYPE { + continue + } + for _, spec := range x.Specs { + ts, ok := spec.(*ast.TypeSpec) + if !ok { + continue + } + + switch t := ts.Type.(type) { + case *ast.StructType: + if !t.Struct.IsValid() { + continue + } + s := &astNode{ + name: ts.Name.String(), + expr: t, + } + if pkg.Name != "main" { // ignore main package prefix + s.name = fmt.Sprintf("%s.%s", pkg.Name, s.name) + } + if x.Doc != nil { + s.doc = x.Doc.Text() + } + p.structs[s.name] = s + case *ast.InterfaceType: + continue + default: // for alias, such as `type MyInt int` + alias := ts.Name.String() + if pkg.Name != "main" { + alias = fmt.Sprintf("%s.%s", pkg.Name, alias) + } + name, err := p.getModelName(t) + if err != nil { + continue + } + p.alias[alias] = &astNode{ + name: name, + expr: t, + } + } + } + } + } + } + + // gets the true type (ast node) of the alias + for alias := range p.alias { + n := p.recursiveAlias(alias) + if n != nil { + p.alias[alias] = n + } + } + + return p, nil +} + +func walkGoSrc(dir string) (map[string]*ast.Package, error) { + info, err := os.Stat(dir) + if err != nil { + return nil, err + } + if !info.IsDir() { + return nil, errors.Errorf("%q is not a directory", dir) + } + + fset := token.NewFileSet() + pkgs := make(map[string]*ast.Package) + walk := func(path string, info fs.FileInfo, err error) error { + if !info.IsDir() { + return nil + } + tmp, err := parser.ParseDir(fset, path, nil, parser.ParseComments) + if err != nil { + return err + } + for k, v := range tmp { + pkgs[k] = v + } + return nil + } + if err := filepath.Walk(dir, walk); err != nil { + return nil, errors.Wrapf(err, "failed to walk path %q", dir) + } + return pkgs, nil +} + +func (p *ModelParser) recursiveAlias(alias string) *astNode { + if s, ok := p.structs[alias]; ok { + return s + } + if n, ok := p.alias[alias]; ok { + if n.name != alias { + ret := p.recursiveAlias(n.name) + if ret != nil { + return ret + } + } + return n + } + return nil +} + +// GetModel return the specified model +func (p *ModelParser) GetModel(model string) (*Model, error) { + fields, err := p.parseModelFields(model) + if err != nil { + return nil, err + } + + m := &Model{ + Name: model, + Type: "object", + Fields: fields, + } + m.setDoc(p.structs[model].doc) + return m, nil +} + +func (p *ModelParser) parseModelFields(model string) (fields []Model, err error) { + var s *astNode + if _, ok := p.structs[model]; ok { + s = p.structs[model] + } else if _, ok = p.alias[model]; ok { + s = p.alias[model] + } else { + return nil, ErrInvalidModel + } + + st, ok := s.expr.(*ast.StructType) + if !ok || st.Fields == nil { + return nil, ErrInvalidModel + } + pkgName := "" + if idx := strings.Index(model, "."); idx != -1 { + pkgName = model[:idx+1] // pkgName includes "." + } + for _, field := range st.Fields.List { + if skipField(field) { + continue + } + fd := Model{Name: field.Names[0].String()} + if field.Doc != nil { + fd.setDoc(field.Doc.Text()) + } + if field.Tag != nil { + ignore, err := fd.setTag(field.Tag.Value) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse tag %q of the field %q", fd.Tag, fd.Name) + } + if ignore { + continue + } + } + fd.Type, err = p.parseFiledType(pkgName, field.Type) + if err != nil { + return nil, err + } + if IsObject(fd.Type) { + subModel, err := p.getModelName(field.Type) + if err != nil { + return nil, err + } + fd.Fields, err = p.parseModelFields(subModel) + if err != nil { + return nil, err + } + } + fields = append(fields, fd) + } + return fields, nil +} + +func skipField(field *ast.Field) bool { + name := field.Names + return field == nil || name == nil || len(name) < 1 || name[0] == nil || name[0].String() == "_" +} + +func (m *Model) setDoc(str string) { + m.Doc = strings.TrimSpace(str) +} + +func (m *Model) setTag(str string) (bool, error) { + str = strings.Trim(str, "` ") + if str == "" { + return false, nil + } + + ignore := false + tag, err := structtag.Parse(str) + if err != nil { + return false, err + } + m.Tag = str + val, err := tag.Get("yaml") + if err == nil { + if val.Name == "-" || val.Name == "" { + ignore = true + } + } + return ignore, nil +} + +func (p *ModelParser) getModelName(typ ast.Expr) (string, error) { + return p.doGetModelName("", typ) +} + +func (p *ModelParser) doGetModelName(pkgName string, typ ast.Expr) (string, error) { + switch t := typ.(type) { + case *ast.StarExpr: // *int -> int + return p.doGetModelName(pkgName, t.X) + case *ast.ArrayType: // slice or array + return p.doGetModelName(pkgName, t.Elt) + case *ast.SelectorExpr: // . + pkg, ok := t.X.(*ast.Ident) + if !ok { + return "", ErrInvalidFiledType + } + pName := pkg.Name + "." + return p.doGetModelName(pName, t.Sel) + case *ast.Ident: + return pkgName + t.Name, nil + default: + return "", ErrInvalidFiledType + } +} + +func (p *ModelParser) parseFiledType(pkgName string, typ ast.Expr) (string, error) { + switch t := typ.(type) { + case *ast.StructType: // nested struct + return string(JsonTypeObject), nil + case *ast.StarExpr: // *int -> int + return p.parseFiledType(pkgName, t.X) + case *ast.ArrayType: // slice or array + ret, err := p.parseFiledType(pkgName, t.Elt) + if err != nil { + return "", err + } + return ArrayPrefix + ret, nil + case *ast.SelectorExpr: // . + pkg, ok := t.X.(*ast.Ident) + if !ok { + return "", ErrInvalidFiledType + } + pName := pkg.Name + "." + return p.parseFiledType(pName, t.Sel) + case *ast.Ident: + fName := pkgName + t.Name + if _, ok := p.structs[fName]; ok { + return string(JsonTypeObject), nil + } + if alias, ok := p.alias[fName]; ok { + return p.parseFiledType(pkgName, alias.expr) + } + jsonType, err := convert2JsonType(t.Name) + return string(jsonType), err + default: + return "", ErrInvalidFiledType + } +} + +func convert2JsonType(typ string) (JsonType, error) { + switch typ { + case "int", "int8", "int16", "int32", "int64", + "uint", "uint8", "uint16", "uint32", "uint64": + return JsonTypeInteger, nil + case "float32", "float64": + return JsonTypeNumber, nil + case "bool": + return JsonTypeBoolean, nil + case "string": + return JsonTypeString, nil + case "struct": + return JsonTypeObject, nil + default: + return "", ErrInvalidFiledType + } +} + +type JsonType string + +const ( + JsonTypeInteger JsonType = "integer" + JsonTypeNumber JsonType = "number" + JsonTypeBoolean JsonType = "boolean" + JsonTypeString JsonType = "string" + JsonTypeObject JsonType = "object" + JsonTypeArray JsonType = "array" +) diff --git a/pkg/cmd/hgctl/plugin/types/model_parser_test.go b/pkg/cmd/hgctl/plugin/types/model_parser_test.go new file mode 100644 index 0000000000..13f40c2591 --- /dev/null +++ b/pkg/cmd/hgctl/plugin/types/model_parser_test.go @@ -0,0 +1,379 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestGetModel(t *testing.T) { + var ( + BasicStructField = []Model{ + { + Name: "Name", + Type: "string", + }, + { + Name: "Age", + Type: "integer", + }, + { + Name: "Married", + Type: "boolean", + }, + { + Name: "Salary", + Type: "number", + }, + } + + ExternalStructField = []Model{ + { + Name: "one", + Type: "string", + }, + { + Name: "two", + Type: "integer", + }, + { + Name: "three", + Type: "array of boolean", + }, + } + + NestedStructField = []Model{ + { + Name: "Simple", + Type: "string", + }, + { + Name: "Complex", + Type: "array of integer", + }, + } + ) + + cases := []struct { + name string + expected *Model + errMsg string + }{ + { + name: "TestBasicStruct", + expected: &Model{ + Name: "TestBasicStruct", + Type: "object", + Fields: BasicStructField, + }, + }, + { + name: "TestComplexStruct", + expected: &Model{ + Name: "TestComplexStruct", + Type: "object", + Fields: []Model{ + { + Name: "Array", + Type: "array of integer", + }, + { + Name: "Slice", + Type: "array of string", + }, + { + Name: "Pointer", + Type: "string", + }, + { + Name: "PPPointer", + Type: "boolean", + }, + { + Name: "ArrayPointer", + Type: "array of integer", + }, + { + Name: "SlicePointer", + Type: "array of integer", + }, + { + Name: "StructPointerSlice", + Type: "array of object", + Fields: BasicStructField, + }, + { + Name: "StructArrayPointer", + Type: "array of object", + Fields: BasicStructField, + }, + }, + }, + }, + { + name: "TestAliasStruct", + expected: &Model{ + Name: "TestAliasStruct", + Type: "object", + Fields: []Model{ + { + Name: "MyString", + Type: "string", + }, + { + Name: "MyPointerInt", + Type: "integer", + }, + { + Name: "MyStruct", + Type: "object", + Fields: BasicStructField, + }, + }, + }, + }, + { + name: "TestExternalStruct", + expected: &Model{ + Name: "TestExternalStruct", + Type: "object", + Fields: []Model{ + { + Name: "InternalFloat", + Type: "number", + }, + { + Name: "ExStruct", + Type: "object", + Fields: ExternalStructField, + }, + { + Name: "ExternalInt", + Type: "integer", + }, + { + Name: "ExBool", + Type: "boolean", + }, + { + Name: "ExSlice", + Type: "array of string", + }, + }, + }, + }, + { + name: "TestNestedStruct", + expected: &Model{ + Name: "TestNestedStruct", + Type: "object", + Fields: []Model{ + { + Name: "NestedStruct", + Type: "object", + Fields: []Model{ + { + Name: "NestedStruct", + Type: "object", + Fields: NestedStructField, + }, + { + Name: "NestedInt", + Type: "integer", + }, + { + Name: "NestedString", + Type: "string", + }, + }, + }, + }, + }, + }, + { + name: "ext.TestExStruct", + expected: &Model{ + Name: "ext.TestExStruct", + Type: "object", + Fields: ExternalStructField, + }, + }, + { + name: "ext.TestNestedStruct", + expected: &Model{ + Name: "ext.TestNestedStruct", + Type: "object", + Fields: []Model{ + { + Name: "NestedStruct", + Type: "object", + Fields: NestedStructField, + }, + { + Name: "NestedInt", + Type: "integer", + }, + { + Name: "NestedString", + Type: "string", + }, + }, + }, + }, + } + + p, err := NewModelParser("./testdata/types") + require.NoError(t, err) + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual, err := p.GetModel(c.name) + if c.errMsg != "" { + require.EqualError(t, err, c.errMsg) + } else { + require.NoError(t, err) + require.Equal(t, c.expected, actual) + } + }) + } +} + +func TestParseStructAndAlias(t *testing.T) { + cases := []struct { + name string + dir string + expectedStructs map[string]struct{} + expectedAlias map[string]string + }{ + { + name: "Basic", + dir: "./testdata/types", + expectedStructs: map[string]struct{}{ + "TestBasicStruct": {}, + "TestComplexStruct": {}, + "TestAliasStruct": {}, + "TestExternalStruct": {}, + "TestNestedStruct": {}, + "ext.TestExStruct": {}, + "ext.TestNestedStruct": {}, + "nested.TestNestedStruct": {}, + }, + expectedAlias: map[string]string{ + "MyString": "string", + "MyPointerInt": "int", + "MyStruct": "TestBasicStruct", + "NestedAlias": "nested.TestNestedStruct", + "NestedBasicAlias": "bool", + "ext.ExAlias": "nested.TestNestedStruct", + "ext.ExPointerInt": "int", + "ext.ExBool": "bool", + "ext.ExSlice": "string", + "nested.NestedInt": "int", + "nested.NestedString": "string", + }, + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + p, err := NewModelParser(c.dir) + require.NoError(t, err) + + actualStructs := make(map[string]struct{}) + for _, s := range p.structs { + actualStructs[s.name] = struct{}{} + } + require.Equal(t, c.expectedStructs, actualStructs) + + actualAlias := make(map[string]string) + for name, alias := range p.alias { + actualAlias[name] = alias.name + } + require.Equal(t, c.expectedAlias, actualAlias) + }) + } +} + +func TestStructFieldDocAndTag(t *testing.T) { + var BasicStructField = []Model{ + { + Name: "Name", + Type: "string", + Doc: "Name, specify username", + Tag: `yaml:"name" required:"true" minLength:"1" maxLength:"32"`, + }, + { + Name: "Age", + Type: "integer", + Doc: "Age, specify age", + Tag: `yaml:"age" required:"true" minimum:"0" maximum:"140"`, + }, + { + Name: "Married", + Type: "boolean", + Doc: "Married, specify marital status [true, false]\nand optional", + Tag: `yaml:"married" required:"false"`, + }, + { + Name: "Salary", + Type: "number", + Doc: "Salary, specify income status, optional", + Tag: `yaml:"salary" required:"false"`, + }, + { + Name: "Children", + Type: "array of string", + Doc: "Children, specify a list of children's names, optional", + Tag: `yaml:"children" required:"false"`, + }, + } + + cases := []struct { + name string + model string + expected []Model + }{ + { + name: "TestBasicDocTag", + model: "TestBasicDocTag", + expected: BasicStructField, + }, + { + name: "TestNestedStructDocTag", + model: "TestNestedStructDocTag", + expected: []Model{ + { + Name: "Struct", + Type: "array of object", + Doc: "This is the comment of the nested struct field", + Tag: `yaml:"struct" required:"true" minItems:"1" maxItems:"10"`, + Fields: BasicStructField, + }, + }, + }, + } + + p, err := NewModelParser("./testdata/doc_tag") + require.NoError(t, err) + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + m, err := p.GetModel(c.model) + require.NoError(t, err) + require.Equal(t, c.expected, m.Fields) + }) + } +} diff --git a/pkg/cmd/hgctl/plugin/types/schema.go b/pkg/cmd/hgctl/plugin/types/schema.go new file mode 100644 index 0000000000..3b6773e7cf --- /dev/null +++ b/pkg/cmd/hgctl/plugin/types/schema.go @@ -0,0 +1,426 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "encoding/json" + "fmt" + "sort" + "strconv" + "strings" + + "github.com/alibaba/higress/pkg/cmd/hgctl/plugin/utils" + + "github.com/fatih/structtag" + "github.com/iancoleman/orderedmap" +) + +// JSONSchemaProps is a JSON-Schema following Specification Draft 4 (http://json-schema.org/). +// Borrowed from https://github.com/kubernetes/apiextensions-apiserver/blob/master/pkg/apis/apiextensions/v1/types_jsonschema.go +type JSONSchemaProps struct { + ID string `json:"id,omitempty" yaml:"id,omitempty"` + Schema JSONSchemaURL `json:"$schema,omitempty" yaml:"$schema,omitempty"` + Ref *string `json:"$ref,omitempty" yaml:"$ref,omitempty"` + Type string `json:"type,omitempty" yaml:"type,omitempty"` + Format string `json:"format,omitempty" yaml:"format,omitempty"` + Scope Scope `json:"scope,omitempty" yaml:"scope,omitempty"` + Title string `json:"title,omitempty" yaml:"title,omitempty"` + XTitleI18n map[I18nType]string `json:"x-title-i18n,omitempty" yaml:"x-title-i18n,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + XDescriptionI18n map[I18nType]string `json:"x-description-i18n,omitempty" yaml:"x-description-i18n,omitempty"` + Default *JSON `json:"default,omitempty" yaml:"default,omitempty"` + Minimum *float64 `json:"minimum,omitempty" yaml:"minimum,omitempty"` + ExclusiveMinimum bool `json:"exclusiveMinimum,omitempty" yaml:"exclusiveMinimum,omitempty"` + Maximum *float64 `json:"maximum,omitempty" yaml:"maximum,omitempty"` + ExclusiveMaximum bool `json:"exclusiveMaximum,omitempty" yaml:"exclusiveMaximum,omitempty"` + MinLength *int64 `json:"minLength,omitempty" yaml:"minLength,omitempty"` + MaxLength *int64 `json:"maxLength,omitempty" yaml:"maxLength,omitempty"` + Pattern string `json:"pattern,omitempty" yaml:"pattern,omitempty"` + MaxItems *int64 `json:"maxItems,omitempty" yaml:"maxItems,omitempty"` + MinItems *int64 `json:"minItems,omitempty" yaml:"minItems,omitempty"` + UniqueItems bool `json:"uniqueItems,omitempty" yaml:"uniqueItems,omitempty"` + MultipleOf *float64 `json:"multipleOf,omitempty" yaml:"multipleOf,omitempty"` + Enum []JSON `json:"enum,omitempty" yaml:"enum,omitempty"` + MinProperties *int64 `json:"minProperties,omitempty" yaml:"minProperties,omitempty"` + MaxProperties *int64 `json:"maxProperties,omitempty" yaml:"maxProperties,omitempty"` + Required []string `json:"required,omitempty" yaml:"required,omitempty"` + Items *JSONSchemaPropsOrArray `json:"items,omitempty" yaml:"items,omitempty"` + AllOf []JSONSchemaProps `json:"allOf,omitempty" yaml:"allOf,omitempty"` + OneOf []JSONSchemaProps `json:"oneOf,omitempty" yaml:"oneOf,omitempty"` + AnyOf []JSONSchemaProps `json:"anyOf,omitempty" yaml:"anyOf,omitempty"` + Not *JSONSchemaProps `json:"not,omitempty" yaml:"not,omitempty"` + Properties map[string]JSONSchemaProps `json:"properties,omitempty" yaml:"properties,omitempty"` + AdditionalProperties *JSONSchemaPropsOrBool `json:"additionalProperties,omitempty" yaml:"additionalProperties,omitempty"` + PatternProperties map[string]JSONSchemaProps `json:"patternProperties,omitempty" yaml:"patternProperties,omitempty"` + Dependencies JSONSchemaDependencies `json:"dependencies,omitempty" yaml:"dependencies,omitempty"` + AdditionalItems *JSONSchemaPropsOrBool `json:"additionalItems,omitempty" yaml:"additionalItems,omitempty"` + Definitions JSONSchemaDefinitions `json:"definitions,omitempty" yaml:"definitions,omitempty"` + ExternalDocs *ExternalDocumentation `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"` + Example *JSON `json:"example,omitempty" yaml:"example,omitempty"` + Nullable bool `json:"nullable,omitempty" yaml:"nullable,omitempty"` +} + +type Scope string + +const ( + ScopeGlobal Scope = "GLOBAL" + ScopeInstance Scope = "INSTANCE" + ScopeAll Scope = "ALL" + ScopeDefault = ScopeInstance +) + +// JSON represents any valid JSON value. +// These types are supported: bool, int64, float64, string, []interface{}, map[string]interface{} and nil. +type JSON struct { + Raw []byte `json:"-" yaml:"-"` +} + +// JSONSchemaPropsOrArray represents a value that can either be a JSONSchemaProps +// or an array of JSONSchemaProps. Mainly here for serialization purposes. +type JSONSchemaPropsOrArray struct { + Schema *JSONSchemaProps + JSONSchemas []JSONSchemaProps +} + +// JSONSchemaPropsOrBool represents JSONSchemaProps or a boolean value. +// Defaults to true for the boolean property. +type JSONSchemaPropsOrBool struct { + Allows bool + Schema *JSONSchemaProps +} + +// JSONSchemaDependencies represent a dependencies property. +type JSONSchemaDependencies map[string]JSONSchemaPropsOrStringArray + +// JSONSchemaPropsOrStringArray represents a JSONSchemaProps or a string array. +type JSONSchemaPropsOrStringArray struct { + Schema *JSONSchemaProps + Property []string +} + +// JSONSchemaURL represents a schema url. +type JSONSchemaURL string + +// JSONSchemaDefinitions contains the models explicitly defined in this spec. +type JSONSchemaDefinitions map[string]JSONSchemaProps + +// ExternalDocumentation allows referencing an external resource for extended documentation. +type ExternalDocumentation struct { + Description string `json:"description,omitempty" yaml:"description,omitempty"` + URL string `json:"url,omitempty" yaml:"url,omitempty"` +} + +func NewJSONSchemaProps() *JSONSchemaProps { + return &JSONSchemaProps{ + XTitleI18n: make(map[I18nType]string), + XDescriptionI18n: make(map[I18nType]string), + Properties: make(map[string]JSONSchemaProps), + } +} + +// IsRequired determines whether the given `name` field is required +func (s *JSONSchemaProps) IsRequired(name string) bool { + req := false + for _, n := range s.Required { + if name == n { + req = true + break + } + } + return req +} + +// GetDefaultValue returns the default value of the schema +func (s *JSONSchemaProps) GetDefaultValue() string { + d := "-" + if s.Default == nil { + return d + } + if len(s.Default.Raw) > 0 { + d = string(s.Default.Raw) + } + return d +} + +// GetExample returns the pretty example of the schema +func (s *JSONSchemaProps) GetExample() string { + ret := "" + if s.Example != nil && len(s.Example.Raw) > 0 { + ret = string(s.Example.Raw) + if ret[0] == '{' { + // string(s.Example.Raw) might look like (when the schema is generated through go src): + // {"allow":["consumer1"],"consumers":[{"credential":"admin:123456","name":"consumer1"}]} + var obj interface{} + err := json.Unmarshal(s.Example.Raw, &obj) + if err != nil { + return "" + } + b, err := utils.MarshalYamlWithIndent(obj, 2) + if err != nil { + return "" + } + ret = string(b) + } + } + return ret +} + +// GetPropertiesOrderMap converts the schema Properties map to +// an ordered map (dictionary order) and returns it +func (s *JSONSchemaProps) GetPropertiesOrderMap() *orderedmap.OrderedMap { + m := orderedmap.New() + for name, prop := range s.Properties { + m.Set(name, prop) + } + m.SortKeys(sort.Strings) + return m +} + +// HandleFieldAnnotations parses the comment (annotations look like `// @ [LANGUAGE] `) +// and sets the schema properties +func (s *JSONSchemaProps) HandleFieldAnnotations(comment string) { + as := GetAnnotations(comment) + for _, a := range as { + switch a.Type { + case ATitle: + if s.Title == "" { + s.Title = a.Text + } + s.XTitleI18n[a.I18nType] = a.Text + case ADescription: + if s.Description == "" { + s.Description = a.Text + } + s.XDescriptionI18n[a.I18nType] = a.Text + case AScope: + s.Scope = Scope(a.Text) + case AExample: + s.Example = &JSON{Raw: []byte(a.Text)} + } + } +} + +// HandleFieldTags parses the struct field tags and sets the schema properties +// TODO: Add more tags (now supported yaml, minimum, maximum, ...) +func (s *JSONSchemaProps) HandleFieldTags(tags string, parent *JSONSchemaProps, fieldName string) string { + if tags == "" { + return fieldName + } + st, err := structtag.Parse(tags) + if err != nil { + return fieldName + } + + newName := fieldName + for _, tag := range st.Tags() { + switch tag.Key { + case "yaml": + newName = tag.Name + if s.Title == "" { + s.Title = newName + s.XTitleI18n[I18nDefault] = newName + } + case "required": + required, _ := strconv.ParseBool(tag.Name) + if !required { + continue + } + parent.Required = append(parent.Required, newName) + case "minimum": + min, err := strconv.ParseFloat(tag.Name, 64) + if err != nil { + continue + } + s.Minimum = &min + case "maximum": + max, err := strconv.ParseFloat(tag.Name, 64) + if err != nil { + continue + } + s.Maximum = &max + case "minLength": + minL, err := strconv.ParseInt(tag.Name, 10, 64) + if err != nil { + continue + } + s.MinLength = &minL + case "maxLength": + maxL, err := strconv.ParseInt(tag.Name, 10, 64) + if err != nil { + continue + } + s.MaxLength = &maxL + case "minItems": + minI, err := strconv.ParseInt(tag.Name, 10, 64) + if err != nil { + continue + } + s.MinItems = &minI + case "maxItems": + maxI, err := strconv.ParseInt(tag.Name, 10, 64) + if err != nil { + continue + } + s.MaxItems = &maxI + case "pattern": + s.Pattern = tag.Name + } + } + + return newName +} + +// JoinRequirementsBy joins the requirements by the given i18n type. Return value looks like: +// required, minLength 10, regular expression "^.*$" +func (s *JSONSchemaProps) JoinRequirementsBy(i18n I18nType, required bool) string { + reqs := s.getRequirements(required) + switch i18n { + case I18nZH_CN: + return strings.Join(reqs[I18nZH_CN], ",") + case I18nEN_US: + fallthrough + default: + return strings.Join(reqs[I18nDefault], ", ") + } +} + +func (s *JSONSchemaProps) getRequirements(required bool) map[I18nType][]string { + reqs := make(map[I18nType][]string) + + for i18n, str := range s.GetRequired(required) { + reqs[i18n] = append(reqs[i18n], str) + } + + for i18n, str := range s.GetMinimum() { + reqs[i18n] = append(reqs[i18n], str) + } + + for i18n, str := range s.GetMaximum() { + reqs[i18n] = append(reqs[i18n], str) + } + + for i18n, str := range s.GetMinLength() { + reqs[i18n] = append(reqs[i18n], str) + } + + for i18n, str := range s.GetMaxLength() { + reqs[i18n] = append(reqs[i18n], str) + } + + for i18n, str := range s.GetMinItems() { + reqs[i18n] = append(reqs[i18n], str) + } + + for i18n, str := range s.GetMaxItems() { + reqs[i18n] = append(reqs[i18n], str) + } + + for i18n, str := range s.GetPattern() { + reqs[i18n] = append(reqs[i18n], str) + } + + return reqs +} + +func (s *JSONSchemaProps) GetMinimum() map[I18nType]string { + if s.Minimum == nil { + return nil + } + + return map[I18nType]string{ + I18nZH_CN: fmt.Sprintf("最小值 %f", *s.Minimum), + I18nEN_US: fmt.Sprintf("minimum %f", *s.Minimum), + } +} + +func (s *JSONSchemaProps) GetMaximum() map[I18nType]string { + if s.Maximum == nil { + return nil + } + + return map[I18nType]string{ + I18nZH_CN: fmt.Sprintf("最大值 %f", *s.Maximum), + I18nEN_US: fmt.Sprintf("maximum %f", *s.Maximum), + } +} + +func (s *JSONSchemaProps) GetMinLength() map[I18nType]string { + if s.MinLength == nil { + return nil + } + + return map[I18nType]string{ + I18nZH_CN: fmt.Sprintf("最小长度 %d", *s.MinLength), + I18nEN_US: fmt.Sprintf("minLength %d", *s.MinLength), + } +} + +func (s *JSONSchemaProps) GetMaxLength() map[I18nType]string { + if s.MaxLength == nil { + return nil + } + + return map[I18nType]string{ + I18nZH_CN: fmt.Sprintf("最大长度 %d", *s.MaxLength), + I18nEN_US: fmt.Sprintf("maxLength %d", *s.MaxLength), + } +} + +func (s *JSONSchemaProps) GetPattern() map[I18nType]string { + if s.Pattern == "" { + return nil + } + + return map[I18nType]string{ + I18nZH_CN: fmt.Sprintf("正则表达式 %q", s.Pattern), + I18nEN_US: fmt.Sprintf("regular expression %q", s.Pattern), + } +} + +func (s *JSONSchemaProps) GetMinItems() map[I18nType]string { + if s.MinItems == nil { + return nil + } + + return map[I18nType]string{ + I18nZH_CN: fmt.Sprintf("最小 item 个数 %d", *s.MinItems), + I18nEN_US: fmt.Sprintf("minItems %d", *s.MinItems), + } +} + +func (s *JSONSchemaProps) GetMaxItems() map[I18nType]string { + if s.MaxItems == nil { + return nil + } + + return map[I18nType]string{ + I18nZH_CN: fmt.Sprintf("最大 item 个数 %d", *s.MaxItems), + I18nEN_US: fmt.Sprintf("maxItems %d", *s.MaxItems), + } +} + +func (s *JSONSchemaProps) GetRequired(req bool) map[I18nType]string { + if req { + return map[I18nType]string{ + I18nZH_CN: "必填", + I18nEN_US: "required", + } + } + + return map[I18nType]string{ + I18nZH_CN: "选填", + I18nEN_US: "optional", + } +} diff --git a/pkg/cmd/hgctl/plugin/types/testdata/doc_tag/main.go b/pkg/cmd/hgctl/plugin/types/testdata/doc_tag/main.go new file mode 100644 index 0000000000..ed1ec4b2eb --- /dev/null +++ b/pkg/cmd/hgctl/plugin/types/testdata/doc_tag/main.go @@ -0,0 +1,45 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +// TestBasicDocTag This is a test struct for documents(comments) and tags +type TestBasicDocTag struct { + // Name, specify username + Name string `yaml:"name" required:"true" minLength:"1" maxLength:"32"` + + // Age, specify age + Age uint `yaml:"age" required:"true" minimum:"0" maximum:"140" ` + + // Married, specify marital status [true, false] + // and optional + Married bool `yaml:"married" required:"false"` + + // Salary, specify income status, optional + Salary float64 `yaml:"salary" required:"false"` + + // Children, specify a list of children's names, optional + Children []string `yaml:"children" required:"false"` + + // ignore1 + Ignore1 string `yaml:"-"` + + // ignore 2 + Ignore2 string `yaml:""` +} + +type TestNestedStructDocTag struct { + // This is the comment of the nested struct field + Struct []*TestBasicDocTag `yaml:"struct" required:"true" minItems:"1" maxItems:"10"` +} diff --git a/pkg/cmd/hgctl/plugin/types/testdata/types/ext/ext.go b/pkg/cmd/hgctl/plugin/types/testdata/types/ext/ext.go new file mode 100644 index 0000000000..f321fc0bae --- /dev/null +++ b/pkg/cmd/hgctl/plugin/types/testdata/types/ext/ext.go @@ -0,0 +1,34 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ext + +import "github.com/alibaba/higress/pkg/cmd/hgctl/plugin/types/testdata/types/ext/nested" + +type TestExStruct struct { + one string + two *int + three []bool +} + +type ExPointerInt **int +type ExBool bool +type ExSlice []*string +type ExAlias nested.TestNestedStruct + +type TestNestedStruct struct { + NestedStruct *nested.TestNestedStruct + NestedInt *nested.NestedInt + NestedString nested.NestedString +} diff --git a/pkg/cmd/hgctl/plugin/types/testdata/types/ext/nested/nested.go b/pkg/cmd/hgctl/plugin/types/testdata/types/ext/nested/nested.go new file mode 100644 index 0000000000..607696fb9a --- /dev/null +++ b/pkg/cmd/hgctl/plugin/types/testdata/types/ext/nested/nested.go @@ -0,0 +1,23 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package nested + +type TestNestedStruct struct { + Simple string + Complex **[]*int +} + +type NestedInt ***int +type NestedString string diff --git a/pkg/cmd/hgctl/plugin/types/testdata/types/main.go b/pkg/cmd/hgctl/plugin/types/testdata/types/main.go new file mode 100644 index 0000000000..19bda093d9 --- /dev/null +++ b/pkg/cmd/hgctl/plugin/types/testdata/types/main.go @@ -0,0 +1,70 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import "github.com/alibaba/higress/pkg/cmd/hgctl/plugin/types/testdata/types/ext" + +type TestBasicStruct struct { + Name string + Age uint + Married bool + Salary float64 +} + +type TestComplexStruct struct { + Array [2]int + Slice []string + Pointer *string + PPPointer ***bool + ArrayPointer [2]*int + SlicePointer []*int + StructPointerSlice []*TestBasicStruct + StructArrayPointer *[]TestBasicStruct + _ struct { + one int + two string + } +} + +type TestAliasStruct struct { + MyString *MyString + MyPointerInt MyPointerInt + MyStruct MyStruct +} + +type MyString string +type MyPointerInt *int +type MyStruct TestBasicStruct +type NestedAlias ext.ExAlias +type NestedBasicAlias ext.ExBool + +type TestExternalStruct struct { + InternalFloat float64 + ExStruct ext.TestExStruct + ExternalInt ext.ExPointerInt + ExBool ext.ExBool + ExSlice ext.ExSlice +} + +type TestNestedStruct struct { + NestedStruct *ext.TestNestedStruct +} + +type MyInterface interface { +} + +var MyConst bool + +var MyVar int diff --git a/pkg/cmd/hgctl/plugin/uninstall/uninstall.go b/pkg/cmd/hgctl/plugin/uninstall/uninstall.go new file mode 100644 index 0000000000..0bf65836d8 --- /dev/null +++ b/pkg/cmd/hgctl/plugin/uninstall/uninstall.go @@ -0,0 +1,102 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package uninstall + +import ( + "context" + "fmt" + "io" + + k8s "github.com/alibaba/higress/pkg/cmd/hgctl/kubernetes" + "github.com/alibaba/higress/pkg/cmd/options" + "github.com/pkg/errors" + + "github.com/spf13/cobra" + k8serr "k8s.io/apimachinery/pkg/api/errors" + cmdutil "k8s.io/kubectl/pkg/cmd/util" +) + +func NewCommand() *cobra.Command { + var ( + name string + all bool + ) + + uninstallCmd := &cobra.Command{ + Use: "uninstall", + Aliases: []string{"u", "uins"}, + Short: "Uninstall WASM plugin", + Example: ` # Uninstall WASM plugin using the WasmPlugin name + hgctl plugin uninstall -p example-plugin-name + + # Uninstall all WASM plugins + hgctl plugin uninstall -A + `, + Run: func(cmd *cobra.Command, args []string) { + cmdutil.CheckErr(uninstall(cmd.OutOrStdout(), name, all)) + }, + } + + flags := uninstallCmd.PersistentFlags() + options.AddKubeConfigFlags(flags) + k8s.AddHigressNamespaceFlags(flags) + flags.StringVarP(&name, "name", "p", "", "Name of the WASM plugin you want to uninstall") + flags.BoolVarP(&all, "all", "A", false, "Delete all installed WASM plugin") + + return uninstallCmd +} + +func uninstall(w io.Writer, name string, all bool) error { + dynCli, err := k8s.NewDynamicClient(options.DefaultConfigFlags.ToRawKubeConfigLoader()) + if err != nil { + return errors.Wrap(err, "failed to build kubernetes dynamic client") + } + cli := k8s.NewWasmPluginClient(dynCli) + + ctx := context.TODO() + plugins := make([]string, 0) + if all { + list, err := cli.List(ctx) + if err != nil { + return errors.Wrap(err, "failed to get information of all wasm plugins") + } + for _, item := range list.Items { + plugins = append(plugins, item.GetName()) + } + } else { + plugins = append(plugins, name) + } + + for _, p := range plugins { + err = deleteOne(ctx, w, cli, p) + if err != nil { + fmt.Fprintln(w, err.Error()) + } + } + + return nil +} + +func deleteOne(ctx context.Context, w io.Writer, cli *k8s.WasmPluginClient, name string) error { + result, err := cli.Delete(ctx, name) + if err != nil && k8serr.IsNotFound(err) { + return errors.Errorf("wasm plugin %q is not found", fmt.Sprintf("%s/%s", k8s.HigressNamespace, name)) + } else if err != nil { + return errors.Wrapf(err, "failed to uninstall wasm plugin %q", fmt.Sprintf("%s/%s", k8s.HigressNamespace, name)) + } + + fmt.Fprintf(w, "Uninstalled wasm plugin %q\n", fmt.Sprintf("%s/%s", result.GetNamespace(), result.GetName())) + return nil +} diff --git a/pkg/cmd/hgctl/plugin/utils/common.go b/pkg/cmd/hgctl/plugin/utils/common.go new file mode 100644 index 0000000000..0d59fa23d0 --- /dev/null +++ b/pkg/cmd/hgctl/plugin/utils/common.go @@ -0,0 +1,92 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package utils + +import ( + "bytes" + "fmt" + "io" + "path/filepath" + "strings" + + "github.com/mitchellh/go-homedir" + "github.com/pkg/errors" + "gopkg.in/yaml.v3" +) + +// GetAbsolutePath returns the absolute path, e.g.: +// - ~/foo -> /home/user/foo +// - ./foo -> /current/dir/foo +// - /foo/ -> /foo +func GetAbsolutePath(path string) (newPath string, err error) { + if strings.HasPrefix(path, "~") { + newPath, err = homedir.Expand(path) + if err != nil { + return "", errors.Wrapf(err, "failed to expand path: %q", path) + } + } else { + newPath, err = filepath.Abs(path) + if err != nil { + return "", errors.Wrapf(err, "failed to get absolute path of %q", path) + } + } + + l := len(newPath) + if l > 1 && newPath[l-1] == '/' { // if l == 1, the path might be "/" + newPath = newPath[:l-1] + } + + return newPath, nil +} + +// AddIndent for each line of str +func AddIndent(str, indent string) string { + ret := "" + ss := strings.Split(str, "\n") + for i, s := range ss { + if i == 0 { + ret = fmt.Sprintf("%s%s", indent, s) + } else { + ret = fmt.Sprintf("%s\n%s%s", ret, indent, s) + } + } + + return ret +} + +// MarshalYamlWithIndent marshals v to yaml with indent, specify space width with spaces +func MarshalYamlWithIndent(v interface{}, spaces int) ([]byte, error) { + w := new(bytes.Buffer) + ec := yaml.NewEncoder(w) + defer ec.Close() + ec.SetIndent(spaces) + if err := ec.Encode(v); err != nil { + return w.Bytes(), err + } + + return w.Bytes(), nil +} + +// MarshalYamlWithIndentTo marshals v to yaml with indent, specify space width with spaces, and output to w +func MarshalYamlWithIndentTo(w io.Writer, v interface{}, spaces int) error { + ec := yaml.NewEncoder(w) + defer ec.Close() + ec.SetIndent(spaces) + if err := ec.Encode(v); err != nil { + return err + } + + return nil +} diff --git a/pkg/cmd/hgctl/plugin/utils/debugger.go b/pkg/cmd/hgctl/plugin/utils/debugger.go new file mode 100644 index 0000000000..ec7cca0c0a --- /dev/null +++ b/pkg/cmd/hgctl/plugin/utils/debugger.go @@ -0,0 +1,58 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package utils + +import ( + "fmt" + "io" +) + +type Debugger interface { + Debugf(format string, a ...any) (int, error) + Debugln(a ...any) (int, error) +} + +type DefaultDebugger struct { + debug bool + w io.Writer +} + +func NewDefaultDebugger(debug bool, w io.Writer) *DefaultDebugger { + return &DefaultDebugger{debug: debug, w: w} +} + +func (d DefaultDebugger) Debugf(format string, a ...any) (int, error) { + l := len(format) + if l > 0 && format[l-1] != '\n' { + format += "\n" + } + if d.debug { + format = "[debug] " + format + return fmt.Fprintf(d.w, format, a...) + } + return 0, nil +} + +func (d DefaultDebugger) Debugln(a ...any) (int, error) { + if d.debug { + n1, err1 := fmt.Fprintf(d.w, "[debug] ") + if err1 != nil { + return n1, err1 + } + n2, err2 := fmt.Fprintln(d.w, a...) + return n1 + n2, err2 + } + return 0, nil +} diff --git a/pkg/cmd/hgctl/plugin/utils/printer.go b/pkg/cmd/hgctl/plugin/utils/printer.go new file mode 100644 index 0000000000..df3cfec8b9 --- /dev/null +++ b/pkg/cmd/hgctl/plugin/utils/printer.go @@ -0,0 +1,165 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package utils + +import ( + "fmt" + "io" + "os" + "strings" + + "github.com/fatih/color" +) + +type YesOrNoPrinter struct { + out io.Writer + indent *Indent + yes, no *color.Color +} + +var ( + DefaultOut = os.Stdout + DefaultIdent = NewIndent(strings.Repeat(" ", 2), 0) + DefaultYes = color.New(color.FgHiGreen) + DefaultNo = color.New(color.FgHiRed) +) + +func NewPrinter(out io.Writer, indent *Indent, yes, no *color.Color) *YesOrNoPrinter { + return &YesOrNoPrinter{ + out: out, + indent: indent, + yes: yes, + no: no, + } +} + +func DefaultPrinter() *YesOrNoPrinter { + return NewPrinter(DefaultOut, DefaultIdent, DefaultYes, DefaultNo) +} + +func (p *YesOrNoPrinter) Printf(format string, a ...interface{}) (int, error) { + return fmt.Fprintf(p.out, format, a...) +} + +func (p *YesOrNoPrinter) Println(a ...interface{}) (int, error) { + return fmt.Fprintln(p.out, a...) +} + +func (p *YesOrNoPrinter) PrintWithIndentf(format string, a ...interface{}) (int, error) { + format = fmt.Sprintf("%s%s", p.indent, format) + return fmt.Fprintf(p.out, format, a...) +} + +func (p *YesOrNoPrinter) PrintWithIndentln(a ...interface{}) (int, error) { + n1, err := fmt.Fprintf(p.out, "%s", p.indent) + if err != nil { + return n1, err + } + n2, err := fmt.Fprintln(p.out, a...) + if err != nil { + return n1 + n2, err + } + return n1 + n2, nil +} + +func (p *YesOrNoPrinter) Yesf(format string, a ...interface{}) (int, error) { + return p.yes.Fprintf(p.out, format, a...) +} + +func (p *YesOrNoPrinter) Yesln(a ...interface{}) (int, error) { + return p.yes.Fprintln(p.out, a...) +} + +func (p *YesOrNoPrinter) YesWithIndentf(format string, a ...interface{}) (int, error) { + format = fmt.Sprintf("%s%s", p.indent, format) + return p.yes.Fprintf(p.out, format, a...) +} + +func (p *YesOrNoPrinter) YesWithIndentln(a ...interface{}) (int, error) { + n1, err := p.yes.Fprintf(p.out, "%s", p.indent) + if err != nil { + return n1, err + } + n2, err := p.yes.Fprintln(p.out, a...) + if err != nil { + return n1 + n2, err + } + return n1 + n2, nil +} + +func (p *YesOrNoPrinter) Nof(format string, a ...interface{}) (int, error) { + return p.no.Fprintf(p.out, format, a...) +} + +func (p *YesOrNoPrinter) Noln(a ...interface{}) (int, error) { + return p.no.Fprintln(p.out, a...) +} + +func (p *YesOrNoPrinter) NoWithIndentf(format string, a ...interface{}) (int, error) { + format = fmt.Sprintf("%s%s", p.indent, format) + return p.no.Fprintf(p.out, format, a...) +} + +func (p *YesOrNoPrinter) NoWithIndentln(a ...interface{}) (int, error) { + n1, err := p.no.Fprintf(p.out, "%s", p.indent) + if err != nil { + return n1, err + } + n2, err := p.no.Fprintln(p.out, a...) + if err != nil { + return n1 + n2, err + } + return n1 + n2, nil +} + +func (p *YesOrNoPrinter) Ident() string { return p.indent.String() } + +func (p *YesOrNoPrinter) IncIdentRepeat() { p.indent.IncRepeat() } + +func (p *YesOrNoPrinter) DecIndentRepeat() { p.indent.DecRepeat() } + +func (p *YesOrNoPrinter) SetIdentRepeat(v int) { p.indent.SetRepeat(v) } + +type Indent struct { + format string + repeat int +} + +func NewIndent(format string, repeat int) *Indent { + return &Indent{ + format: format, + repeat: repeat, + } +} + +func (i *Indent) String() string { + return strings.Repeat(i.format, i.repeat) +} + +func (i *Indent) IncRepeat() { i.repeat++ } + +func (i *Indent) DecRepeat() { + i.repeat-- + if i.repeat < 0 { + i.repeat = 0 + } +} + +func (i *Indent) SetRepeat(v int) { + if v < 0 { + v = 0 + } + i.repeat = v +} diff --git a/pkg/cmd/hgctl/plugin/utils/survey_wrapper.go b/pkg/cmd/hgctl/plugin/utils/survey_wrapper.go new file mode 100644 index 0000000000..b239855d2c --- /dev/null +++ b/pkg/cmd/hgctl/plugin/utils/survey_wrapper.go @@ -0,0 +1,31 @@ +// Copyright (c) 2022 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package utils + +import "github.com/AlecAivazis/survey/v2" + +func Ask(qs []*survey.Question, response interface{}, opts ...survey.AskOpt) error { + opts = append(opts, survey.WithIcons(func(set *survey.IconSet) { + set.Error.Format = "red+hb" + })) + return survey.Ask(qs, response, opts...) +} + +func AskOne(p survey.Prompt, response interface{}, opts ...survey.AskOpt) error { + opts = append(opts, survey.WithIcons(func(set *survey.IconSet) { + set.Error.Format = "red+hb" + })) + return survey.AskOne(p, response, opts...) +} diff --git a/pkg/cmd/hgctl/root.go b/pkg/cmd/hgctl/root.go index 9dd58c095d..f1179e42f9 100644 --- a/pkg/cmd/hgctl/root.go +++ b/pkg/cmd/hgctl/root.go @@ -14,7 +14,10 @@ package hgctl -import "github.com/spf13/cobra" +import ( + "github.com/alibaba/higress/pkg/cmd/hgctl/plugin" + "github.com/spf13/cobra" +) // GetRootCommand returns the root cobra command to be executed // by hgctl main. @@ -34,6 +37,7 @@ func GetRootCommand() *cobra.Command { rootCmd.AddCommand(newProfileCmd()) rootCmd.AddCommand(newDashboardCmd()) rootCmd.AddCommand(newManifestCmd()) + rootCmd.AddCommand(plugin.NewCommand()) return rootCmd }