diff --git a/.vscode/settings.json b/.vscode/settings.json index 6c33fe3..b8f009c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ { "go.testEnvVars": { - "KUBEBUILDER_ASSETS": "${workspaceFolder}/bin/k8s/current" + "KUBEBUILDER_ASSETS": "${workspaceFolder}/bin/k8s/current", + "TEST_ASSET_COREDNS": "${workspaceFolder}/bin/coredns" } } \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index fb20db4..556b6d1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,10 +18,11 @@ COPY pkg/ pkg/ COPY internal/ internal/ COPY crds/ crds/ COPY Makefile Makefile +COPY hack/ hack/ # Run tests and build -RUN make envtest \ - && CGO_ENABLED=0 KUBEBUILDER_ASSETS="/workspace/bin/k8s/current" go test ./... \ +RUN make envtest coredns \ + && CGO_ENABLED=0 KUBEBUILDER_ASSETS="/workspace/bin/k8s/current" TEST_ASSET_COREDNS=/workspace/bin/coredns go test ./... \ && CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager main.go # Use distroless as minimal base image to package the manager binary diff --git a/Makefile b/Makefile index fafe65c..7aa5a1f 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,7 @@ IMG ?= controller:latest # ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. ENVTEST_K8S_VERSION = 1.25.0 +COREDNS_VERSION= 1.10.1 # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) ifeq (,$(shell go env GOBIN)) @@ -57,8 +58,8 @@ vet: ## Run go vet against code. go vet ./... .PHONY: test -test: manifests generate fmt vet envtest ## Run tests. - KUBEBUILDER_ASSETS="$(LOCALBIN)/k8s/current" go test ./... -coverprofile cover.out +test: manifests generate fmt vet envtest coredns ## Run tests. + KUBEBUILDER_ASSETS="$(LOCALBIN)/k8s/current" TEST_ASSET_COREDNS="$(LOCALBIN)/coredns" go test ./... -coverprofile cover.out ##@ Build @@ -132,6 +133,7 @@ $(LOCALBIN): KUSTOMIZE ?= $(LOCALBIN)/kustomize CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen ENVTEST ?= $(LOCALBIN)/setup-envtest +COREDNS ?= $(LOCALBIN)/coredns CLIENT_GEN ?= $(shell pwd)/bin/client-gen INFORMER_GEN ?= $(shell pwd)/bin/informer-gen LISTER_GEN ?= $(shell pwd)/bin/lister-gen @@ -160,6 +162,11 @@ $(ENVTEST): $(LOCALBIN) rm -f $(LOCALBIN)/k8s/current ;\ ln -s $$ENVTESTDIR $(LOCALBIN)/k8s/current +.PHONY: coredns +coredns: $(COREDNS) ## Download coredns +$(COREDNS): $(LOCALBIN) + test -s $(LOCALBIN)/coredns || ./hack/download-coredns $(COREDNS_VERSION) $(LOCALBIN)/coredns + .PHONY: client-gen client-gen: $(CLIENT_GEN) ## Download client-gen $(CLIENT_GEN): $(LOCALBIN) diff --git a/api/v1alpha1/masqueradingrule_webhook.go b/api/v1alpha1/masqueradingrule_webhook.go index 5137ac4..b7b81dd 100644 --- a/api/v1alpha1/masqueradingrule_webhook.go +++ b/api/v1alpha1/masqueradingrule_webhook.go @@ -8,7 +8,7 @@ package v1alpha1 import ( "fmt" - "github.com/sap/dns-masquerading-operator/internal/netutil" + "github.com/sap/dns-masquerading-operator/internal/coredns" "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" logf "sigs.k8s.io/controller-runtime/pkg/log" @@ -59,17 +59,9 @@ func (r *MasqueradingRule) ValidateDelete() error { } func (r *MasqueradingRule) validate() error { - if err := netutil.CheckDnsName(r.Spec.From, true); err != nil { - return fmt.Errorf("invalid .spec.from: %s (%s)", r.Spec.From, err) - } - if netutil.IsIpAddress(r.Spec.To) { - if netutil.IsWildcardDnsName(r.Spec.From) { - return fmt.Errorf("invalid .spec.to: %s (.spec.from must not be a wildcard DNS name if .spec.to is an IP address)", r.Spec.To) - } - } else { - if err := netutil.CheckDnsName(r.Spec.To, false); err != nil { - return fmt.Errorf("invalid .spec.to: %s (%s)", r.Spec.To, err) - } + _, err := coredns.NewRewriteRule("", r.Spec.From, r.Spec.To) + if err != nil { + return fmt.Errorf("invalid rule specification: %s", err) } return nil } diff --git a/go.mod b/go.mod index fbbde95..fbe2bcb 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,8 @@ go 1.19 require ( github.com/hashicorp/go-multierror v1.1.1 + github.com/onsi/ginkgo/v2 v2.11.0 + github.com/onsi/gomega v1.27.9 github.com/pkg/errors v0.9.1 github.com/sap/go-generics v0.1.1 istio.io/client-go v1.17.3 @@ -26,43 +28,45 @@ require ( github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.20.0 // indirect github.com/go-openapi/swag v0.19.14 // indirect + github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect github.com/google/go-cmp v0.5.9 // indirect - github.com/google/gofuzz v1.1.0 // indirect + github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect github.com/google/uuid v1.3.0 // indirect - github.com/hashicorp/errwrap v1.0.0 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/mailru/easyjson v0.7.6 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect + github.com/kr/pretty v0.2.1 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/moby/spdystream v0.2.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/onsi/ginkgo/v2 v2.11.0 // indirect - github.com/onsi/gomega v1.27.9 // indirect github.com/prometheus/client_golang v1.14.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.37.0 // indirect + github.com/prometheus/common v0.39.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - go.uber.org/atomic v1.7.0 // indirect + github.com/stretchr/testify v1.8.1 // indirect + go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.24.0 // indirect golang.org/x/net v0.12.0 // indirect - golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect + golang.org/x/oauth2 v0.3.0 // indirect golang.org/x/sys v0.10.0 // indirect golang.org/x/term v0.10.0 // indirect golang.org/x/text v0.11.0 // indirect golang.org/x/time v0.3.0 // indirect + golang.org/x/tools v0.9.3 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20221018160656-63c7b68cfc55 // indirect + google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect @@ -70,7 +74,7 @@ require ( istio.io/api v0.0.0-20230523222408-21e7bb4b66a2 // indirect k8s.io/apiextensions-apiserver v0.26.1 // indirect k8s.io/component-base v0.26.1 // indirect - k8s.io/klog/v2 v2.80.1 // indirect + k8s.io/klog/v2 v2.90.0 // indirect k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 // indirect sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect diff --git a/go.sum b/go.sum index 0b9af5e..c82b978 100644 --- a/go.sum +++ b/go.sum @@ -1,59 +1,17 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/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/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/beorn7/perks v0.0.0-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/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/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= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -62,9 +20,7 @@ github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3 github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= @@ -73,17 +29,6 @@ github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJ github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -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-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -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-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= @@ -98,29 +43,17 @@ github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXym 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/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= 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= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= @@ -128,168 +61,107 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= -github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= 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 v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -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/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2 h1:hAHbPm5IJGijwng3PWk09JkG9WeqChjprR5s9bBZ+OM= -github.com/matttproud/golang_protobuf_extensions v1.0.2/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= 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-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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= github.com/onsi/gomega v1.27.9 h1:qIyVWbOsvQEye2QCqLsNSeH/5L1RS9vS382erEWfT3o= github.com/onsi/gomega v1.27.9/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= -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 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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.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.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= -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.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -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/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= -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/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/common v0.39.0 h1:oOyhkDq05hPZKItWVBkJ6g6AtGxi+fy7F4JvUV8uhsI= +github.com/prometheus/common v0.39.0/go.mod h1:6XBZ7lYdLCbkAVhwRsWTZn+IN5AB9F/NXd5w0BbEX0Y= github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/sap/go-generics v0.1.1 h1:3T4McSGF5epH8Yp4bbxD07oa5J0pH7Nfx4r4zwdsr4o= github.com/sap/go-generics v0.1.1/go.mod h1:8L5jyDKi/FHKG/R+qwyy9h1TymA7XO5YSu+J/27gTCg= -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/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 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/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -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= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= @@ -297,299 +169,104 @@ go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9i go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= 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-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= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -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= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= 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= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b h1:clP8eMhB30EHdc0bd2Twtq6kgU7yl5ub2cQLSdrv1Dg= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.3.0 h1:6l90koy8/LaBLmLu8jpHeHexzMwEita0zFfYlggy2F8= +golang.org/x/oauth2 v0.3.0/go.mod h1:rQrIauxkUhJ6CuwEXwymO2/eh4xz2ZWF1nBkcxS+tGk= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 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/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-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= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/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= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -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-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= golang.org/x/sys v0.10.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/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= +golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20221018160656-63c7b68cfc55 h1:U1u4KB2kx6KR/aJDjQ97hZ15wQs8ZPvDcGcRynBhkvg= -google.golang.org/genproto v0.0.0-20221018160656-63c7b68cfc55/go.mod h1:45EK0dUbEZ2NHjCeAd2LXmyjAgGUGrpGROgjhC3ADck= +google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef h1:uQ2vjV/sHTsWSqdKeLqmwitzgvjMl7o4IdtHwUDXSJY= +google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/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= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/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= @@ -600,12 +277,7 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= istio.io/api v0.0.0-20230523222408-21e7bb4b66a2 h1:wZ7gy7926Vh/i++Kq/PCOdgODCwOnV9eqbB+e0Ce2Ao= istio.io/api v0.0.0-20230523222408-21e7bb4b66a2/go.mod h1:owGDRg9uqMob8CN1gxaOzk6nJxnbT8wrP7PmggpJHHY= istio.io/client-go v1.17.3 h1:LLx9y8d2a3+vgPnKb2/OwJIDSKUe8syteHTcxjUFHAU= @@ -620,15 +292,12 @@ k8s.io/client-go v0.26.3 h1:k1UY+KXfkxV2ScEL3gilKcF7761xkYsSD6BC9szIu8s= k8s.io/client-go v0.26.3/go.mod h1:ZPNu9lm8/dbRIPAgteN30RSXea6vrCpFvq+MateTUuQ= k8s.io/component-base v0.26.1 h1:4ahudpeQXHZL5kko+iDHqLj/FSGAEUnSVO0EBbgDd+4= k8s.io/component-base v0.26.1/go.mod h1:VHrLR0b58oC035w6YQiBSbtsf0ThuSwXP+p5dD/kAWU= -k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= -k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/klog/v2 v2.90.0 h1:VkTxIV/FjRXn1fgNNcKGM8cfmL1Z33ZjXRTVxKCoF5M= +k8s.io/klog/v2 v2.90.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E= k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 h1:KTgPnR10d5zhztWptI952TNtt/4u5h3IzDXkdIMuo2Y= k8s.io/utils v0.0.0-20221128185143-99ec85e7a448/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/controller-runtime v0.14.6 h1:oxstGVvXGNnMvY7TAESYk+lzr6S3V5VFxQ6d92KcwQA= sigs.k8s.io/controller-runtime v0.14.6/go.mod h1:WqIdsAY6JBsjfc/CqO0CORmNtoCtE4S6qbPc9s68h+0= sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= diff --git a/hack/download-coredns b/hack/download-coredns new file mode 100755 index 0000000..a4cf300 --- /dev/null +++ b/hack/download-coredns @@ -0,0 +1,48 @@ +#!/usr/bin/env bash + +set -eo pipefail + +if [ $# -ne 2 ]; then + >&2 echo "Usage: $0 " + exit 1 +fi + +coredns_version=$1 +target_path=$2 + +os=$(uname) +case $os in + Darwin) + os=darwin + ;; + Linux) + os=linux + ;; + *) + >&2 echo "Unsupported OS: $os" + exit 1 + ;; +esac + +arch=$(uname -m) +case $arch in + amd64|x86_64) + arch=amd64 + ;; + arm64|aarch64) + arch=arm64 + ;; + *) + >&2 echo "Unsupported architecture: $arch" + exit 1 + ;; +esac + +echo "Downloading coredns $coredns_version to $target_path" + +tmpdir=$(mktemp -d) +trap 'rm -rf $tmpdir' EXIT + +curl -sSfLo $tmpdir/coredns.tar.gz https://github.com/coredns/coredns/releases/download/v${coredns_version}/coredns_${coredns_version}_${os}_${arch}.tgz +tar xfz $tmpdir/coredns.tar.gz -C $tmpdir +cp $tmpdir/coredns $target_path diff --git a/hack/patch-coredns b/hack/patch-coredns new file mode 100755 index 0000000..f8930fb --- /dev/null +++ b/hack/patch-coredns @@ -0,0 +1,46 @@ +#!/usr/bin/env bash + +set -eo pipefail + +tempdir=$(mktemp -d) +trap 'rm -rf $tempdir' EXIT + +# patch coredns config map + +kubectl -n kube-system get cm coredns -o jsonpath='{.data.Corefile}' > $tempdir/Corefile + +cat > $tempdir/Corefile.patch < $tempdir/configmap-patch.yaml +kubectl -n kube-system patch cm coredns --patch-file $tempdir/configmap-patch.yaml + +# patch coredns deployment + +cat > $tempdir/deployment-patch.yaml < 1 are configured, while maintaining the coredns custom config map; + // this in in principle harmless, but it will pollute the logs with 409 error messages; + // to overcome this, we would need to introduce a mutex to synchronize the reconciliation (at least the relevant parts of the logic) + // across the workers; but this is maybe not a good idea as well (so we leave it for now as it is) ... if masqueradingRule.DeletionTimestamp.IsZero() { // Create/update case if !slices.Contains(masqueradingRule.Finalizers, finalizer) { @@ -162,11 +176,17 @@ func (r *MasqueradingRuleReconciler) Reconcile(ctx context.Context, req ctrl.Req } } + rule, err := coredns.NewRewriteRule(owner, masqueradingRule.Spec.From, masqueradingRule.Spec.To) + if err != nil { + return ctrl.Result{}, errors.Wrap(err, "error adding rewrite rule") + } + if configMap == nil { ruleset := coredns.NewRewriteRuleSet() - if err := ruleset.AddRule(coredns.RewriteRule{Owner: owner, From: masqueradingRule.Spec.From, To: masqueradingRule.Spec.To}); err != nil { + if _, err := ruleset.AddRule(rule); err != nil { return ctrl.Result{}, errors.Wrap(err, "error adding rewrite rule") } + masqueradingRule.SetState(dnsv1alpha1.MasqueradingRuleStateProcessing, "waiting for masquerading rule to be reconciled") configMap = &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Namespace: r.CorednsConfigMapNamespace, @@ -180,47 +200,67 @@ func (r *MasqueradingRuleReconciler) Reconcile(ctx context.Context, req ctrl.Req return ctrl.Result{}, errors.Wrapf(err, "error creating config map %s/%s", configMap.Namespace, configMap.Name) } log.V(1).Info("configmap successfully created", "namespace", r.CorednsConfigMapNamespace, "name", r.CorednsConfigMapName) - masqueradingRule.SetState(dnsv1alpha1.MasqueradingRuleStateProcessing, "waiting for masquerading rule to be reconciled") return ctrl.Result{RequeueAfter: 1 * time.Second}, nil } else { ruleset, err := coredns.ParseRewriteRuleSet(configMap.Data[r.CorednsConfigMapKey]) if err != nil { return ctrl.Result{}, errors.Wrapf(err, "error loading rewrite rules from config map %s/%s (key: %s)", configMap.Namespace, configMap.Name, r.CorednsConfigMapKey) } - if rule := ruleset.GetRule(owner); rule == nil || rule.From != masqueradingRule.Spec.From || rule.To != masqueradingRule.Spec.To { - if err := ruleset.AddRule(coredns.RewriteRule{Owner: owner, From: masqueradingRule.Spec.From, To: masqueradingRule.Spec.To}); err != nil { - return ctrl.Result{}, errors.Wrap(err, "error adding rewrite rule") - } + changed, err := ruleset.AddRule(rule) + if err != nil { + return ctrl.Result{}, errors.Wrap(err, "error adding rewrite rule") + } + if changed { + masqueradingRule.SetState(dnsv1alpha1.MasqueradingRuleStateProcessing, "waiting for masquerading rule to be reconciled") if configMap.Data == nil { configMap.Data = make(map[string]string) } + // TODO: the following is needed (for test execution) until we have https://github.com/coredns/coredns/issues/6243 or a similar fix; + // note: delaying the update is probably not required in 'real' deployments, since high-frequency configmap updates are + // anyway buffered there by kubelet's configmap/secret distribution logic. + now := time.Now() + if val, ok := configMap.Annotations[annotationLastUpdatedAt]; ok { + lastUpdatedAt, err := time.Parse(time.RFC3339Nano, val) + if err != nil { + return ctrl.Result{}, errors.Wrapf(err, "found invalid timestamp in configmap annotation %s: %s", annotationLastUpdatedAt, val) + } + if now.Before(lastUpdatedAt.Add(r.CorednsConfigMapUpdateDelay)) { + log.V(1).Info("delaying update of configmap", "namespace", r.CorednsConfigMapNamespace, "name", r.CorednsConfigMapName) + return ctrl.Result{RequeueAfter: 1 * time.Second}, nil + } + } + if configMap.Annotations == nil { + configMap.Annotations = make(map[string]string) + } + configMap.Annotations[annotationLastUpdatedAt] = now.Format(time.RFC3339Nano) + // end configMap.Data[r.CorednsConfigMapKey] = ruleset.String() if err := r.Update(ctx, configMap); err != nil { return ctrl.Result{}, errors.Wrapf(err, "error updating config map %s/%s", configMap.Namespace, configMap.Name) } log.V(1).Info("configmap successfully updated", "namespace", r.CorednsConfigMapNamespace, "name", r.CorednsConfigMapName) - masqueradingRule.SetState(dnsv1alpha1.MasqueradingRuleStateProcessing, "waiting for masquerading rule to be reconciled") return ctrl.Result{RequeueAfter: 1 * time.Second}, nil } } - active, err := coredns.CheckRecord(ctx, r.Client, r.Config, regexp.MustCompile(`^\*\.(.+)$`).ReplaceAllString(masqueradingRule.Spec.From, `wildcard.$1`), masqueradingRule.Spec.To, r.InCluster) + active, err := r.Resolver.CheckRecord(ctx, regexp.MustCompile(`^\*(.*)$`).ReplaceAllString(masqueradingRule.Spec.From, `wildcard$1`), masqueradingRule.Spec.To) if err != nil { return ctrl.Result{}, errors.Wrap(err, "error check DNS record") } if active { - log.V(1).Info("dns record active") masqueradingRule.SetState(dnsv1alpha1.MasqueradingRuleStateReady, "masquerading rule completely reconciled") + r.Recorder.Eventf(masqueradingRule, corev1.EventTypeNormal, "ReconcilationSucceeded", "masquerading rule completely reconciled") + log.V(1).Info("dns record active") return ctrl.Result{RequeueAfter: 10 * time.Minute}, nil } else { - log.V(1).Info("dns record not (active); rechecking in 10s ...") masqueradingRule.SetState(dnsv1alpha1.MasqueradingRuleStateProcessing, "waiting for masquerading rule to be reconciled") + r.Recorder.Eventf(masqueradingRule, corev1.EventTypeWarning, "ReconcilationProcessing", "waiting for masquerading rule to be reconciled") + log.V(1).Info("dns record not (active); rechecking in 10s ...") return ctrl.Result{RequeueAfter: 10 * time.Second}, nil } } else if len(slices.Remove(masqueradingRule.Finalizers, finalizer)) > 0 { masqueradingRule.SetState(dnsv1alpha1.MasqueradingRuleStateDeletionBlocked, "Deletion blocked due to foreign finalizers") - // TODO: apply some increasing period, depending on the age of the last update return ctrl.Result{RequeueAfter: 10 * time.Second}, nil } else { // Deletion case @@ -229,19 +269,36 @@ func (r *MasqueradingRuleReconciler) Reconcile(ctx context.Context, req ctrl.Req if err != nil { return ctrl.Result{}, errors.Wrapf(err, "error loading rewrite rules from config map %s/%s (key: %s)", configMap.Namespace, configMap.Name, r.CorednsConfigMapKey) } - if rule := ruleset.GetRule(owner); rule != nil { - if err := ruleset.RemoveRule(owner); err != nil { - return ctrl.Result{}, errors.Wrap(err, "error removing rewrite rule") - } + changed := ruleset.RemoveRule(owner) + if changed { + masqueradingRule.SetState(dnsv1alpha1.MasqueradingRuleStateDeleting, "waiting for masquerading rule to be deleted") if configMap.Data == nil { configMap.Data = make(map[string]string) } + // TODO: the following is needed (for test execution) until we have https://github.com/coredns/coredns/issues/6243 or a similar fix; + // note: delaying the update is probably not required in 'real' deployments, since high-frequency configmap updates are + // anyway buffered there by kubelet's configmap/secret distribution logic. + now := time.Now() + if val, ok := configMap.Annotations[annotationLastUpdatedAt]; ok { + lastUpdatedAt, err := time.Parse(time.RFC3339Nano, val) + if err != nil { + return ctrl.Result{}, errors.Wrapf(err, "found invalid timestamp in configmap annotation %s: %s", annotationLastUpdatedAt, val) + } + if now.Before(lastUpdatedAt.Add(r.CorednsConfigMapUpdateDelay)) { + log.V(1).Info("delaying update of configmap", "namespace", r.CorednsConfigMapNamespace, "name", r.CorednsConfigMapName) + return ctrl.Result{RequeueAfter: 1 * time.Second}, nil + } + } + if configMap.Annotations == nil { + configMap.Annotations = make(map[string]string) + } + configMap.Annotations[annotationLastUpdatedAt] = now.Format(time.RFC3339Nano) + // end configMap.Data[r.CorednsConfigMapKey] = ruleset.String() if err := r.Update(ctx, configMap); err != nil { return ctrl.Result{}, errors.Wrapf(err, "error updating config map %s/%s", configMap.Namespace, configMap.Name) } log.V(1).Info("configmap successfully updated", "namespace", r.CorednsConfigMapNamespace, "name", r.CorednsConfigMapName) - masqueradingRule.SetState(dnsv1alpha1.MasqueradingRuleStateDeleting, "waiting for masquerading rule to be deleted") return ctrl.Result{RequeueAfter: 1 * time.Second}, nil } } @@ -260,7 +317,7 @@ func (r *MasqueradingRuleReconciler) Reconcile(ctx context.Context, req ctrl.Req } } -// Record an event for specified object +// record an event for specified object func (r *MasqueradingRuleReconciler) createEventForObject(ctx context.Context, gvk schema.GroupVersionKind, namespace string, name string, eventType string, reason string, message string, args ...interface{}) error { owner, err := r.Scheme.New(gvk) if err != nil { @@ -278,5 +335,6 @@ func (r *MasqueradingRuleReconciler) SetupWithManager(mgr ctrl.Manager) error { predicate := predicate.Or(predicate.GenerationChangedPredicate{}, predicate.AnnotationChangedPredicate{}) return ctrl.NewControllerManagedBy(mgr). For(&dnsv1alpha1.MasqueradingRule{}, builder.WithPredicates(predicate)). + WithOptions(controller.Options{MaxConcurrentReconciles: 3}). Complete(r) } diff --git a/internal/controllers/service_controller.go b/internal/controllers/service_controller.go index 22d3999..3f98497 100644 --- a/internal/controllers/service_controller.go +++ b/internal/controllers/service_controller.go @@ -68,7 +68,7 @@ func getHostsFromService(service *corev1.Service) []string { return maps.Keys(hosts) } -// Custom predicate to filter for service type +// custom predicate to filter for service type func serviceTypePredicate(serviceType corev1.ServiceType) predicate.Predicate { f := func(obj client.Object, serviceType corev1.ServiceType) bool { if service, ok := obj.(*corev1.Service); ok { diff --git a/internal/controllers/suite_test.go b/internal/controllers/suite_test.go new file mode 100644 index 0000000..2ad3d28 --- /dev/null +++ b/internal/controllers/suite_test.go @@ -0,0 +1,730 @@ +/* +SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and dns-masquerading-operator contributors +SPDX-License-Identifier: Apache-2.0 +*/ + +package controllers_test + +import ( + "bytes" + "context" + "fmt" + "html/template" + "math/rand" + "net" + "os" + "os/exec" + "regexp" + "strconv" + "strings" + "sync" + "testing" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + admissionv1 "k8s.io/api/admissionregistration/v1" + corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + clientcmdapi "k8s.io/client-go/tools/clientcmd/api" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/envtest" + ctrllog "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + + dnsv1alpha1 "github.com/sap/dns-masquerading-operator/api/v1alpha1" + "github.com/sap/dns-masquerading-operator/internal/controllers" + "github.com/sap/dns-masquerading-operator/internal/coredns" + "github.com/sap/go-generics/slices" + // +kubebuilder:scaffold:imports +) + +func TestOperator(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Operator") +} + +const controllerName = "masquerading-operator.cs.sap.com" +const corednsConfigMapNamespace = "kube-system" +const corednsConfigMapName = "coredns-custom" +const corednsConfigMapKey = "masquerading.override" +const corednsAddress = "127.0.0.1" +const corefileTemplate = ` +.:{{ .listenPort }} { + bind {{ .listenAddress }} + errors + log . + debug + prometheus + kubernetes cluster.local in-addr.arpa ip6.arpa { + kubeconfig {{ .kubeconfigPath }} + pods insecure + fallthrough in-addr.arpa ip6.arpa + ttl 1 + } + forward . {{ .forwarderAddress }} { + max_concurrent 1000 + } + loop + reload 2s 1s + loadbalance + import *.override +} +` + +var testEnv *envtest.Environment +var cfg *rest.Config +var cli client.Client +var ctx context.Context +var cancel context.CancelFunc +var threads sync.WaitGroup +var tmpdir string +var namespace string +var resolver coredns.Resolver + +var _ = BeforeSuite(func() { + var err error + + By("initializing") + ctrllog.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) + ctx, cancel = context.WithCancel(context.TODO()) + tmpdir, err = os.MkdirTemp("", "") + Expect(err).NotTo(HaveOccurred()) + + By("bootstrapping test environment") + testEnv = &envtest.Environment{ + CRDDirectoryPaths: []string{"../../crds"}, + WebhookInstallOptions: envtest.WebhookInstallOptions{ + ValidatingWebhooks: []*admissionv1.ValidatingWebhookConfiguration{ + buildValidatingWebhookConfiguration(), + }, + }, + // uncomment the following line to show control plane logs + // AttachControlPlaneOutput: true, + } + cfg, err = testEnv.Start() + Expect(err).NotTo(HaveOccurred()) + Expect(cfg).NotTo(BeNil()) + webhookInstallOptions := &testEnv.WebhookInstallOptions + + kubeconfigPath := fmt.Sprintf("%s/kubeconfig", tmpdir) + err = clientcmd.WriteToFile(*kubeConfigFromRestConfig(cfg), kubeconfigPath) + Expect(err).NotTo(HaveOccurred()) + fmt.Printf("A temporary kubeconfig for the envtest environment can be found here: %s/kubeconfig\n", tmpdir) + + By("populating scheme") + scheme := runtime.NewScheme() + utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + utilruntime.Must(dnsv1alpha1.AddToScheme(scheme)) + + By("initializing client") + cli, err = client.New(cfg, client.Options{Scheme: scheme}) + Expect(err).NotTo(HaveOccurred()) + + By("starting coredns") + corednsPath := os.Getenv("TEST_ASSET_COREDNS") + if corednsPath == "" { + corednsPath = os.Getenv("KUBEBUILDER_ASSETS") + "/coredns" + } + if corednsPath == "" { + corednsPath = "/usr/local/kubebuilder/bin/coredns" + } + Expect(corednsPath).To(BeAnExistingFile()) + + corednsPort, err := getFreePort(corednsAddress, true, true) + Expect(err).NotTo(HaveOccurred()) + + // note: we use the google resolver as forwarder, because using /etc/resolv.conf causes too many sporadic i/o timeout errors + // TODO: find out why ... + corednsForwarderAddress := "8.8.8.8" + + var corefileBuffer bytes.Buffer + corefileData := map[string]string{ + "listenAddress": corednsAddress, + "listenPort": strconv.Itoa(int(corednsPort)), + "kubeconfigPath": kubeconfigPath, + "forwarderAddress": corednsForwarderAddress, + } + err = template.Must(template.New("corefile").Parse(corefileTemplate)).Execute(&corefileBuffer, corefileData) + Expect(err).NotTo(HaveOccurred()) + corefile := corefileBuffer.Bytes() + corefilePath := fmt.Sprintf("%s/Corefile", tmpdir) + err = os.WriteFile(corefilePath, []byte(corefile), 0644) + Expect(err).NotTo(HaveOccurred()) + + threads.Add(1) + go func() { + defer threads.Done() + defer GinkgoRecover() + + cmd := exec.CommandContext(ctx, corednsPath, "-conf", corefilePath) + // uncomment the following lines to display coredns output + // cmd.Stdout = os.Stdout + // cmd.Stderr = os.Stderr + err = cmd.Run() + if ctx.Err() == nil { + Expect(err).NotTo(HaveOccurred()) + } + }() + + By("starting coredns configmap extractor") + threads.Add(1) + go func() { + defer threads.Done() + defer GinkgoRecover() + + for { + select { + case <-ctx.Done(): + return + case <-time.After(time.Second): + configMap := &corev1.ConfigMap{} + err := cli.Get(context.Background(), types.NamespacedName{Namespace: corednsConfigMapNamespace, Name: corednsConfigMapName}, configMap) + if apierrors.IsNotFound(err) { + continue + } + Expect(err).NotTo(HaveOccurred()) + + newData, ok := configMap.Data[corednsConfigMapKey] + if !ok { + continue + } + + path := fmt.Sprintf("%s/%s", tmpdir, corednsConfigMapKey) + + data, err := os.ReadFile(path) + if os.IsNotExist(err) { + err = nil + data = nil + } + Expect(err).NotTo(HaveOccurred()) + + if data == nil || newData != string(data) { + tmpPath := fmt.Sprintf("%s.tmp", path) + err = os.WriteFile(tmpPath, []byte(newData), 0644) + Expect(err).NotTo(HaveOccurred()) + err = os.Rename(tmpPath, path) + Expect(err).NotTo(HaveOccurred()) + } + } + } + }() + + By("initializing resolver") + resolver = coredns.NewResolver(cli, cfg, false, coredns.Endpoint{Address: corednsAddress, Port: corednsPort}) + + By("creating manager") + mgr, err := ctrl.NewManager(cfg, ctrl.Options{ + Scheme: scheme, + MetricsBindAddress: "0", + HealthProbeBindAddress: "0", + Host: webhookInstallOptions.LocalServingHost, + Port: webhookInstallOptions.LocalServingPort, + CertDir: webhookInstallOptions.LocalServingCertDir, + ClientDisableCacheFor: []client.Object{&dnsv1alpha1.MasqueradingRule{}}}) + Expect(err).NotTo(HaveOccurred()) + + err = (&controllers.MasqueradingRuleReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Recorder: mgr.GetEventRecorderFor(controllerName), + CorednsConfigMapNamespace: corednsConfigMapNamespace, + CorednsConfigMapName: corednsConfigMapName, + CorednsConfigMapKey: corednsConfigMapKey, + CorednsConfigMapUpdateDelay: 5 * time.Second, + Resolver: resolver, + }).SetupWithManager(mgr) + Expect(err).NotTo(HaveOccurred()) + + err = (&controllers.ServiceReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr) + Expect(err).NotTo(HaveOccurred()) + + err = (&controllers.IngressReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr) + Expect(err).NotTo(HaveOccurred()) + + err = (&dnsv1alpha1.MasqueradingRule{}).SetupWebhookWithManager(mgr) + Expect(err).NotTo(HaveOccurred()) + + By("starting dummy controller-manager") + threads.Add(1) + go func() { + defer threads.Done() + defer GinkgoRecover() + // since there is no controller-manager in envtest, we have to confirm foreground deletion finalizer explicitly + for { + select { + case <-ctx.Done(): + return + case <-time.After(100 * time.Millisecond): + masqueradingRuleList := &dnsv1alpha1.MasqueradingRuleList{} + err := cli.List(context.Background(), masqueradingRuleList) + Expect(err).NotTo(HaveOccurred()) + for _, masqueradingRule := range masqueradingRuleList.Items { + if !masqueradingRule.DeletionTimestamp.IsZero() { + if controllerutil.RemoveFinalizer(&masqueradingRule, metav1.FinalizerDeleteDependents) { + err = cli.Update(context.Background(), &masqueradingRule) + if apierrors.IsNotFound(err) || apierrors.IsConflict(err) { + err = nil + } + Expect(err).NotTo(HaveOccurred()) + } + } + } + } + } + }() + + By("starting manager") + threads.Add(1) + go func() { + defer threads.Done() + defer GinkgoRecover() + err := mgr.Start(ctx) + Expect(err).NotTo(HaveOccurred()) + }() + + By("waiting for operator to become ready") + Eventually(func() error { return mgr.GetWebhookServer().StartedChecker()(nil) }, "10s", "100ms").Should(Succeed()) + + By("create testing namespace") + namespace, err = createNamespace() + Expect(err).NotTo(HaveOccurred()) +}) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + cancel() + threads.Wait() + err := testEnv.Stop() + Expect(err).NotTo(HaveOccurred()) + err = os.RemoveAll(tmpdir) + Expect(err).NotTo(HaveOccurred()) +}) + +var _ = Describe("Create masquerading rules", func() { + var fromSpecific string + var fromWildcard string + var toDnsName string + var toIpAddress string + + BeforeEach(func() { + fromSpecific = fmt.Sprintf("%s.%s", randomString(10), randomString(5)) + fromWildcard = fmt.Sprintf("*.%s", randomString(8)) + toDnsName = "kubernetes.default.svc.cluster.local" + toIpAddress = fmt.Sprintf("%d.%d.%d.%d", rand.Intn(255), rand.Intn(255), rand.Intn(255), rand.Intn(255)) + }) + + It("should create a rule with specific source and IP target", func() { + mr := &dnsv1alpha1.MasqueradingRule{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + GenerateName: "test-", + }, + Spec: dnsv1alpha1.MasqueradingRuleSpec{ + From: fromSpecific, + To: toIpAddress, + }, + } + err := cli.Create(ctx, mr) + Expect(err).NotTo(HaveOccurred()) + waitForMasqueradingRuleReady(mr) + validateRecord(mr.Spec.From, mr.Spec.To, 0) + }) + + It("should create a rule with specific source and DNS name target", func() { + mr := &dnsv1alpha1.MasqueradingRule{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + GenerateName: "test-", + }, + Spec: dnsv1alpha1.MasqueradingRuleSpec{ + From: fromSpecific, + To: toDnsName, + }, + } + err := cli.Create(ctx, mr) + Expect(err).NotTo(HaveOccurred()) + waitForMasqueradingRuleReady(mr) + validateRecord(mr.Spec.From, mr.Spec.To, 0) + }) + + It("should reject a rule with wildcard source and IP target", func() { + mr := &dnsv1alpha1.MasqueradingRule{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + GenerateName: "test-", + }, + Spec: dnsv1alpha1.MasqueradingRuleSpec{ + From: fromWildcard, + To: toIpAddress, + }, + } + err := cli.Create(ctx, mr) + if !apierrors.IsForbidden(err) { + Expect(err).NotTo(HaveOccurred()) + } + }) + + It("should create a rule with wildcard source and DNS name target", func() { + mr := &dnsv1alpha1.MasqueradingRule{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + GenerateName: "test-", + }, + Spec: dnsv1alpha1.MasqueradingRuleSpec{ + From: fromWildcard, + To: toDnsName, + }, + } + err := cli.Create(ctx, mr) + Expect(err).NotTo(HaveOccurred()) + waitForMasqueradingRuleReady(mr) + validateRecord(mr.Spec.From, mr.Spec.To, 0) + }) +}) + +var _ = Describe("Update masquerading rules", func() { + var fromBefore string + var fromAfter string + var toBefore string + var toAfter string + var masqueradingRule *dnsv1alpha1.MasqueradingRule + + BeforeEach(func() { + fromBefore = fmt.Sprintf("%s.%s", randomString(10), randomString(5)) + fromAfter = fmt.Sprintf("%s.%s", randomString(10), randomString(5)) + toBefore = "kubernetes.default.svc.cluster.local" + toAfter = fmt.Sprintf("%d.%d.%d.%d", rand.Intn(255), rand.Intn(255), rand.Intn(255), rand.Intn(255)) + + masqueradingRule = &dnsv1alpha1.MasqueradingRule{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + GenerateName: "test-", + }, + Spec: dnsv1alpha1.MasqueradingRuleSpec{ + From: fromBefore, + To: toBefore, + }, + } + err := cli.Create(ctx, masqueradingRule) + Expect(err).NotTo(HaveOccurred()) + waitForMasqueradingRuleReady(masqueradingRule) + validateRecord(masqueradingRule.Spec.From, masqueradingRule.Spec.To, 0) + }) + + It("should update the target of a rule", func() { + masqueradingRule.Spec.To = toAfter + err := cli.Update(ctx, masqueradingRule) + Expect(err).NotTo(HaveOccurred()) + waitForMasqueradingRuleReady(masqueradingRule) + validateRecord(masqueradingRule.Spec.From, masqueradingRule.Spec.To, 0) + }) + + It("should update the source of a rule", func() { + masqueradingRule.Spec.From = fromAfter + err := cli.Update(ctx, masqueradingRule) + Expect(err).NotTo(HaveOccurred()) + waitForMasqueradingRuleReady(masqueradingRule) + validateRecord(masqueradingRule.Spec.From, masqueradingRule.Spec.To, 0) + }) +}) + +var _ = Describe("Delete masquerading rules", func() { + var from string + var to string + var masqueradingRule *dnsv1alpha1.MasqueradingRule + + BeforeEach(func() { + from = fmt.Sprintf("%s.%s", randomString(10), randomString(5)) + to = fmt.Sprintf("%d.%d.%d.%d", rand.Intn(255), rand.Intn(255), rand.Intn(255), rand.Intn(255)) + + masqueradingRule = &dnsv1alpha1.MasqueradingRule{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + GenerateName: "test-", + }, + Spec: dnsv1alpha1.MasqueradingRuleSpec{ + From: from, + To: to, + }, + } + err := cli.Create(ctx, masqueradingRule) + Expect(err).NotTo(HaveOccurred()) + waitForMasqueradingRuleReady(masqueradingRule) + validateRecord(masqueradingRule.Spec.From, masqueradingRule.Spec.To, 0) + }) + + It("should delete the rule", func() { + err := cli.Delete(ctx, masqueradingRule) + Expect(err).NotTo(HaveOccurred()) + waitForMasqueradingRuleGone(masqueradingRule) + validateRecord(masqueradingRule.Spec.From, "", 20) + }) +}) + +var _ = Describe("Ingress tests", func() { + var host1 string + var host2 string + var host3 string + + BeforeEach(func() { + host1 = fmt.Sprintf("%s.%s", randomString(10), randomString(5)) + host2 = fmt.Sprintf("%s.%s", randomString(10), randomString(5)) + host3 = fmt.Sprintf("%s.%s", randomString(10), randomString(5)) + }) + + It("should maintain masquerading rules for the ingress", func() { + ingress := &networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + GenerateName: "test-", + Annotations: map[string]string{ + "dns.cs.sap.com/masquerade-to": "kubernetes.default.svc.cluster.local", + }, + }, + Spec: networkingv1.IngressSpec{ + Rules: []networkingv1.IngressRule{ + { + Host: host1, + }, + { + Host: host2, + }, + }, + }, + } + + err := cli.Create(ctx, ingress) + Expect(err).NotTo(HaveOccurred()) + ensureMasqueradingRulesForIngress(ingress) + + err = cli.Get(ctx, types.NamespacedName{Namespace: ingress.Namespace, Name: ingress.Name}, ingress) + Expect(err).NotTo(HaveOccurred()) + ingress.Spec.Rules[1].Host = host3 + err = cli.Update(ctx, ingress) + Expect(err).NotTo(HaveOccurred()) + ensureMasqueradingRulesForIngress(ingress) + + err = cli.Get(ctx, types.NamespacedName{Namespace: ingress.Namespace, Name: ingress.Name}, ingress) + Expect(err).NotTo(HaveOccurred()) + ingress.Spec.Rules = ingress.Spec.Rules[1:] + err = cli.Update(ctx, ingress) + Expect(err).NotTo(HaveOccurred()) + ensureMasqueradingRulesForIngress(ingress) + + err = cli.Get(ctx, types.NamespacedName{Namespace: ingress.Namespace, Name: ingress.Name}, ingress) + Expect(err).NotTo(HaveOccurred()) + err = cli.Delete(ctx, ingress) + Expect(err).NotTo(HaveOccurred()) + ingress.DeletionTimestamp = &[]metav1.Time{metav1.Now()}[0] + ensureMasqueradingRulesForIngress(ingress) + }) + +}) + +func waitForMasqueradingRuleReady(masqueradingRule *dnsv1alpha1.MasqueradingRule) { + Eventually(func() error { + if err := cli.Get(ctx, types.NamespacedName{Namespace: masqueradingRule.Namespace, Name: masqueradingRule.Name}, masqueradingRule); err != nil { + return err + } + if masqueradingRule.Status.ObservedGeneration != masqueradingRule.Generation || masqueradingRule.Status.State != dnsv1alpha1.MasqueradingRuleStateReady { + return fmt.Errorf("again") + } + return nil + }, "120s", "500ms").Should(Succeed()) +} + +func waitForMasqueradingRuleGone(masqueradingRule *dnsv1alpha1.MasqueradingRule) { + Eventually(func() error { + err := cli.Get(ctx, types.NamespacedName{Namespace: masqueradingRule.Namespace, Name: masqueradingRule.Name}, masqueradingRule) + if client.IgnoreNotFound(err) != nil { + return err + } + if err == nil { + return fmt.Errorf("again") + } + return nil + }, "120s", "500ms").Should(Succeed()) +} + +func validateRecord(from string, to string, timeout int) { + from = regexp.MustCompile(`^\*(.*)$`).ReplaceAllString(from, `wildcard$1`) + if timeout == 0 { + active, err := resolver.CheckRecord(ctx, from, to) + Expect(err).Error().NotTo(HaveOccurred()) + Expect(active).To(BeTrue()) + } else { + Eventually(func() error { + active, err := resolver.CheckRecord(ctx, from, to) + if err != nil { + return err + } + if !active { + return fmt.Errorf("again") + } + return nil + }, timeout, "500ms") + } +} + +func ensureMasqueradingRulesForIngress(ingress *networkingv1.Ingress) { + Eventually(func(g Gomega) { + masqueradingRuleList := &dnsv1alpha1.MasqueradingRuleList{} + err := cli.List(ctx, masqueradingRuleList, client.MatchingLabels{"dns.cs.sap.com/controller-uid": string(ingress.UID)}) + g.Expect(err).NotTo(HaveOccurred()) + if to, ok := ingress.Annotations["dns.cs.sap.com/masquerade-to"]; ok && ingress.DeletionTimestamp.IsZero() { + g.Expect(masqueradingRuleList.Items).To(HaveLen(len(ingress.Spec.Rules))) + for _, masqueradingRule := range masqueradingRuleList.Items { + g.Expect(masqueradingRule.Spec.From).To(BeElementOf(slices.Collect(ingress.Spec.Rules, func(rule networkingv1.IngressRule) string { return rule.Host }))) + g.Expect(masqueradingRule.Spec.To).To(Equal(to)) + } + } else { + g.Expect(masqueradingRuleList.Items).To(BeEmpty()) + } + }, "120s", "500ms").Should(Succeed()) +} + +// create namespace with a generated unique name +func createNamespace() (string, error) { + namespace := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{GenerateName: "test-"}} + if err := cli.Create(ctx, namespace); err != nil { + return "", err + } + return namespace.Name, nil +} + +// assemble validatingwebhookconfiguration descriptor +func buildValidatingWebhookConfiguration() *admissionv1.ValidatingWebhookConfiguration { + return &admissionv1.ValidatingWebhookConfiguration{ + ObjectMeta: metav1.ObjectMeta{ + Name: "validate-masqueradingrule", + }, + Webhooks: []admissionv1.ValidatingWebhook{{ + Name: "validate-masqueradingrule.test.local", + AdmissionReviewVersions: []string{"v1"}, + ClientConfig: admissionv1.WebhookClientConfig{ + Service: &admissionv1.ServiceReference{ + Path: &[]string{fmt.Sprintf("/validate-%s-%s-%s", strings.ReplaceAll(dnsv1alpha1.GroupVersion.Group, ".", "-"), dnsv1alpha1.GroupVersion.Version, "masqueradingrule")}[0], + }, + }, + Rules: []admissionv1.RuleWithOperations{{ + Operations: []admissionv1.OperationType{ + admissionv1.Create, + admissionv1.Update, + admissionv1.Delete, + }, + Rule: admissionv1.Rule{ + APIGroups: []string{dnsv1alpha1.GroupVersion.Group}, + APIVersions: []string{dnsv1alpha1.GroupVersion.Version}, + Resources: []string{"masqueradingrules"}, + }, + }}, + SideEffects: &[]admissionv1.SideEffectClass{admissionv1.SideEffectClassNone}[0], + }}, + } +} + +// convert rest.Config into kubeconfig +func kubeConfigFromRestConfig(restConfig *rest.Config) *clientcmdapi.Config { + apiConfig := clientcmdapi.NewConfig() + + apiConfig.Clusters["envtest"] = clientcmdapi.NewCluster() + cluster := apiConfig.Clusters["envtest"] + cluster.Server = restConfig.Host + cluster.CertificateAuthorityData = restConfig.CAData + + apiConfig.AuthInfos["envtest"] = clientcmdapi.NewAuthInfo() + authInfo := apiConfig.AuthInfos["envtest"] + authInfo.ClientKeyData = restConfig.KeyData + authInfo.ClientCertificateData = restConfig.CertData + + apiConfig.Contexts["envtest"] = clientcmdapi.NewContext() + context := apiConfig.Contexts["envtest"] + context.Cluster = "envtest" + context.AuthInfo = "envtest" + + apiConfig.CurrentContext = "envtest" + + return apiConfig +} + +// get free port +func getFreePort(address string, tcp bool, udp bool) (uint16, error) { + for i := 0; i < 10; i++ { + port := 0 + if tcp { + addr, err := net.ResolveTCPAddr("tcp", net.JoinHostPort(address, strconv.Itoa(port))) + if err != nil { + return 0, err + } + listener, err := net.ListenTCP("tcp", addr) + listener.Close() + if err != nil { + continue + } + port = listener.Addr().(*net.TCPAddr).Port + } + if udp { + addr, err := net.ResolveUDPAddr("udp", net.JoinHostPort(address, strconv.Itoa(port))) + if err != nil { + return 0, err + } + connection, err := net.ListenUDP("udp", addr) + connection.Close() + if err != nil { + continue + } + port = connection.LocalAddr().(*net.UDPAddr).Port + } + if port != 0 { + // TODO: the following cast is potentially unsafe (however no port numbers outside the 0-65535 range should occur) + return uint16(port), nil + } + } + return 0, fmt.Errorf("unable to find free port") +} + +/* +// get system DNS resolver; not used, as we use 8.8.8.8 as forwarder +func getSystemResolver() (string, error) { + cfg, err := dns.ClientConfigFromFile("/etc/resolv.conf") + if err != nil { + return "", err + } + for _, server := range cfg.Servers { + ip := net.ParseIP(server) + if ip == nil || ip.To4() == nil { + continue + } + return server, nil + } + return "", fmt.Errorf("no usable system resolver found") +} +*/ + +// create random lowercase character string of given length +func randomString(n int) string { + charset := []byte("abcdefghijklmnopqrstuvwxyz") + b := make([]byte, n) + for i := range b { + b[i] = charset[rand.Intn(len(charset))] + } + return string(b) +} diff --git a/internal/coredns/record.go b/internal/coredns/record.go deleted file mode 100644 index 41a1947..0000000 --- a/internal/coredns/record.go +++ /dev/null @@ -1,166 +0,0 @@ -/* -SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and dns-masquerading-operator contributors -SPDX-License-Identifier: Apache-2.0 -*/ - -package coredns - -import ( - "context" - "fmt" - "strings" - - "github.com/hashicorp/go-multierror" - "github.com/sap/go-generics/pairs" - "github.com/sap/go-generics/slices" - - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/rest" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - - "github.com/sap/dns-masquerading-operator/internal/dns" - "github.com/sap/dns-masquerading-operator/internal/portforward" -) - -type Endpoint struct { - Namespace string - Name string - Address string - Port int32 -} - -// Check that specified host and expectedHost lead to the same DNS resolution result; -// the check is executed in parallel for all authoritative coredns pods found in the cluster. -func CheckRecord(ctx context.Context, c client.Client, cfg *rest.Config, host string, expectedHost string, inCluster bool) (bool, error) { - log := ctrl.LoggerFrom(ctx) - - endpoints, err := discoverEndpoints(ctx, c) - if err != nil { - return false, err - } - - results := make([]chan *pairs.Pair[bool, error], len(endpoints)) - for i := 0; i < len(endpoints); i++ { - results[i] = make(chan *pairs.Pair[bool, error], 1) - go func(i int) { - if inCluster { - log.V(1).Info("starting in-cluster lookup", "host", host, "serverAddress", endpoints[i].Address, "serverPort", endpoints[i].Port) - var merr error - addresses, err := dns.Lookup(host, endpoints[i].Address, endpoints[i].Port) - if err != nil { - merr = multierror.Append(merr, err) - } - expectedAddresses, err := dns.Lookup(expectedHost, endpoints[i].Address, endpoints[i].Port) - if err != nil { - merr = multierror.Append(merr, err) - } - results[i] <- pairs.New(merr == nil && len(addresses) > 0 && slices.Equal(addresses, expectedAddresses), merr) - } else { - log.V(1).Info("starting out-of-cluster lookup", "host", host, "serverNamespace", endpoints[i].Namespace, "serverName", endpoints[i].Name, "serverPort", endpoints[i].Port) - localhost := "127.0.0.1" - localport := int32(10000 + i) - portforward := portforward.New(cfg, localhost, localport, endpoints[i].Namespace, endpoints[i].Name, endpoints[i].Port) - if err := portforward.Start(); err != nil { - results[i] <- pairs.New(false, err) - return - } - defer portforward.Stop() - var merr error - addresses, err := dns.Lookup(host, localhost, localport) - if err != nil { - merr = multierror.Append(merr, err) - } - expectedAddresses, err := dns.Lookup(expectedHost, localhost, localport) - if err != nil { - merr = multierror.Append(merr, err) - } - results[i] <- pairs.New(merr == nil && len(addresses) > 0 && slices.Equal(addresses, expectedAddresses), merr) - } - }(i) - } - - var merr error - var active bool = true - for _, result := range results { - p := <-result - if p.Y != nil { - active = false - merr = multierror.Append(merr, p.Y) - continue - } - if !p.X { - active = false - } - } - - return active, merr -} - -// Discover (tcp) endpoints of all authoritative coredns pods found in the cluster. -func discoverEndpoints(ctx context.Context, c client.Client) ([]*Endpoint, error) { - // TODO: parameterize things - namespace := "kube-system" // same as corednsConfigMapNamespace, actually ... - serviceName := "kube-dns" - - var portName string - var targetPort int32 - - service := &corev1.Service{} - if err := c.Get(ctx, types.NamespacedName{Namespace: namespace, Name: serviceName}, service); err != nil { - return nil, err - } - for _, servicePort := range service.Spec.Ports { - if servicePort.Protocol == corev1.ProtocolTCP && servicePort.Port == 53 { - portName = servicePort.Name - targetPort = servicePort.TargetPort.IntVal - break - } - } - if portName == "" { - return nil, fmt.Errorf("service %s does not have port tcp/53", serviceName) - } - - var endpoints []*Endpoint - - if fakeEndpoints, ok := service.Annotations["testing.cs.sap.com/fake-endpoints"]; ok { - // This is for testing only, to allow loopback addresses as endpoints (which is otherwise rejected by the endpoints api) - for _, address := range strings.Split(fakeEndpoints, ",") { - endpoint := &Endpoint{ - Address: address, - Port: targetPort, - } - endpoints = append(endpoints, endpoint) - } - } else { - serviceEndpoints := &corev1.Endpoints{} - if err := c.Get(ctx, types.NamespacedName{Namespace: namespace, Name: serviceName}, serviceEndpoints); err != nil { - return nil, err - } - for _, subset := range serviceEndpoints.Subsets { - var port int32 - for _, endpointPort := range subset.Ports { - if endpointPort.Name == portName { - port = endpointPort.Port - break - } - } - if port == 0 { - continue - } - for _, address := range subset.Addresses { - endpoint := &Endpoint{ - Namespace: address.TargetRef.Namespace, - Name: address.TargetRef.Name, - Address: address.IP, - Port: port, - } - endpoints = append(endpoints, endpoint) - } - } - } - - // TODO: are endpoints unique by definition ? - return endpoints, nil -} diff --git a/internal/coredns/resolver.go b/internal/coredns/resolver.go new file mode 100644 index 0000000..510a1b2 --- /dev/null +++ b/internal/coredns/resolver.go @@ -0,0 +1,205 @@ +/* +SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and dns-masquerading-operator contributors +SPDX-License-Identifier: Apache-2.0 +*/ + +package coredns + +import ( + "context" + "fmt" + + "github.com/hashicorp/go-multierror" + "github.com/sap/go-generics/pairs" + "github.com/sap/go-generics/slices" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/rest" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/sap/dns-masquerading-operator/internal/dnsutil" + "github.com/sap/dns-masquerading-operator/internal/portforward" +) + +// Resolver interface +type Resolver interface { + // Check that the DNS resolution of host and expectedResult return the same address(es); + // host must be a real DNS name, and must not be a wildcard name; + // expectedResult may be a DNS name, an IP address, or empty, which means that the resolution of host + // should not return any results, in order to make the check successful; + // the boolean return value indicates success or failure of the check, the error return value + // should be used to raise technical errors while performing the DNS resolution. + CheckRecord(ctx context.Context, host string, expectedResult string) (bool, error) +} + +// Endpoint representation for a namesever to be used be the resolver; +// Address and Port are mandatory; InCluster has to be set to true if the nameserver is runnning +// as a pod inside the cluster; in that case, Address and Port must point to that pod, and +// also Namespace and Name are required, referring to the according pod. +// If Incluster is false, Namespace and Name have no meaning and can be omitted. +type Endpoint struct { + Address string + Port uint16 + InCluster bool + Namespace string + Name string +} + +type resolver struct { + client client.Client + restConfig *rest.Config + inCluster bool + endpoints []Endpoint +} + +// Create new default resolver; the inCluster parameter has to be set to true if this operator is running inside the target cluster; +// if at least one endpoint is supplied, the specified endpoint(s) will be used for DNS queries; +// otherwise, the pod endpoints of the kube-system/kube-dns service will be used. +func NewResolver(client client.Client, restConfig *rest.Config, inCluster bool, endpoints ...Endpoint) Resolver { + return &resolver{ + client: client, + restConfig: restConfig, + inCluster: inCluster, + endpoints: endpoints, + } +} + +// Check record (see Resolver interface) +func (r *resolver) CheckRecord(ctx context.Context, host string, expectedResult string) (bool, error) { + log := ctrl.LoggerFrom(ctx) + + endpoints := r.endpoints + if len(endpoints) == 0 { + clusterEndpoints, err := discoverEndpoints(ctx, r.client) + if err != nil { + return false, err + } + endpoints = clusterEndpoints + } + + results := make([]chan *pairs.Pair[bool, error], len(endpoints)) + for i := 0; i < len(endpoints); i++ { + results[i] = make(chan *pairs.Pair[bool, error], 1) + go func(i int) { + if endpoints[i].InCluster && !r.inCluster { + log.V(1).Info("starting out-of-cluster lookup", "host", host, "serverNamespace", endpoints[i].Namespace, "serverName", endpoints[i].Name, "serverPort", endpoints[i].Port) + localhost := "127.0.0.1" + portforward := portforward.New(r.restConfig, localhost, 0, endpoints[i].Namespace, endpoints[i].Name, endpoints[i].Port) + if err := portforward.Start(); err != nil { + results[i] <- pairs.New(false, err) + return + } + defer portforward.Stop() + localport := portforward.LocalPort() + var merr error + addresses, err := dnsutil.Lookup(host, localhost, localport) + if err != nil { + merr = multierror.Append(merr, err) + } + if expectedResult == "" { + results[i] <- pairs.New(merr == nil && len(addresses) == 0, merr) + } else { + expectedAddresses, err := dnsutil.Lookup(expectedResult, localhost, localport) + if err != nil { + merr = multierror.Append(merr, err) + } + results[i] <- pairs.New(merr == nil && len(addresses) > 0 && slices.Equal(addresses, expectedAddresses), merr) + } + } else { + log.V(1).Info("starting lookup", "host", host, "serverAddress", endpoints[i].Address, "serverPort", endpoints[i].Port) + var merr error + addresses, err := dnsutil.Lookup(host, endpoints[i].Address, endpoints[i].Port) + if err != nil { + merr = multierror.Append(merr, err) + } + if expectedResult == "" { + results[i] <- pairs.New(merr == nil && len(addresses) == 0, merr) + } else { + expectedAddresses, err := dnsutil.Lookup(expectedResult, endpoints[i].Address, endpoints[i].Port) + if err != nil { + merr = multierror.Append(merr, err) + } + results[i] <- pairs.New(merr == nil && len(addresses) > 0 && slices.Equal(addresses, expectedAddresses), merr) + } + } + }(i) + } + + var merr error + var active bool = true + for _, result := range results { + p := <-result + if p.Y != nil { + active = false + merr = multierror.Append(merr, p.Y) + continue + } + if !p.X { + active = false + } + } + + return active, merr +} + +// discover endpoints of the kube-system/kube-dns service in target cluster +func discoverEndpoints(ctx context.Context, client client.Client) ([]Endpoint, error) { + // TODO: parameterize things + namespace := "kube-system" // same as corednsConfigMapNamespace, actually ... + serviceName := "kube-dns" + + var portName string + + service := &corev1.Service{} + if err := client.Get(ctx, types.NamespacedName{Namespace: namespace, Name: serviceName}, service); err != nil { + return nil, err + } + for _, servicePort := range service.Spec.Ports { + if servicePort.Protocol == corev1.ProtocolTCP && servicePort.Port == 53 { + portName = servicePort.Name + break + } + } + if portName == "" { + return nil, fmt.Errorf("service %s does not have port tcp/53", serviceName) + } + + var endpoints []Endpoint + + serviceEndpoints := &corev1.Endpoints{} + if err := client.Get(ctx, types.NamespacedName{Namespace: namespace, Name: serviceName}, serviceEndpoints); err != nil { + return nil, err + } + + for _, subset := range serviceEndpoints.Subsets { + var port uint16 + for _, endpointPort := range subset.Ports { + if endpointPort.Name == portName { + // TODO: the following cast is potentially unsafe (however no port numbers outside the 0-65535 range should occur) + port = uint16(endpointPort.Port) + break + } + } + if port == 0 { + continue + } + for _, address := range subset.Addresses { + if address.TargetRef.Kind != "Pod" { + continue + } + endpoint := Endpoint{ + Address: address.IP, + Port: port, + InCluster: true, + Namespace: address.TargetRef.Namespace, + Name: address.TargetRef.Name, + } + endpoints = append(endpoints, endpoint) + } + } + + // TODO: are endpoints unique by definition ? + return endpoints, nil +} diff --git a/internal/coredns/rewrite.go b/internal/coredns/rewrite.go index 5edf720..30f9cf6 100644 --- a/internal/coredns/rewrite.go +++ b/internal/coredns/rewrite.go @@ -7,33 +7,93 @@ package coredns import ( "fmt" + "net" "regexp" "strings" + "github.com/sap/dns-masquerading-operator/internal/dnsutil" "github.com/sap/go-generics/maps" "github.com/sap/go-generics/slices" - - "github.com/sap/dns-masquerading-operator/internal/netutil" ) +// Rewrite rule (usually derived from a MasqueradingRule object) type RewriteRule struct { - Owner string - From string - To string + owner string + from string + to string +} + +// Create new RewriteRule object (and validate input) +func NewRewriteRule(owner string, from string, to string) (*RewriteRule, error) { + if err := dnsutil.CheckDnsName(from, false, true); err != nil { + return nil, err + } + if net.ParseIP(to) == nil { + if err := dnsutil.CheckDnsName(to, false, false); err != nil { + return nil, err + } + } else { + if strings.Split(from, ".")[0] == "*" { + return nil, fmt.Errorf("error validating rewrite rule: source must not be a wildcard DNS name if target is an IP address") + } + } + return &RewriteRule{owner: owner, from: from, to: to}, nil +} + +// Return owner of a RewriteRule +func (r *RewriteRule) Owner() string { + return r.owner +} + +// Return rewrite source (from) of a RewriteRule +func (r *RewriteRule) From() string { + return r.from +} + +// Return rewrite target (to) of a RewriteRule +func (r *RewriteRule) To() string { + return r.to +} + +// Check if RewriteRule matches given DNS name; that is, if the rewrite rule's source +// is a wildcard DNS name, it is checked whether that wildcard name matches host +// (note that in that case, host may be a - less specific - wildcard pattern itself); +// otherwise, just check for equality of the rewrite rule's source and host. +func (r *RewriteRule) Matches(host string) bool { + if strings.Split(r.from, ".")[0] == "*" { + return strings.HasSuffix(host, r.from[1:]) + } else { + return host == r.from + } +} + +// check if rewrite rule source is a wildcard DNS name +func (r *RewriteRule) fromIsWildcard() bool { + return strings.Split(r.from, ".")[0] == "*" +} + +// check if rewrite rule target is an IP address +func (r *RewriteRule) toIsIpaddress() bool { + return net.ParseIP(r.to) != nil } +// Set of RewriteRule type RewriteRuleSet struct { - rulesByFrom map[string]*RewriteRule rulesByOwner map[string]*RewriteRule } +// Create empty RewriteRuleSet; RewriteRuleSet gives the following guarantees: +// - uniquness of owners, that is, for a given owner, the set contains +// at most one RewriteRule with that owner +// - rewrite sources in the set are free of clashes; that is, for a given DNS name, +// there will be at most one RewriteRule matching that DNS name (via Matches()). func NewRewriteRuleSet() *RewriteRuleSet { return &RewriteRuleSet{ - rulesByFrom: make(map[string]*RewriteRule), rulesByOwner: make(map[string]*RewriteRule), } } +// Parse RewriteRuleSet from a coredns config file format func ParseRewriteRuleSet(s string) (*RewriteRuleSet, error) { rs := NewRewriteRuleSet() if s == "" { @@ -42,13 +102,13 @@ func ParseRewriteRuleSet(s string) (*RewriteRuleSet, error) { lines := strings.Split(s, "\n") have_hosts := false for i := 0; i < len(lines); i++ { - if lines[i] == "hosts {" && !have_hosts { + if lines[i] == "hosts /dev/null {" && !have_hosts { have_hosts = true continue } - if lines[i] == " fallthrough" && i+1 < len(lines) && lines[i+1] == "}" { + if i+2 < len(lines) && lines[i] == " ttl 10" && lines[i+1] == " fallthrough" && lines[i+2] == "}" { have_hosts = false - i++ + i += 2 continue } owner := "" @@ -90,107 +150,102 @@ func ParseRewriteRuleSet(s string) (*RewriteRuleSet, error) { return nil, fmt.Errorf("error parsing rewrite rules (at line %d)", i+1) } } - if err := rs.AddRule(RewriteRule{Owner: owner, From: from, To: to}); err != nil { + r, err := NewRewriteRule(owner, from, to) + if err != nil { + return nil, err + } + if _, err := rs.AddRule(r); err != nil { return nil, err } } return rs, nil } +// Get RewriteRule for specified owner; return nil if none was found; +// otherwise, the result is unique because of the guarantees given by RewriteRuleSet. func (rs *RewriteRuleSet) GetRule(owner string) *RewriteRule { - if s, ok := rs.rulesByOwner[owner]; ok { - return &RewriteRule{ - Owner: s.Owner, - From: s.From, - To: s.To, - } + if r, ok := rs.rulesByOwner[owner]; ok { + return r } return nil } -func (rs *RewriteRuleSet) AddRule(r RewriteRule) error { - if err := netutil.CheckDnsName(r.From, true); err != nil { - return fmt.Errorf("error adding rewrite rule %s:%s (%s); invalid source: %s", r.From, r.To, r.Owner, err) - } - if netutil.IsIpAddress(r.To) { - if netutil.IsWildcardDnsName(r.From) { - return fmt.Errorf("error adding rewrite rule %s:%s (%s); wildcards are not allowed with IP address targets", r.From, r.To, r.Owner) - } - } else { - if err := netutil.CheckDnsName(r.To, false); err != nil { - return fmt.Errorf("error adding rewrite rule %s:%s (%s); invalid target: %s", r.From, r.To, r.Owner, err) +// Find RewriteRule matching given DNS name; return nil if none was found; +// otherwise, the result is unique because of the guarantees given by RewriteRuleSet. +func (rs *RewriteRuleSet) FindMatchingRule(host string) *RewriteRule { + for _, s := range rs.rulesByOwner { + if s.Matches(host) { + return s } } - if s, ok := rs.rulesByFrom[r.From]; ok { - // a rule with r.From exists - if s.Owner == r.Owner { - // and that rule's owner matches r.Owner - s.To = r.To - } else { - // and that rule's owner does not match r.Owner (i.e. duplicate rule) - return fmt.Errorf("error adding rewrite rule %s:%s (%s); conflicts with rule %s:%s (%s)", r.From, r.To, r.Owner, s.From, s.To, s.Owner) - } - } else { - // no rule with r.From exists - if s, ok := rs.rulesByOwner[r.Owner]; ok { - // and a rule with r.Owner exists - delete(rs.rulesByFrom, s.From) - s.From = r.From - s.To = r.To - rs.rulesByFrom[s.From] = s - } else { - // and no rule with r.Owner exists - rs.rulesByFrom[r.From] = &r - rs.rulesByOwner[r.Owner] = &r + return nil +} + +// Add RewriteRule to set; may fail if the given rule would violate the consistency guarantees of the RewriteRuleSet; +// the boolean return value indicates whether something changed in the set (true) or if the rule was already there (false). +func (rs *RewriteRuleSet) AddRule(r *RewriteRule) (bool, error) { + var s *RewriteRule + for _, t := range rs.rulesByOwner { + if t.owner != r.owner && (t.Matches(r.from) || r.Matches(t.from)) { + s = t + break } } - return nil + if s != nil { + return false, fmt.Errorf("error adding rewrite rule %s:%s (%s); conflicts with rule %s:%s (%s)", r.from, r.to, r.owner, s.from, s.to, s.owner) + } + s = rs.rulesByOwner[r.owner] + changed := s == nil || r.from != s.from || r.to != s.to + rs.rulesByOwner[r.owner] = r + return changed, nil } -func (rs *RewriteRuleSet) RemoveRule(owner string) error { - if s, ok := rs.rulesByOwner[owner]; ok { - delete(rs.rulesByFrom, s.From) +// Remove rule with given owner from set; +// the boolean return value indicates whether something changed in the set (true) or if no rule with that owner was existing (false). +func (rs *RewriteRuleSet) RemoveRule(owner string) bool { + if _, ok := rs.rulesByOwner[owner]; ok { delete(rs.rulesByOwner, owner) - } else { - return fmt.Errorf("error deleting rewrite rule with owner %s; no rule found with that owner", owner) + return true } - return nil + return false } +// Serialize RewriteRuleSet into coredns config file format func (rs *RewriteRuleSet) String() string { - lines := make([]string, 0, 4*len(rs.rulesByFrom)+3) + lines := make([]string, 0, 4*len(rs.rulesByOwner)+3) haveHosts := false for _, o := range slices.Sort(maps.Keys(rs.rulesByOwner)) { r := rs.rulesByOwner[o] - if !netutil.IsIpAddress(r.To) { + if !r.toIsIpaddress() { continue } if !haveHosts { haveHosts = true - lines = append(lines, "hosts {") + lines = append(lines, "hosts /dev/null {") } - lines = append(lines, fmt.Sprintf(" # owner: %s", r.Owner)) - lines = append(lines, fmt.Sprintf(" # from: %s", r.From)) - lines = append(lines, fmt.Sprintf(" # to: %s", r.To)) - lines = append(lines, fmt.Sprintf(" %s %s", r.To, r.From)) + lines = append(lines, fmt.Sprintf(" # owner: %s", r.owner)) + lines = append(lines, fmt.Sprintf(" # from: %s", r.from)) + lines = append(lines, fmt.Sprintf(" # to: %s", r.to)) + lines = append(lines, fmt.Sprintf(" %s %s", r.to, r.from)) } if haveHosts { haveHosts = false + lines = append(lines, " ttl 10") lines = append(lines, " fallthrough") lines = append(lines, "}") } for _, o := range slices.Sort(maps.Keys(rs.rulesByOwner)) { r := rs.rulesByOwner[o] - if netutil.IsIpAddress(r.To) { + if r.toIsIpaddress() { continue } - lines = append(lines, fmt.Sprintf("# owner: %s", r.Owner)) - lines = append(lines, fmt.Sprintf("# from: %s", r.From)) - lines = append(lines, fmt.Sprintf("# to: %s", r.To)) - if netutil.IsWildcardDnsName(r.From) { - lines = append(lines, fmt.Sprintf("rewrite name regex %s %s", strings.ReplaceAll(strings.ReplaceAll(r.From, `.`, `\.`), `*`, `.*`), r.To)) + lines = append(lines, fmt.Sprintf("# owner: %s", r.owner)) + lines = append(lines, fmt.Sprintf("# from: %s", r.from)) + lines = append(lines, fmt.Sprintf("# to: %s", r.to)) + if r.fromIsWildcard() { + lines = append(lines, fmt.Sprintf("rewrite name regex %s %s", strings.ReplaceAll(strings.ReplaceAll(r.from, `.`, `\.`), `*`, `.*`), r.to)) } else { - lines = append(lines, fmt.Sprintf("rewrite name exact %s %s", r.From, r.To)) + lines = append(lines, fmt.Sprintf("rewrite name exact %s %s", r.from, r.to)) } } return strings.Join(lines, "\n") diff --git a/internal/coredns/rewrite_test.go b/internal/coredns/rewrite_test.go index 33e0bec..3ef3335 100644 --- a/internal/coredns/rewrite_test.go +++ b/internal/coredns/rewrite_test.go @@ -12,38 +12,7 @@ import ( "testing" ) -func checkRuleSetConsistency(rs *RewriteRuleSet) error { - /* - assumptions: - for all keys rulesByOwner (owner) - rulesByOwner[owner].Owner == owner - rulesByFrom[rulesByOwner[owner].From] == rulesByOwner[owner] - for all keys rulesByFrom (from) - rulesByFrom[from].From == from - rulesByOwner[rulesByFrom[from].Owner] == rulesByFrom[from] - consequences: - values rulesByOwner == values rulesByFrom := values - keys rulesByOwner == values.collect(.Owner) - keys rulesByFrom == values.collect(.From) - */ - for o, r := range rs.rulesByOwner { - if r.Owner != o { - return fmt.Errorf("ruleset inconsistent (1)") - } - if rs.rulesByFrom[r.From] != r { - return fmt.Errorf("ruleset inconsistent (2)") - } - } - for f, r := range rs.rulesByFrom { - if r.From != f { - return fmt.Errorf("ruleset inconsistent (3)") - } - if rs.rulesByOwner[r.Owner] != r { - return fmt.Errorf("ruleset inconsistent (4)") - } - } - return nil -} +// TODO: add tests for NewRewriteRule and RewriteRule methods const ( owner1 = "owner1" @@ -55,6 +24,8 @@ const ( from2 = "from2.example.io" from3 = "*.other.io" from4 = "from4.example.io" + from7 = "*.example.io" + from8 = "from10.example.io" from9 = "from9.other.io" to1 = "to1.example.io" to2 = "to2.example.io" @@ -63,14 +34,35 @@ const ( to9 = "to9.example.io" ) +func mustNewRewriteRule(owner string, from string, to string) *RewriteRule { + r, err := NewRewriteRule(owner, from, to) + if err != nil { + panic(err) + } + return r +} + +func checkRuleSetConsistency(rs *RewriteRuleSet) error { + for o, r := range rs.rulesByOwner { + if r.owner != o { + return fmt.Errorf("ruleset inconsistent (1)") + } + for _, s := range rs.rulesByOwner { + if (s.Matches(r.from) || r.Matches(s.from)) && s.owner != o { + return fmt.Errorf("ruleset inconsistent (2)") + } + } + } + return nil +} + func createSampleRuleSet() *RewriteRuleSet { - r1 := &RewriteRule{Owner: owner1, From: from1, To: to1} - r2 := &RewriteRule{Owner: owner2, From: from2, To: to2} - r3 := &RewriteRule{Owner: owner3, From: from3, To: to3} - r4 := &RewriteRule{Owner: owner4, From: from4, To: to4} + r1 := mustNewRewriteRule(owner1, from1, to1) + r2 := mustNewRewriteRule(owner2, from2, to2) + r3 := mustNewRewriteRule(owner3, from3, to3) + r4 := mustNewRewriteRule(owner4, from4, to4) rs := &RewriteRuleSet{ - rulesByFrom: map[string]*RewriteRule{from1: r1, from2: r2, from3: r3, from4: r4}, rulesByOwner: map[string]*RewriteRule{owner1: r1, owner2: r2, owner3: r3, owner4: r4}, } @@ -82,7 +74,7 @@ func createSampleRuleSet() *RewriteRuleSet { } func createSampleRuleSetString() string { - return fmt.Sprintf("hosts {\n # owner: %[11]s\n # from: %[12]s\n # to: %[13]s\n %[13]s %[12]s\n fallthrough\n}\n# owner: %[1]s\n# from: %[2]s\n# to: %[3]s\nrewrite name exact %[2]s %[3]s\n# owner: %[4]s\n# from: %[5]s\n# to: %[6]s\nrewrite name exact %[5]s %[6]s\n# owner: %[7]s\n# from: %[8]s\n# to: %[9]s\nrewrite name regex %[10]s %[9]s", + return fmt.Sprintf("hosts /dev/null {\n # owner: %[11]s\n # from: %[12]s\n # to: %[13]s\n %[13]s %[12]s\n ttl 10\n fallthrough\n}\n# owner: %[1]s\n# from: %[2]s\n# to: %[3]s\nrewrite name exact %[2]s %[3]s\n# owner: %[4]s\n# from: %[5]s\n# to: %[6]s\nrewrite name exact %[5]s %[6]s\n# owner: %[7]s\n# from: %[8]s\n# to: %[9]s\nrewrite name regex %[10]s %[9]s", owner1, from1, to1, @@ -106,7 +98,7 @@ func TestGetRule1(t *testing.T) { if r == nil { t.Fatalf("%s: unable to get existing rule", testName) } - if !reflect.DeepEqual(r, &RewriteRule{Owner: owner2, From: from2, To: to2}) { + if !reflect.DeepEqual(r, &RewriteRule{owner: owner2, from: from2, to: to2}) { t.Fatalf("%s: got unexpected rule", testName) } } @@ -123,29 +115,37 @@ func TestGetRule2(t *testing.T) { func TestAddRule1(t *testing.T) { testName := "add identical rule" rs := createSampleRuleSet() - if err := rs.AddRule(RewriteRule{Owner: owner1, From: from1, To: to1}); err != nil { + changed, err := rs.AddRule(mustNewRewriteRule(owner1, from1, to1)) + if err != nil { t.Fatalf("%s: got unexpected error: %s", testName, err) } if err := checkRuleSetConsistency(rs); err != nil { t.Fatalf("%s: %s", testName, err) } + if changed { + t.Errorf("%s: ruleset change indicated although there was none", testName) + } rsexp := createSampleRuleSet() if !reflect.DeepEqual(rs, rsexp) { - t.Errorf("%s: add identical rule: ruleset changed although it shouldn't be", testName) + t.Errorf("%s: unexpected ruleset", testName) } } func TestAddRule2(t *testing.T) { testName := "add rule with existing owner and same from" rs := createSampleRuleSet() - if err := rs.AddRule(RewriteRule{Owner: owner1, From: from1, To: to9}); err != nil { + changed, err := rs.AddRule(mustNewRewriteRule(owner1, from1, to9)) + if err != nil { t.Fatalf("%s: got unexpected error: %s", testName, err) } if err := checkRuleSetConsistency(rs); err != nil { t.Fatalf("%s: %s", testName, err) } + if !changed { + t.Errorf("%s: no ruleset change indicated although there was one", testName) + } rsexp := createSampleRuleSet() - rsexp.rulesByOwner[owner1].To = to9 + rsexp.rulesByOwner[owner1].to = to9 if !reflect.DeepEqual(rs, rsexp) { t.Errorf("%s: unexpected ruleset", testName) } @@ -154,26 +154,28 @@ func TestAddRule2(t *testing.T) { func TestAddRule3(t *testing.T) { testName := "add rule with existing owner and new from" rs := createSampleRuleSet() - if err := rs.AddRule(RewriteRule{Owner: owner1, From: from9, To: to9}); err != nil { + changed, err := rs.AddRule(mustNewRewriteRule(owner1, from8, to9)) + if err != nil { t.Fatalf("%s: got unexpected error: %s", testName, err) } if err := checkRuleSetConsistency(rs); err != nil { t.Fatalf("%s: %s", testName, err) } + if !changed { + t.Errorf("%s: no ruleset change indicated although there was one", testName) + } rsexp := createSampleRuleSet() - rsexp.rulesByOwner[owner1].From = from9 - rsexp.rulesByOwner[owner1].To = to9 - rsexp.rulesByFrom[from9] = rsexp.rulesByOwner[owner1] - delete(rsexp.rulesByFrom, from1) + rsexp.rulesByOwner[owner1].from = from8 + rsexp.rulesByOwner[owner1].to = to9 if !reflect.DeepEqual(rs, rsexp) { t.Errorf("%s: unexpected ruleset", testName) } } func TestAddRule4(t *testing.T) { - testName := "add rule with existing owner and conflicting from" + testName := "add rule with existing owner and conflicting from (1)" rs := createSampleRuleSet() - if err := rs.AddRule(RewriteRule{Owner: owner1, From: from2, To: to9}); err == nil { + if _, err := rs.AddRule(mustNewRewriteRule(owner1, from2, to9)); err == nil { t.Fatalf("%s: got unexpected success", testName) } else { t.Logf("%s: got error: %s", testName, err) @@ -181,26 +183,69 @@ func TestAddRule4(t *testing.T) { } func TestAddRule5(t *testing.T) { + testName := "add rule with existing owner and conflicting from (2)" + rs := createSampleRuleSet() + if _, err := rs.AddRule(mustNewRewriteRule(owner1, from9, to9)); err == nil { + t.Fatalf("%s: got unexpected success", testName) + } else { + t.Logf("%s: got error: %s", testName, err) + } +} + +func TestAddRule6(t *testing.T) { + testName := "add rule with existing owner and conflicting from (3)" + rs := createSampleRuleSet() + if _, err := rs.AddRule(mustNewRewriteRule(owner1, from7, to9)); err == nil { + t.Fatalf("%s: got unexpected success", testName) + } else { + t.Logf("%s: got error: %s", testName, err) + } +} + +func TestAddRule7(t *testing.T) { testName := "add rule with new owner and new from" rs := createSampleRuleSet() - if err := rs.AddRule(RewriteRule{Owner: owner9, From: from9, To: to9}); err != nil { + changed, err := rs.AddRule(mustNewRewriteRule(owner9, from8, to9)) + if err != nil { t.Fatalf("%s: got unexpected error: %s", testName, err) } if err := checkRuleSetConsistency(rs); err != nil { t.Fatalf("%s: %s", testName, err) } + if !changed { + t.Errorf("%s: no ruleset change indicated although there was one", testName) + } rsexp := createSampleRuleSet() - rsexp.rulesByOwner[owner9] = &RewriteRule{Owner: owner9, From: from9, To: to9} - rsexp.rulesByFrom[from9] = rsexp.rulesByOwner[owner9] + rsexp.rulesByOwner[owner9] = mustNewRewriteRule(owner9, from8, to9) if !reflect.DeepEqual(rs, rsexp) { t.Errorf("%s: unexpected ruleset", testName) } } -func TestAddRule6(t *testing.T) { - testName := "add rule with new owner and existing from" +func TestAddRule8(t *testing.T) { + testName := "add rule with new owner and conflicting from (1)" + rs := createSampleRuleSet() + if _, err := rs.AddRule(mustNewRewriteRule(owner9, from1, to9)); err == nil { + t.Fatalf("%s: got unexpected success", testName) + } else { + t.Logf("%s: got error: %s", testName, err) + } +} + +func TestAddRule9(t *testing.T) { + testName := "add rule with new owner and conflicting from (2)" + rs := createSampleRuleSet() + if _, err := rs.AddRule(mustNewRewriteRule(owner9, from7, to9)); err == nil { + t.Fatalf("%s: got unexpected success", testName) + } else { + t.Logf("%s: got error: %s", testName, err) + } +} + +func TestAddRule10(t *testing.T) { + testName := "add rule with new owner and conflicting from (3)" rs := createSampleRuleSet() - if err := rs.AddRule(RewriteRule{Owner: owner9, From: from1, To: to9}); err == nil { + if _, err := rs.AddRule(mustNewRewriteRule(owner9, from9, to9)); err == nil { t.Fatalf("%s: got unexpected success", testName) } else { t.Logf("%s: got error: %s", testName, err) @@ -210,12 +255,12 @@ func TestAddRule6(t *testing.T) { func TestRemoveRule1(t *testing.T) { testName := "remove existing rule" rs := createSampleRuleSet() - if err := rs.RemoveRule(owner1); err != nil { - t.Fatalf("%s: got unexpected error: %s", testName, err) + changed := rs.RemoveRule(owner1) + if !changed { + t.Errorf("%s: no ruleset change indicated although there was one", testName) } rsexp := createSampleRuleSet() delete(rsexp.rulesByOwner, owner1) - delete(rsexp.rulesByFrom, from1) if !reflect.DeepEqual(rs, rsexp) { t.Errorf("%s: unexpected ruleset", testName) } @@ -224,10 +269,13 @@ func TestRemoveRule1(t *testing.T) { func TestRemoveRule2(t *testing.T) { testName := "remove non-existing rule" rs := createSampleRuleSet() - if err := rs.RemoveRule(owner9); err == nil { - t.Fatalf("%s: got unexpected success", testName) - } else { - t.Logf("%s: got error: %s", testName, err) + changed := rs.RemoveRule(owner9) + if changed { + t.Errorf("%s: ruleset change indicated although there was none", testName) + } + rsexp := createSampleRuleSet() + if !reflect.DeepEqual(rs, rsexp) { + t.Errorf("%s: unexpected ruleset", testName) } } diff --git a/internal/dnsutil/name.go b/internal/dnsutil/name.go new file mode 100644 index 0000000..fec06ed --- /dev/null +++ b/internal/dnsutil/name.go @@ -0,0 +1,38 @@ +/* +SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and dns-masquerading-operator contributors +SPDX-License-Identifier: Apache-2.0 +*/ + +package dnsutil + +import ( + "fmt" + "regexp" +) + +var ( + anycaseRegex = regexp.MustCompile(`^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$`) + lowercaseRegex = regexp.MustCompile(`^([a-z0-9]|[a-z0-9][a-z0-9\-]{0,61}[a-z0-9])(\.([a-z0-9]|[a-z0-9][a-z0-9\-]{0,61}[a-z0-9]))*$`) +) + +// Check that given string represents a valid DNS name according to RFC1123; +// allowUppercase is self-explanatory; +// allowWildcard means that the first DNS label may be an asterisk (*). +func CheckDnsName(s string, allowUppercase bool, allowWildcard bool) error { + if allowWildcard { + s = regexp.MustCompile(`^\*(.*)$`).ReplaceAllString(s, `wildcard$1`) + } + if len(s) > 255 { + return fmt.Errorf("not a valid DNS name") + } + var regex *regexp.Regexp + if allowUppercase { + regex = anycaseRegex + } else { + regex = lowercaseRegex + } + if !regex.MatchString(s) { + return fmt.Errorf("not a valid DNS name") + } + return nil +} diff --git a/internal/dns/record.go b/internal/dnsutil/record.go similarity index 85% rename from internal/dns/record.go rename to internal/dnsutil/record.go index 36242f6..9e8f318 100644 --- a/internal/dns/record.go +++ b/internal/dnsutil/record.go @@ -3,7 +3,7 @@ SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and dns-masquera SPDX-License-Identifier: Apache-2.0 */ -package dns +package dnsutil import ( "context" @@ -16,8 +16,9 @@ import ( // Lookup a DNS name on the specified DNS server, and return all IP addresses; // returned slice of addresses will be nil if host was not found; +// if host is an IP address, it will be returned as such; // err will be set for all other error situations. -func Lookup(host string, serverAddress string, serverPort int32) ([]string, error) { +func Lookup(host string, serverAddress string, serverPort uint16) ([]string, error) { r := &net.Resolver{ PreferGo: true, Dial: func(ctx context.Context, network, address string) (net.Conn, error) { diff --git a/internal/netutil/utils.go b/internal/netutil/utils.go deleted file mode 100644 index 3f15643..0000000 --- a/internal/netutil/utils.go +++ /dev/null @@ -1,33 +0,0 @@ -/* -SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and dns-masquerading-operator contributors -SPDX-License-Identifier: Apache-2.0 -*/ - -package netutil - -import ( - "fmt" - "net" - "regexp" -) - -func CheckDnsName(s string, allowWildcard bool) error { - if allowWildcard { - s = regexp.MustCompile(`^\*\.(.+)$`).ReplaceAllString(s, `wildcard.$1`) - } - if len(s) > 255 { - return fmt.Errorf("not a valid DNS name") - } - if !regexp.MustCompile(`^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$`).MatchString(s) { - return fmt.Errorf("not a valid DNS name") - } - return nil -} - -func IsWildcardDnsName(s string) bool { - return len(s) > 0 && s[0] == '*' -} - -func IsIpAddress(s string) bool { - return net.ParseIP(s) != nil -} diff --git a/internal/portforward/portforward.go b/internal/portforward/portforward.go index e803bf5..d5e205f 100644 --- a/internal/portforward/portforward.go +++ b/internal/portforward/portforward.go @@ -11,24 +11,31 @@ import ( "net/http" "net/url" "strings" + "sync" "time" + "github.com/pkg/errors" "k8s.io/client-go/rest" "k8s.io/client-go/tools/portforward" "k8s.io/client-go/transport/spdy" ) +// PortForward is a handle represents a port-forward connection. type PortForward struct { config *rest.Config localAddress string - localPort int32 + localPort uint16 namespace string name string - port int32 + port uint16 stopCh chan struct{} + mu sync.Mutex + started bool + stopped bool } -func New(cfg *rest.Config, localAddress string, localPort int32, namespace string, name string, port int32) *PortForward { +// Create new PortForward handle +func New(cfg *rest.Config, localAddress string, localPort uint16, namespace string, name string, port uint16) *PortForward { return &PortForward{ config: cfg, localAddress: localAddress, @@ -40,13 +47,20 @@ func New(cfg *rest.Config, localAddress string, localPort int32, namespace strin } } +// Start port-forwarding; blocks up to 10 seconds, until port-forward is ready, or an error or timeout occurred; +// Start() may be called only once (even after error); any further call will return an error. func (pfw *PortForward) Start() error { + pfw.mu.Lock() + defer pfw.mu.Unlock() + if pfw.started { + return fmt.Errorf("error starting port forward %s:%d to %s/%s:%d: already started", pfw.localAddress, pfw.localPort, pfw.namespace, pfw.name, pfw.port) + } path := fmt.Sprintf("/api/v1/namespaces/%s/pods/%s/portforward", pfw.namespace, pfw.name) host := strings.TrimPrefix(pfw.config.Host, "https://") transport, upgrader, err := spdy.RoundTripperFor(pfw.config) if err != nil { - return err + return errors.Wrapf(err, "error starting port forward %s:%d to %s/%s:%d", pfw.localAddress, pfw.localPort, pfw.namespace, pfw.name, pfw.port) } dialer := spdy.NewDialer(upgrader, &http.Client{Transport: transport}, http.MethodPost, &url.URL{Scheme: "https", Path: path, Host: host}) @@ -55,7 +69,7 @@ func (pfw *PortForward) Start() error { errorCh := make(chan error) fw, err := portforward.NewOnAddresses(dialer, []string{pfw.localAddress}, []string{fmt.Sprintf("%d:%d", pfw.localPort, pfw.port)}, pfw.stopCh, readyCh, io.Discard, io.Discard) if err != nil { - return err + return errors.Wrapf(err, "error starting port forward %s:%d to %s/%s:%d", pfw.localAddress, pfw.localPort, pfw.namespace, pfw.name, pfw.port) } go func() { if err := fw.ForwardPorts(); err != nil { @@ -65,16 +79,46 @@ func (pfw *PortForward) Start() error { select { case <-readyCh: + ports, err := fw.GetPorts() + if err != nil { + return errors.Wrapf(err, "error starting port forward %s:%d to %s/%s:%d", pfw.localAddress, pfw.localPort, pfw.namespace, pfw.name, pfw.port) + } + if len(ports) != 1 { + return fmt.Errorf("error starting port forward %s:%d to %s/%s:%d: invalid port count returned (%d)", pfw.localAddress, pfw.localPort, pfw.namespace, pfw.name, pfw.port, len(ports)) + } + if pfw.localPort != 0 && ports[0].Local != pfw.localPort { + return fmt.Errorf("error starting port forward %s:%d to %s/%s:%d: invalid local port returned (%d)", pfw.localAddress, pfw.localPort, pfw.namespace, pfw.name, pfw.port, ports[0].Local) + } + if ports[0].Remote != pfw.port { + return fmt.Errorf("error starting port forward %s:%d to %s/%s:%d: invalid remote port returned (%d)", pfw.localAddress, pfw.localPort, pfw.namespace, pfw.name, pfw.port, ports[0].Remote) + } + pfw.localPort = ports[0].Local + pfw.started = true return nil case err := <-errorCh: close(pfw.stopCh) - return err + return errors.Wrapf(err, "error starting port forward %s:%d to %s/%s:%d", pfw.localAddress, pfw.localPort, pfw.namespace, pfw.name, pfw.port) case <-time.After(10 * time.Second): close(pfw.stopCh) - return fmt.Errorf("error creating port forward %s:%d to %s/%s:%d (timeout)", pfw.localAddress, pfw.localPort, pfw.namespace, pfw.name, pfw.port) + return fmt.Errorf("error starting port forward %s:%d to %s/%s:%d: timeout", pfw.localAddress, pfw.localPort, pfw.namespace, pfw.name, pfw.port) } } +// Stop port-forwarding; calling Stop() on a not yet started or already stopped handle has no effect. func (pfw *PortForward) Stop() { + pfw.mu.Lock() + defer pfw.mu.Unlock() + if !pfw.started || pfw.stopped { + return + } + pfw.stopped = true close(pfw.stopCh) } + +func (pfw *PortForward) LocalAddress() string { + return pfw.localAddress +} + +func (pfw *PortForward) LocalPort() uint16 { + return pfw.localPort +} diff --git a/main.go b/main.go index 2121272..62a833f 100644 --- a/main.go +++ b/main.go @@ -28,6 +28,7 @@ import ( dnsv1alpha1 "github.com/sap/dns-masquerading-operator/api/v1alpha1" "github.com/sap/dns-masquerading-operator/internal/controllers" + "github.com/sap/dns-masquerading-operator/internal/coredns" //+kubebuilder:scaffold:imports ) @@ -116,7 +117,7 @@ func main() { MetricsBindAddress: metricsAddr, HealthProbeBindAddress: probeAddr, Host: host, - Port: port, + Port: int(port), LeaderElection: enableLeaderElection, LeaderElectionNamespace: leaderElectionNamespace, LeaderElectionID: LeaderElectionID, @@ -160,12 +161,11 @@ func main() { if err = (&controllers.MasqueradingRuleReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), - Config: mgr.GetConfig(), Recorder: mgr.GetEventRecorderFor(controllerName), CorednsConfigMapNamespace: corednsConfigMapNamespace, CorednsConfigMapName: corednsConfigMapName, CorednsConfigMapKey: corednsConfigMapKey, - InCluster: inCluster, + Resolver: coredns.NewResolver(mgr.GetClient(), mgr.GetConfig(), inCluster), }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "MasqueradingRule") os.Exit(1) @@ -192,16 +192,16 @@ func main() { } } -func parseAddress(address string) (string, int, error) { +func parseAddress(address string) (string, uint16, error) { host, p, err := net.SplitHostPort(address) if err != nil { - return "", -1, err + return "", 0, err } - port, err := strconv.Atoi(p) + port, err := strconv.ParseUint(p, 10, 16) if err != nil { - return "", -1, err + return "", 0, err } - return host, port, nil + return host, uint16(port), nil } func checkInCluster() (bool, string, error) {