diff --git a/.drone.yml b/.drone.yml deleted file mode 100644 index a68bc8d..0000000 --- a/.drone.yml +++ /dev/null @@ -1,40 +0,0 @@ -workspace: - base: /go - path: src/github.com/uswitch/yggdrasil - -pipeline: - test: - image: golang:1.17 - environment: - - GO111MODULE=on - commands: - - make test - - build: - image: golang:1.17 - environment: - - GO111MODULE=on - commands: - - make build-linux - - docker-latest: - image: plugins/docker - repo: quay.io/uswitch/yggdrasil - registry: quay.io - secrets: [ docker_username, docker_password ] - tags: - - latest - - ${DRONE_COMMIT_SHA} - when: - event: push - branch: master - - docker-tagged: - image: plugins/docker - repo: quay.io/uswitch/yggdrasil - registry: quay.io - secrets: [ docker_username, docker_password ] - tags: - - ${DRONE_TAG} - when: - event: tag diff --git a/.github/rvu/labels.yaml b/.github/rvu/labels.yaml new file mode 100644 index 0000000..7b192d6 --- /dev/null +++ b/.github/rvu/labels.yaml @@ -0,0 +1 @@ +service.rvu.co.uk/brand: airship diff --git a/.github/workflows/push.yaml b/.github/workflows/push.yaml new file mode 100644 index 0000000..4b4e37c --- /dev/null +++ b/.github/workflows/push.yaml @@ -0,0 +1,57 @@ +name: push +on: push +permissions: + contents: read + id-token: write +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v4 + with: + go-version: "1.17" + - run: make test + build: + runs-on: ubuntu-latest + needs: test + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v4 + with: + go-version: "1.17" + - run: make build-linux + - uses: actions/upload-artifact@v3 + with: + name: bin + path: bin/ + docker-build-push: + if: github.ref_name == 'master' || startsWith(github.ref, 'refs/tags/v') + needs: build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/download-artifact@v3 + with: + name: bin + path: bin/ + - name: Login to Quay.io + uses: docker/login-action@v3 + with: + registry: quay.io + username: ${{ secrets.QUAY_USERNAME }} + password: ${{ secrets.QUAY_PASSWORD }} + - id: meta + uses: docker/metadata-action@v5 + with: + images: quay.io/uswitch/yggdrasil + tags: | + type=semver,pattern={{raw}} + type=sha,prefix=,format=long, + - uses: docker/build-push-action@v5 + with: + context: . + labels: ${{ steps.meta.outputs.labels }} + push: true + tags: ${{ steps.meta.outputs.tags }} + diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 0000000..c70536b --- /dev/null +++ b/.tool-versions @@ -0,0 +1 @@ +golang 1.17.13 diff --git a/Dockerfile b/Dockerfile index 3010abd..4463ff7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ FROM scratch -ADD bin/yggdrasil-linux-amd64 yggdrasil +COPY --chmod=755 bin/yggdrasil-linux-amd64 yggdrasil ENTRYPOINT ["/yggdrasil"] diff --git a/Makefile b/Makefile index b9a1695..d38c987 100644 --- a/Makefile +++ b/Makefile @@ -5,14 +5,16 @@ BIN_DARWIN = $(BIN)-darwin-$(ARCH) SOURCES := $(shell find . -iname '*.go') -.PHONY: test clean all +.PHONY: test clean all build-linux -all: build-darwin build-linux +all: build-darwin $(BIN_LINUX) build-darwin: $(SOURCES) GOARCH=$(ARCH) GOOS=darwin go build -o $(BIN_DARWIN) -build-linux: $(SOURCES) +build-linux: $(BIN_LINUX) + +$(BIN_LINUX): $(SOURCES) GOARCH=$(ARCH) GOOS=linux CGO_ENABLED=0 go build -o $(BIN_LINUX) test: $(SOURCES) @@ -22,7 +24,7 @@ bench: $(SOURCES) go test -run=XX -bench=. $(shell go list ./... | grep -v /vendor) docker: Dockerfile $(BIN_LINUX) - docker image build -t quay.io/uswitch/yggdrasil:devel . + docker image build -t registry.airship.rvu.cloud/cloud/yggdrasil:devel . clean: rm -rf bin/ diff --git a/README.md b/README.md index 9de9e42..17116bf 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Yggdrasil Yggdrasil is an Envoy control plane that configures listeners and clusters based off Kubernetes ingresses from multiple Kube Clusters. This allows you to have an envoy cluster acting as a mutli-cluster loadbalancer for Kubernetes. This was something we needed as we wanted our apps to be highly available in the event of a cluster outage but did not want the solution to live inside of Kubernetes itself. -`Note:` Currently we support version 1.19.x of Envoy.
+`Note:` Currently we support versions 1.20.x to 1.26.x of Envoy.
`Note:` Yggdrasil now uses [Go modules](https://github.com/golang/go/wiki/Modules) to handle dependencies. ## Usage @@ -133,12 +133,22 @@ spec: servicePort: 80 ``` +## Dynamic TLS certificates synchronization from Kubernetes secrets + +Downstream TLS certificates can be dynamically fetched and updated from Kubernetes secrets configured under ingresses' `spec.tls` by setting `syncSecrets` true in Yggdrasil configuration (false by default). + +In this mode, only a single `certificate` may be specified in Yggdrasil configuration. It will be used for hosts with misconfigured or invalid secret. + +**Note**: ECDSA >256 keys are not supported by envoy and will be discarded. See https://github.com/envoyproxy/envoy/issues/10855 + ## Configuration Yggdrasil can be configured using a config file e.g: ```json { "nodeName": "foo", "ingressClasses": ["multi-cluster", "multi-cluster-staging"], + "accessLog": "/var/log/envoy/", + "syncSecrets": false, "certificates": [ { "hosts": ["*.api.com"], @@ -199,17 +209,19 @@ The Yggdrasil-specific metrics which are available from the API are: --ca string trustedCA --cert string certfile --config string config file +--config-dump Enable config dump endpoint at /configdump on the health-address HTTP server --debug Log at debug level +--access-log path for the file logs --envoy-listener-ipv4-address strings IPv4 addresses by the envoy proxy to accept incoming connections (default "0.0.0.0") --envoy-port uint32 port by the envoy proxy to accept incoming connections (default 10000) --health-address string yggdrasil health API listen address (default "0.0.0.0:8081") ---help help for yggdrasil +-h, --help help for yggdrasil --host-selection-retry-attempts int Number of host selection retry attempts. Set to value >=0 to enable (default -1) ---retry-on Default comma-separated list of retry policies (default 5xx) --http-ext-authz-allow-partial-message When this field is true, Envoy will buffer the message until max_request_bytes is reached (default true) --http-ext-authz-cluster string The name of the upstream gRPC cluster --http-ext-authz-failure-mode-allow Changes filters behaviour on errors (default true) --http-ext-authz-max-request-bytes uint32 Sets the maximum size of a message body that the filter will hold in memory (default 8192) +--http-ext-authz-pack-as-bytes When this field is true, Envoy will send the body as raw bytes. --http-ext-authz-timeout duration The timeout for the gRPC request. This is the timeout for a specific request. (default 200ms) --http-grpc-logger-cluster string The name of the upstream gRPC cluster --http-grpc-logger-name string Name of the access log @@ -221,6 +233,8 @@ The Yggdrasil-specific metrics which are available from the API are: --kube-config stringArray Path to kube config --max-ejection-percentage int32 maximal percentage of hosts ejected via outlier detection. Set to >=0 to activate outlier detection in envoy. (default -1) --node-name string envoy node name +--retry-on string default comma-separated list of retry policies (default "5xx") +--tracing-provider name of HTTP Connection Manager tracing provider to include - currently only zipkin config is supported --upstream-healthcheck-healthy uint32 number of successful healthchecks before the backend is considered healthy (default 3) --upstream-healthcheck-interval duration duration of the upstream health check interval (default 10s) --upstream-healthcheck-timeout duration timeout of the upstream healthchecks (default 5s) diff --git a/cmd/root.go b/cmd/root.go index f86f5ff..bef8965 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -36,6 +36,7 @@ type config struct { NodeName string `json:"nodeName"` Clusters []clusterConfig `json:"clusters"` SyncSecrets bool `json:"syncSecrets"` + AccessLog string `json:"accessLog"` Certificates []envoy.Certificate `json:"certificates"` TrustCA string `json:"trustCA"` UpstreamPort uint32 `json:"upstreamPort"` @@ -49,6 +50,7 @@ type config struct { HttpGrpcLogger envoy.HttpGrpcLogger `json:"httpGrpcLogger"` DefaultTimeouts envoy.DefaultTimeouts `json:"defaultTimeouts"` AlpnProtocols []string `json:"alpnProtocols"` + AccessLogger envoy.AccessLogger `json:"accessLogger"` } // Hasher returns node ID as an ID @@ -80,18 +82,21 @@ func init() { rootCmd.PersistentFlags().String("address", "0.0.0.0:8080", "yggdrasil envoy control plane listen address") rootCmd.PersistentFlags().String("health-address", "0.0.0.0:8081", "yggdrasil health API listen address") rootCmd.PersistentFlags().String("node-name", "", "envoy node name") + rootCmd.PersistentFlags().String("access-log", "/var/log/envoy/", "envoy default access log file") rootCmd.PersistentFlags().String("cert", "", "certfile") rootCmd.PersistentFlags().String("key", "", "keyfile") rootCmd.PersistentFlags().String("ca", "", "trustedCA") rootCmd.PersistentFlags().StringSlice("ingress-classes", nil, "Ingress classes to watch") rootCmd.PersistentFlags().StringArrayVar(&kubeConfig, "kube-config", nil, "Path to kube config") rootCmd.PersistentFlags().Bool("debug", false, "Log at debug level") + rootCmd.PersistentFlags().Bool("config-dump", false, "Enable config dump endpoint at /configdump on the health-address HTTP server") rootCmd.PersistentFlags().Uint32("upstream-port", 443, "port used to connect to the upstream ingresses") rootCmd.PersistentFlags().StringSlice("envoy-listener-ipv4-address", []string{"0.0.0.0"}, "IPv4 address by the envoy proxy to accept incoming connections") rootCmd.PersistentFlags().Uint32("envoy-port", 10000, "port by the envoy proxy to accept incoming connections") rootCmd.PersistentFlags().Int32("max-ejection-percentage", -1, "maximal percentage of hosts ejected via outlier detection. Set to >=0 to activate outlier detection in envoy.") rootCmd.PersistentFlags().Int64("host-selection-retry-attempts", -1, "Number of host selection retry attempts. Set to value >=0 to enable") rootCmd.PersistentFlags().String("retry-on", "5xx", "default comma-separated list of retry policies") + rootCmd.PersistentFlags().String("tracing-provider", "", "HTTP Connection Manager tracing provider block to include") rootCmd.PersistentFlags().Duration("upstream-healthcheck-interval", 10*time.Second, "duration of the upstream health check interval") rootCmd.PersistentFlags().Duration("upstream-healthcheck-timeout", 5*time.Second, "timeout of the upstream healthchecks") rootCmd.PersistentFlags().Uint32("upstream-healthcheck-healthy", 3, "number of successful healthchecks before the backend is considered healthy") @@ -106,15 +111,19 @@ func init() { rootCmd.PersistentFlags().Duration("http-ext-authz-timeout", 200*time.Millisecond, "The timeout for the gRPC request. This is the timeout for a specific request.") rootCmd.PersistentFlags().Uint32("http-ext-authz-max-request-bytes", 8192, "Sets the maximum size of a message body that the filter will hold in memory") rootCmd.PersistentFlags().Bool("http-ext-authz-allow-partial-message", true, "When this field is true, Envoy will buffer the message until max_request_bytes is reached") + rootCmd.PersistentFlags().Bool("http-ext-authz-pack-as-bytes", false, "When this field is true, Envoy will send the body as raw bytes.") rootCmd.PersistentFlags().Bool("http-ext-authz-failure-mode-allow", true, "Changes filters behaviour on errors") + rootCmd.PersistentFlags().Duration("default-route-timeout", 15*time.Second, "Default timeout of the routes") rootCmd.PersistentFlags().Duration("default-cluster-timeout", 30*time.Second, "Default timeout of the cluster") rootCmd.PersistentFlags().Duration("default-per-try-timeout", 5*time.Second, "Default timeout of PerTry") rootCmd.PersistentFlags().StringSlice("alpn-protocols", []string{}, "exposed listener ALPN protocols") viper.BindPFlag("debug", rootCmd.PersistentFlags().Lookup("debug")) + viper.BindPFlag("configDump", rootCmd.PersistentFlags().Lookup("config-dump")) viper.BindPFlag("address", rootCmd.PersistentFlags().Lookup("address")) viper.BindPFlag("healthAddress", rootCmd.PersistentFlags().Lookup("health-address")) viper.BindPFlag("nodeName", rootCmd.PersistentFlags().Lookup("node-name")) + viper.BindPFlag("accessLog", rootCmd.PersistentFlags().Lookup("access-log")) viper.BindPFlag("ingressClasses", rootCmd.PersistentFlags().Lookup("ingress-classes")) viper.BindPFlag("cert", rootCmd.PersistentFlags().Lookup("cert")) viper.BindPFlag("key", rootCmd.PersistentFlags().Lookup("key")) @@ -125,6 +134,7 @@ func init() { viper.BindPFlag("maxEjectionPercentage", rootCmd.PersistentFlags().Lookup("max-ejection-percentage")) viper.BindPFlag("hostSelectionRetryAttempts", rootCmd.PersistentFlags().Lookup("host-selection-retry-attempts")) viper.BindPFlag("retryOn", rootCmd.PersistentFlags().Lookup("retry-on")) + viper.BindPFlag("tracingProvider", rootCmd.PersistentFlags().Lookup("tracing-provider")) viper.BindPFlag("upstreamHealthCheck.interval", rootCmd.PersistentFlags().Lookup("upstream-healthcheck-interval")) viper.BindPFlag("upstreamHealthCheck.timeout", rootCmd.PersistentFlags().Lookup("upstream-healthcheck-timeout")) viper.BindPFlag("upstreamHealthCheck.healthyThreshold", rootCmd.PersistentFlags().Lookup("upstream-healthcheck-healthy")) @@ -139,6 +149,7 @@ func init() { viper.BindPFlag("httpExtAuthz.timeout", rootCmd.PersistentFlags().Lookup("http-ext-authz-timeout")) viper.BindPFlag("httpExtAuthz.maxRequestBytes", rootCmd.PersistentFlags().Lookup("http-ext-authz-max-request-bytes")) viper.BindPFlag("httpExtAuthz.allowPartialMessage", rootCmd.PersistentFlags().Lookup("http-ext-authz-allow-partial-message")) + viper.BindPFlag("httpExtAuthz.packAsBytes", rootCmd.PersistentFlags().Lookup("http-ext-authz-pack-as-bytes")) viper.BindPFlag("httpExtAuthz.FailureModeAllow", rootCmd.PersistentFlags().Lookup("http-ext-authz-failure-mode-allow")) viper.BindPFlag("defaultTimeouts.Route", rootCmd.PersistentFlags().Lookup("default-route-timeout")) viper.BindPFlag("defaultTimeouts.Cluster", rootCmd.PersistentFlags().Lookup("default-cluster-timeout")) @@ -234,6 +245,7 @@ func main(*cobra.Command, []string) error { c.Certificates, viper.GetString("trustCA"), viper.GetStringSlice("ingressClasses"), + viper.GetString("accessLog"), envoy.WithUpstreamPort(uint32(viper.GetInt32("upstreamPort"))), envoy.WithEnvoyListenerIpv4Address(viper.GetStringSlice("envoyListenerIpv4Address")), envoy.WithEnvoyPort(uint32(viper.GetInt32("envoyPort"))), @@ -246,15 +258,18 @@ func main(*cobra.Command, []string) error { envoy.WithSyncSecrets(c.SyncSecrets), envoy.WithDefaultTimeouts(c.DefaultTimeouts), envoy.WithDefaultRetryOn(viper.GetString("retryOn")), + envoy.WithAccessLog(c.AccessLogger), + envoy.WithTracingProvider(viper.GetString("tracingProvider")), envoy.WithAlpnProtocols(viper.GetStringSlice("alpnProtocols")), ) + configurator.ValidateAndFormatPath() snapshotter := envoy.NewSnapshotter(envoyCache, configurator, aggregator) go snapshotter.Run(aggregator) go aggregator.Run() envoyServer := server.NewServer(ctx, envoyCache, &callbacks{}) - go runEnvoyServer(envoyServer, viper.GetString("address"), viper.GetString("healthAddress"), ctx.Done()) + go runEnvoyServer(envoyServer, snapshotter, viper.GetBool("configDump"), viper.GetString("address"), viper.GetString("healthAddress"), ctx.Done()) <-stopCh return nil diff --git a/cmd/server.go b/cmd/server.go index 14544c3..89df418 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -2,6 +2,7 @@ package cmd import ( "context" + "encoding/json" "fmt" "net" "net/http" @@ -16,6 +17,8 @@ import ( "github.com/prometheus/client_golang/prometheus/promhttp" log "github.com/sirupsen/logrus" "google.golang.org/grpc" + + "github.com/uswitch/yggdrasil/pkg/envoy" ) type callbacks struct { @@ -49,7 +52,7 @@ func (c *callbacks) OnFetchResponse(*discovery.DiscoveryRequest, *discovery.Disc c.fetchResp++ } -func runEnvoyServer(envoyServer server.Server, address string, healthAddress string, stopCh <-chan struct{}) { +func runEnvoyServer(envoyServer server.Server, snapshotter *envoy.Snapshotter, enableConfigDump bool, address string, healthAddress string, stopCh <-chan struct{}) { grpcServer := grpc.NewServer( grpc.StreamInterceptor(grpc_prometheus.StreamServerInterceptor), @@ -76,6 +79,9 @@ func runEnvoyServer(envoyServer server.Server, address string, healthAddress str healthMux.Handle("/metrics", promhttp.Handler()) healthMux.HandleFunc("/healthz", health) + if enableConfigDump { + healthMux.HandleFunc("/configdump", handleConfigDump(snapshotter)) + } go func() { if err = grpcServer.Serve(lis); err != nil { @@ -97,3 +103,34 @@ func runEnvoyServer(envoyServer server.Server, address string, healthAddress str func health(w http.ResponseWriter, r *http.Request) { w.WriteHeader(200) } + +type ConfigDumpError struct { + Error error + Message string +} + +func handleConfigDump(snapshotter *envoy.Snapshotter) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + w.WriteHeader(http.StatusMethodNotAllowed) + return + } + + snapshot, err := snapshotter.ConfigDump() + if err != nil { + respErr := ConfigDumpError{ + Error: err, + Message: "Unable to get current snapshot from snapshotter, see error for details.", + } + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusInternalServerError) + json.NewEncoder(w).Encode(respErr) + return + } + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + json.NewEncoder(w).Encode(snapshot) + } +} diff --git a/docs/ACCESSLOG.md b/docs/ACCESSLOG.md new file mode 100644 index 0000000..25540e2 --- /dev/null +++ b/docs/ACCESSLOG.md @@ -0,0 +1,37 @@ +# Access Log + +The Access log format is configurable via the Yggdrasil config file only. It is defined as a json object as follows: + +```json +{ + "accessLogger": { + "format": { + "bytes_received": "%BYTES_RECEIVED%", + "bytes_sent": "%BYTES_SENT%", + "downstream_local_address": "%DOWNSTREAM_LOCAL_ADDRESS%", + "downstream_remote_address": "%DOWNSTREAM_REMOTE_ADDRESS%", + "duration": "%DURATION%", + "forwarded_for": "%REQ(X-FORWARDED-FOR)%", + "protocol": "%PROTOCOL%", + "request_id": "%REQ(X-REQUEST-ID)%", + "request_method": "%REQ(:METHOD)%", + "request_path": "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%", + "response_code": "%RESPONSE_CODE%", + "response_flags": "%RESPONSE_FLAGS%", + "start_time": "%START_TIME(%s.%3f)%", + "upstream_cluster": "%UPSTREAM_CLUSTER%", + "upstream_host": "%UPSTREAM_HOST%", + "upstream_local_address": "%UPSTREAM_LOCAL_ADDRESS%", + "upstream_service_time": "%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%", + "user_agent": "%REQ(USER-AGENT)%" + } + } +} + +``` + +The config above would be the same as the default access logger config shipped with Yggdasil. Thus if no format is provided this will be the format used. + +[See Envoy docs for more on access log formats](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage#config-access-log-default-format) + +The access log is written to `/var/log/envoy/access.log` which is not currently configurable. diff --git a/docs/GETTINGSTARTED.md b/docs/GETTINGSTARTED.md index 2341741..2c87891 100644 --- a/docs/GETTINGSTARTED.md +++ b/docs/GETTINGSTARTED.md @@ -138,7 +138,7 @@ By default, Yggdrasil will use an upstream ingress port of 443 (HTTPS), as we ar With the Yggdrasil container running, we can now configure an envoy node. Pull an envoy v1.10 docker image with the following command: ```console -$ docker pull envoyproxy/envoy:v1.19-latest +$ docker pull envoyproxy/envoy:v1.26-latest ``` Next, we will need to setup a minimal config file to create the admin listener for envoy, as well as pointing to our dynamic configuration provider - Yggdrasil: @@ -190,7 +190,7 @@ Where `` is the IP address of the Yggdrasil dock Run the envoy docker container with the following command, making sure to mount the minimal config file that you've created: ```console -$ docker run -e ENVOY_UID=0 -w /var/log/envoy/ -v /path/to/envoy.yaml:/etc/envoy/envoy.yaml -p 10000:10000 -d envoyproxy/envoy:v1.19-latest --service-node envoy-node --service-cluster envoy-node --config-path /etc/envoy/envoy.yaml +$ docker run -e ENVOY_UID=0 -w /var/log/envoy/ -v /path/to/envoy.yaml:/etc/envoy/envoy.yaml -p 10000:10000 -d envoyproxy/envoy:v1.26-latest --service-node envoy-node --service-cluster envoy-node --config-path /etc/envoy/envoy.yaml ``` The working directory for the container is set to `/var/log/envoy/` in order to create it at runtime, as Yggdrasil will configure envoy to write access logs to this directory. diff --git a/go.mod b/go.mod index 29ec00a..01f3071 100644 --- a/go.mod +++ b/go.mod @@ -6,8 +6,8 @@ require ( github.com/envoyproxy/go-control-plane v0.10.3 github.com/golang/protobuf v1.5.2 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 - github.com/prometheus/client_golang v0.9.0 - github.com/sirupsen/logrus v1.1.1 + github.com/prometheus/client_golang v1.11.1 + github.com/sirupsen/logrus v1.6.0 github.com/spf13/cobra v0.0.3 github.com/spf13/viper v1.2.1 google.golang.org/grpc v1.45.0 @@ -20,8 +20,9 @@ require ( require ( github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect - github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 // indirect + github.com/beorn7/perks v1.0.1 // indirect github.com/census-instrumentation/opencensus-proto v0.3.0 // indirect + github.com/cespare/xxhash/v2 v2.1.1 // indirect github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/emicklei/go-restful/v3 v3.8.0 // indirect @@ -40,7 +41,7 @@ require ( github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/konsorten/go-windows-terminal-sequences v1.0.1 // indirect + github.com/konsorten/go-windows-terminal-sequences v1.0.3 // indirect github.com/magiconair/properties v1.8.0 // indirect github.com/mailru/easyjson v0.7.6 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect @@ -50,18 +51,17 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pelletier/go-toml v1.2.0 // indirect github.com/prometheus/client_model v0.2.1-0.20200623203004-60555c9708c7 // indirect - github.com/prometheus/common v0.0.0-20170427095455-13ba4ddd0caa // indirect - github.com/prometheus/procfs v0.0.0-20170519190837-65c1f6f8f0fc // indirect + github.com/prometheus/common v0.26.0 // indirect + github.com/prometheus/procfs v0.6.0 // indirect github.com/spf13/afero v1.6.0 // indirect github.com/spf13/cast v1.3.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/crypto v0.0.0-20220214200702-86341886e292 // indirect - golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect + golang.org/x/net v0.7.0 // indirect golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect - golang.org/x/sys v0.0.0-20220209214540-3681064d5158 // indirect - golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect - golang.org/x/text v0.3.7 // indirect + golang.org/x/sys v0.5.0 // indirect + golang.org/x/term v0.5.0 // indirect + golang.org/x/text v0.7.0 // indirect golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20220329172620-7be39ac1afc7 // indirect diff --git a/go.sum b/go.sum index b509ae8..10c9f25 100644 --- a/go.sum +++ b/go.sum @@ -52,15 +52,24 @@ github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tN github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= 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/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= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= @@ -110,6 +119,12 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v1.2.0 h1:QK40JKJyMdUDz+h+xvCsru/bJhvG0UxvePV0ufL/AcE= @@ -123,6 +138,8 @@ github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -219,17 +236,23 @@ github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NH github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +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/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -252,12 +275,15 @@ github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0Gq 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= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= @@ -275,25 +301,38 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.9.0 h1:tXuTFVHC03mW0D+Ua1Q2d1EAVqLTuggX50V0VLICCzY= -github.com/prometheus/client_golang v0.9.0/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 v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.1 h1:+4eQaD7vAZ6DsfsxB15hbE0odUjGI5ARs9yskGu1v4s= +github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.1-0.20200623203004-60555c9708c7 h1:NkLt0ne/zifxULGse6IDsHU45hKk3w6lIVs8yFSVzKU= github.com/prometheus/client_model v0.2.1-0.20200623203004-60555c9708c7/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20170427095455-13ba4ddd0caa h1:WBOqSBZzK9pqPXiewLT2aL9evdTCy4hUefz0h3iJGGI= -github.com/prometheus/common v0.0.0-20170427095455-13ba4ddd0caa/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/procfs v0.0.0-20170519190837-65c1f6f8f0fc h1:eEx6/InsHC/w5bo5ADfs4u7uf7NXgmDDui12UF205Ag= -github.com/prometheus/procfs v0.0.0-20170519190837-65c1f6f8f0fc/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/sirupsen/logrus v1.1.1 h1:VzGj7lhU7KEB9e9gMpAV/v5XT2NVSvLJhJLCWbnkgXg= -github.com/sirupsen/logrus v1.1.1/go.mod h1:zrgwTnHtNr00buQ1vSptGe8m1f/BbgsPukg8qsT7A+A= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= @@ -315,6 +354,7 @@ github.com/spf13/viper v1.2.1 h1:bIcUwXqLseLF3BDAZduuNfekWG87ibtFxi59Bq+oI9M= github.com/spf13/viper v1.2.1/go.mod h1:P4AexN0a+C9tGAnUFNwDMYYZv3pjFuvmeiMyKRaNVlI= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -328,6 +368,7 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -345,7 +386,7 @@ golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -383,9 +424,11 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -393,6 +436,7 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -422,8 +466,10 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -448,13 +494,16 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180906133057-8cf3aee42992/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -466,6 +515,7 @@ golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -479,6 +529,8 @@ golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -486,6 +538,7 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -493,14 +546,19 @@ golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/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-20220209214540-3681064d5158 h1:rm+CHSpPEEW2IsXUib1ThaHIjuBVZjxNgSKmBLFfD4c= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -509,8 +567,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 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= @@ -566,6 +625,7 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -682,6 +742,7 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ 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= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -697,6 +758,7 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= diff --git a/pkg/envoy/boilerplate.go b/pkg/envoy/boilerplate.go index 20fb71a..c42c776 100644 --- a/pkg/envoy/boilerplate.go +++ b/pkg/envoy/boilerplate.go @@ -3,6 +3,7 @@ package envoy import ( "fmt" "log" + "path/filepath" "strings" cal "github.com/envoyproxy/go-control-plane/envoy/config/accesslog/v3" @@ -11,12 +12,14 @@ import ( endpoint "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" + tracing "github.com/envoyproxy/go-control-plane/envoy/config/trace/v3" eal "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/file/v3" gal "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/grpc/v3" eauthz "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ext_authz/v3" hcfg "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/health_check/v3" tls_inspector "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/tls_inspector/v3" hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" + previousHosts "github.com/envoyproxy/go-control-plane/envoy/extensions/retry/host/previous_hosts/v3" auth "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" envoy_extension_http "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/v3" matcherv3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" @@ -32,35 +35,30 @@ import ( var ( jsonFormat *structpb.Struct allowedRetryOns map[string]bool -) -func init() { - format := map[string]interface{}{ - "start_time": "%START_TIME(%s.%3f)%", + DefaultAccessLogFormat = map[string]interface{}{ "bytes_received": "%BYTES_RECEIVED%", - "protocol": "%PROTOCOL%", - "response_code": "%RESPONSE_CODE%", "bytes_sent": "%BYTES_SENT%", + "downstream_local_address": "%DOWNSTREAM_LOCAL_ADDRESS%", + "downstream_remote_address": "%DOWNSTREAM_REMOTE_ADDRESS%", "duration": "%DURATION%", + "forwarded_for": "%REQ(X-FORWARDED-FOR)%", + "protocol": "%PROTOCOL%", + "request_id": "%REQ(X-REQUEST-ID)%", + "request_method": "%REQ(:METHOD)%", + "request_path": "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%", + "response_code": "%RESPONSE_CODE%", "response_flags": "%RESPONSE_FLAGS%", - "upstream_host": "%UPSTREAM_HOST%", + "start_time": "%START_TIME(%s.%3f)%", "upstream_cluster": "%UPSTREAM_CLUSTER%", + "upstream_host": "%UPSTREAM_HOST%", "upstream_local_address": "%UPSTREAM_LOCAL_ADDRESS%", - "downstream_remote_address": "%DOWNSTREAM_REMOTE_ADDRESS%", - "downstream_local_address": "%DOWNSTREAM_LOCAL_ADDRESS%", - "request_method": "%REQ(:METHOD)%", - "request_path": "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%", "upstream_service_time": "%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%", - "forwarded_for": "%REQ(X-FORWARDED-FOR)%", "user_agent": "%REQ(USER-AGENT)%", - "request_id": "%REQ(X-REQUEST-ID)%", - } - b, err := structpb.NewValue(format) - if err != nil { - log.Fatal(err) } - jsonFormat = b.GetStructValue() +) +func init() { allowedRetryOns = map[string]bool{ "5xx": true, "gateway-error": true, @@ -75,7 +73,7 @@ func init() { } } -func makeVirtualHost(vhost *virtualHost, reselectionAttempts int64, defaultRetryOn string) *route.VirtualHost { +func makeVirtualHost(vhost *virtualHost, reselectionAttempts int64, defaultRetryOn string) (*route.VirtualHost, error) { retryOn := vhost.RetryOn if retryOn == "" { retryOn = defaultRetryOn @@ -94,10 +92,18 @@ func makeVirtualHost(vhost *virtualHost, reselectionAttempts int64, defaultRetry }, } + hosts := &previousHosts.PreviousHostsPredicate{} + + anyHosts, err := anypb.New(hosts) + if err != nil { + return &route.VirtualHost{}, fmt.Errorf("failed to marshal hosts config struct to typed struct: %s", err) + } + if reselectionAttempts >= 0 { action.Route.RetryPolicy.RetryHostPredicate = []*route.RetryPolicy_RetryHostPredicate{ { - Name: "envoy.retry_host_predicates.previous_hosts", + Name: "envoy.retry_host_predicates.previous_hosts", + ConfigType: &route.RetryPolicy_RetryHostPredicate_TypedConfig{TypedConfig: anyHosts}, }, } action.Route.RetryPolicy.HostSelectionRetryMaxAttempts = reselectionAttempts @@ -116,7 +122,7 @@ func makeVirtualHost(vhost *virtualHost, reselectionAttempts int64, defaultRetry }, }, } - return &virtualHost + return &virtualHost, nil } func makeHealthConfig() *hcfg.HealthCheck { @@ -127,7 +133,7 @@ func makeHealthConfig() *hcfg.HealthCheck { Name: ":path", HeaderMatchSpecifier: &route.HeaderMatcher_StringMatch{ StringMatch: &matcherv3.StringMatcher{ - MatchPattern: &matcherv3.StringMatcher_Exact{"/yggdrasil/status"}, + MatchPattern: &matcherv3.StringMatcher_Exact{Exact: "/yggdrasil/status"}, }, }, }, @@ -151,6 +157,7 @@ func makeExtAuthzConfig(cfg HttpExtAuthz) *eauthz.ExtAuthz { WithRequestBody: &eauthz.BufferSettings{ MaxRequestBytes: cfg.MaxRequestBytes, AllowPartialMessage: cfg.AllowPartialMessage, + PackAsBytes: cfg.PackAsBytes, }, FailureModeAllow: cfg.FailureModeAllow, } @@ -170,15 +177,25 @@ func makeGrpcLoggerConfig(cfg HttpGrpcLogger) *gal.HttpGrpcAccessLogConfig { }, TransportApiVersion: core.ApiVersion_V3, }, - AdditionalRequestHeadersToLog: cfg.AdditionalRequestHeaders, - AdditionalResponseHeadersToLog: cfg.AdditionalResponseHeaders, + AdditionalRequestHeadersToLog: cfg.RequestHeaders, + AdditionalResponseHeadersToLog: cfg.ResponseHeaders, } } -func (c *KubernetesConfigurator) makeConnectionManager(virtualHosts []*route.VirtualHost) *hcm.HttpConnectionManager { - // Access Logs +func makeFileAccessLog(cfg AccessLogger, accessLog string) *eal.FileAccessLog { + format := DefaultAccessLogFormat + if len(cfg.Format) > 0 { + format = cfg.Format + } + + b, err := structpb.NewValue(format) + if err != nil { + log.Fatal(err) + } + jsonFormat = b.GetStructValue() + accessLogConfig := &eal.FileAccessLog{ - Path: "/var/log/envoy/access.log", + Path: filepath.Join(accessLog, "access.log"), AccessLogFormat: &eal.FileAccessLog_LogFormat{ LogFormat: &core.SubstitutionFormatString{ Format: &core.SubstitutionFormatString_JsonFormat{ @@ -187,6 +204,23 @@ func (c *KubernetesConfigurator) makeConnectionManager(virtualHosts []*route.Vir }, }, } + + return accessLogConfig +} + +func makeZipkinTracingProvider() *tracing.ZipkinConfig { + zipkinTracingProviderConfig := &tracing.ZipkinConfig{ + CollectorCluster: "zipkin", + CollectorEndpoint: "/api/v2/spans", + CollectorEndpointVersion: tracing.ZipkinConfig_HTTP_JSON, + } + + return zipkinTracingProviderConfig +} + +func (c *KubernetesConfigurator) makeConnectionManager(virtualHosts []*route.VirtualHost, accessLog string) (*hcm.HttpConnectionManager, error) { + // Access Logs + accessLogConfig := makeFileAccessLog(c.accessLogger, accessLog) anyAccessLogConfig, err := anypb.New(accessLogConfig) if err != nil { log.Fatalf("failed to marshal access log config struct to typed struct: %s", err) @@ -235,10 +269,29 @@ func (c *KubernetesConfigurator) makeConnectionManager(virtualHosts []*route.Vir }) } + filter, err := filterBuilder.Filters() + if err != nil { + return &hcm.HttpConnectionManager{}, err + } + + tracingConfig := &hcm.HttpConnectionManager_Tracing{} + + if c.tracingProvider == "zipkin" { + zipkinTracingProvider, err := anypb.New(makeZipkinTracingProvider()) + if err != nil { + log.Fatalf("failed to set zipkin tracing provider config: %s", err) + } + + tracingConfig.Provider = &tracing.Tracing_Http{ + Name: "config.trace.v3.Tracing.Http", + ConfigType: &tracing.Tracing_Http_TypedConfig{TypedConfig: zipkinTracingProvider}, + } + } + return &hcm.HttpConnectionManager{ CodecType: hcm.HttpConnectionManager_AUTO, StatPrefix: "ingress_http", - HttpFilters: filterBuilder.Filters(), + HttpFilters: filter, UpgradeConfigs: []*hcm.HttpConnectionManager_UpgradeConfig{ { UpgradeType: "websocket", @@ -250,15 +303,18 @@ func (c *KubernetesConfigurator) makeConnectionManager(virtualHosts []*route.Vir VirtualHosts: virtualHosts, }, }, - Tracing: &hcm.HttpConnectionManager_Tracing{}, + Tracing: tracingConfig, AccessLog: accessLoggers, UseRemoteAddress: &wrapperspb.BoolValue{Value: c.useRemoteAddress}, StripMatchingHostPort: true, - } + }, nil } -func (c *KubernetesConfigurator) makeFilterChain(certificate Certificate, virtualHosts []*route.VirtualHost) (listener.FilterChain, error) { - httpConnectionManager := c.makeConnectionManager(virtualHosts) +func (c *KubernetesConfigurator) makeFilterChain(certificate Certificate, virtualHosts []*route.VirtualHost, accessLog string) (listener.FilterChain, error) { + httpConnectionManager, err := c.makeConnectionManager(virtualHosts, accessLog) + if err != nil { + return listener.FilterChain{}, fmt.Errorf("failed to get httpConnectionManager: %s", err) + } anyHttpConfig, err := anypb.New(httpConnectionManager) if err != nil { return listener.FilterChain{}, fmt.Errorf("failed to marshal HTTP config struct to typed struct: %s", err) @@ -316,11 +372,10 @@ func (c *KubernetesConfigurator) makeFilterChain(certificate Certificate, virtua }, nil } -func makeListener(filterChains []*listener.FilterChain, envoyListenerIpv4Address []string, envoyListenPort uint32) *listener.Listener { - // TODO make typedConfigs static +func makeListener(filterChains []*listener.FilterChain, envoyListenerIpv4Address []string, envoyListenPort uint32) (*listener.Listener, error) { tlsInspectorConfig, err := anypb.New(&tls_inspector.TlsInspector{}) if err != nil { - log.Fatalf("failed to marshal tls_inspector config struct to typed struct: %s", err) + return &listener.Listener{}, fmt.Errorf("failed to marshal tls_inspector config struct to typed struct: %s", err) } additional_addresses := make([]*listener.AdditionalAddress, len(envoyListenerIpv4Address)-1) @@ -368,7 +423,7 @@ func makeListener(filterChains []*listener.FilterChain, envoyListenerIpv4Address TrafficDirection: core.TrafficDirection_OUTBOUND, } - return &listener + return &listener, nil } func makeAddresses(addresses []LBHost, upstreamPort uint32) []*core.Address { diff --git a/pkg/envoy/boilerplate_test.go b/pkg/envoy/boilerplate_test.go index 93b8705..27e8ffb 100644 --- a/pkg/envoy/boilerplate_test.go +++ b/pkg/envoy/boilerplate_test.go @@ -2,10 +2,12 @@ package envoy import ( "fmt" + "reflect" "testing" "time" core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" + eal "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/file/v3" "github.com/golang/protobuf/ptypes/duration" ) @@ -56,6 +58,48 @@ func TestMakeHealthChecksValidPath(t *testing.T) { } +type accessLoggerTestCase struct { + name string + format map[string]interface{} + custom bool +} + +func TestAccessLoggerConfig(t *testing.T) { + testCases := []accessLoggerTestCase{ + {name: "default log format", format: DefaultAccessLogFormat, custom: false}, + {name: "custom log format", format: map[string]interface{}{"a-key": "a-format-specifier"}, custom: true}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + cfg := AccessLogger{} + if tc.custom { + cfg.Format = tc.format + } + + fileAccessLog := makeFileAccessLog(cfg, "/var/log/envoy/") + if fileAccessLog.Path != "/var/log/envoy/access.log" { + t.Errorf("Expected access log to use default path but was, %s", fileAccessLog.Path) + } + + alf, ok := fileAccessLog.AccessLogFormat.(*eal.FileAccessLog_LogFormat) + if !ok { + t.Fatalf("File Access Log Format had incorrect type, should be FileAccessLog_LogFormat") + } + + lf, ok := alf.LogFormat.Format.(*core.SubstitutionFormatString_JsonFormat) + if !ok { + t.Fatalf("LogFormat had incorrect type, should be SubstitutionFormatString_JsonFormat") + } + + format := lf.JsonFormat.AsMap() + if !reflect.DeepEqual(format, tc.format) { + t.Errorf("Log format map should match configuration") + } + }) + } +} + func mustParseDuration(dur string) time.Duration { d, err := time.ParseDuration(dur) if err != nil { diff --git a/pkg/envoy/config_dump.go b/pkg/envoy/config_dump.go new file mode 100644 index 0000000..3c18c02 --- /dev/null +++ b/pkg/envoy/config_dump.go @@ -0,0 +1,26 @@ +package envoy + +import ( + types "github.com/envoyproxy/go-control-plane/pkg/cache/types" + resource "github.com/envoyproxy/go-control-plane/pkg/resource/v3" +) + +type EnvoySnapshot struct { + Listeners map[string]types.Resource + Clusters map[string]types.Resource +} + +func (s *Snapshotter) ConfigDump() (EnvoySnapshot, error) { + snapshot, err := s.CurrentSnapshot() + if err != nil { + return EnvoySnapshot{}, err + } + + listeners := snapshot.GetResources(resource.ListenerType) + clusters := snapshot.GetResources(resource.ClusterType) + + return EnvoySnapshot{ + Listeners: listeners, + Clusters: clusters, + }, nil +} diff --git a/pkg/envoy/configurator.go b/pkg/envoy/configurator.go index db3ce51..40903fe 100644 --- a/pkg/envoy/configurator.go +++ b/pkg/envoy/configurator.go @@ -3,6 +3,7 @@ package envoy import ( "errors" "log" + "path/filepath" "strings" "sync" "time" @@ -11,7 +12,6 @@ import ( route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" tcache "github.com/envoyproxy/go-control-plane/pkg/cache/types" cache "github.com/envoyproxy/go-control-plane/pkg/cache/v3" - util "github.com/envoyproxy/go-control-plane/pkg/conversion" "github.com/sirupsen/logrus" "github.com/uswitch/yggdrasil/pkg/k8s" "google.golang.org/protobuf/types/known/anypb" @@ -42,15 +42,20 @@ type HttpExtAuthz struct { Timeout time.Duration `json:"timeout"` MaxRequestBytes uint32 `json:"maxRequestBytes"` AllowPartialMessage bool `json:"allowPartialMessage"` + PackAsBytes bool `json:"packAsBytes"` FailureModeAllow bool `json:"FailureModeAllow"` } type HttpGrpcLogger struct { - Name string `json:"name"` - Cluster string `json:"cluster"` - Timeout time.Duration `json:"timeout"` - AdditionalRequestHeaders []string `json:"additionalRequestHeaders"` - AdditionalResponseHeaders []string `json:"additionalResponseHeaders"` + Name string `json:"name"` + Cluster string `json:"cluster"` + Timeout time.Duration `json:"timeout"` + RequestHeaders []string `json:"requestHeaders"` + ResponseHeaders []string `json:"responseHeaders"` +} + +type AccessLogger struct { + Format map[string]interface{} `json:"format"` } // KubernetesConfigurator takes a given Ingress Class and lister to find only ingresses of that class @@ -58,6 +63,7 @@ type KubernetesConfigurator struct { ingressClasses []string nodeID string syncSecrets bool + accessLog string certificates []Certificate trustCA string upstreamPort uint32 @@ -70,7 +76,9 @@ type KubernetesConfigurator struct { httpExtAuthz HttpExtAuthz httpGrpcLogger HttpGrpcLogger defaultTimeouts DefaultTimeouts + accessLogger AccessLogger defaultRetryOn string + tracingProvider string alpnProtocols []string previousConfig *envoyConfiguration @@ -80,26 +88,48 @@ type KubernetesConfigurator struct { } // NewKubernetesConfigurator returns a Kubernetes configurator given a lister and ingress class -func NewKubernetesConfigurator(nodeID string, certificates []Certificate, ca string, ingressClasses []string, options ...option) *KubernetesConfigurator { - c := &KubernetesConfigurator{ingressClasses: ingressClasses, nodeID: nodeID, certificates: certificates, trustCA: ca} +func NewKubernetesConfigurator(nodeID string, certificates []Certificate, ca string, ingressClasses []string, accessLog string, options ...option) *KubernetesConfigurator { + c := &KubernetesConfigurator{ingressClasses: ingressClasses, nodeID: nodeID, certificates: certificates, trustCA: ca, accessLog: accessLog} for _, opt := range options { opt(c) } return c } +func (c *KubernetesConfigurator) ValidateAndFormatPath() { + if c.accessLog == "" { + logrus.Fatal("accessLog path cannot be empty") + } + + // Clean the path and make it absolute + c.accessLog = filepath.Clean(c.accessLog) + absolutePath, err := filepath.Abs(c.accessLog) + if err != nil { + logrus.Fatalf("invalid path: %v", err) + } + c.accessLog = absolutePath + + // Ensure the path ends with a directory separator if it's a directory + if strings.HasSuffix(c.accessLog, string(filepath.Separator)) { + c.accessLog = string(filepath.Separator) + } +} + // Generate creates a new snapshot -func (c *KubernetesConfigurator) Generate(ingresses []*k8s.Ingress, secrets []*v1.Secret) cache.Snapshot { +func (c *KubernetesConfigurator) Generate(ingresses []*k8s.Ingress, secrets []*v1.Secret) (cache.Snapshot, error) { c.Lock() defer c.Unlock() validIngresses := validIngressFilter(classFilter(ingresses, c.ingressClasses)) - config := translateIngresses(validIngresses, c.syncSecrets, secrets, c.defaultTimeouts) + config := translateIngresses(validIngresses, c.syncSecrets, secrets, c.defaultTimeouts, c.accessLog) vmatch, cmatch := config.equals(c.previousConfig) clusters := c.generateClusters(config) - listeners := c.generateListeners(config) + listeners, err := c.generateListeners(config) + if err != nil { + return cache.Snapshot{}, err + } if !vmatch { c.listenerVersion = time.Now().String() @@ -115,7 +145,7 @@ func (c *KubernetesConfigurator) Generate(ingresses []*k8s.Ingress, secrets []*v snap := cache.Snapshot{} snap.Resources[tcache.Cluster] = cache.NewResources(c.clusterVersion, []tcache.Resource(clusters)) snap.Resources[tcache.Listener] = cache.NewResources(c.listenerVersion, []tcache.Resource(listeners)) - return snap + return snap, nil } // NodeID returns the NodeID @@ -161,25 +191,33 @@ func (c *KubernetesConfigurator) matchCertificateIndices(virtualHost *virtualHos return []int{}, errNoCertificateMatch } -func (c *KubernetesConfigurator) generateListeners(config *envoyConfiguration) []tcache.Resource { +func (c *KubernetesConfigurator) generateListeners(config *envoyConfiguration) ([]tcache.Resource, error) { var filterChains []*listener.FilterChain + var err error if c.syncSecrets { - filterChains = c.generateDynamicTLSFilterChains(config) + filterChains, err = c.generateDynamicTLSFilterChains(config) } else if len(c.certificates) > 0 { - filterChains = c.generateTLSFilterChains(config) + filterChains, err = c.generateTLSFilterChains(config) } else { - filterChains = c.generateHTTPFilterChain(config) + filterChains, err = c.generateHTTPFilterChain(config) } - return []tcache.Resource{makeListener(filterChains, c.envoyListenerIpv4Address, c.envoyListenPort)} + if err != nil { + return []tcache.Resource{}, err + } + listener, err := makeListener(filterChains, c.envoyListenerIpv4Address, c.envoyListenPort) + return []tcache.Resource{listener}, err } -func (c *KubernetesConfigurator) generateDynamicTLSFilterChains(config *envoyConfiguration) []*listener.FilterChain { +func (c *KubernetesConfigurator) generateDynamicTLSFilterChains(config *envoyConfiguration) ([]*listener.FilterChain, error) { filterChains := []*listener.FilterChain{} allVhosts := []*route.VirtualHost{} for _, virtualHost := range config.VirtualHosts { - envoyVhost := makeVirtualHost(virtualHost, c.hostSelectionRetryAttempts, c.defaultRetryOn) + envoyVhost, err := makeVirtualHost(virtualHost, c.hostSelectionRetryAttempts, c.defaultRetryOn) + if err != nil { + return nil, err + } allVhosts = append(allVhosts, envoyVhost) if virtualHost.TlsCert == "" || virtualHost.TlsKey == "" { @@ -195,7 +233,7 @@ func (c *KubernetesConfigurator) generateDynamicTLSFilterChains(config *envoyCon Cert: virtualHost.TlsCert, Key: virtualHost.TlsKey, } - filterChain, err := c.makeFilterChain(certificate, []*route.VirtualHost{envoyVhost}) + filterChain, err := c.makeFilterChain(certificate, []*route.VirtualHost{envoyVhost}, config.AccessLog) if err != nil { logrus.Warnf("error making filter chain: %v", err) } @@ -208,28 +246,31 @@ func (c *KubernetesConfigurator) generateDynamicTLSFilterChains(config *envoyCon Cert: c.certificates[0].Cert, Key: c.certificates[0].Key, } - if defaultFC, err := c.makeFilterChain(defaultCert, allVhosts); err != nil { + if defaultFC, err := c.makeFilterChain(defaultCert, allVhosts, config.AccessLog); err != nil { logrus.Warnf("error making default filter chain: %v", err) } else { filterChains = append(filterChains, &defaultFC) } } - return filterChains + return filterChains, nil } -func (c *KubernetesConfigurator) generateHTTPFilterChain(config *envoyConfiguration) []*listener.FilterChain { +func (c *KubernetesConfigurator) generateHTTPFilterChain(config *envoyConfiguration) ([]*listener.FilterChain, error) { virtualHosts := []*route.VirtualHost{} for _, virtualHost := range config.VirtualHosts { - virtualHosts = append(virtualHosts, makeVirtualHost(virtualHost, c.hostSelectionRetryAttempts, c.defaultRetryOn)) + vhost, err := makeVirtualHost(virtualHost, c.hostSelectionRetryAttempts, c.defaultRetryOn) + if err != nil { + return nil, err + } + virtualHosts = append(virtualHosts, vhost) } - httpConnectionManager := c.makeConnectionManager(virtualHosts) - httpConfig, err := util.MessageToStruct(httpConnectionManager) + httpConnectionManager, err := c.makeConnectionManager(virtualHosts, config.AccessLog) if err != nil { - log.Fatalf("failed to convert virtualHost to envoy control plane struct: %s", err) + return nil, err } - anyHttpConfig, err := anypb.New(httpConfig) + anyHttpConfig, err := anypb.New(httpConnectionManager) if err != nil { log.Fatalf("failed to marshal HTTP config struct to typed struct: %s", err) } @@ -242,10 +283,10 @@ func (c *KubernetesConfigurator) generateHTTPFilterChain(config *envoyConfigurat }, }, }, - } + }, nil } -func (c *KubernetesConfigurator) generateTLSFilterChains(config *envoyConfiguration) []*listener.FilterChain { +func (c *KubernetesConfigurator) generateTLSFilterChains(config *envoyConfiguration) ([]*listener.FilterChain, error) { virtualHostsForCertificates := make([][]*route.VirtualHost, len(c.certificates)) for _, virtualHost := range config.VirtualHosts { @@ -254,7 +295,11 @@ func (c *KubernetesConfigurator) generateTLSFilterChains(config *envoyConfigurat log.Printf("error matching certificate for '%s': %v", virtualHost.Host, err) } else { for _, idx := range certificateIndicies { - virtualHostsForCertificates[idx] = append(virtualHostsForCertificates[idx], makeVirtualHost(virtualHost, c.hostSelectionRetryAttempts, c.defaultRetryOn)) + vhost, err := makeVirtualHost(virtualHost, c.hostSelectionRetryAttempts, c.defaultRetryOn) + if err != nil { + return nil, err + } + virtualHostsForCertificates[idx] = append(virtualHostsForCertificates[idx], vhost) } } } @@ -267,14 +312,14 @@ func (c *KubernetesConfigurator) generateTLSFilterChains(config *envoyConfigurat continue } - filterChain, err := c.makeFilterChain(certificate, virtualHosts) + filterChain, err := c.makeFilterChain(certificate, virtualHosts, config.AccessLog) if err != nil { log.Printf("error making filter chain: %v", err) } filterChains = append(filterChains, &filterChain) } - return filterChains + return filterChains, nil } func (c *KubernetesConfigurator) generateClusters(config *envoyConfiguration) []tcache.Resource { diff --git a/pkg/envoy/configurator_test.go b/pkg/envoy/configurator_test.go index fcc952c..a69a0ca 100644 --- a/pkg/envoy/configurator_test.go +++ b/pkg/envoy/configurator_test.go @@ -4,32 +4,21 @@ import ( "testing" "time" - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" tcache "github.com/envoyproxy/go-control-plane/pkg/cache/types" - util "github.com/envoyproxy/go-control-plane/pkg/conversion" - "github.com/golang/protobuf/ptypes" "github.com/uswitch/yggdrasil/pkg/k8s" v1 "k8s.io/api/core/v1" ) func assertNumberOfVirtualHosts(t *testing.T, filterChain *listener.FilterChain, expected int) { - var connManager hcm.HttpConnectionManager - var dynamicAny ptypes.DynamicAny - - err := ptypes.UnmarshalAny(filterChain.Filters[0].GetTypedConfig(), &dynamicAny) + filter, err := filterChain.Filters[0].GetTypedConfig().UnmarshalNew() if err != nil { t.Fatal(err) } - structMessage, err := util.MessageToStruct(dynamicAny.Message) - if err != nil { - t.Fatal(err) - } - - err = util.StructToMessage(structMessage, &connManager) - if err != nil { + connManager, ok := filter.(*hcm.HttpConnectionManager) + if !ok { t.Fatal(err) } @@ -42,21 +31,6 @@ func assertNumberOfVirtualHosts(t *testing.T, filterChain *listener.FilterChain, } -func assertTlsCertificate(t *testing.T, filterChain listener.FilterChain, expectedCert, expectedKey string) { - certificate := filterChain.HiddenEnvoyDeprecatedTlsContext.CommonTlsContext.TlsCertificates[0] - - certFile := certificate.CertificateChain.Specifier.(*core.DataSource_InlineString) - keyFile := certificate.PrivateKey.Specifier.(*core.DataSource_InlineString) - - if certFile.InlineString != expectedCert { - t.Fatalf("certficiate chain filename: '%s' expected '%s'", certFile.InlineString, expectedCert) - } - - if keyFile.InlineString != expectedKey { - t.Fatalf("private key filename: '%s' expected '%s'", keyFile.InlineString, expectedKey) - } -} - func assertServerNames(t *testing.T, filterChain *listener.FilterChain, expectedServerNames []string) { serverNames := filterChain.FilterChainMatch.ServerNames @@ -78,9 +52,9 @@ func TestGenerate(t *testing.T) { configurator := NewKubernetesConfigurator("a", []Certificate{ {Hosts: []string{"*"}, Cert: "b", Key: "c"}, - }, "d", []string{"bar"}) + }, "d", []string{"bar"}, "/var/log/envoy/", func(c *KubernetesConfigurator) { c.envoyListenerIpv4Address = []string{"1.1.1.1"} }) - snapshot := configurator.Generate(ingresses, []*v1.Secret{}) + snapshot, _ := configurator.Generate(ingresses, []*v1.Secret{}) if len(snapshot.Resources[tcache.Listener].Items) != 1 { t.Fatalf("Num listeners: %d", len(snapshot.Resources[tcache.Listener].Items)) @@ -99,9 +73,13 @@ func TestGenerateMultipleCerts(t *testing.T) { configurator := NewKubernetesConfigurator("a", []Certificate{ {Hosts: []string{"*.internal.api.com"}, Cert: "com", Key: "com"}, {Hosts: []string{"*.internal.api.co.uk"}, Cert: "couk", Key: "couk"}, - }, "d", []string{"bar"}) + }, "d", []string{"bar"}, "/var/log/envoy/", func(c *KubernetesConfigurator) { c.envoyListenerIpv4Address = []string{"1.1.1.1"} }) + + snapshot, err := configurator.Generate(ingresses, []*v1.Secret{}) + if err != nil { + t.Fatalf("Error generating snapshot %v", err) + } - snapshot := configurator.Generate(ingresses, []*v1.Secret{}) listener := snapshot.Resources[tcache.Listener].Items["listener_0"].Resource.(*listener.Listener) if len(listener.FilterChains) != 2 { @@ -120,9 +98,13 @@ func TestGenerateMultipleHosts(t *testing.T) { configurator := NewKubernetesConfigurator("a", []Certificate{ {Hosts: []string{"*.internal.api.com", "*.internal.api.co.uk"}, Cert: "com", Key: "com"}, - }, "d", []string{"bar"}) + }, "d", []string{"bar"}, "/var/log/envoy/", func(c *KubernetesConfigurator) { c.envoyListenerIpv4Address = []string{"1.1.1.1"} }) + + snapshot, err := configurator.Generate(ingresses, []*v1.Secret{}) + if err != nil { + t.Fatalf("Error generating snapshot %v", err) + } - snapshot := configurator.Generate(ingresses, []*v1.Secret{}) listener := snapshot.Resources[tcache.Listener].Items["listener_0"].Resource.(*listener.Listener) if len(listener.FilterChains) != 1 { @@ -141,9 +123,13 @@ func TestGenerateNoMatchingCert(t *testing.T) { configurator := NewKubernetesConfigurator("a", []Certificate{ {Hosts: []string{"*.internal.api.com"}, Cert: "com", Key: "com"}, - }, "d", []string{"bar"}) + }, "d", []string{"bar"}, "/var/log/envoy/", func(c *KubernetesConfigurator) { c.envoyListenerIpv4Address = []string{"1.1.1.1"} }) + + snapshot, err := configurator.Generate(ingresses, []*v1.Secret{}) + if err != nil { + t.Fatalf("Error generating snapshot %v", err) + } - snapshot := configurator.Generate(ingresses, []*v1.Secret{}) listener := snapshot.Resources[tcache.Listener].Items["listener_0"].Resource.(*listener.Listener) if len(listener.FilterChains) != 1 { @@ -159,9 +145,13 @@ func TestGenerateIntoTwoCerts(t *testing.T) { configurator := NewKubernetesConfigurator("a", []Certificate{ {Hosts: []string{"*.internal.api.com"}, Cert: "com", Key: "com"}, {Hosts: []string{"*"}, Cert: "all", Key: "all"}, - }, "d", []string{"bar"}) + }, "d", []string{"bar"}, "/var/log/envoy/", func(c *KubernetesConfigurator) { c.envoyListenerIpv4Address = []string{"1.1.1.1"} }) + + snapshot, err := configurator.Generate(ingresses, []*v1.Secret{}) + if err != nil { + t.Fatalf("Error generating snapshot %v", err) + } - snapshot := configurator.Generate(ingresses, []*v1.Secret{}) listener := snapshot.Resources[tcache.Listener].Items["listener_0"].Resource.(*listener.Listener) if len(listener.FilterChains) != 2 { @@ -228,8 +218,11 @@ func TestGenerateListeners(t *testing.T) { } for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { - configurator := NewKubernetesConfigurator("a", tc.certs, "", nil) - ret := configurator.generateListeners(&envoyConfiguration{VirtualHosts: tc.virtualHost}) + configurator := NewKubernetesConfigurator("a", tc.certs, "", nil, "/var/log/envoy/", func(c *KubernetesConfigurator) { c.envoyListenerIpv4Address = []string{"1.1.1.1"} }) + ret, err := configurator.generateListeners(&envoyConfiguration{VirtualHosts: tc.virtualHost}) + if err != nil { + t.Fatalf("Error generating listeners %v", err) + } listener := ret[0].(*listener.Listener) if len(listener.FilterChains) != 1 { t.Fatalf("filterchain number missmatch") diff --git a/pkg/envoy/http_filters.go b/pkg/envoy/http_filters.go index 81bdcbd..67be464 100644 --- a/pkg/envoy/http_filters.go +++ b/pkg/envoy/http_filters.go @@ -1,7 +1,7 @@ package envoy import ( - "log" + "fmt" router "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/router/v3" hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" @@ -17,15 +17,15 @@ func (b *httpFilterBuilder) Add(filter *hcm.HttpFilter) *httpFilterBuilder { return b } -func (b *httpFilterBuilder) Filters() []*hcm.HttpFilter { +func (b *httpFilterBuilder) Filters() ([]*hcm.HttpFilter, error) { httpFilterConfig, err := anypb.New(&router.Router{}) if err != nil { - log.Fatalf("failed to marshal http router filter config struct to typed struct: %s", err) + return nil, fmt.Errorf("failed to marshal router config struct to typed struct: %s", err) } b.Add(&hcm.HttpFilter{ Name: "envoy.filters.http.router", ConfigType: &hcm.HttpFilter_TypedConfig{TypedConfig: httpFilterConfig}, }) - return b.filters + return b.filters, nil } diff --git a/pkg/envoy/ingress_translator.go b/pkg/envoy/ingress_translator.go index 6ddd6f2..62c8ae1 100644 --- a/pkg/envoy/ingress_translator.go +++ b/pkg/envoy/ingress_translator.go @@ -65,6 +65,7 @@ func VirtualHostsEquals(a, b []*virtualHost) bool { type envoyConfiguration struct { VirtualHosts []*virtualHost Clusters []*cluster + AccessLog string } type virtualHost struct { @@ -246,10 +247,18 @@ func (ing *envoyIngress) addHealthCheckPath(path string) { ing.cluster.HealthCheckPath = path } +func (ing *envoyIngress) removeHealthCheckPath() { + ing.cluster.HealthCheckPath = "" +} + func (ing *envoyIngress) addHealthCheckHost(host string) { ing.cluster.HealthCheckHost = host } +func (ing *envoyIngress) removeHealthCheckHost() { + ing.cluster.HealthCheckHost = "" +} + func (ing *envoyIngress) addTimeout(timeout time.Duration) { ing.cluster.Timeout = timeout ing.vhost.Timeout = timeout @@ -365,7 +374,7 @@ func validateSubdomain(ruleHost, host string) bool { return strings.HasSuffix(host, ruleHost) } -func translateIngresses(ingresses []*k8s.Ingress, syncSecrets bool, secrets []*v1.Secret, timeouts DefaultTimeouts) *envoyConfiguration { +func translateIngresses(ingresses []*k8s.Ingress, syncSecrets bool, secrets []*v1.Secret, timeouts DefaultTimeouts, accessLog string) *envoyConfiguration { cfg := &envoyConfiguration{} envoyIngresses := map[string]*envoyIngress{} ruleHostToIngresses := map[string][]*k8s.Ingress{} @@ -397,7 +406,7 @@ func translateIngresses(ingresses []*k8s.Ingress, syncSecrets bool, secrets []*v // Add upstreams based on maintenance status for _, ingress := range ingressList { for _, j := range ingress.Upstreams { - // Skip this upstream if cluster is in maintenance but keep it if no other cluster can serve it + // Skip this upstream if cluster is in maintenance but keep it if no other cluster can serve it if !hasNonMaintenance || !ingress.Maintenance { // Check if the upstream is already added exists := false @@ -501,6 +510,7 @@ func translateIngresses(ingresses []*k8s.Ingress, syncSecrets bool, secrets []*v for _, ingress := range envoyIngresses { cfg.Clusters = append(cfg.Clusters, ingress.cluster) cfg.VirtualHosts = append(cfg.VirtualHosts, ingress.vhost) + cfg.AccessLog = accessLog } numVhosts.Set(float64(len(cfg.VirtualHosts))) diff --git a/pkg/envoy/ingress_translator_test.go b/pkg/envoy/ingress_translator_test.go index 5930f70..7119217 100644 --- a/pkg/envoy/ingress_translator_test.go +++ b/pkg/envoy/ingress_translator_test.go @@ -209,8 +209,8 @@ func TestEquals(t *testing.T) { Route: 15 * time.Second, PerTry: 5 * time.Second, } - c := translateIngresses([]*k8s.Ingress{ingress, ingress2}, false, []*v1.Secret{}, timeouts) - c2 := translateIngresses([]*k8s.Ingress{ingress, ingress2}, false, []*v1.Secret{}, timeouts) + c := translateIngresses([]*k8s.Ingress{ingress, ingress2}, false, []*v1.Secret{}, timeouts, "/var/log/envoy/") + c2 := translateIngresses([]*k8s.Ingress{ingress, ingress2}, false, []*v1.Secret{}, timeouts, "/var/log/envoy/") vmatch, cmatch := c.equals(c2) if vmatch != true { @@ -231,8 +231,8 @@ func TestNotEquals(t *testing.T) { Route: 15 * time.Second, PerTry: 5 * time.Second, } - c := translateIngresses([]*k8s.Ingress{ingress, ingress3, ingress2}, false, []*v1.Secret{}, timeouts) - c2 := translateIngresses([]*k8s.Ingress{ingress, ingress2, ingress4}, false, []*v1.Secret{}, timeouts) + c := translateIngresses([]*k8s.Ingress{ingress, ingress3, ingress2}, false, []*v1.Secret{}, timeouts, "/var/log/envoy/") + c2 := translateIngresses([]*k8s.Ingress{ingress, ingress2, ingress4}, false, []*v1.Secret{}, timeouts, "/var/log/envoy/") vmatch, cmatch := c.equals(c2) if vmatch == true { @@ -252,8 +252,8 @@ func TestPartialEquals(t *testing.T) { Route: 15 * time.Second, PerTry: 5 * time.Second, } - c := translateIngresses([]*k8s.Ingress{ingress2}, false, []*v1.Secret{}, timeouts) - c2 := translateIngresses([]*k8s.Ingress{ingress}, false, []*v1.Secret{}, timeouts) + c := translateIngresses([]*k8s.Ingress{ingress2}, false, []*v1.Secret{}, timeouts, "/var/log/envoy/") + c2 := translateIngresses([]*k8s.Ingress{ingress}, false, []*v1.Secret{}, timeouts, "/var/log/envoy/") vmatch, cmatch := c2.equals(c) if vmatch != true { @@ -272,7 +272,7 @@ func TestGeneratesForSingleIngress(t *testing.T) { Route: 15 * time.Second, PerTry: 5 * time.Second, } - c := translateIngresses([]*k8s.Ingress{ingress}, false, []*v1.Secret{}, timeouts) + c := translateIngresses([]*k8s.Ingress{ingress}, false, []*v1.Secret{}, timeouts, "/var/log/envoy/") if len(c.VirtualHosts) != 1 { t.Error("expected 1 virtual host") @@ -293,7 +293,7 @@ func TestGeneratesForSingleIngress(t *testing.T) { } if c.Clusters[0].Hosts[0].Weight != 1 { - t.Errorf("expected cluster host's weight for 1, was %v", c.Clusters[0].Hosts[0].Weight) + t.Errorf("expected cluster host's weight for 1, was %d", c.Clusters[0].Hosts[0].Weight) } if c.VirtualHosts[0].UpstreamCluster != c.Clusters[0].Name { @@ -313,7 +313,7 @@ func TestGeneratesForMultipleIngressSharingSpecHost(t *testing.T) { Route: 15 * time.Second, PerTry: 5 * time.Second, } - c := translateIngresses([]*k8s.Ingress{fooIngress, barIngress}, false, []*v1.Secret{}, timeouts) + c := translateIngresses([]*k8s.Ingress{fooIngress, barIngress}, false, []*v1.Secret{}, timeouts, "/var/log/envoy/") if len(c.VirtualHosts) != 1 { t.Error("expected 1 virtual host") @@ -334,10 +334,10 @@ func TestGeneratesForMultipleIngressSharingSpecHost(t *testing.T) { t.Errorf("expected 2 host, was %d", len(c.Clusters[0].Hosts)) } if c.Clusters[0].Hosts[0].Host != "foo.com" { - t.Errorf("expected cluster host for foo.com, was %s", c.Clusters[0].Hosts[0].Host) + t.Errorf("expected cluster host for foo.com, was %v", c.Clusters[0].Hosts[0].Host) } if c.Clusters[0].Hosts[1].Host != "bar.com" { - t.Errorf("expected cluster host for bar.com, was %s", c.Clusters[0].Hosts[1].Host) + t.Errorf("expected cluster host for bar.com, was %v", c.Clusters[0].Hosts[1].Host) } if c.VirtualHosts[0].UpstreamCluster != c.Clusters[0].Name { @@ -373,9 +373,9 @@ func TestIngressWithIP(t *testing.T) { Route: 15 * time.Second, PerTry: 5 * time.Second, } - c := translateIngresses([]*k8s.Ingress{ingress}, false, []*v1.Secret{}, timeouts) + c := translateIngresses([]*k8s.Ingress{ingress}, false, []*v1.Secret{}, timeouts, "/var/log/envoy/") if c.Clusters[0].Hosts[0].Host != "127.0.0.1" { - t.Errorf("expected cluster host to be IP address, was %s", c.Clusters[0].Hosts[0].Host) + t.Errorf("expected cluster host to be IP address, was %v", c.Clusters[0].Hosts[0].Host) } } diff --git a/pkg/envoy/options.go b/pkg/envoy/options.go index 2ad469c..9395afe 100644 --- a/pkg/envoy/options.go +++ b/pkg/envoy/options.go @@ -86,6 +86,20 @@ func WithDefaultRetryOn(defaultRetryOn string) option { } } +// WithAccessLog configures the access log formats +func WithAccessLog(accessLogger AccessLogger) option { + return func(c *KubernetesConfigurator) { + c.accessLogger = accessLogger + } +} + +// WithTracingProvider configures the tracing provider for HTTP connection manager +func WithTracingProvider(tracingProvider string) option { + return func(c *KubernetesConfigurator) { + c.tracingProvider = tracingProvider + } +} + // WithAlpnProtocols configures the the exposed listener ALPN protocols func WithAlpnProtocols(alpnProtocols []string) option { return func(c *KubernetesConfigurator) { diff --git a/pkg/envoy/snapshotter.go b/pkg/envoy/snapshotter.go index d6cae03..ec79843 100644 --- a/pkg/envoy/snapshotter.go +++ b/pkg/envoy/snapshotter.go @@ -11,21 +11,21 @@ import ( "github.com/uswitch/yggdrasil/pkg/k8s" ) -//Configurator is an interface that implements Generate and NodeID +// Configurator is an interface that implements Generate and NodeID type Configurator interface { - Generate([]*k8s.Ingress, []*v1.Secret) cache.Snapshot + Generate([]*k8s.Ingress, []*v1.Secret) (cache.Snapshot, error) NodeID() string } -//Snapshotter watches for Ingress changes and updates the -//config snapshot +// Snapshotter watches for Ingress changes and updates the +// config snapshot type Snapshotter struct { snapshotCache cache.SnapshotCache configurator Configurator aggregator *k8s.Aggregator } -//NewSnapshotter returns a new Snapshotter +// NewSnapshotter returns a new Snapshotter func NewSnapshotter(snapshotCache cache.SnapshotCache, config Configurator, aggregator *k8s.Aggregator) *Snapshotter { return &Snapshotter{snapshotCache: snapshotCache, configurator: config, aggregator: aggregator} } @@ -40,15 +40,20 @@ func (s *Snapshotter) snapshot() error { return err } - snapshot := s.configurator.Generate(genericIngresses, secrets) + snapshot, err := s.configurator.Generate(genericIngresses, secrets) log.Debugf("took snapshot: %+v", snapshot) s.snapshotCache.SetSnapshot(context.Background(), s.configurator.NodeID(), &snapshot) + return nil } -//Run will periodically refresh the snapshot +func (s *Snapshotter) CurrentSnapshot() (cache.ResourceSnapshot, error) { + return s.snapshotCache.GetSnapshot(s.configurator.NodeID()) +} + +// Run will periodically refresh the snapshot func (s *Snapshotter) Run(a *k8s.Aggregator) { log.Infof("started snapshotter") hadChanges := false