From f04186746d6888b5e1d8eef78fbdd99633cbd22f Mon Sep 17 00:00:00 2001 From: Vibhu Prashar Date: Thu, 27 Jul 2023 20:09:41 +0530 Subject: [PATCH 1/2] =?UTF-8?q?Add=20integration=E2=80=93test=20code=20bas?= =?UTF-8?q?e=20to=20rhobs/config?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Vibhu Prashar --- Dockerfile | 12 + go.mod | 40 +- go.sum | 80 ++-- tests/integration_tests/Dockerfile | 2 +- .../framework/.bingo/.gitignore | 13 + .../framework/.bingo/README.md | 14 + .../framework/.bingo/Variables.mk | 43 ++ .../integration_tests/framework/.bingo/go.mod | 1 + .../framework/.bingo/gojsontoyaml.mod | 5 + .../framework/.bingo/gojsontoyaml.sum | 7 + .../framework/.bingo/jsonnet-lint.mod | 5 + .../framework/.bingo/jsonnet-lint.sum | 17 + .../framework/.bingo/jsonnet.mod | 5 + .../framework/.bingo/jsonnet.sum | 17 + .../framework/.bingo/jsonnetfmt.mod | 5 + .../framework/.bingo/jsonnetfmt.sum | 17 + .../framework/.bingo/variables.env | 18 + tests/integration_tests/framework/Makefile | 111 ++++++ tests/integration_tests/framework/README.md | 55 +++ .../framework/cmd/rhobs-test/main.go | 94 +++++ .../manifests/dev/test-deployment-faulty.yaml | 70 ++++ .../manifests/dev/test-deployment.yaml | 70 ++++ .../examples/manifests/dev/test-job.yaml | 32 ++ .../examples/manifests/dev/test-rbac.yaml | 86 ++++ .../openshift/rhobs-rbac-template.yaml | 288 ++++++++++++++ .../openshift/rhobs-test-job-template.yaml | 49 +++ .../test-deployment-faulty-template.yaml | 83 ++++ .../openshift/test-deployment-template.yaml | 83 ++++ .../framework/integration-test.png | Bin 0 -> 104821 bytes .../framework/jsonnet/dev-manifests.jsonnet | 139 +++++++ .../framework/jsonnet/job.libsonnet | 54 +++ .../framework/jsonnet/ocp-manifests.jsonnet | 240 ++++++++++++ .../framework/jsonnet/rbac.libsonnet | 61 +++ .../jsonnet/test-deployment.libsonnet | 113 ++++++ .../framework/pkg/client/client.go | 38 ++ .../framework/pkg/deployment/deployment.go | 176 +++++++++ .../pkg/deployment/deployment_test.go | 367 ++++++++++++++++++ .../framework/pkg/logger/logger.go | 109 ++++++ .../framework/pkg/pod/pod.go | 105 +++++ .../framework/pkg/pod/pod_test.go | 228 +++++++++++ .../framework/pkg/service/service.go | 162 ++++++++ .../framework/pkg/service/service_test.go | 284 ++++++++++++++ .../framework/pkg/statefulset/statefulset.go | 177 +++++++++ .../pkg/statefulset/statefulset_test.go | 367 ++++++++++++++++++ 44 files changed, 3907 insertions(+), 35 deletions(-) create mode 100644 Dockerfile create mode 100644 tests/integration_tests/framework/.bingo/.gitignore create mode 100644 tests/integration_tests/framework/.bingo/README.md create mode 100644 tests/integration_tests/framework/.bingo/Variables.mk create mode 100644 tests/integration_tests/framework/.bingo/go.mod create mode 100644 tests/integration_tests/framework/.bingo/gojsontoyaml.mod create mode 100644 tests/integration_tests/framework/.bingo/gojsontoyaml.sum create mode 100644 tests/integration_tests/framework/.bingo/jsonnet-lint.mod create mode 100644 tests/integration_tests/framework/.bingo/jsonnet-lint.sum create mode 100644 tests/integration_tests/framework/.bingo/jsonnet.mod create mode 100644 tests/integration_tests/framework/.bingo/jsonnet.sum create mode 100644 tests/integration_tests/framework/.bingo/jsonnetfmt.mod create mode 100644 tests/integration_tests/framework/.bingo/jsonnetfmt.sum create mode 100644 tests/integration_tests/framework/.bingo/variables.env create mode 100644 tests/integration_tests/framework/Makefile create mode 100644 tests/integration_tests/framework/README.md create mode 100644 tests/integration_tests/framework/cmd/rhobs-test/main.go create mode 100644 tests/integration_tests/framework/examples/manifests/dev/test-deployment-faulty.yaml create mode 100644 tests/integration_tests/framework/examples/manifests/dev/test-deployment.yaml create mode 100644 tests/integration_tests/framework/examples/manifests/dev/test-job.yaml create mode 100644 tests/integration_tests/framework/examples/manifests/dev/test-rbac.yaml create mode 100644 tests/integration_tests/framework/examples/manifests/openshift/rhobs-rbac-template.yaml create mode 100644 tests/integration_tests/framework/examples/manifests/openshift/rhobs-test-job-template.yaml create mode 100644 tests/integration_tests/framework/examples/manifests/openshift/test-deployment-faulty-template.yaml create mode 100644 tests/integration_tests/framework/examples/manifests/openshift/test-deployment-template.yaml create mode 100644 tests/integration_tests/framework/integration-test.png create mode 100644 tests/integration_tests/framework/jsonnet/dev-manifests.jsonnet create mode 100644 tests/integration_tests/framework/jsonnet/job.libsonnet create mode 100644 tests/integration_tests/framework/jsonnet/ocp-manifests.jsonnet create mode 100644 tests/integration_tests/framework/jsonnet/rbac.libsonnet create mode 100644 tests/integration_tests/framework/jsonnet/test-deployment.libsonnet create mode 100644 tests/integration_tests/framework/pkg/client/client.go create mode 100644 tests/integration_tests/framework/pkg/deployment/deployment.go create mode 100644 tests/integration_tests/framework/pkg/deployment/deployment_test.go create mode 100644 tests/integration_tests/framework/pkg/logger/logger.go create mode 100644 tests/integration_tests/framework/pkg/pod/pod.go create mode 100644 tests/integration_tests/framework/pkg/pod/pod_test.go create mode 100644 tests/integration_tests/framework/pkg/service/service.go create mode 100644 tests/integration_tests/framework/pkg/service/service_test.go create mode 100644 tests/integration_tests/framework/pkg/statefulset/statefulset.go create mode 100644 tests/integration_tests/framework/pkg/statefulset/statefulset_test.go diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000..49f169592f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM golang:1.20-alpine +RUN apk update && apk add git + +WORKDIR /integration-tests + +COPY go.mod go.sum ./ +RUN go mod download + +COPY . . + +RUN go build ./tests/integration_tests/framework/cmd/rhobs-test +ENTRYPOINT [ "./rhobs-test" ] diff --git a/go.mod b/go.mod index d0e1f0590c..51a5a4a8ba 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,9 @@ require ( github.com/observatorium/api v0.1.3-0.20220621123450-69c5f2661d01 github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.60.1 github.com/pyrra-dev/pyrra v0.5.2 - k8s.io/apimachinery v0.25.3 + k8s.io/api v0.27.4 + k8s.io/apimachinery v0.27.4 + k8s.io/client-go v0.27.4 ) require ( @@ -17,18 +19,30 @@ require ( github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dennwc/varint v1.0.0 // indirect + github.com/emicklei/go-restful/v3 v3.9.0 // indirect + github.com/evanphx/json-patch v4.12.0+incompatible // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/go-logr/logr v1.2.3 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.1 // indirect + github.com/go-openapi/swag v0.22.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.2 // 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.2.0 // indirect + github.com/google/uuid v1.3.0 // indirect github.com/hashicorp/hcl v1.0.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.7 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // 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/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.13.1 // indirect @@ -37,24 +51,28 @@ require ( github.com/prometheus/procfs v0.8.0 // indirect github.com/prometheus/prometheus v1.8.2-0.20220211202545-56e14463bccf // indirect github.com/rodaine/hclencoder v0.0.1 // indirect + github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/testify v1.8.1 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/goleak v1.1.12 // indirect - golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b // indirect - golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect - golang.org/x/sys v0.0.0-20221010170243-090e33056c14 // indirect - golang.org/x/text v0.3.7 // indirect - golang.org/x/tools v0.1.12 // indirect + golang.org/x/net v0.8.0 // indirect + golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect + golang.org/x/sync v0.1.0 // indirect + golang.org/x/sys v0.6.0 // indirect + golang.org/x/term v0.6.0 // indirect + golang.org/x/text v0.8.0 // indirect + golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect + google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/alecthomas/kingpin.v2 v2.2.6 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.25.3 // indirect - k8s.io/klog/v2 v2.80.0 // indirect - k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73 // indirect + k8s.io/klog/v2 v2.90.1 // indirect + k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect + k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect sigs.k8s.io/controller-runtime v0.13.0 // indirect - sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index edc892c7ab..010c4d0465 100644 --- a/go.sum +++ b/go.sum @@ -383,6 +383,8 @@ github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkg github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/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.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -397,6 +399,8 @@ github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= +github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= @@ -471,12 +475,16 @@ github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwds github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= +github.com/go-openapi/jsonreference v0.20.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTrLC1F86HID8= +github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= @@ -529,6 +537,8 @@ github.com/go-openapi/swag v0.19.12/go.mod h1:eFdyEBkTdoAf/9RXBvj4cr1nH7GD8Kzo5H github.com/go-openapi/swag v0.19.13/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= github.com/go-openapi/validate v0.19.3/go.mod h1:90Vh6jjkTn+OT1Eefm0ZixWNFjhtOH7vS9k0lo6zwJo= @@ -545,6 +555,7 @@ github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= @@ -627,8 +638,9 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw 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.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -640,6 +652,8 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/flatbuffers v2.0.0+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +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= @@ -653,6 +667,7 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -677,12 +692,15 @@ github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20211214055906-6f57359322fd h1:1FjCyPC+syAzJ5/2S8fqdZK1R22vvA0J7JZKcuOIQ7Y= github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= @@ -771,6 +789,7 @@ github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/flux v0.65.0/go.mod h1:BwN2XG2lMszOoquQaFdPET8FRQfrXiZsWmcMO9rkaVY= @@ -802,6 +821,7 @@ github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHW github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +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 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= @@ -845,6 +865,7 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB 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/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -870,6 +891,8 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= @@ -939,6 +962,7 @@ github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7P github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= @@ -952,7 +976,6 @@ github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxzi github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= -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/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= @@ -974,6 +997,7 @@ github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo/v2 v2.9.1 h1:zie5Ly042PD3bsCvsSOPvRnFwyo3rKe64TJlD6nu0mk= github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -981,7 +1005,7 @@ github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= -github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q= +github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -1124,6 +1148,7 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/cors v1.8.0/go.mod h1:EBwu+T5AvHOcXwvZIkQFjUN6s8Czyqw12GL/Y0tUyRM= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -1454,8 +1479,8 @@ golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220105145211-5b0dc2dfae98/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.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY= -golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= 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= @@ -1486,8 +1511,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1603,12 +1628,14 @@ golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20221010170243-090e33056c14 h1:k5II8e6QD8mITdi+okbbmR/cIyEbeXLBhy5Ha4nevyc= -golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 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= @@ -1618,8 +1645,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 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= @@ -1630,6 +1658,8 @@ golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220609170525-579cf78fd858 h1:Dpdu/EMxGMFgq0CeYMh4fazTD2vtlZRYE7wyynxJb9U= +golang.org/x/time v0.0.0-20220609170525-579cf78fd858/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1711,8 +1741,7 @@ golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.9-0.20211209172050-90a85b2969be/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= -golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= 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= @@ -1892,8 +1921,9 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 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/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= @@ -1945,15 +1975,15 @@ k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= k8s.io/api v0.22.4/go.mod h1:Rgs+9gIGYC5laXQSZZ9JqT5NevNgoGiOdVWi1BAB3qk= -k8s.io/api v0.25.3 h1:Q1v5UFfYe87vi5H7NU0p4RXC26PPMT8KOpr1TLQbCMQ= -k8s.io/api v0.25.3/go.mod h1:o42gKscFrEVjHdQnyRenACrMtbuJsVdP+WVjqejfzmI= +k8s.io/api v0.27.4 h1:0pCo/AN9hONazBKlNUdhQymmnfLRbSZjd5H5H3f0bSs= +k8s.io/api v0.27.4/go.mod h1:O3smaaX15NfxjzILfiln1D8Z3+gEYpjEpiNA/1EVK1Y= k8s.io/apimachinery v0.17.5/go.mod h1:ioIo1G/a+uONV7Tv+ZmCbMG1/a3kVw5YcDdncd8ugQ0= k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= k8s.io/apimachinery v0.22.4/go.mod h1:yU6oA6Gnax9RrxGzVvPFFJ+mpnW6PBSqp0sx0I0HHW0= -k8s.io/apimachinery v0.25.3 h1:7o9ium4uyUOM76t6aunP0nZuex7gDf8VGwkR5RcJnQc= -k8s.io/apimachinery v0.25.3/go.mod h1:jaF9C/iPNM1FuLl7Zuy5b9v+n35HGSh6AQ4HYRkCqwo= +k8s.io/apimachinery v0.27.4 h1:CdxflD4AF61yewuid0fLl6bM4a3q04jWel0IlP+aYjs= +k8s.io/apimachinery v0.27.4/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E= k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= @@ -1962,6 +1992,8 @@ k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= k8s.io/client-go v0.22.4/go.mod h1:Yzw4e5e7h1LNHA4uqnMVrpEpUs1hJOiuBsJKIlRCHDA= +k8s.io/client-go v0.27.4 h1:vj2YTtSJ6J4KxaC88P4pMPEQECWMY8gqPqsTgUKzvjk= +k8s.io/client-go v0.27.4/go.mod h1:ragcly7lUlN0SRPk5/ZkGnDjPknzb37TICq07WhI6Xc= k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= @@ -1978,18 +2010,20 @@ k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= k8s.io/klog/v2 v2.40.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/klog/v2 v2.80.0 h1:lyJt0TWMPaGoODa8B8bUuxgHS3W/m/bNr2cca3brA/g= -k8s.io/klog/v2 v2.80.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= +k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20200316234421-82d701f24f9d/go.mod h1:F+5wygcW0wmRTnM3cOgIqGivxkwSWIWT5YdsDbeAOaU= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= +k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg= +k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20200414100711-2df71ebbae66/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73 h1:H9TCJUUx+2VA0ZiD9lvtaX8fthFsMoD+Izn93E/hm8U= -k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20230209194617-a36077c30491 h1:r0BAOLElQnnFhE/ApUsg3iHdVYYPBjNSSOMowRZxxsY= +k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= @@ -1998,8 +2032,8 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyz sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/controller-runtime v0.13.0 h1:iqa5RNciy7ADWnIc8QxCbOX5FEKVR3uxVxKHRMc2WIQ= sigs.k8s.io/controller-runtime v0.13.0/go.mod h1:Zbz+el8Yg31jubvAEyglRZGdLAjplZl+PgtYNI6WNTI= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/structured-merge-diff/v2 v2.0.1/go.mod h1:Wb7vfKAodbKgf6tn1Kl0VvGj7mRH6DGaRcixXEJXTsE= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= diff --git a/tests/integration_tests/Dockerfile b/tests/integration_tests/Dockerfile index 926bdfe751..7b3fb4cbcb 100644 --- a/tests/integration_tests/Dockerfile +++ b/tests/integration_tests/Dockerfile @@ -13,4 +13,4 @@ RUN microdnf update -y &&\ COPY ./tests/integration_tests/runtest.sh /tests/runtest.sh WORKDIR /tests -ENTRYPOINT ["/bin/sh", "runtest.sh"] \ No newline at end of file +ENTRYPOINT ["/bin/sh", "runtest.sh"] diff --git a/tests/integration_tests/framework/.bingo/.gitignore b/tests/integration_tests/framework/.bingo/.gitignore new file mode 100644 index 0000000000..9efccf683c --- /dev/null +++ b/tests/integration_tests/framework/.bingo/.gitignore @@ -0,0 +1,13 @@ + +# Ignore everything +* + +# But not these files: +!.gitignore +!*.mod +!*.sum +!README.md +!Variables.mk +!variables.env + +*tmp.mod diff --git a/tests/integration_tests/framework/.bingo/README.md b/tests/integration_tests/framework/.bingo/README.md new file mode 100644 index 0000000000..7a5c2d4f6d --- /dev/null +++ b/tests/integration_tests/framework/.bingo/README.md @@ -0,0 +1,14 @@ +# Project Development Dependencies. + +This is directory which stores Go modules with pinned buildable package that is used within this repository, managed by https://github.com/bwplotka/bingo. + +* Run `bingo get` to install all tools having each own module file in this directory. +* Run `bingo get ` to install that have own module file in this directory. +* For Makefile: Make sure to put `include .bingo/Variables.mk` in your Makefile, then use $() variable where is the .bingo/.mod. +* For shell: Run `source .bingo/variables.env` to source all environment variable for each tool. +* For go: Import `.bingo/variables.go` to for variable names. +* See https://github.com/bwplotka/bingo or -h on how to add, remove or change binaries dependencies. + +## Requirements + +* Go 1.14+ diff --git a/tests/integration_tests/framework/.bingo/Variables.mk b/tests/integration_tests/framework/.bingo/Variables.mk new file mode 100644 index 0000000000..220d1ddb7a --- /dev/null +++ b/tests/integration_tests/framework/.bingo/Variables.mk @@ -0,0 +1,43 @@ +# Auto generated binary variables helper managed by https://github.com/bwplotka/bingo v0.8. DO NOT EDIT. +# All tools are designed to be build inside $GOBIN. +BINGO_DIR := $(dir $(lastword $(MAKEFILE_LIST))) +GOPATH ?= $(shell go env GOPATH) +GOBIN ?= $(firstword $(subst :, ,${GOPATH}))/bin +GO ?= $(shell which go) + +# Below generated variables ensure that every time a tool under each variable is invoked, the correct version +# will be used; reinstalling only if needed. +# For example for gojsontoyaml variable: +# +# In your main Makefile (for non array binaries): +# +#include .bingo/Variables.mk # Assuming -dir was set to .bingo . +# +#command: $(GOJSONTOYAML) +# @echo "Running gojsontoyaml" +# @$(GOJSONTOYAML) +# +GOJSONTOYAML := $(GOBIN)/gojsontoyaml-v0.1.0 +$(GOJSONTOYAML): $(BINGO_DIR)/gojsontoyaml.mod + @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. + @echo "(re)installing $(GOBIN)/gojsontoyaml-v0.1.0" + @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=gojsontoyaml.mod -o=$(GOBIN)/gojsontoyaml-v0.1.0 "github.com/brancz/gojsontoyaml" + +JSONNET_LINT := $(GOBIN)/jsonnet-lint-v0.20.0 +$(JSONNET_LINT): $(BINGO_DIR)/jsonnet-lint.mod + @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. + @echo "(re)installing $(GOBIN)/jsonnet-lint-v0.20.0" + @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=jsonnet-lint.mod -o=$(GOBIN)/jsonnet-lint-v0.20.0 "github.com/google/go-jsonnet/cmd/jsonnet-lint" + +JSONNET := $(GOBIN)/jsonnet-v0.20.0 +$(JSONNET): $(BINGO_DIR)/jsonnet.mod + @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. + @echo "(re)installing $(GOBIN)/jsonnet-v0.20.0" + @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=jsonnet.mod -o=$(GOBIN)/jsonnet-v0.20.0 "github.com/google/go-jsonnet/cmd/jsonnet" + +JSONNETFMT := $(GOBIN)/jsonnetfmt-v0.20.0 +$(JSONNETFMT): $(BINGO_DIR)/jsonnetfmt.mod + @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. + @echo "(re)installing $(GOBIN)/jsonnetfmt-v0.20.0" + @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=jsonnetfmt.mod -o=$(GOBIN)/jsonnetfmt-v0.20.0 "github.com/google/go-jsonnet/cmd/jsonnetfmt" + diff --git a/tests/integration_tests/framework/.bingo/go.mod b/tests/integration_tests/framework/.bingo/go.mod new file mode 100644 index 0000000000..610249af0b --- /dev/null +++ b/tests/integration_tests/framework/.bingo/go.mod @@ -0,0 +1 @@ +module _ // Fake go.mod auto-created by 'bingo' for go -moddir compatibility with non-Go projects. Commit this file, together with other .mod files. \ No newline at end of file diff --git a/tests/integration_tests/framework/.bingo/gojsontoyaml.mod b/tests/integration_tests/framework/.bingo/gojsontoyaml.mod new file mode 100644 index 0000000000..5e46051d62 --- /dev/null +++ b/tests/integration_tests/framework/.bingo/gojsontoyaml.mod @@ -0,0 +1,5 @@ +module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT + +go 1.19 + +require github.com/brancz/gojsontoyaml v0.1.0 diff --git a/tests/integration_tests/framework/.bingo/gojsontoyaml.sum b/tests/integration_tests/framework/.bingo/gojsontoyaml.sum new file mode 100644 index 0000000000..155d6d2de8 --- /dev/null +++ b/tests/integration_tests/framework/.bingo/gojsontoyaml.sum @@ -0,0 +1,7 @@ +github.com/brancz/gojsontoyaml v0.1.0 h1:SdzR3+BCVOqaI42nFGTeaB7/2DgDM4fhuvRLqxatA8M= +github.com/brancz/gojsontoyaml v0.1.0/go.mod h1:+ycZY94+V11XZBUaDEsbLr3hPNS/ZPrDVKKNUg3Sgvg= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/tests/integration_tests/framework/.bingo/jsonnet-lint.mod b/tests/integration_tests/framework/.bingo/jsonnet-lint.mod new file mode 100644 index 0000000000..49d15d133a --- /dev/null +++ b/tests/integration_tests/framework/.bingo/jsonnet-lint.mod @@ -0,0 +1,5 @@ +module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT + +go 1.19 + +require github.com/google/go-jsonnet v0.20.0 // cmd/jsonnet-lint diff --git a/tests/integration_tests/framework/.bingo/jsonnet-lint.sum b/tests/integration_tests/framework/.bingo/jsonnet-lint.sum new file mode 100644 index 0000000000..b44e9d3c30 --- /dev/null +++ b/tests/integration_tests/framework/.bingo/jsonnet-lint.sum @@ -0,0 +1,17 @@ +github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= +github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= +github.com/google/go-jsonnet v0.20.0 h1:WG4TTSARuV7bSm4PMB4ohjxe33IHT5WVTrJSU33uT4g= +github.com/google/go-jsonnet v0.20.0/go.mod h1:VbgWF9JX7ztlv770x/TolZNGGFfiHEVx9G6ca2eUmeA= +github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= +gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/tests/integration_tests/framework/.bingo/jsonnet.mod b/tests/integration_tests/framework/.bingo/jsonnet.mod new file mode 100644 index 0000000000..3dc540d816 --- /dev/null +++ b/tests/integration_tests/framework/.bingo/jsonnet.mod @@ -0,0 +1,5 @@ +module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT + +go 1.19 + +require github.com/google/go-jsonnet v0.20.0 // cmd/jsonnet diff --git a/tests/integration_tests/framework/.bingo/jsonnet.sum b/tests/integration_tests/framework/.bingo/jsonnet.sum new file mode 100644 index 0000000000..b44e9d3c30 --- /dev/null +++ b/tests/integration_tests/framework/.bingo/jsonnet.sum @@ -0,0 +1,17 @@ +github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= +github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= +github.com/google/go-jsonnet v0.20.0 h1:WG4TTSARuV7bSm4PMB4ohjxe33IHT5WVTrJSU33uT4g= +github.com/google/go-jsonnet v0.20.0/go.mod h1:VbgWF9JX7ztlv770x/TolZNGGFfiHEVx9G6ca2eUmeA= +github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= +gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/tests/integration_tests/framework/.bingo/jsonnetfmt.mod b/tests/integration_tests/framework/.bingo/jsonnetfmt.mod new file mode 100644 index 0000000000..21e92d0e9d --- /dev/null +++ b/tests/integration_tests/framework/.bingo/jsonnetfmt.mod @@ -0,0 +1,5 @@ +module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT + +go 1.19 + +require github.com/google/go-jsonnet v0.20.0 // cmd/jsonnetfmt diff --git a/tests/integration_tests/framework/.bingo/jsonnetfmt.sum b/tests/integration_tests/framework/.bingo/jsonnetfmt.sum new file mode 100644 index 0000000000..b44e9d3c30 --- /dev/null +++ b/tests/integration_tests/framework/.bingo/jsonnetfmt.sum @@ -0,0 +1,17 @@ +github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= +github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= +github.com/google/go-jsonnet v0.20.0 h1:WG4TTSARuV7bSm4PMB4ohjxe33IHT5WVTrJSU33uT4g= +github.com/google/go-jsonnet v0.20.0/go.mod h1:VbgWF9JX7ztlv770x/TolZNGGFfiHEVx9G6ca2eUmeA= +github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= +gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/tests/integration_tests/framework/.bingo/variables.env b/tests/integration_tests/framework/.bingo/variables.env new file mode 100644 index 0000000000..f85e3b99dd --- /dev/null +++ b/tests/integration_tests/framework/.bingo/variables.env @@ -0,0 +1,18 @@ +# Auto generated binary variables helper managed by https://github.com/bwplotka/bingo v0.8. DO NOT EDIT. +# All tools are designed to be build inside $GOBIN. +# Those variables will work only until 'bingo get' was invoked, or if tools were installed via Makefile's Variables.mk. +GOBIN=${GOBIN:=$(go env GOBIN)} + +if [ -z "$GOBIN" ]; then + GOBIN="$(go env GOPATH)/bin" +fi + + +GOJSONTOYAML="${GOBIN}/gojsontoyaml-v0.1.0" + +JSONNET_LINT="${GOBIN}/jsonnet-lint-v0.20.0" + +JSONNET="${GOBIN}/jsonnet-v0.20.0" + +JSONNETFMT="${GOBIN}/jsonnetfmt-v0.20.0" + diff --git a/tests/integration_tests/framework/Makefile b/tests/integration_tests/framework/Makefile new file mode 100644 index 0000000000..63504cb506 --- /dev/null +++ b/tests/integration_tests/framework/Makefile @@ -0,0 +1,111 @@ +include .bingo/Variables.mk + +IMG ?= quay.io/app-sre/rhobs-test +BRANCH := $(strip $(shell git rev-parse --abbrev-ref HEAD)) +BUILD_DATE :=$(shell date -u +"%Y-%m-%d") +TAG := $(shell git rev-parse --short=7 HEAD) +EXAMPLES := examples +OCP_MANIFESTS := $(EXAMPLES)/manifests/openshift +DEV_MANIFESTS := $(EXAMPLES)/manifests/dev +XARGS ?= $(shell which gxargs 2>/dev/null || which xargs) +all: build +build: rhobs-test + +.PHONY: rhobs-test +rhobs-test: + CGO_ENABLED=0 go build ./cmd/rhobs-test + +.PHONY: vendor +vendor: go.mod go.sum + go mod tidy + go mod vendor +.PHONY: go-fmt +go-fmt: + @fmt_res=$$(gofmt -d -s $$(find . -type f -name '*.go' -not -path './vendor/*')); if [ -n "$$fmt_res" ]; then printf '\nGofmt found style issues. Please check the reported issues\nand fix them if necessary before submitting the code for review:\n\n%s' "$$fmt_res"; exit 1; fi + +.PHONY: container-dev +container-dev: kind + @docker build \ + -f ../../../Dockerfile \ + -t $(IMG):$(BRANCH)-$(BUILD_DATE)-$(TAG) \ + ../../../ + docker tag $(IMG):$(BRANCH)-$(BUILD_DATE)-$(TAG) localhost:5001/rhobs-test:latest + docker push localhost:5001/rhobs-test:latest + +.PHONY: kind +kind: + wget https://kind.sigs.k8s.io/examples/kind-with-registry.sh + chmod 755 kind-with-registry.sh + ./kind-with-registry.sh + +.PHONY: test +test: + go test ./... + +.PHONY: local +local: kind container-dev + kubectl apply -f $(DEV_MANIFESTS)/test-deployment.yaml + kubectl apply -f $(DEV_MANIFESTS)/rbac.yaml + kubectl apply -f $(DEV_MANIFESTS)/job.yaml + +.PHONY: local-faulty +local-faulty: kind container-dev + kubectl apply -f $(DEV_MANIFESTS)/test-deployment-faulty-template.yaml + kubectl apply -f $(DEV_MANIFESTS)/rbac-template.yaml + kubectl apply -f $(DEV_MANIFESTS)/job-template.yaml + +.PHONY: clean +clean: + find $(EXAMPLES) -type f ! -name '*.yaml' -delete + find $(OCP_MANIFESTS) -type f ! -name '*.yaml' -delete + find $(DEV_MANIFESTS) -type f ! -name '*.yaml' -delete + +.PHONY: clean-local +clean-local: + rm -f kind-with-registry.sh + rm -f ./rhobs-test + kind delete cluster + docker ps -a -q | xargs docker rm -f + +.PHONY: clean-test +clean-test: + rm -f kind-with-registry.sh + rm -f ./rhobs-test + kind delete cluster + docker ps -a -q | xargs docker rm -f + rm -f kubeconfig + +.PHONY: container-build +container-build: + docker buildx build \ + -f ../../../Dockerfile \ + --platform linux/amd64,linux/arm64 \ + --build-arg DOCKERFILE_PATH="/Dockerfile" \ + -t $(IMG):$(BRANCH)-$(BUILD_DATE)-$(TAG) \ + -t $(IMG):$(TAG) \ + ../../../ +.PHONY: container-build-push +container-build-push: + @docker buildx build \ + -f ../../../Dockerfile \ + --push \ + --platform linux/amd64,linux/arm64 \ + -t $(IMG):$(BRANCH)-$(BUILD_DATE)-$(TAG) \ + -t $(IMG):$(TAG) \ + ../../../ +.PHONY: ocp-manifests +ocp-manifests: $(JSONNET) $(JSONNETFMT) $(GOJSONTOYAML) + echo "ocp manifests" + $(JSONNETFMT) -n 2 --max-blank-lines 2 --string-style s --comment-style s -i jsonnet/ocp-manifests.jsonnet + $(JSONNET) -m $(OCP_MANIFESTS) jsonnet/ocp-manifests.jsonnet | $(XARGS) -I{} sh -c 'cat {} | $(GOJSONTOYAML) > {}.yaml' -- {} + make clean +.PHONY: dev-manifests + +dev-manifests: $(JSONNET) $(JSONNETFMT) $(GOJSONTOYAML) + echo "dev manifests" + $(JSONNETFMT) -n 2 --max-blank-lines 2 --string-style s --comment-style s -i jsonnet/dev-manifests.jsonnet + $(JSONNET) -m $(DEV_MANIFESTS) jsonnet/dev-manifests.jsonnet | $(XARGS) -I{} sh -c 'cat {} | $(GOJSONTOYAML) > {}.yaml' -- {} + make clean + +.PHONY: manifests +manifests: dev-manifests ocp-manifests diff --git a/tests/integration_tests/framework/README.md b/tests/integration_tests/framework/README.md new file mode 100644 index 0000000000..f169d027a2 --- /dev/null +++ b/tests/integration_tests/framework/README.md @@ -0,0 +1,55 @@ +# Integration-Test +A tool that automates the integration testing of your applications on OpenShift/Kubernetes cluster. + +## Goals +Currently, we do not have a convenient way to interact with OpenShift/Kubernetes cluster and perform sanity checks on the cluster once our applications are deployed/rollout to the cluster. We need to rely on manual interventions for that. + +The tool aims to provide a simple and easy-to-use way for defining such cluster sanity test cases so that we can have checks in place that can provide us correct deployment status of our applications and flag them in case of any failures. + +## Features(WIP) +- Intract with OpenShift/Kubernetes cluster via Incluster config or Kubeconfig. +- Validates deployments, statefulsets, services, pvc's health(***Not implemented yet***) inside the namespace after rollout. +- Validates external network connectivity(***Not implemented yet***). +- Checks API endpoint's used(***Not implemented yet***). +- Validates resource utilization(***Not implemented yet***). + +## Design(WIP) +![integration-test-design](integration-test.png) +## Usage +``` +Usage of ./rhobs-test: + -interval duration + Wait before retry status check again (default 1m0s) + -kubeconfig string + path of kubeconfig file + -namespaces string + List of Namespaces to be monitored (default "default") + -timeout duration + Timeout for retry (default 5m0s) +``` +This repository contains Jsonnet configuration that allows generating OpenShift/Kubernetes objects that are required for local testing. + +To generate all required files into example/manifests directory run: +``` +make manifests +``` + +To use the integration test automation locally, follow these steps: +- Make sure you have docker or podman installed and running. +- To trigger integration tests against sample manifests run: + ``` + make local + ``` + > :point_right: This will install a kind cluster along with the local registry and build a docker image to push to the local registry from where the test job will fetch the image when running on the Kubernetes cluster. This will also take care of deploying service accounts and necessary rbac along with a prometheus-example-app which the integration-test will validate. +- To trigger integration tests against a faulty deployment run: + ``` + make local-faulty + ``` + > This can be used in case of negative testing/faulty deployment. + +- To destroy the local testing setup run: + ``` + make clean-local + ``` +## Contributing +If you'd like to contribute to the integration test tool, please fork the repository and create a pull request with your changes. \ No newline at end of file diff --git a/tests/integration_tests/framework/cmd/rhobs-test/main.go b/tests/integration_tests/framework/cmd/rhobs-test/main.go new file mode 100644 index 0000000000..eed5150909 --- /dev/null +++ b/tests/integration_tests/framework/cmd/rhobs-test/main.go @@ -0,0 +1,94 @@ +package main + +import ( + "strings" + + "flag" + "time" + + "github.com/rhobs/configuration/tests/integration_tests/framework/pkg/client" + "github.com/rhobs/configuration/tests/integration_tests/framework/pkg/deployment" + "github.com/rhobs/configuration/tests/integration_tests/framework/pkg/logger" + "github.com/rhobs/configuration/tests/integration_tests/framework/pkg/service" + "github.com/rhobs/configuration/tests/integration_tests/framework/pkg/statefulset" + "k8s.io/client-go/kubernetes" +) + +const ( + defaultInterval = 1 * time.Minute + defaultTimeout = 5 * time.Minute + defaultNamespace = "default" +) + +var ( + namespace string + kubeconfig string + loglevel string + interval time.Duration + timeout time.Duration + errList []error +) + +type Config struct { + NsList []string + KubeConfig string + ClientSet kubernetes.Interface + LogLevel string + Interval time.Duration + Timeout time.Duration +} + +func init() { + flag.StringVar(&namespace, "namespaces", defaultNamespace, "Namespace to be monitored") + flag.StringVar(&kubeconfig, "kubeconfig", "", "path of kubeconfig file") + flag.DurationVar(&interval, "interval", defaultInterval, "Wait before retry status check again") + flag.DurationVar(&timeout, "timeout", defaultTimeout, "Timeout for retry") + flag.StringVar(&loglevel, "loglevel", "", "log level") + flag.Parse() + if loglevel == "" { + loglevel = "info" + logger.NewLogger(logger.LevelInfo) + } else if loglevel == "debug" { + logger.NewLogger(logger.LevelDebug) + } else if loglevel == "error" { + logger.NewLogger(logger.LevelError) + } else if loglevel == "info" { + logger.NewLogger(logger.LevelInfo) + } else if loglevel == "warn" { + logger.NewLogger(logger.LevelWarning) + } else { + logger.NewLogger(logger.LevelFatal) + logger.AppLog.LogFatal("invalid log level. supported levels are warn, info, error, debug") + } +} + +func main() { + cfg := &Config{ + NsList: strings.Split(namespace, ","), + ClientSet: client.GetClient(kubeconfig), + KubeConfig: kubeconfig, + LogLevel: loglevel, + Interval: interval, + Timeout: timeout, + } + logger.AppLog.LogStartup(cfg.NsList, cfg.ClientSet, cfg.KubeConfig, cfg.LogLevel, cfg.Interval, cfg.Timeout) + err := deployment.CheckDeployments(cfg.NsList, cfg.ClientSet, interval, timeout) + if err != nil { + logger.AppLog.LogError("cannot validate deployements. reason: %v\n", err) + errList = append(errList, err) + } + err = statefulset.CheckStatefulSets(cfg.NsList, cfg.ClientSet, interval, timeout) + if err != nil { + logger.AppLog.LogError("cannot validate statefulsets. reason: %v\n", err) + errList = append(errList, err) + } + err = service.CheckServices(cfg.NsList, cfg.ClientSet, interval, timeout) + if err != nil { + logger.AppLog.LogError("cannot validate services. reason: %v\n", err) + errList = append(errList, err) + } + if len(errList) > 0 { + //TODO: Print out the list of errors + logger.AppLog.LogFatal("integration-tests failed. See the above list of errors") + } +} diff --git a/tests/integration_tests/framework/examples/manifests/dev/test-deployment-faulty.yaml b/tests/integration_tests/framework/examples/manifests/dev/test-deployment-faulty.yaml new file mode 100644 index 0000000000..8180834d56 --- /dev/null +++ b/tests/integration_tests/framework/examples/manifests/dev/test-deployment-faulty.yaml @@ -0,0 +1,70 @@ +apiVersion: v1 +items: +- apiVersion: v1 + kind: Namespace + metadata: + name: prometheus-example +- apiVersion: v1 + data: + prometheus.yml: | + global: + scrape_interval: 15s + evaluation_interval: 15s + scrape_configs: + - job_name: 'prometheus' + scrape_interval: 5s + static_configs: + - targets: ['localhost:9090'] + kind: ConfigMap + metadata: + name: prometheus-example-app-config + namespace: prometheus-example +- apiVersion: apps/v1 + kind: Deployment + metadata: + labels: + app: prometheus-example-app + name: prometheus-example-app + namespace: prometheus-example + spec: + replicas: 4 + selector: + matchLabels: + app: prometheus-example-app + template: + metadata: + labels: + app: prometheus-example-app + spec: + containers: + - args: + - --config.file=/etc/prometheus/prometheus.yaml + image: prom/prometheus + name: prometheus-example-app + ports: + - containerPort: 9090 + name: http + volumeMounts: + - mountPath: /etc/prometheus + name: config + volumes: + - configMap: + name: prometheus-example-app-config + name: config +- apiVersion: v1 + kind: Service + metadata: + labels: + app: prometheus-example-app + name: prometheus-example + namespace: prometheus-example + spec: + ports: + - name: http + port: 9090 + protocol: TCP + targetPort: 9090 + selector: + app: prometheus-example-app + type: ClusterIP +kind: List diff --git a/tests/integration_tests/framework/examples/manifests/dev/test-deployment.yaml b/tests/integration_tests/framework/examples/manifests/dev/test-deployment.yaml new file mode 100644 index 0000000000..6785479e82 --- /dev/null +++ b/tests/integration_tests/framework/examples/manifests/dev/test-deployment.yaml @@ -0,0 +1,70 @@ +apiVersion: v1 +items: +- apiVersion: v1 + kind: Namespace + metadata: + name: prometheus-example +- apiVersion: v1 + data: + prometheus.yml: | + global: + scrape_interval: 15s + evaluation_interval: 15s + scrape_configs: + - job_name: 'prometheus' + scrape_interval: 5s + static_configs: + - targets: ['localhost:9090'] + kind: ConfigMap + metadata: + name: prometheus-example-app-config + namespace: prometheus-example +- apiVersion: apps/v1 + kind: Deployment + metadata: + labels: + app: prometheus-example-app + name: prometheus-example-app + namespace: prometheus-example + spec: + replicas: 4 + selector: + matchLabels: + app: prometheus-example-app + template: + metadata: + labels: + app: prometheus-example-app + spec: + containers: + - args: + - --config.file=/etc/prometheus/prometheus.yml + image: prom/prometheus + name: prometheus-example-app + ports: + - containerPort: 9090 + name: http + volumeMounts: + - mountPath: /etc/prometheus + name: config + volumes: + - configMap: + name: prometheus-example-app-config + name: config +- apiVersion: v1 + kind: Service + metadata: + labels: + app: prometheus-example-app + name: prometheus-example + namespace: prometheus-example + spec: + ports: + - name: http + port: 9090 + protocol: TCP + targetPort: 9090 + selector: + app: prometheus-example-app + type: ClusterIP +kind: List diff --git a/tests/integration_tests/framework/examples/manifests/dev/test-job.yaml b/tests/integration_tests/framework/examples/manifests/dev/test-job.yaml new file mode 100644 index 0000000000..d4b07e4011 --- /dev/null +++ b/tests/integration_tests/framework/examples/manifests/dev/test-job.yaml @@ -0,0 +1,32 @@ +apiVersion: v1 +items: +- apiVersion: batch/v1 + kind: Job + metadata: + labels: + app.kubernetes.io/component: test + app.kubernetes.io/instance: rhobs-test + app.kubernetes.io/name: rhobs-test-job + name: rhobs-test-job + spec: + template: + metadata: + labels: + app.kubernetes.io/component: test + app.kubernetes.io/instance: rhobs-test + app.kubernetes.io/name: rhobs-test-job + spec: + containers: + - args: + - --namespaces=prometheus-example + - --interval=5s + - --timeout=60s + image: localhost:5001/rhobs-test:latest + name: rhobs-test-job + resources: {} + volumeMounts: [] + initContainers: [] + restartPolicy: OnFailure + serviceAccountName: rhobs-test-job + volumes: [] +kind: List diff --git a/tests/integration_tests/framework/examples/manifests/dev/test-rbac.yaml b/tests/integration_tests/framework/examples/manifests/dev/test-rbac.yaml new file mode 100644 index 0000000000..577c35e801 --- /dev/null +++ b/tests/integration_tests/framework/examples/manifests/dev/test-rbac.yaml @@ -0,0 +1,86 @@ +apiVersion: v1 +items: +- apiVersion: v1 + kind: ServiceAccount + metadata: + labels: + app.kubernetes.io/component: observability + name: rhobs-test-job + namespace: default +- apiVersion: rbac.authorization.k8s.io/v1 + kind: Role + metadata: + labels: + app.kubernetes.io/component: observability + name: rhobs-test + namespace: default + rules: + - apiGroups: + - "" + - apps + resources: + - deployments + - statefulsets + - services + - endpoints + - pods + - namespaces + - pods/log + verbs: + - get + - list + - watch +- apiVersion: rbac.authorization.k8s.io/v1 + kind: RoleBinding + metadata: + labels: + app.kubernetes.io/component: observability + name: rhobs-test + namespace: default + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: rhobs-test + subjects: + - kind: ServiceAccount + name: rhobs-test-job + namespace: default +- apiVersion: rbac.authorization.k8s.io/v1 + kind: Role + metadata: + labels: + app.kubernetes.io/component: observability + name: rhobs-test + namespace: prometheus-example + rules: + - apiGroups: + - "" + - apps + resources: + - deployments + - statefulsets + - services + - endpoints + - pods + - namespaces + - pods/log + verbs: + - get + - list + - watch +- apiVersion: rbac.authorization.k8s.io/v1 + kind: RoleBinding + metadata: + labels: + app.kubernetes.io/component: observability + name: rhobs-test + namespace: prometheus-example + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: rhobs-test + subjects: + - kind: ServiceAccount + name: rhobs-test-job + namespace: default +kind: List diff --git a/tests/integration_tests/framework/examples/manifests/openshift/rhobs-rbac-template.yaml b/tests/integration_tests/framework/examples/manifests/openshift/rhobs-rbac-template.yaml new file mode 100644 index 0000000000..d975c29372 --- /dev/null +++ b/tests/integration_tests/framework/examples/manifests/openshift/rhobs-rbac-template.yaml @@ -0,0 +1,288 @@ +apiVersion: template.openshift.io/v1 +kind: Template +metadata: + name: rhobs-test-rbac +objects: +- apiVersion: v1 + kind: ServiceAccount + metadata: + labels: + app.kubernetes.io/component: observability + name: ${SERVICE_ACCOUNT_NAME} + namespace: ${NAMESPACE} +- apiVersion: rbac.authorization.k8s.io/v1 + kind: Role + metadata: + labels: + app.kubernetes.io/component: observability + name: rhobs-test + namespace: ${NAMESPACE} + rules: + - apiGroups: + - "" + - apps + resources: + - services + - endpoints + - pods + - deployments + - statefulsets + - pods/log + verbs: + - get + - list + - watch +- apiVersion: rbac.authorization.k8s.io/v1 + kind: RoleBinding + metadata: + labels: + app.kubernetes.io/component: observability + name: rhobs-test + namespace: ${NAMESPACE} + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: rhobs-test + subjects: + - kind: ServiceAccount + name: ${SERVICE_ACCOUNT_NAME} + namespace: ${NAMESPACE} +- apiVersion: rbac.authorization.k8s.io/v1 + kind: Role + metadata: + labels: + app.kubernetes.io/component: observability + name: rhobs-test + namespace: ${OBSERVATORIUM_NAMESPACE} + rules: + - apiGroups: + - "" + - apps + resources: + - services + - endpoints + - pods + - deployments + - statefulsets + - pods/log + verbs: + - get + - list + - watch +- apiVersion: rbac.authorization.k8s.io/v1 + kind: RoleBinding + metadata: + labels: + app.kubernetes.io/component: observability + name: rhobs-test + namespace: ${OBSERVATORIUM_NAMESPACE} + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: rhobs-test + subjects: + - kind: ServiceAccount + name: ${SERVICE_ACCOUNT_NAME} + namespace: ${NAMESPACE} +- apiVersion: rbac.authorization.k8s.io/v1 + kind: Role + metadata: + labels: + app.kubernetes.io/component: observability + name: rhobs-test + namespace: ${OBSERVATORIUM_METRICS_NAMESPACE} + rules: + - apiGroups: + - "" + - apps + resources: + - services + - endpoints + - pods + - deployments + - statefulsets + - pods/log + verbs: + - get + - list + - watch +- apiVersion: rbac.authorization.k8s.io/v1 + kind: RoleBinding + metadata: + labels: + app.kubernetes.io/component: observability + name: rhobs-test + namespace: ${OBSERVATORIUM_METRICS_NAMESPACE} + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: rhobs-test + subjects: + - kind: ServiceAccount + name: ${SERVICE_ACCOUNT_NAME} + namespace: ${NAMESPACE} +- apiVersion: rbac.authorization.k8s.io/v1 + kind: Role + metadata: + labels: + app.kubernetes.io/component: observability + name: rhobs-test + namespace: ${OBSERVATORIUM_LOGS_NAMESPACE} + rules: + - apiGroups: + - "" + - apps + resources: + - services + - endpoints + - pods + - deployments + - statefulsets + - pods/log + verbs: + - get + - list + - watch +- apiVersion: rbac.authorization.k8s.io/v1 + kind: RoleBinding + metadata: + labels: + app.kubernetes.io/component: observability + name: rhobs-test + namespace: ${OBSERVATORIUM_LOGS_NAMESPACE} + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: rhobs-test + subjects: + - kind: ServiceAccount + name: ${SERVICE_ACCOUNT_NAME} + namespace: ${NAMESPACE} +- apiVersion: rbac.authorization.k8s.io/v1 + kind: Role + metadata: + labels: + app.kubernetes.io/component: observability + name: rhobs-test + namespace: ${MINIO_NAMESPACE} + rules: + - apiGroups: + - "" + - apps + resources: + - services + - endpoints + - pods + - deployments + - statefulsets + - pods/log + verbs: + - get + - list + - watch +- apiVersion: rbac.authorization.k8s.io/v1 + kind: RoleBinding + metadata: + labels: + app.kubernetes.io/component: observability + name: rhobs-test + namespace: ${MINIO_NAMESPACE} + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: rhobs-test + subjects: + - kind: ServiceAccount + name: ${SERVICE_ACCOUNT_NAME} + namespace: ${NAMESPACE} +- apiVersion: rbac.authorization.k8s.io/v1 + kind: Role + metadata: + labels: + app.kubernetes.io/component: observability + name: rhobs-test + namespace: ${DEX_NAMESPACE} + rules: + - apiGroups: + - "" + - apps + resources: + - services + - endpoints + - pods + - deployments + - statefulsets + - pods/log + verbs: + - get + - list + - watch +- apiVersion: rbac.authorization.k8s.io/v1 + kind: RoleBinding + metadata: + labels: + app.kubernetes.io/component: observability + name: rhobs-test + namespace: ${DEX_NAMESPACE} + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: rhobs-test + subjects: + - kind: ServiceAccount + name: ${SERVICE_ACCOUNT_NAME} + namespace: ${NAMESPACE} +- apiVersion: rbac.authorization.k8s.io/v1 + kind: Role + metadata: + labels: + app.kubernetes.io/component: observability + name: rhobs-test + namespace: ${TELEMETER_NAMESPACE} + rules: + - apiGroups: + - "" + - apps + resources: + - services + - endpoints + - pods + - deployments + - statefulsets + - pods/log + verbs: + - get + - list + - watch +- apiVersion: rbac.authorization.k8s.io/v1 + kind: RoleBinding + metadata: + labels: + app.kubernetes.io/component: observability + name: rhobs-test + namespace: ${TELEMETER_NAMESPACE} + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: rhobs-test + subjects: + - kind: ServiceAccount + name: ${SERVICE_ACCOUNT_NAME} + namespace: ${NAMESPACE} +parameters: +- name: NAMESPACE + value: observatorium +- name: OBSERVATORIUM_NAMESPACE + value: observatorium +- name: OBSERVATORIUM_METRICS_NAMESPACE + value: observatorium-metrics +- name: OBSERVATORIUM_LOGS_NAMESPACE + value: observatorium-logs +- name: MINIO_NAMESPACE + value: minio +- name: DEX_NAMESPACE + value: dex +- name: TELEMETER_NAMESPACE + value: telemeter +- name: SERVICE_ACCOUNT_NAME + value: rhobs-test-job diff --git a/tests/integration_tests/framework/examples/manifests/openshift/rhobs-test-job-template.yaml b/tests/integration_tests/framework/examples/manifests/openshift/rhobs-test-job-template.yaml new file mode 100644 index 0000000000..5bf1c41645 --- /dev/null +++ b/tests/integration_tests/framework/examples/manifests/openshift/rhobs-test-job-template.yaml @@ -0,0 +1,49 @@ +apiVersion: template.openshift.io/v1 +kind: Template +metadata: + name: rhobs-test-job +objects: +- apiVersion: batch/v1 + kind: Job + metadata: + labels: + app.kubernetes.io/component: test + app.kubernetes.io/instance: rhobs-test + app.kubernetes.io/name: ${JOB_NAME} + name: ${JOB_NAME} + spec: + template: + metadata: + labels: + app.kubernetes.io/component: test + app.kubernetes.io/instance: rhobs-test + app.kubernetes.io/name: ${JOB_NAME} + spec: + containers: + - args: + - --namespaces=${JOB_NAMESPACES} + - --interval=${JOB_INTERVAL} + - --timeout=${JOB_TIMEOUT} + image: ${JOB_IMAGE}:${JOB_IMAGE_TAG} + name: ${JOB_NAME} + resources: {} + volumeMounts: [] + initContainers: [] + restartPolicy: OnFailure + serviceAccountName: ${SERVICE_ACCOUNT_NAME} + volumes: [] +parameters: +- name: JOB_NAMESPACES + value: observatorium,observatorium-metrics,observatorium-logs,minio,dex,telemeter +- name: JOB_NAME + value: rhobs-test-job +- name: JOB_INTERVAL + value: 10s +- name: JOB_TIMEOUT + value: 1m +- name: JOB_IMAGE + value: quay.io/app-sre/rhobs-test +- name: JOB_IMAGE_TAG + value: latest +- name: SERVICE_ACCOUNT_NAME + value: rhobs-test-job diff --git a/tests/integration_tests/framework/examples/manifests/openshift/test-deployment-faulty-template.yaml b/tests/integration_tests/framework/examples/manifests/openshift/test-deployment-faulty-template.yaml new file mode 100644 index 0000000000..b7b483dcaf --- /dev/null +++ b/tests/integration_tests/framework/examples/manifests/openshift/test-deployment-faulty-template.yaml @@ -0,0 +1,83 @@ +apiVersion: template.openshift.io/v1 +kind: Template +metadata: + name: test-deployment +objects: +- apiVersion: v1 + kind: Namespace + metadata: + name: ${PROM_NAMESPACE} +- apiVersion: v1 + data: + prometheus.yml: | + global: + scrape_interval: 15s + evaluation_interval: 15s + scrape_configs: + - job_name: 'prometheus' + scrape_interval: 5s + static_configs: + - targets: ['localhost:9090'] + kind: ConfigMap + metadata: + name: ${PROM_CONFIG_MAP} + namespace: ${PROM_NAMESPACE} +- apiVersion: apps/v1 + kind: Deployment + metadata: + labels: + app: prometheus-example-app + name: prometheus-example-app + namespace: ${PROM_NAMESPACE} + spec: + replicas: ${PROM_REPLICAS} + selector: + matchLabels: + app: prometheus-example-app + template: + metadata: + labels: + app: prometheus-example-app + spec: + containers: + - args: + - --config.file=/etc/prometheus/prometheus.yaml + image: ${PROM_IMAGE} + name: prometheus-example-app + ports: + - containerPort: 9090 + name: http + volumeMounts: + - mountPath: /etc/prometheus + name: config + volume: + - configMap: + name: ${PROM_CONFIG_MAP} + name: config +- apiVersion: v1 + kind: Service + metadata: + labels: + app: prometheus-example-app + name: ${PROM_SERVICE_NAME} + namespace: ${PROM_NAMESPACE} + spec: + ports: + - name: http + port: 9090 + protocol: TCP + targetPort: 9090 + selector: + app: prometheus-example-app + type: ClusterIP +parameters: +- name: PROM_NAMESPACE + value: prometheus-example +- name: PROM_CONFIG_MAP + value: prometheus-example-app-config +- name: PROM_IMAGE + value: prom/prometheus +- name: PROM_REPLICAS + value: "4" +- name: PROM_SERVICE_NAME + value: prometheus-example diff --git a/tests/integration_tests/framework/examples/manifests/openshift/test-deployment-template.yaml b/tests/integration_tests/framework/examples/manifests/openshift/test-deployment-template.yaml new file mode 100644 index 0000000000..866000ef0e --- /dev/null +++ b/tests/integration_tests/framework/examples/manifests/openshift/test-deployment-template.yaml @@ -0,0 +1,83 @@ +apiVersion: template.openshift.io/v1 +kind: Template +metadata: + name: test-deployment +objects: +- apiVersion: v1 + kind: Namespace + metadata: + name: ${PROM_NAMESPACE} +- apiVersion: v1 + data: + prometheus.yml: | + global: + scrape_interval: 15s + evaluation_interval: 15s + scrape_configs: + - job_name: 'prometheus' + scrape_interval: 5s + static_configs: + - targets: ['localhost:9090'] + kind: ConfigMap + metadata: + name: ${PROM_CONFIG_MAP} + namespace: ${PROM_NAMESPACE} +- apiVersion: apps/v1 + kind: Deployment + metadata: + labels: + app: prometheus-example-app + name: prometheus-example-app + namespace: ${PROM_NAMESPACE} + spec: + replicas: ${PROM_REPLICAS} + selector: + matchLabels: + app: prometheus-example-app + template: + metadata: + labels: + app: prometheus-example-app + spec: + containers: + - args: + - --config.file=/etc/prometheus/prometheus.yml + image: ${PROM_IMAGE} + name: prometheus-example-app + ports: + - containerPort: 9090 + name: http + volumeMounts: + - mountPath: /etc/prometheus + name: config + volumes: + - configMap: + name: ${PROM_CONFIG_MAP} + name: config +- apiVersion: v1 + kind: Service + metadata: + labels: + app: prometheus-example-app + name: ${PROM_SERVICE_NAME} + namespace: ${PROM_NAMESPACE} + spec: + ports: + - name: http + port: 9090 + protocol: TCP + targetPort: 9090 + selector: + app: prometheus-example-app + type: ClusterIP +parameters: +- name: PROM_NAMESPACE + value: prometheus-example +- name: PROM_CONFIG_MAP + value: prometheus-example-app-config +- name: PROM_IMAGE + value: prom/prometheus +- name: PROM_REPLICAS + value: "4" +- name: PROM_SERVICE_NAME + value: prometheus-example diff --git a/tests/integration_tests/framework/integration-test.png b/tests/integration_tests/framework/integration-test.png new file mode 100644 index 0000000000000000000000000000000000000000..96cd44248406441a84b464aa9b12052ab3eee8bf GIT binary patch literal 104821 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

d<}%%#EQa7k3B4GT>PG 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 literal 0 HcmV?d00001 diff --git a/tests/integration_tests/framework/jsonnet/dev-manifests.jsonnet b/tests/integration_tests/framework/jsonnet/dev-manifests.jsonnet new file mode 100644 index 0000000000..d98aac04cc --- /dev/null +++ b/tests/integration_tests/framework/jsonnet/dev-manifests.jsonnet @@ -0,0 +1,139 @@ +local job = import 'job.libsonnet'; +local rbac = import 'rbac.libsonnet'; +local testdeployment = import 'test-deployment.libsonnet'; + +local rbacConfig = { + roleName: 'rhobs-test', + namespace: 'prometheus-example', + roleBindingName: 'rhobs-test', + serviceAccountName: 'rhobs-test-job', +}; +local jobConfig = { + name: 'rhobs-test-job', + namespaces: 'prometheus-example', + interval: '5s', + timeout: '60s', + image: 'localhost:5001/rhobs-test', + imageTag: 'latest', + serviceAccountName: rbacConfig.serviceAccountName, +}; +local testConfig = { + namespace: 'prometheus-example', + configMapName: 'prometheus-example-app-config', + replicas: 4, + image: 'prom/prometheus', + serviceName: 'prometheus-example', +}; +local r = rbac(rbacConfig); +local roleBinding = r.roleBinding { + subjects: [{ + kind: 'ServiceAccount', + name: rbacConfig.serviceAccountName, + namespace: 'default', + }], +}; +local j = job(jobConfig); +local d = testdeployment(testConfig); +local deployment = d.deployment { + spec: { + replicas: testConfig.replicas, + selector: { + matchLabels: { + app: 'prometheus-example-app', + }, + }, + template: { + metadata: { + labels: { + app: 'prometheus-example-app', + }, + }, + spec: { + containers: [ + { + name: 'prometheus-example-app', + image: testConfig.image, + args: ['--config.file=/etc/prometheus/prometheus.yaml'], + ports: [ + { + name: 'http', + containerPort: 9090, + }, + ], + volumeMounts+: [ + { + name: 'config', + mountPath: '/etc/prometheus', + }, + ], + + + }, + ], + volumes: [ + { + name: 'config', + configMap: { + name: testConfig.configMapName, + }, + }, + ], + }, + }, + }, +}; + +{ + 'test-rbac': { + apiVersion: 'v1', + kind: 'List', + items: [ + r.serviceAccount { + metadata+: { namespace: 'default' }, + }, + r.role { + metadata+: { namespace: 'default' }, + }, + roleBinding { + metadata+: { namespace: 'default' }, + }, + r.role {}, + roleBinding {}, + ], + }, + 'test-job': { + apiVersion: 'v1', + kind: 'List', + items: [ + j.job {}, + ], + }, + 'test-deployment': { + apiVersion: 'v1', + kind: 'List', + items: [ + d.namespace {}, + d.configMap {}, + d.deployment {}, + d.service { + metadata+: { name: testConfig.serviceName }, + }, + ], + }, + 'test-deployment-faulty': { + apiVersion: 'v1', + kind: 'List', + items: [ + d.namespace {}, + d.configMap {}, + deployment {}, + d.service { + metadata+: { name: testConfig.serviceName }, + }, + ], + }, + +} +// { [name]: r[name] for name in std.objectFields(r) if r[name] != null }+ +// { [name]: j[name] for name in std.objectFields(j) if j[name] != null }+ +// {for name in std.} diff --git a/tests/integration_tests/framework/jsonnet/job.libsonnet b/tests/integration_tests/framework/jsonnet/job.libsonnet new file mode 100644 index 0000000000..bb6042e943 --- /dev/null +++ b/tests/integration_tests/framework/jsonnet/job.libsonnet @@ -0,0 +1,54 @@ +local defaults = { + local defaults = self, + name: error 'must provide job name', + namespaces: error 'must provide namespaces', + interval: error 'must provide interval', + timeout: error 'must provide timeout', + image: error 'must provide image', + imageTag: error 'must provide image tag', + serviceAccountName: error 'must provide service account name', + labels: { + 'app.kubernetes.io/component': 'test', + 'app.kubernetes.io/instance': 'rhobs-test', + 'app.kubernetes.io/name': defaults.name, + }, +}; +function(params) { + local job = self, + config:: defaults + params, + job: { + apiVersion: 'batch/v1', + kind: 'Job', + metadata: { + labels: job.config.labels, + name: job.config.name, + }, + spec: { + template: { + metadata: { + labels: job.config.labels, + }, + spec: { + serviceAccountName: job.config.serviceAccountName, + containers: [ + { + args: [ + '--namespaces=' + job.config.namespaces, + '--interval=' + job.config.interval, + '--timeout=' + job.config.timeout, + ], + name: job.config.name, + image: job.config.image + ':' + job.config.imageTag, + resources: {}, + volumeMounts: [], + }, + ], + initContainers: [], + restartPolicy: 'OnFailure', + volumes: [], + }, + }, + //backoffLimit: 4, + }, + }, +} diff --git a/tests/integration_tests/framework/jsonnet/ocp-manifests.jsonnet b/tests/integration_tests/framework/jsonnet/ocp-manifests.jsonnet new file mode 100644 index 0000000000..2334758863 --- /dev/null +++ b/tests/integration_tests/framework/jsonnet/ocp-manifests.jsonnet @@ -0,0 +1,240 @@ +local job = import 'job.libsonnet'; +local rbac = import 'rbac.libsonnet'; +local testdeployment = import 'test-deployment.libsonnet'; + +local rbacConfig = { + roleName: 'rhobs-test', + namespace: '${NAMESPACE}', + roleBindingName: 'rhobs-test', + serviceAccountName: '${SERVICE_ACCOUNT_NAME}', + + namespaces: { + default: '${NAMESPACE}', + observatorium: '${OBSERVATORIUM_NAMESPACE}', + observatoriumMetrics: '${OBSERVATORIUM_METRICS_NAMESPACE}', + observatoriumLogs: '${OBSERVATORIUM_LOGS_NAMESPACE}', + minio: '${MINIO_NAMESPACE}', + dex: '${DEX_NAMESPACE}', + telemeter: '${TELEMETER_NAMESPACE}', + }, +}; +local jobConfig = { + name: '${JOB_NAME}', + namespaces: '${JOB_NAMESPACES}', + interval: '${JOB_INTERVAL}', + timeout: '${JOB_TIMEOUT}', + image: '${JOB_IMAGE}', + imageTag: '${JOB_IMAGE_TAG}', + serviceAccountName: rbacConfig.serviceAccountName, +}; +local testConfig = { + namespace: '${PROM_NAMESPACE}', + configMapName: '${PROM_CONFIG_MAP}', + replicas: '${PROM_REPLICAS}', + image: '${PROM_IMAGE}', + serviceName: '${PROM_SERVICE_NAME}', +}; +local r = rbac(rbacConfig); +local j = job(jobConfig); +local d = testdeployment(testConfig); +local role = r.role { + rules: [{ + apiGroups: ['', 'apps'], + resources: ['services', 'endpoints', 'pods', 'deployments', 'statefulsets', 'pods/log'], + verbs: ['get', 'list', 'watch'], + + }], +}; +local roleBinding = r.roleBinding { + subjects: [{ + kind: 'ServiceAccount', + name: rbacConfig.serviceAccountName, + namespace: rbacConfig.namespaces.default, + }], +}; +local deployment = d.deployment { + spec: { + replicas: testConfig.replicas, + selector: { + matchLabels: { + app: 'prometheus-example-app', + }, + }, + template: { + metadata: { + labels: { + app: 'prometheus-example-app', + }, + }, + spec: { + containers: [ + { + name: 'prometheus-example-app', + image: '${PROM_IMAGE}', + args: ['--config.file=/etc/prometheus/prometheus.yaml'], + ports: [ + { + name: 'http', + containerPort: 9090, + }, + ], + volumeMounts+: [ + { + name: 'config', + mountPath: '/etc/prometheus', + }, + ], + + + }, + ], + volume: [ + { + name: 'config', + configMap: { + name: '${PROM_CONFIG_MAP}', + }, + }, + ], + }, + }, + }, +}; +{ + 'rhobs-rbac-template': { + apiVersion: 'template.openshift.io/v1', + kind: 'Template', + metadata: { + name: 'rhobs-test-rbac', + }, + objects: [ + r.serviceAccount { + metadata+: { name: '${SERVICE_ACCOUNT_NAME}' }, + }, + role { + metadata+: { namespace: '${NAMESPACE}' }, + }, + roleBinding { + metadata+: { namespace: '${NAMESPACE}' }, + }, + role { + metadata+: { namespace: '${OBSERVATORIUM_NAMESPACE}' }, + }, + roleBinding { + metadata+: { namespace: '${OBSERVATORIUM_NAMESPACE}' }, + }, + role { + metadata+: { namespace: '${OBSERVATORIUM_METRICS_NAMESPACE}' }, + }, + roleBinding { + metadata+: { namespace: '${OBSERVATORIUM_METRICS_NAMESPACE}' }, + }, + role { + metadata+: { namespace: '${OBSERVATORIUM_LOGS_NAMESPACE}' }, + }, + roleBinding { + metadata+: { namespace: '${OBSERVATORIUM_LOGS_NAMESPACE}' }, + }, + role { + metadata+: { namespace: '${MINIO_NAMESPACE}' }, + }, + roleBinding { + metadata+: { namespace: '${MINIO_NAMESPACE}' }, + }, + role { + metadata+: { namespace: '${DEX_NAMESPACE}' }, + }, + roleBinding { + metadata+: { namespace: '${DEX_NAMESPACE}' }, + }, + role { + metadata+: { namespace: '${TELEMETER_NAMESPACE}' }, + }, + roleBinding { + metadata+: { namespace: '${TELEMETER_NAMESPACE}' }, + }, + + ], + parameters: [ + { name: 'NAMESPACE', value: 'observatorium' }, + { name: 'OBSERVATORIUM_NAMESPACE', value: 'observatorium' }, + { name: 'OBSERVATORIUM_METRICS_NAMESPACE', value: 'observatorium-metrics' }, + { name: 'OBSERVATORIUM_LOGS_NAMESPACE', value: 'observatorium-logs' }, + { name: 'MINIO_NAMESPACE', value: 'minio' }, + { name: 'DEX_NAMESPACE', value: 'dex' }, + { name: 'TELEMETER_NAMESPACE', value: 'telemeter' }, + { name: 'SERVICE_ACCOUNT_NAME', value: 'rhobs-test-job' }, + ], + }, + 'rhobs-test-job-template': { + apiVersion: 'template.openshift.io/v1', + kind: 'Template', + metadata: { + name: 'rhobs-test-job', + }, + objects: [ + j.job, + ], + parameters: [ + { name: 'JOB_NAMESPACES', value: 'observatorium,observatorium-metrics,observatorium-logs,minio,dex,telemeter' }, + { name: 'JOB_NAME', value: 'rhobs-test-job' }, + { name: 'JOB_INTERVAL', value: '10s' }, + { name: 'JOB_TIMEOUT', value: '1m' }, + { name: 'JOB_IMAGE', value: 'quay.io/app-sre/rhobs-test' }, + { name: 'JOB_IMAGE_TAG', value: 'latest' }, + { name: 'SERVICE_ACCOUNT_NAME', value: 'rhobs-test-job' }, + ], + }, + 'test-deployment-template': { + apiVersion: 'template.openshift.io/v1', + kind: 'Template', + metadata: { + name: 'test-deployment', + }, + objects: [ + d.namespace { + metadata+: { name: '${PROM_NAMESPACE}' }, + }, + d.configMap { + metadata+: { name: '${PROM_CONFIG_MAP}', namespace: '${PROM_NAMESPACE}' }, + }, + d.deployment {}, + d.service { + metadata+: { name: '${PROM_SERVICE_NAME}' }, + }, + ], + parameters: [ + { name: 'PROM_NAMESPACE', value: 'prometheus-example' }, + { name: 'PROM_CONFIG_MAP', value: 'prometheus-example-app-config' }, + { name: 'PROM_IMAGE', value: 'prom/prometheus' }, + { name: 'PROM_REPLICAS', value: '4' }, + { name: 'PROM_SERVICE_NAME', value: 'prometheus-example' }, + ], + }, + 'test-deployment-faulty-template': { + apiVersion: 'template.openshift.io/v1', + kind: 'Template', + metadata: { + name: 'test-deployment', + }, + objects: [ + d.namespace { + metadata+: { name: '${PROM_NAMESPACE}' }, + }, + d.configMap { + metadata+: { name: '${PROM_CONFIG_MAP}', namespace: '${PROM_NAMESPACE}' }, + }, + deployment {}, + d.service { + metadata+: { name: '${PROM_SERVICE_NAME}' }, + }, + ], + parameters: [ + { name: 'PROM_NAMESPACE', value: 'prometheus-example' }, + { name: 'PROM_CONFIG_MAP', value: 'prometheus-example-app-config' }, + { name: 'PROM_IMAGE', value: 'prom/prometheus' }, + { name: 'PROM_REPLICAS', value: '4' }, + { name: 'PROM_SERVICE_NAME', value: 'prometheus-example' }, + ], + }, +} diff --git a/tests/integration_tests/framework/jsonnet/rbac.libsonnet b/tests/integration_tests/framework/jsonnet/rbac.libsonnet new file mode 100644 index 0000000000..0466f01269 --- /dev/null +++ b/tests/integration_tests/framework/jsonnet/rbac.libsonnet @@ -0,0 +1,61 @@ +local defaults = { + roleName: error 'must provide role name', + namespace: error 'must provide namespace', + roleBindingName: error 'must provide rolebinding name', + serviceAccountName: error 'must provide service account name', + + labels:: { + 'app.kubernetes.io/component': 'observability', + }, + +}; +function(params) { + local rbac = self, + config:: defaults + params, + serviceAccount: { + apiVersion: 'v1', + kind: 'ServiceAccount', + metadata: { + name: rbac.config.serviceAccountName, + namespace: rbac.config.namespace, + labels: rbac.config.labels, + }, + }, + role: { + apiVersion: 'rbac.authorization.k8s.io/v1', + kind: 'Role', + metadata: { + labels: rbac.config.labels, + name: rbac.config.roleName, + namespace: rbac.config.namespace, + }, + rules: [ + { + apiGroups: ['', 'apps'], + resources: ['deployments', 'statefulsets', 'services', 'endpoints', 'pods', 'namespaces', 'pods/log'], + verbs: ['get', 'list', 'watch'], + }, + ], + }, + roleBinding: { + apiVersion: 'rbac.authorization.k8s.io/v1', + kind: 'RoleBinding', + metadata: { + labels: rbac.config.labels, + name: rbac.config.roleBindingName, + namespace: rbac.config.namespace, + }, + roleRef: { + apiGroup: 'rbac.authorization.k8s.io', + kind: 'Role', + name: rbac.role.metadata.name, + }, + subjects: [ + { + kind: 'ServiceAccount', + name: rbac.serviceAccount.metadata.name, + namespace: rbac.serviceAccount.metadata.namespace, + }, + ], + }, +} diff --git a/tests/integration_tests/framework/jsonnet/test-deployment.libsonnet b/tests/integration_tests/framework/jsonnet/test-deployment.libsonnet new file mode 100644 index 0000000000..672f235fa0 --- /dev/null +++ b/tests/integration_tests/framework/jsonnet/test-deployment.libsonnet @@ -0,0 +1,113 @@ +local defaults = { + namespace: error 'must provide namespace', + configMapName: error 'must provide config map name', + replicas: error 'must provide replicas', + image: error 'must provide image', + data:: { + 'prometheus.yml': "global:\n scrape_interval: 15s\n evaluation_interval: 15s\nscrape_configs:\n - job_name: 'prometheus'\n scrape_interval: 5s\n static_configs:\n - targets: ['localhost:9090']\n", + }, + serviceName: error 'must provide service name', + labels:: { + app: 'prometheus-example-app', + }, +}; +function(params) { + local prom = self, + config:: defaults + params, + namespace: { + apiVersion: 'v1', + kind: 'Namespace', + metadata: { + name: prom.config.namespace, + }, + }, + configMap: { + apiVersion: 'v1', + kind: 'ConfigMap', + metadata: { + name: prom.config.configMapName, + namespace: prom.config.namespace, + }, + data: prom.config.data, + }, + deployment: { + apiVersion: 'apps/v1', + kind: 'Deployment', + metadata: { + name: 'prometheus-example-app', + labels: prom.config.labels, + namespace: prom.config.namespace, + }, + spec: { + replicas: prom.config.replicas, + selector: { + matchLabels: { + app: 'prometheus-example-app', + }, + }, + template: { + metadata: { + labels: { + app: 'prometheus-example-app', + }, + }, + spec: { + containers: [ + { + name: 'prometheus-example-app', + image: prom.config.image, + args: [ + '--config.file=/etc/prometheus/prometheus.yml', + ], + ports: [ + { + name: 'http', + containerPort: 9090, + }, + ], + volumeMounts: [ + { + name: 'config', + mountPath: '/etc/prometheus', + }, + ], + }, + ], + volumes: [ + { + name: 'config', + configMap: { + name: prom.config.configMapName, + }, + }, + ], + }, + }, + }, + }, + service: { + apiVersion: 'v1', + kind: 'Service', + metadata: { + name: prom.config.serviceName, + labels: prom.config.labels, + namespace: prom.config.namespace, + }, + spec: { + type: 'ClusterIP', + ports: [ + { + protocol: 'TCP', + port: 9090, + targetPort: 9090, + name: 'http', + + + }, + ], + selector: { + app: 'prometheus-example-app', + }, + }, + }, +} diff --git a/tests/integration_tests/framework/pkg/client/client.go b/tests/integration_tests/framework/pkg/client/client.go new file mode 100644 index 0000000000..c017f9927c --- /dev/null +++ b/tests/integration_tests/framework/pkg/client/client.go @@ -0,0 +1,38 @@ +package client + +import ( + "github.com/rhobs/configuration/tests/integration_tests/framework/pkg/logger" + + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" +) + +func client(kubeconfig string) *kubernetes.Clientset { + var ( + config *rest.Config + err error + ) + // If no kubeconfig file specified, then use in-cluster config + if kubeconfig == "" { + config, err = rest.InClusterConfig() + if err != nil { + logger.AppLog.LogFatal("Error getting in-cluster config: %v\n", err) + } + } else { + // If kubeconfig file is specified, then use it + config, err = clientcmd.BuildConfigFromFlags("", kubeconfig) + if err != nil { + logger.AppLog.LogFatal("Error building kubeconfig from file %s: %v\n", kubeconfig, err) + } + } + // Create Kubernetes clientset + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + logger.AppLog.LogFatal("Error creating Kubernetes clientset: %v\n", err) + } + return clientset +} +func GetClient(kubeconfig string) *kubernetes.Clientset { + return client(kubeconfig) +} diff --git a/tests/integration_tests/framework/pkg/deployment/deployment.go b/tests/integration_tests/framework/pkg/deployment/deployment.go new file mode 100644 index 0000000000..576ead5fa0 --- /dev/null +++ b/tests/integration_tests/framework/pkg/deployment/deployment.go @@ -0,0 +1,176 @@ +package deployment + +import ( + "context" + "time" + + "errors" + + "github.com/rhobs/configuration/tests/integration_tests/framework/pkg/logger" + "github.com/rhobs/configuration/tests/integration_tests/framework/pkg/pod" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/util/retry" +) + +// TODO: Come up with better solution +// This is done so that unit test can work fine as retry.RetryOnConflict only works on real Kubernetes cluster +// we are using fake to build up mock client for UT +type Retryer interface { + RetryOnConflict(backoff wait.Backoff, fn func() error) error +} + +type DefaultRetryer struct{} + +func (r *DefaultRetryer) RetryOnConflict(backoff wait.Backoff, fn func() error) error { + return retry.RetryOnConflict(backoff, fn) +} + +var ( + retryer Retryer = &DefaultRetryer{} +) + +var ( + ErrListingDeployment = errors.New("error listing deployments in namespace") + ErrNoDeployment = errors.New("error no deployments found inside namespace") + ErrNoNamespace = errors.New("error no namespace provided") + ErrDeploymentsNotHealthy = errors.New("error deployment not in healthy state") + ErrNamespaceEmpty = errors.New("error namespace list empty") + ErrInvalidInterval = errors.New("error interval or timeout is invalid") + ErrDeploymentFailed = errors.New("error deployment test validation failed") +) + +func getDeployment(namespace string, clientset kubernetes.Interface) (*appsv1.DeploymentList, error) { + deployment, err := clientset.AppsV1().Deployments(namespace).List(context.Background(), metav1.ListOptions{}) + if err != nil { + return nil, ErrListingDeployment + } + if len(deployment.Items) <= 0 { + return nil, ErrNoDeployment + } + return deployment, nil +} +func storeDeploymentsByNamespace(namespaces []string, clientset kubernetes.Interface) (map[string][]appsv1.Deployment, error) { + if len(namespaces) <= 0 { + logger.AppLog.LogWarning("no namespace provided") + return nil, ErrNoNamespace + } + deploymentsByNamespace := make(map[string][]appsv1.Deployment) + for _, namespace := range namespaces { + logger.AppLog.LogInfo("Checking Deployments status inside namespace %s\n", namespace) + // If namespace name is invalid + if namespace != "" { + deploymentList, err := getDeployment(namespace, clientset) + // unable to get deployments + if err != nil { + return nil, err + } + deploymentsByNamespace[namespace] = deploymentList.Items + } else { + logger.AppLog.LogError("invalid namespace provided: %s\n", namespace) + } + + } + if len(deploymentsByNamespace) <= 0 { + logger.AppLog.LogWarning("there is no deployment in provided namespaces: %v\n", namespaces) + return nil, ErrNoDeployment + } + return deploymentsByNamespace, nil +} +func checkDeploymentStatus(namespace string, deployment appsv1.Deployment, clientset kubernetes.Interface) error { + err := retryer.RetryOnConflict(retry.DefaultRetry, func() error { + updatedDeployment, err := clientset.AppsV1().Deployments(namespace).Get(context.Background(), deployment.Name, metav1.GetOptions{}) + if err != nil { + return err + } + if updatedDeployment.Status.UpdatedReplicas == *deployment.Spec.Replicas && + updatedDeployment.Status.Replicas == *deployment.Spec.Replicas && + updatedDeployment.Status.AvailableReplicas == *deployment.Spec.Replicas && + updatedDeployment.Status.ObservedGeneration >= deployment.Generation { + logger.AppLog.LogInfo("deployment %s is available in namespace %s\n", deployment.Name, namespace) + return nil + } else { + logger.AppLog.LogWarning("deployment %s is not available in namespace %s. Checking condition\n", deployment.Name, namespace) + for _, condition := range updatedDeployment.Status.Conditions { + if condition.Type == appsv1.DeploymentAvailable && condition.Status == corev1.ConditionFalse { + logger.AppLog.LogError("reason: %v\n", condition.Reason) + break + } + } + } + logger.AppLog.LogWarning("waiting for deployment %s to be available in namespace %s\n", deployment.Name, namespace) + logger.AppLog.LogWarning("deployment %v is not in healthy state inside namespace %v\n", deployment.Name, namespace) + return ErrDeploymentsNotHealthy + }) + return err +} +func validateDeploymentsByNamespace(namespaces []string, deploymentsByNamespace map[string][]appsv1.Deployment, clientset kubernetes.Interface, interval, timeout time.Duration) error { + var depErrList []error + var podErrList []error + if len(namespaces) <= 0 { + logger.AppLog.LogError("namespace list empty %v. no namespace provided. please provide atleast one namespace\n", namespaces) + return ErrNamespaceEmpty + } + if len(deploymentsByNamespace) <= 0 { + return ErrNoDeployment + } + if interval.Seconds() <= 0 || timeout.Seconds() <= 0 { + logger.AppLog.LogError("interval or timeout is invalid. please provide the valid interval or timeout duration\n") + return ErrInvalidInterval + // fmt.Errorf("interval or timeout is invalid. please provide the valid interval or timeout duration\n") + } + for _, namespace := range namespaces { + for _, deployment := range deploymentsByNamespace[namespace] { + err := wait.PollUntilContextTimeout(context.TODO(), interval, timeout, false, func(context.Context) (bool, error) { + err := checkDeploymentStatus(namespace, deployment, clientset) + if err != nil { + return false, err + } + return true, nil + + }) + if err != nil { + logger.AppLog.LogError("error checking the deployment %s in namespace %s reason: %v\n", deployment.Name, namespace, err) + depErrList = append(depErrList, err) + } + err = wait.PollUntilContextTimeout(context.TODO(), interval, timeout, false, func(context.Context) (bool, error) { + err := pod.GetPodStatus(namespace, labels.SelectorFromSet(deployment.Spec.Selector.MatchLabels), clientset) + if err != nil { + return false, err + } + return true, nil + }) + if err != nil { + logger.AppLog.LogError("error checking the pod logs of deployment %s in namespace %s, reason: %v\n", deployment.Name, namespace, err) + podErrList = append(podErrList, err) + } + + } + } + if len(depErrList) != 0 || len(podErrList) != 0 { + logger.AppLog.LogError("to many errors. deployment validation test's failed\n") + return ErrDeploymentFailed + } + return nil +} +func CheckDeployments(namespace []string, clientset kubernetes.Interface, interval, timeout time.Duration) error { + logger.AppLog.LogInfo("Begin Deployment validation") + deploymentsByNamespace, err := storeDeploymentsByNamespace(namespace, clientset) + if err != nil { + if errors.Is(err, ErrNoDeployment) { + logger.AppLog.LogWarning("no deployment found in namespace, skipping deployment validations\n") + return nil + } + return err + } + err = validateDeploymentsByNamespace(namespace, deploymentsByNamespace, clientset, interval, timeout) + if err != nil { + return err + } + logger.AppLog.LogInfo("End Deployment validation") + return nil +} diff --git a/tests/integration_tests/framework/pkg/deployment/deployment_test.go b/tests/integration_tests/framework/pkg/deployment/deployment_test.go new file mode 100644 index 0000000000..9ce65eea91 --- /dev/null +++ b/tests/integration_tests/framework/pkg/deployment/deployment_test.go @@ -0,0 +1,367 @@ +package deployment + +import ( + "testing" + "time" + + "github.com/rhobs/configuration/tests/integration_tests/framework/pkg/logger" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes/fake" +) + +var ( + testNS = "test-namespace" + testDep = "test-deployment" + testLabels = make(map[string]string) + testDepList = appsv1.DeploymentList{ + Items: []appsv1.Deployment{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: testDep, + Namespace: testNS, + }, + Spec: appsv1.DeploymentSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: testLabels, + }, + }, + }, + }, + } +) + +// Limitation: Since retry.RetryOnConflict only works on a running Kubernetes cluster. +// we use fake to build client we have to agument the way retryOnConflict works. +// TODO: Come up with good way to handle this + +type mockRetryer struct { + err error +} + +func (m *mockRetryer) RetryOnConflict(backoff wait.Backoff, fn func() error) error { + return m.err +} + +func TestGetDeployment(t *testing.T) { + clienset := fake.NewSimpleClientset(&testDepList) + logger.NewLogger(logger.LevelInfo) + dep, err := getDeployment(testNS, clienset) + if err != nil { + t.Fatalf("expected nil got: %v", err) + } + if len(dep.Items) != 1 { + t.Errorf("expected 1 deployment, got: %v", len(dep.Items)) + } +} + +func TestGetDeploymentNoDeployment(t *testing.T) { + clientset := fake.NewSimpleClientset() + logger.NewLogger(logger.LevelInfo) + _, err := getDeployment(testNS, clientset) + if err != ErrNoDeployment { + t.Fatalf("expected ErrNoDeployment, got: %v", err) + } +} + +func TestStoreDeploymentsByNamespace(t *testing.T) { + clientset := fake.NewSimpleClientset(&testDepList) + namespaces := []string{testNS} + deploymentsByNamespace, err := storeDeploymentsByNamespace(namespaces, clientset) + if err != nil { + t.Fatalf("expected nil, got: %v", err) + } + if len(deploymentsByNamespace) != 1 { + t.Errorf("expected 1 deployment, got: %v", len(deploymentsByNamespace)) + } +} + +func TestStoreDeploymentsByNamespaceNoNamespace(t *testing.T) { + clientset := fake.NewSimpleClientset(&testDepList) + logger.NewLogger(logger.LevelInfo) + namespaces := []string{} + _, err := storeDeploymentsByNamespace(namespaces, clientset) + if err != ErrNoNamespace { + t.Fatalf("expected ErrNoNamespace, got: %v", err) + } +} + +func TestStoreDeploymentsByNamespaceNoDeployments(t *testing.T) { + clientset := fake.NewSimpleClientset() + logger.NewLogger(logger.LevelInfo) + namespaces := []string{testNS} + _, err := storeDeploymentsByNamespace(namespaces, clientset) + if err != ErrNoDeployment { + t.Fatalf("expected ErrNoDeployment, got: %v", err) + } +} + +func TestCheckDeploymentStatus(t *testing.T) { + clientset := fake.NewSimpleClientset(&testDepList) + // TODO: Come up with good way to write this + retryer = &mockRetryer{err: nil} + logger.NewLogger(logger.LevelInfo) + err := checkDeploymentStatus(testNS, testDepList.Items[0], clientset) + if err != nil { + t.Fatalf("expected nil, got: %v", err) + } +} + +func TestCheckDeploymentStatusNotHealthy(t *testing.T) { + faultyDep := appsv1.DeploymentList{ + Items: []appsv1.Deployment{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: testDep, + Namespace: testNS, + }, + Status: appsv1.DeploymentStatus{ + AvailableReplicas: 0, + }, + }, + }, + } + clientset := fake.NewSimpleClientset(&faultyDep) + // TODO: Come up with good way to write this + retryer = &mockRetryer{err: ErrDeploymentsNotHealthy} + logger.NewLogger(logger.LevelInfo) + err := checkDeploymentStatus(testNS, faultyDep.Items[0], clientset) + if err != ErrDeploymentsNotHealthy { + t.Fatalf("expected ErrDeploymentsNotHealthy, got: %v", err) + } +} + +func TestValidateDeploymentsByNamespace(t *testing.T) { + testLabels["app"] = "test-app" + testPods := corev1.PodList{ + Items: []corev1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "testPod", + Namespace: testNS, + Labels: testLabels, + }, + Status: corev1.PodStatus{ + Phase: corev1.PodRunning, + }, + }, + }, + } + clientset := fake.NewSimpleClientset(&testDepList, &testPods) + // TODO: Come up with good way to write this + retryer = &mockRetryer{err: nil} + logger.NewLogger(logger.LevelInfo) + namespaces := []string{testNS} + deploymentsByNamepace := make(map[string][]appsv1.Deployment) + deploymentsByNamepace[testNS] = testDepList.Items + interval := 1 * time.Second + timeout := 5 * time.Second + err := validateDeploymentsByNamespace(namespaces, deploymentsByNamepace, clientset, interval, timeout) + if err != nil { + t.Fatalf("expected nil, got: %v", err) + } +} + +func TestValidateDeploymentsByNamespaceNoNamespace(t *testing.T) { + testLabels["app"] = "test-app" + testPods := corev1.PodList{ + Items: []corev1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "testPod", + Namespace: testNS, + Labels: testLabels, + }, + Status: corev1.PodStatus{ + Phase: corev1.PodRunning, + }, + }, + }, + } + clientset := fake.NewSimpleClientset(&testDepList, &testPods) + // TODO: Come up with good way to write this + retryer = &mockRetryer{err: nil} + logger.NewLogger(logger.LevelInfo) + namespaces := []string{} + deploymentsByNamepace := make(map[string][]appsv1.Deployment) + interval := 1 * time.Second + timeout := 5 * time.Second + err := validateDeploymentsByNamespace(namespaces, deploymentsByNamepace, clientset, interval, timeout) + if err != ErrNamespaceEmpty { + t.Fatalf("expected ErrNamespaceEmpty, got: %v", err) + } + +} + +func TestValidateDeploymentsByNamespaceInvalidInterval(t *testing.T) { + testLabels["app"] = "test-app" + testPods := corev1.PodList{ + Items: []corev1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "testPod", + Namespace: testNS, + Labels: testLabels, + }, + Status: corev1.PodStatus{ + Phase: corev1.PodRunning, + }, + }, + }, + } + clientset := fake.NewSimpleClientset(&testDepList, &testPods) + // TODO: Come up with good way to write this + retryer = &mockRetryer{err: nil} + logger.NewLogger(logger.LevelInfo) + namespaces := []string{testNS} + deploymentsByNamepace := make(map[string][]appsv1.Deployment) + deploymentsByNamepace[testNS] = testDepList.Items + interval := -1 * time.Second + timeout := -5 * time.Second + err := validateDeploymentsByNamespace(namespaces, deploymentsByNamepace, clientset, interval, timeout) + if err != ErrInvalidInterval { + t.Fatalf("expected ErrInvalidInterval, got: %v", err) + } +} + +func TestValidateDeploymentsByNamespaceNoDeploymentsByNamespace(t *testing.T) { + clientset := fake.NewSimpleClientset() + // TODO: Come up with good way to write this + retryer = &mockRetryer{err: nil} + logger.NewLogger(logger.LevelInfo) + namespaces := []string{testNS} + deploymentsByNamepace := make(map[string][]appsv1.Deployment) + interval := 1 * time.Second + timeout := 5 * time.Second + err := validateDeploymentsByNamespace(namespaces, deploymentsByNamepace, clientset, interval, timeout) + if err != ErrNoDeployment { + t.Fatalf("expected ErrNoDeployment, got: %v", err) + } +} + +func TestValidateDeploymentsByNamespaceDeploymentFailed(t *testing.T) { + testLabels["app"] = "test-app" + faultyPods := corev1.PodList{ + Items: []corev1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "testPod", + Namespace: testNS, + Labels: testLabels, + }, + Status: corev1.PodStatus{ + Phase: corev1.PodUnknown, + }, + }, + }, + } + faultyDep := appsv1.DeploymentList{ + Items: []appsv1.Deployment{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: testDep, + Namespace: testNS, + }, + Spec: appsv1.DeploymentSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: testLabels, + }, + }, + Status: appsv1.DeploymentStatus{ + AvailableReplicas: 0, + }, + }, + }, + } + + clientset := fake.NewSimpleClientset(&faultyDep, &faultyPods) + // TODO: Come up with good way to write this + retryer = &mockRetryer{err: ErrDeploymentsNotHealthy} + logger.NewLogger(logger.LevelInfo) + namespaces := []string{testNS} + deploymentsByNamepace := make(map[string][]appsv1.Deployment) + deploymentsByNamepace[testNS] = faultyDep.Items + interval := 1 * time.Second + timeout := 5 * time.Second + err := validateDeploymentsByNamespace(namespaces, deploymentsByNamepace, clientset, interval, timeout) + if err != ErrDeploymentFailed { + t.Fatalf("expected ErrDeploymentFailed, got: %v", err) + } +} + +func TestValidateDeploymentsByNamespacePodFailed(t *testing.T) { + testLabels["app"] = "test-app" + faultyPods := corev1.PodList{ + Items: []corev1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "testPod", + Namespace: testNS, + Labels: testLabels, + }, + Status: corev1.PodStatus{ + Phase: corev1.PodUnknown, + }, + }, + }, + } + + clientset := fake.NewSimpleClientset(&testDepList, &faultyPods) + // TODO: Come up with good way to write this + retryer = &mockRetryer{err: nil} + logger.NewLogger(logger.LevelInfo) + namespaces := []string{testNS} + deploymentsByNamepace := make(map[string][]appsv1.Deployment) + deploymentsByNamepace[testNS] = testDepList.Items + interval := 1 * time.Second + timeout := 5 * time.Second + err := validateDeploymentsByNamespace(namespaces, deploymentsByNamepace, clientset, interval, timeout) + if err != ErrDeploymentFailed { + t.Fatalf("expected ErrDeploymentFailed, got: %v", err) + } +} + +func TestCheckDeployments(t *testing.T) { + testLabels["app"] = "test-app" + testPods := corev1.PodList{ + Items: []corev1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "testPod", + Namespace: testNS, + Labels: testLabels, + }, + Status: corev1.PodStatus{ + Phase: corev1.PodRunning, + }, + }, + }, + } + clientset := fake.NewSimpleClientset(&testDepList, &testPods) + // TODO: Come up with good way to write this + retryer = &mockRetryer{err: nil} + logger.NewLogger(logger.LevelInfo) + namespaces := []string{testNS} + interval := 1 * time.Second + timeout := 5 * time.Second + err := CheckDeployments(namespaces, clientset, interval, timeout) + if err != nil { + t.Fatalf("expected nil, got: %v", err) + } +} + +func TestCheckDeploymentsNoNamespace(t *testing.T) { + clientset := fake.NewSimpleClientset(&testDepList) + // TODO: Come up with good way to write this + retryer = &mockRetryer{err: nil} + logger.NewLogger(logger.LevelInfo) + namespaces := []string{} + interval := 1 * time.Second + timeout := 5 * time.Second + err := CheckDeployments(namespaces, clientset, interval, timeout) + if err != ErrNoNamespace { + t.Fatalf("expected ErrNoNamespace, got: %v", err) + } +} diff --git a/tests/integration_tests/framework/pkg/logger/logger.go b/tests/integration_tests/framework/pkg/logger/logger.go new file mode 100644 index 0000000000..da59adf72d --- /dev/null +++ b/tests/integration_tests/framework/pkg/logger/logger.go @@ -0,0 +1,109 @@ +package logger + +import ( + "log" + "os" + "path/filepath" + "runtime" + // "strings" + // "golang.org/x/term" +) + +type Level int + +const ( + LevelDebug Level = iota + LevelInfo + LevelWarning + LevelError + LevelFatal +) + +type CustomLogger struct { + Debug *log.Logger + Info *log.Logger + Warning *log.Logger + Error *log.Logger + Fatal *log.Logger + Separator *log.Logger + List *log.Logger + Startup *log.Logger + LogLevel Level +} + +var AppLog = &CustomLogger{} + +func NewLogger(logLevel Level) { + AppLog = &CustomLogger{ + Debug: log.New(os.Stdout, "🛠️ DEBUG: ", log.Ldate|log.Ltime), + Info: log.New(os.Stdout, "ℹ️ INFO: ", log.Ldate|log.Ltime), + Warning: log.New(os.Stdout, "⚠️ WARNING: ", log.Ldate|log.Ltime), + Error: log.New(os.Stdout, "❗️ERROR: ", log.Ldate|log.Ltime), + Fatal: log.New(os.Stdout, "💀 FATAL: ", log.Ldate|log.Ltime), + Separator: log.New(os.Stdout, "", 0), + List: log.New(os.Stdout, "", 0), + Startup: log.New(os.Stdout, "", 0), + LogLevel: logLevel, + } +} + +func (c *CustomLogger) LogInfo(format string, v ...interface{}) { + if c.LogLevel <= LevelInfo { + _, file, line, _ := runtime.Caller(1) + c.Info.Printf("%s:%d "+format, append([]interface{}{filepath.Base(file), line}, v...)...) + } +} + +func (c *CustomLogger) LogWarning(format string, v ...interface{}) { + if c.LogLevel <= LevelWarning { + _, file, line, _ := runtime.Caller(1) + c.Warning.Printf("%s:%d "+format, append([]interface{}{filepath.Base(file), line}, v...)...) + } +} +func (c *CustomLogger) LogError(format string, v ...interface{}) { + if c.LogLevel <= LevelError { + _, file, line, _ := runtime.Caller(1) + c.Error.Printf("%s:%d "+format, append([]interface{}{filepath.Base(file), line}, v...)...) + } +} +func (c *CustomLogger) LogDebug(format string, v ...interface{}) { + if c.LogLevel <= LevelDebug { + _, file, line, _ := runtime.Caller(1) + c.Debug.Printf("%s:%d "+format, append([]interface{}{filepath.Base(file), line}, v...)...) + } +} +func (c *CustomLogger) LogFatal(format string, v ...interface{}) { + if c.LogLevel <= LevelFatal { + _, file, line, _ := runtime.Caller(1) + c.Fatal.Fatalf("%s:%d "+format, append([]interface{}{filepath.Base(file), line}, v...)...) + os.Exit(1) + } +} +func (c *CustomLogger) LogSeperator() { + // TODO: Implement auto line separator + // width, _, _ := term.GetSize(int(os.Stdout.Fd())) + // separator := strings.Repeat("▁", width) + separator := "\n---------------------------------------------------\n" + c.Separator.Printf(separator) +} +func (c *CustomLogger) LogStartup(cfgs ...interface{}) { + c.Startup.Printf("🧪 RHOBS Integration-Test's\n") + c.Startup.Printf("📇 Namespaces: %v", cfgs[0]) + if cfgs[1] != nil { + c.Startup.Printf("👷‍♂️ Client established: True") + } else { + c.Startup.Printf("👷‍♂️ Client established: False") + } + if cfgs[2] != "" { + c.Startup.Printf("📁 KubeConfig: %v", cfgs[2]) + } + c.Startup.Printf("✏️ Log Level: %v", cfgs[3]) + c.Startup.Printf("⏳ Interval: %v", cfgs[4]) + c.Startup.Printf("⏳ Timeout: %v", cfgs[5]) + +} +func (c *CustomLogger) LogErrList(errList []error) { + for i, err := range errList { + c.List.Printf("➡️ Error %d: %v\n", i, err) + } +} diff --git a/tests/integration_tests/framework/pkg/pod/pod.go b/tests/integration_tests/framework/pkg/pod/pod.go new file mode 100644 index 0000000000..88e31e27e3 --- /dev/null +++ b/tests/integration_tests/framework/pkg/pod/pod.go @@ -0,0 +1,105 @@ +package pod + +import ( + "context" + "errors" + "fmt" + "strings" + + "github.com/rhobs/configuration/tests/integration_tests/framework/pkg/logger" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/kubernetes" +) + +var ( + ErrFetchLogs = errors.New("error cannot fetch container logs inside pod") + ErrNoNamespace = errors.New("error no namespace provided") + ErrListingPods = errors.New("error listing pods in namespace") + ErrPodNotRunning = errors.New("error pod is not running in namespace") + ErrNoPod = errors.New("error cannot find pod in namespace") +) + +func getPodLogs(namespace string, clientset kubernetes.Interface, pod corev1.Pod) error { + tailline := int64(10) + seconds := int64(300) + for _, container := range pod.Spec.Containers { + logs, err := clientset.CoreV1().Pods(namespace).GetLogs(pod.Name, &corev1.PodLogOptions{Container: container.Name, SinceSeconds: &seconds, TailLines: &tailline}).Do(context.Background()).Raw() + if err != nil { + logger.AppLog.LogError("cannot fetch container: %s log's inside pod: %s error: %v\n", container.Name, pod.Name, err) + return ErrFetchLogs + } + for _, line := range strings.Split(string(logs), "\\n") { + if strings.Contains(line, "error") || strings.Contains(line, "Error") || strings.Contains(line, "Exception") || strings.Contains(line, "exception") { + logger.AppLog.LogError("container: %s inside pod: %s has errors in logs:", container.Name, pod.Name) + logger.AppLog.LogSeperator() + logger.AppLog.LogError("Log Line: %s", line) + logger.AppLog.LogSeperator() + } else { + logger.AppLog.LogDebug("container: %s inside pod: %s has no errors in logs\n", container.Name, pod.Name) + } + } + } + return nil +} + +func checkPodHealth(namespace string, labels labels.Selector, clientset kubernetes.Interface) error { + + if len(namespace) <= 0 { + return ErrNoNamespace + } + podList, err := clientset.CoreV1().Pods(namespace).List(context.Background(), metav1.ListOptions{LabelSelector: labels.String()}) + if err != nil { + logger.AppLog.LogError("cannot list pods inside namespace %s, err: %v\n", namespace, err) + return ErrListingPods + } + err = checkPodStatus(namespace, *podList, clientset) + if err != nil { + return err + } + logger.AppLog.LogInfo("Checking for error's/exception's in pod logs") + for _, pod := range podList.Items { + if pod.Status.Phase != "Running" { + logger.AppLog.LogInfo("pod: %s is not running inside namespace: %s\n", pod.Name, namespace) + return ErrPodNotRunning + } + err = getPodLogs(namespace, clientset, pod) + if err != nil { + logger.AppLog.LogError("error checking pod logs in namespace %s\n", namespace) + return err + } + } + return nil +} +func checkPodStatus(namespace string, podList corev1.PodList, clientset kubernetes.Interface) error { + if len(namespace) <= 0 { + return ErrNoNamespace + } + if len(podList.Items) <= 0 { + return ErrNoPod + } + + for _, pod := range podList.Items { + logger.AppLog.LogDebug("pod name: %s", pod.Name) + if pod.Status.Phase != "Running" { + logger.AppLog.LogError("pod: %s is not running inside namespace: %s\n", pod.Name, namespace) + return ErrPodNotRunning + + } + for _, container := range pod.Status.ContainerStatuses { + if container.RestartCount >= 1 && container.State.Waiting != nil && container.LastTerminationState.Terminated != nil { + err := getPodLogs(namespace, clientset, pod) + if err != nil { + return err + } + return fmt.Errorf("pod: %s has restart count: %d\ncurrent state: message: %s, reason: %s \nlast state: message: %s, reason: %s\n", container.Name, container.RestartCount, container.State.Waiting.Message, container.State.Waiting.Reason, container.LastTerminationState.Terminated.Message, container.LastTerminationState.Terminated.Reason) + } + } + } + return nil +} +func GetPodStatus(namespace string, labels labels.Selector, clientset kubernetes.Interface) error { + logger.AppLog.LogInfo("Checking pod status") + return checkPodHealth(namespace, labels, clientset) +} diff --git a/tests/integration_tests/framework/pkg/pod/pod_test.go b/tests/integration_tests/framework/pkg/pod/pod_test.go new file mode 100644 index 0000000000..ddec131a02 --- /dev/null +++ b/tests/integration_tests/framework/pkg/pod/pod_test.go @@ -0,0 +1,228 @@ +package pod + +import ( + "testing" + + "github.com/rhobs/configuration/tests/integration_tests/framework/pkg/logger" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/kubernetes/fake" +) + +var ( + testNS = "test-namespace" + testPod = "test-pod" + testContainer = "test-container" + testImage = "quay.io/foo/bar:latest" + testLabels = make(map[string]string) + testPodList = corev1.PodList{ + Items: []corev1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: testPod, + Namespace: testNS, + Labels: testLabels, + }, + Status: corev1.PodStatus{ + Phase: corev1.PodRunning, + ContainerStatuses: []corev1.ContainerStatus{ + { + RestartCount: 0, + State: corev1.ContainerState{ + Running: &corev1.ContainerStateRunning{ + StartedAt: metav1.Now(), + }, + }, + }, + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: testContainer, + Image: testImage, + }, + }, + }, + }, + }, + } +) + +func TestCheckPodStatus(t *testing.T) { + clientset := fake.NewSimpleClientset(&testPodList) + logger.NewLogger(logger.LevelInfo) + err := checkPodStatus(testNS, testPodList, clientset) + if err != nil { + t.Fatalf("expected nil got: %v", err) + } +} +func TestCheckPodStatusNoNamespace(t *testing.T) { + clientset := fake.NewSimpleClientset(&testPodList) + logger.NewLogger(logger.LevelInfo) + err := checkPodStatus("", testPodList, clientset) + if err != ErrNoNamespace { + t.Fatalf("expected ErrNoNamespace, got: %v", err) + } +} +func TestCheckPodStatusNoPod(t *testing.T) { + podList := corev1.PodList{} + clientset := fake.NewSimpleClientset() + logger.NewLogger(logger.LevelInfo) + err := checkPodStatus(testNS, podList, clientset) + if err != ErrNoPod { + t.Fatalf("expected ErrNoPod, got: %v", err) + } +} +func TestCheckPodStatusNoRunningPod(t *testing.T) { + faultyPodList := corev1.PodList{ + Items: []corev1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: testPod, + Namespace: testNS, + }, + Status: corev1.PodStatus{ + Phase: corev1.PodPending, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: testContainer, + Image: testImage, + }, + }, + }, + }, + }, + } + clientset := fake.NewSimpleClientset(&faultyPodList) + logger.NewLogger(logger.LevelInfo) + err := checkPodStatus(testNS, faultyPodList, clientset) + if err != ErrPodNotRunning { + t.Fatalf("expected ErrPodNotRunning. got: %v", err) + } +} + +func TestCheckPodHealth(t *testing.T) { + testLabels["app"] = "test-app" + logger.NewLogger(logger.LevelInfo) + ls := labels.SelectorFromSet(testLabels) + clientset := fake.NewSimpleClientset(&testPodList) + err := checkPodHealth(testNS, ls, clientset) + if err != nil { + t.Fatalf("expected nil, got: %v", err) + } +} +func TestCheckPodHealthNoNamespace(t *testing.T) { + testLabels["app"] = "test-app" + logger.NewLogger(logger.LevelInfo) + ls := labels.SelectorFromSet(testLabels) + clientset := fake.NewSimpleClientset(&testPodList) + err := checkPodHealth("", ls, clientset) + if err != ErrNoNamespace { + t.Fatalf("expected ErrNoNamespace, got: %v", err) + } +} + +func TestCheckPodHealthNoPod(t *testing.T) { + logger.NewLogger(logger.LevelInfo) + ls := labels.SelectorFromSet(testLabels) + clientset := fake.NewSimpleClientset(&testPodList) + err := checkPodHealth("testNS", ls, clientset) + if err != ErrNoPod { + t.Fatalf("expected ErrNoPod, got: %v", err) + } +} +func TestCheckPodHealthNoRunningPod(t *testing.T) { + testLabels["app"] = "test-app" + faultyPodList := corev1.PodList{ + Items: []corev1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: testPod, + Namespace: testNS, + Labels: testLabels, + }, + Status: corev1.PodStatus{ + Phase: corev1.PodPending, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: testContainer, + Image: testImage, + }, + }, + }, + }, + }, + } + + clientset := fake.NewSimpleClientset(&faultyPodList) + logger.NewLogger(logger.LevelInfo) + ls := labels.SelectorFromSet(testLabels) + err := checkPodHealth(testNS, ls, clientset) + if err != ErrPodNotRunning { + t.Fatalf("expected ErrPodNotRunning, got: %v", err) + } + +} +func TestCheckPodHealthWithRestartingContainer(t *testing.T) { + faultyPodList := corev1.PodList{ + Items: []corev1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: testPod, + Namespace: testNS, + Labels: testLabels, + }, + Status: corev1.PodStatus{ + Phase: corev1.PodRunning, + ContainerStatuses: []corev1.ContainerStatus{ + { + RestartCount: 1, + State: corev1.ContainerState{ + Waiting: &corev1.ContainerStateWaiting{ + Reason: "CrashLoopBackOff", + }, + }, + LastTerminationState: corev1.ContainerState{ + Terminated: &corev1.ContainerStateTerminated{ + Reason: "error", + }, + }, + }, + }, + }, + }, + }, + } + clientset := fake.NewSimpleClientset(&faultyPodList) + logger.NewLogger(logger.LevelInfo) + ls := labels.SelectorFromSet(testLabels) + err := checkPodHealth(testNS, ls, clientset) + if err == nil { + t.Errorf("expected an error due to restarting container, got: %v", err) + } +} + +func TestGetPodStatus(t *testing.T) { + ls := labels.SelectorFromSet(testLabels) + clientset := fake.NewSimpleClientset(&testPodList) + logger.NewLogger(logger.LevelInfo) + err := GetPodStatus(testNS, ls, clientset) + if err != nil { + t.Fatalf("expected nil, got: %v", err) + } +} +func TestGetPodStatusNoNamespace(t *testing.T) { + ls := labels.SelectorFromSet(testLabels) + clientset := fake.NewSimpleClientset(&testPodList) + logger.NewLogger(logger.LevelInfo) + err := GetPodStatus("", ls, clientset) + if err != ErrNoNamespace { + t.Fatalf("expected ErrNoNamespace, got: %v", err) + } +} diff --git a/tests/integration_tests/framework/pkg/service/service.go b/tests/integration_tests/framework/pkg/service/service.go new file mode 100644 index 0000000000..5faabaa1df --- /dev/null +++ b/tests/integration_tests/framework/pkg/service/service.go @@ -0,0 +1,162 @@ +package service + +import ( + "context" + "time" + + "errors" + + "github.com/rhobs/configuration/tests/integration_tests/framework/pkg/logger" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/util/retry" +) + +// TODO: Come up with better solution +// This is done so that unit test can work fine as retry.RetryOnConflict only works on real Kubernetes cluster +// we are using fake to build up mock client for UT +type Retryer interface { + RetryOnConflict(backoff wait.Backoff, fn func() error) error +} + +type DefaultRetryer struct{} + +func (r *DefaultRetryer) RetryOnConflict(backoff wait.Backoff, fn func() error) error { + return retry.RetryOnConflict(backoff, fn) +} + +var ( + retryer Retryer = &DefaultRetryer{} +) + +var ( + ErrNoNamespace = errors.New("error no namespace provided") + ErrListingService = errors.New("error listing services in namespace") + ErrNoService = errors.New("error no service in namespace") + ErrNamespaceEmpty = errors.New("error namespace list empty") + ErrServiceNotHealthy = errors.New("error service not in healthy state") + ErrInvalidInterval = errors.New("error interval or timeout is invalid") + ErrServiceFailed = errors.New("error service test validation failed") +) + +func getService(namespace string, clientset kubernetes.Interface) (*corev1.ServiceList, error) { + if len(namespace) <= 0 { + return nil, ErrNoNamespace + } + service, err := clientset.CoreV1().Services(namespace).List(context.Background(), metav1.ListOptions{}) + if err != nil { + logger.AppLog.LogError("error listing services in namespace %s: %v\n", namespace, err) + return nil, ErrListingService + } + if len(service.Items) <= 0 { + logger.AppLog.LogWarning("cannot find service in the namespace: %s\n", namespace) + return nil, ErrNoService + } + return service, nil +} +func storeServicesByNamespace(namespaces []string, clientset kubernetes.Interface) (map[string][]corev1.Service, error) { + if len(namespaces) <= 0 { + return nil, ErrNoNamespace + } + servicesByNamespace := make(map[string][]corev1.Service) + for _, namespace := range namespaces { + logger.AppLog.LogInfo("Checking Service status inside namespace %s\n", namespace) + + if namespace != "" { + serviceList, err := getService(namespace, clientset) + if err != nil { + return nil, err + } + servicesByNamespace[namespace] = serviceList.Items + } else { + logger.AppLog.LogError("invalid namespace provided: %s\n", namespace) + } + } + if len(servicesByNamespace) <= 0 { + logger.AppLog.LogWarning("there is no service in provided namespaces: %v\n", namespaces) + return nil, ErrNoService + } + return servicesByNamespace, nil +} +func checkServiceStatus(namespace string, service corev1.Service, clientset kubernetes.Interface) error { + err := retryer.RetryOnConflict(retry.DefaultRetry, func() error { + updatedService, err := clientset.CoreV1().Services(namespace).Get(context.Background(), service.Name, metav1.GetOptions{}) + if err != nil { + return err + } + for _, port := range updatedService.Spec.Ports { + endpoint, err := clientset.CoreV1().Endpoints(namespace).Get(context.Background(), service.Name, metav1.GetOptions{}) + if err != nil { + return err + } + for _, subset := range endpoint.Subsets { + for _, endpointPort := range subset.Ports { + if endpointPort.Name == port.Name && endpointPort.Port == port.Port { + if len(subset.Addresses) > 0 { + logger.AppLog.LogInfo("service %s is available in namespace %s\n", service.Name, namespace) + return nil + } + } + } + } + } + logger.AppLog.LogWarning("waiting for service %s to be available in namespace %s\n", service.Name, namespace) + logger.AppLog.LogWarning("service %s is not available yet in namespace %s\n", service.Name, namespace) + return ErrServiceNotHealthy + }) + return err +} +func validateServicesByNamespace(namespaces []string, serviceByNamespace map[string][]corev1.Service, clientset kubernetes.Interface, interval, timeout time.Duration) error { + var errList []error + if len(namespaces) <= 0 { + logger.AppLog.LogWarning("namespace list empty %v. no namespace provided. please provide atleast one namespace\n", namespaces) + return ErrNamespaceEmpty + } + if len(serviceByNamespace) <= 0 { + return ErrNoService + } + if interval.Seconds() <= 0 || timeout.Seconds() <= 0 { + logger.AppLog.LogError("interval or timeout is invalid. please provide the valid interval or timeout duration\n") + return ErrInvalidInterval + } + for _, namespace := range namespaces { + for _, service := range serviceByNamespace[namespace] { + err := wait.PollUntilContextTimeout(context.TODO(), interval, timeout, false, func(context.Context) (bool, error) { + err := checkServiceStatus(namespace, service, clientset) + if err != nil { + return false, nil + } + return true, nil + }) + if err != nil { + logger.AppLog.LogError("error checking the service %s in namespace %s status: %v\n", service.Name, namespace, err) + errList = append(errList, err) + } + } + } + if len(errList) != 0 { + logger.AppLog.LogError("to many errors. service validation test's failed") + return ErrServiceFailed + } + return nil +} +func CheckServices(namespaces []string, clientset kubernetes.Interface, interval, timeout time.Duration) error { + logger.AppLog.LogInfo("Begin Service validation") + serviceByNamespace, err := storeServicesByNamespace(namespaces, clientset) + if err != nil { + if errors.Is(err, ErrNoService) { + logger.AppLog.LogWarning("no service found in namespace. skipping service validations") + return nil + } + return err + } + err = validateServicesByNamespace(namespaces, serviceByNamespace, clientset, interval, timeout) + if err != nil { + return err + } + logger.AppLog.LogInfo("End Service validation") + return nil + +} diff --git a/tests/integration_tests/framework/pkg/service/service_test.go b/tests/integration_tests/framework/pkg/service/service_test.go new file mode 100644 index 0000000000..a871d4c0c0 --- /dev/null +++ b/tests/integration_tests/framework/pkg/service/service_test.go @@ -0,0 +1,284 @@ +package service + +import ( + "testing" + "time" + + "github.com/rhobs/configuration/tests/integration_tests/framework/pkg/logger" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes/fake" +) + +var ( + testNS = "test-namespace" + testSvc = "test-service" + testSvcList = corev1.ServiceList{ + Items: []corev1.Service{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: testSvc, + Namespace: testNS, + }, + Spec: corev1.ServiceSpec{ + Type: corev1.ServiceTypeClusterIP, + Ports: []corev1.ServicePort{ + { + Protocol: corev1.ProtocolTCP, + Port: 9090, + Name: "TCP", + }, + }, + }, + }, + }, + } + testEndpointList = corev1.EndpointsList{ + Items: []corev1.Endpoints{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: testSvc, + Namespace: testNS, + }, + Subsets: []corev1.EndpointSubset{ + { + Ports: []corev1.EndpointPort{ + { + Name: "TCP", + Port: 9090, + }, + }, + Addresses: []corev1.EndpointAddress{ + { + IP: "127.0.0.1", + Hostname: "host1", + }, + }, + }, + }, + }, + }, + } +) + +func TestGetService(t *testing.T) { + clientset := fake.NewSimpleClientset(&testSvcList) + logger.NewLogger(logger.LevelInfo) + svc, err := getService(testNS, clientset) + if err != nil { + t.Fatalf("expected nil, got: %v", err) + } + if len(svc.Items) != 1 { + t.Errorf("expected 1 service, got: %v", len(svc.Items)) + } + +} +func TestGetServiceNoNamespace(t *testing.T) { + clientset := fake.NewSimpleClientset(&testSvcList) + logger.NewLogger(logger.LevelInfo) + _, err := getService("", clientset) + if err != ErrNoNamespace { + t.Fatalf("expected ErrNoNamespace, got: %v", err) + } +} +func TestGetServiceNoService(t *testing.T) { + clienset := fake.NewSimpleClientset() + logger.NewLogger(logger.LevelInfo) + _, err := getService(testNS, clienset) + if err != ErrNoService { + t.Fatalf("expected ErrNoService, got: %v", err) + } + +} +func TestStoreServicesByNamespace(t *testing.T) { + namespaces := []string{testNS} + clienset := fake.NewSimpleClientset(&testSvcList) + logger.NewLogger(logger.LevelInfo) + serviceByNamespace, err := storeServicesByNamespace(namespaces, clienset) + if err != nil { + t.Fatalf("expected nil, got: %v", err) + } + if len(serviceByNamespace) != 1 { + t.Errorf("expected 1 service got: %v", len(serviceByNamespace)) + } +} +func TestStoreServicesByNamespaceNoNamespace(t *testing.T) { + namespaces := []string{} + clientset := fake.NewSimpleClientset(&testSvcList) + logger.NewLogger(logger.LevelInfo) + _, err := storeServicesByNamespace(namespaces, clientset) + if err != ErrNoNamespace { + t.Fatalf("expected ErrNoNamespace, got: %v", err) + } +} +func TestStoreServicesByNamespaceNoService(t *testing.T) { + namespace := []string{testNS} + clientset := fake.NewSimpleClientset() + logger.NewLogger(logger.LevelInfo) + _, err := storeServicesByNamespace(namespace, clientset) + if err != ErrNoService { + t.Fatalf("expected ErrNoService, got: %v", err) + } +} +func TestCheckServiceStatus(t *testing.T) { + logger.NewLogger(logger.LevelInfo) + clientset := fake.NewSimpleClientset(&testSvcList, &testEndpointList) + err := checkServiceStatus(testNS, testSvcList.Items[0], clientset) + if err != nil { + t.Fatalf("expected nil, got: %v", err) + } +} +func TestCheckServiceStatusNotHealthy(t *testing.T) { + faultyEndpointList := corev1.EndpointsList{ + Items: []corev1.Endpoints{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: testSvc, + Namespace: testNS, + }, + Subsets: []corev1.EndpointSubset{ + { + Ports: []corev1.EndpointPort{ + { + Name: "TCP", + Port: 9091, + }, + }, + Addresses: []corev1.EndpointAddress{ + { + IP: "127.0.0.1", + Hostname: "host1", + }, + }, + }, + }, + }, + }, + } + logger.NewLogger(logger.LevelInfo) + clientset := fake.NewSimpleClientset(&testSvcList, &faultyEndpointList) + err := checkServiceStatus(testNS, testSvcList.Items[0], clientset) + if err != ErrServiceNotHealthy { + t.Fatalf("expected ErrServiceNotHealthy, got: %v", err) + } +} + +func TestValidateServiceByNamespace(t *testing.T) { + logger.NewLogger(logger.LevelInfo) + namespaces := []string{testNS} + serviceByNamespace := make(map[string][]corev1.Service) + serviceByNamespace[testNS] = testSvcList.Items + interval := 1 * time.Second + timeout := 5 * time.Second + clientset := fake.NewSimpleClientset(&testSvcList, &testEndpointList) + err := validateServicesByNamespace(namespaces, serviceByNamespace, clientset, interval, timeout) + if err != nil { + t.Fatalf("expected nil, got: %v", err) + } +} + +func TestValidateServiceByNamespaceNoNamespace(t *testing.T) { + logger.NewLogger(logger.LevelInfo) + namespaces := []string{} + serviceByNamespace := make(map[string][]corev1.Service) + serviceByNamespace[testNS] = testSvcList.Items + interval := 1 * time.Second + timeout := 5 * time.Second + clientset := fake.NewSimpleClientset(&testSvcList, &testEndpointList) + err := validateServicesByNamespace(namespaces, serviceByNamespace, clientset, interval, timeout) + if err != ErrNamespaceEmpty { + t.Fatalf("expected ErrNamespaceEmpty, got: %v", err) + } +} + +func TestValidateServiceByNamespaceNoServiceByNamespace(t *testing.T) { + logger.NewLogger(logger.LevelInfo) + namespaces := []string{testNS} + serviceByNamespace := make(map[string][]corev1.Service) + interval := 1 * time.Second + timeout := 5 * time.Second + clientset := fake.NewSimpleClientset(&testSvcList, &testEndpointList) + err := validateServicesByNamespace(namespaces, serviceByNamespace, clientset, interval, timeout) + if err != ErrNoService { + t.Fatalf("expected ErrNoService, got: %v", err) + } + +} + +func TestValidateServiceByNamespaceInvalidInterval(t *testing.T) { + logger.NewLogger(logger.LevelInfo) + namespaces := []string{testNS} + serviceByNamespace := make(map[string][]corev1.Service) + serviceByNamespace[testNS] = testSvcList.Items + interval := -1 * time.Second + timeout := -5 * time.Second + clientset := fake.NewSimpleClientset(&testSvcList, &testEndpointList) + err := validateServicesByNamespace(namespaces, serviceByNamespace, clientset, interval, timeout) + if err != ErrInvalidInterval { + t.Fatalf("expected ErrInvalidInterval, got: %v", err) + } +} + +func TestValidateServiceByNamespaceServiceFailed(t *testing.T) { + faultyEndpointList := corev1.EndpointsList{ + Items: []corev1.Endpoints{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: testSvc, + Namespace: testNS, + }, + Subsets: []corev1.EndpointSubset{ + { + Ports: []corev1.EndpointPort{ + { + Name: "TCP", + Port: 9091, + }, + }, + Addresses: []corev1.EndpointAddress{ + { + IP: "127.0.0.1", + Hostname: "host1", + }, + }, + }, + }, + }, + }, + } + logger.NewLogger(logger.LevelInfo) + namespaces := []string{testNS} + serviceByNamespace := make(map[string][]corev1.Service) + serviceByNamespace[testNS] = testSvcList.Items + interval := 1 * time.Second + timeout := 5 * time.Second + clientset := fake.NewSimpleClientset(&testSvcList, &faultyEndpointList) + err := validateServicesByNamespace(namespaces, serviceByNamespace, clientset, interval, timeout) + if err != ErrServiceFailed { + t.Fatalf("expected ErrServiceFailed, got: %v", err) + } + +} +func TestCheckServices(t *testing.T) { + namespaces := []string{testNS} + logger.NewLogger(logger.LevelInfo) + interval := 1 * time.Second + timeout := 5 * time.Second + clientset := fake.NewSimpleClientset(&testSvcList, &testEndpointList) + err := CheckServices(namespaces, clientset, interval, timeout) + if err != nil { + t.Fatalf("expected nil, got: %v", err) + } + +} +func TestCheckServicesNoNamespace(t *testing.T) { + namespaces := []string{} + logger.NewLogger(logger.LevelInfo) + interval := 1 * time.Second + timeout := 5 * time.Second + clientset := fake.NewSimpleClientset(&testSvcList, &testEndpointList) + err := CheckServices(namespaces, clientset, interval, timeout) + if err != ErrNoNamespace { + t.Fatalf("expected ErrNoNamespace, got: %v", err) + } +} diff --git a/tests/integration_tests/framework/pkg/statefulset/statefulset.go b/tests/integration_tests/framework/pkg/statefulset/statefulset.go new file mode 100644 index 0000000000..7a4c419097 --- /dev/null +++ b/tests/integration_tests/framework/pkg/statefulset/statefulset.go @@ -0,0 +1,177 @@ +package statefulset + +import ( + "context" + "errors" + "time" + + "github.com/rhobs/configuration/tests/integration_tests/framework/pkg/logger" + "github.com/rhobs/configuration/tests/integration_tests/framework/pkg/pod" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/util/retry" +) + +// TODO: Come up with better solution +// This is done so that unit test can work fine as retry.RetryOnConflict only works on real Kubernetes cluster +// we are using fake to build up mock client for UT +type Retryer interface { + RetryOnConflict(backoff wait.Backoff, fn func() error) error +} + +type DefaultRetryer struct{} + +func (r *DefaultRetryer) RetryOnConflict(backoff wait.Backoff, fn func() error) error { + return retry.RetryOnConflict(backoff, fn) +} + +var ( + retryer Retryer = &DefaultRetryer{} +) + +var ( + ErrListingStatefulSet = errors.New("error listing statefulsets in namespace") + ErrNoStatefulSet = errors.New("error no statefulset found inside namespace") + ErrNoNamespace = errors.New("error no namespace provided") + ErrStatefulSetNotHealthy = errors.New("error statefulset not in healthy state") + ErrNamespaceEmpty = errors.New("error namespace list empty") + ErrInvalidInterval = errors.New("error interval or timeout is invalid") + ErrStatefulSetFailed = errors.New("error statefulset test validation failed") +) + +func getStatefulSet(namespace string, clientset kubernetes.Interface) (*appsv1.StatefulSetList, error) { + statefulset, err := clientset.AppsV1().StatefulSets(namespace).List(context.Background(), metav1.ListOptions{}) + if err != nil { + return nil, ErrListingStatefulSet + } + if len(statefulset.Items) <= 0 { + return nil, ErrNoStatefulSet + } + return statefulset, nil +} +func storeStatefulSetsByNamespace(namespaces []string, clientset kubernetes.Interface) (map[string][]appsv1.StatefulSet, error) { + if len(namespaces) <= 0 { + logger.AppLog.LogWarning("no namespace provided") + return nil, ErrNoNamespace + } + statefulsetsByNamespace := make(map[string][]appsv1.StatefulSet) + for _, namespace := range namespaces { + logger.AppLog.LogInfo("Checking StatefulSets status inside namespace %s\n", namespace) + // If namespace name is invalid + if namespace != "" { + statefulSetList, err := getStatefulSet(namespace, clientset) + // unable to get the statefulsets + if err != nil { + if errors.Is(err, ErrNoStatefulSet) { + continue + } + return nil, err + } + // Store the statefulsets by namespace in the map + statefulsetsByNamespace[namespace] = statefulSetList.Items + } else { + logger.AppLog.LogError("invalid namespace provided: %s\n", namespace) + } + } + if len(statefulsetsByNamespace) <= 0 { + logger.AppLog.LogWarning("there is no statefulset in provided namespace: %v\n", namespaces) + return nil, ErrNoStatefulSet + } + return statefulsetsByNamespace, nil +} + +func checkStatefulSetStatus(namespace string, statefulset appsv1.StatefulSet, clientset kubernetes.Interface) error { + err := retryer.RetryOnConflict(retry.DefaultRetry, func() error { + updatedStatefulSet, err := clientset.AppsV1().StatefulSets(namespace).Get(context.Background(), statefulset.Name, metav1.GetOptions{}) + if err != nil { + return err + } + if updatedStatefulSet.Status.UpdatedReplicas == *statefulset.Spec.Replicas && + updatedStatefulSet.Status.Replicas == *statefulset.Spec.Replicas && + updatedStatefulSet.Status.CurrentReplicas == *statefulset.Spec.Replicas && + updatedStatefulSet.Status.ObservedGeneration >= statefulset.Generation { + logger.AppLog.LogInfo("statefulset %s is available in namespace %s\n", statefulset.Name, namespace) + return nil + } else { + logger.AppLog.LogWarning("statefulset %s is not available in namespace %s. checking condition\n", statefulset.Name, namespace) + for _, condition := range updatedStatefulSet.Status.Conditions { + if condition.Type == "StatefulSetReplicasReady" && condition.Status == corev1.ConditionFalse { + logger.AppLog.LogError("reason: %v\n", condition.Reason) + break + } + } + } + logger.AppLog.LogWarning("waiting for statefulset %s to be available in namespace %s\n", statefulset.Name, namespace) + logger.AppLog.LogWarning("statefulset %s is not in healthy state inside namespace %s\n", statefulset.Name, namespace) + return ErrStatefulSetNotHealthy + }) + return err +} +func validateStatefulSetsByNamespace(namespaces []string, statefulsetsByNamespace map[string][]appsv1.StatefulSet, clientset kubernetes.Interface, interval, timeout time.Duration) error { + var stsErrorList []error + var podErrList []error + if len(namespaces) <= 0 { + logger.AppLog.LogError("namespace list empty %v. no namespace provided. please provide atleast one namespace\n", namespaces) + return ErrNamespaceEmpty + } + if len(statefulsetsByNamespace) <= 0 { + return ErrNoStatefulSet + } + if interval.Seconds() <= 0 || timeout.Seconds() <= 0 { + logger.AppLog.LogError("interval or timeout is invalid. please provide the valid interval or timeout duration\n") + return ErrInvalidInterval + } + for _, namespace := range namespaces { + for _, statefulset := range statefulsetsByNamespace[namespace] { + err := wait.PollUntilContextTimeout(context.TODO(), interval, timeout, false, func(context.Context) (bool, error) { + err := checkStatefulSetStatus(namespace, statefulset, clientset) + if err != nil { + return false, err + } + return true, nil + }) + if err != nil { + logger.AppLog.LogError("error checking the statefulset %s in namespace %s reason: %v\n", statefulset.Name, namespace, err) + stsErrorList = append(stsErrorList, err) + } + err = wait.PollUntilContextTimeout(context.TODO(), interval, timeout, false, func(context.Context) (bool, error) { + err := pod.GetPodStatus(namespace, labels.SelectorFromSet(statefulset.Spec.Selector.MatchLabels), clientset) + if err != nil { + return false, err + } + return true, nil + }) + if err != nil { + logger.AppLog.LogError("error checking the pod logs for statefulset %s in namespace %s, reason: %v\n", statefulset.Name, namespace, err) + podErrList = append(podErrList, err) + } + + } + } + if len(stsErrorList) != 0 || len(podErrList) != 0 { + logger.AppLog.LogError("to many errors. statefulsets validation test's failed\n") + return ErrStatefulSetFailed + } + return nil +} +func CheckStatefulSets(namespace []string, clientset kubernetes.Interface, interval, timeout time.Duration) error { + logger.AppLog.LogInfo("Begin StatefulSet validation") + statefulsetsByNamespace, err := storeStatefulSetsByNamespace(namespace, clientset) + if err != nil { + if errors.Is(err, ErrNoStatefulSet) { + logger.AppLog.LogWarning("no statefulset found in namespace. skipping statefulset validations\n") + return nil + } + return err + } + err = validateStatefulSetsByNamespace(namespace, statefulsetsByNamespace, clientset, interval, timeout) + if err != nil { + return err + } + logger.AppLog.LogInfo("End StatefulSet validation") + return nil +} diff --git a/tests/integration_tests/framework/pkg/statefulset/statefulset_test.go b/tests/integration_tests/framework/pkg/statefulset/statefulset_test.go new file mode 100644 index 0000000000..567328f16d --- /dev/null +++ b/tests/integration_tests/framework/pkg/statefulset/statefulset_test.go @@ -0,0 +1,367 @@ +package statefulset + +import ( + "testing" + "time" + + "github.com/rhobs/configuration/tests/integration_tests/framework/pkg/logger" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes/fake" +) + +var ( + testNS = "test-namespace" + testStset = "test-statefulset" + testLabels = make(map[string]string) + testSSList = appsv1.StatefulSetList{ + Items: []appsv1.StatefulSet{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: testStset, + Namespace: testNS, + }, + Spec: appsv1.StatefulSetSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: testLabels, + }, + }, + }, + }, + } +) + +// Limitation: Since retry.RetryOnConflict only works on a running Kubernetes cluster. +// we use fake to build client we have to agument the way retryOnConflict works. +// TODO: Come up with good way to handle this + +type mockRetryer struct { + err error +} + +func (m *mockRetryer) RetryOnConflict(backoff wait.Backoff, fn func() error) error { + return m.err +} + +func TestGetStatefulSet(t *testing.T) { + clienset := fake.NewSimpleClientset(&testSSList) + logger.NewLogger(logger.LevelInfo) + sts, err := getStatefulSet(testNS, clienset) + if err != nil { + t.Fatalf("expected nil got: %v", err) + } + if len(sts.Items) != 1 { + t.Errorf("expected 1 statefulset, got: %v", len(sts.Items)) + } +} + +func TestGetStatefulSetNoStatefulSet(t *testing.T) { + clientset := fake.NewSimpleClientset() + logger.NewLogger(logger.LevelInfo) + _, err := getStatefulSet(testNS, clientset) + if err != ErrNoStatefulSet { + t.Fatalf("expected ErrNoStatefulSet, got: %v", err) + } +} + +func TestStoreStatefulSetsByNamespace(t *testing.T) { + clientset := fake.NewSimpleClientset(&testSSList) + namespaces := []string{testNS} + statefulsetsByNamespace, err := storeStatefulSetsByNamespace(namespaces, clientset) + if err != nil { + t.Fatalf("expected nil, got: %v", err) + } + if len(statefulsetsByNamespace) != 1 { + t.Errorf("expected 1 statefulset, got: %v", len(statefulsetsByNamespace)) + } +} + +func TestStoreStatefulSetsByNamespaceNoNamespace(t *testing.T) { + clientset := fake.NewSimpleClientset(&testSSList) + logger.NewLogger(logger.LevelInfo) + namespaces := []string{} + _, err := storeStatefulSetsByNamespace(namespaces, clientset) + if err != ErrNoNamespace { + t.Fatalf("expected ErrNoNamespace, got: %v", err) + } +} + +func TestStoreStatefulSetsByNamespaceNoStatefulSets(t *testing.T) { + clientset := fake.NewSimpleClientset() + logger.NewLogger(logger.LevelInfo) + namespaces := []string{testNS} + _, err := storeStatefulSetsByNamespace(namespaces, clientset) + if err != ErrNoStatefulSet { + t.Fatalf("expected ErrNoStatefulSet, got: %v", err) + } +} + +func TestCheckStatefulSetStatus(t *testing.T) { + clientset := fake.NewSimpleClientset(&testSSList) + // TODO: Come up with good way to write this + retryer = &mockRetryer{err: nil} + logger.NewLogger(logger.LevelInfo) + err := checkStatefulSetStatus(testNS, testSSList.Items[0], clientset) + if err != nil { + t.Fatalf("expected nil, got: %v", err) + } +} + +func TestCheckStatefulSetStatusNotHealthy(t *testing.T) { + faultySS := appsv1.StatefulSetList{ + Items: []appsv1.StatefulSet{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: testStset, + Namespace: testNS, + }, + Status: appsv1.StatefulSetStatus{ + AvailableReplicas: 0, + }, + }, + }, + } + clientset := fake.NewSimpleClientset(&faultySS) + // TODO: Come up with good way to write this + retryer = &mockRetryer{err: ErrStatefulSetNotHealthy} + logger.NewLogger(logger.LevelInfo) + err := checkStatefulSetStatus(testNS, faultySS.Items[0], clientset) + if err != ErrStatefulSetNotHealthy { + t.Fatalf("expected ErrStatefulSetNotHealthy, got: %v", err) + } +} + +func TestValidateStatefulSetsByNamespace(t *testing.T) { + testLabels["app"] = "test-app" + testPods := corev1.PodList{ + Items: []corev1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "testPod", + Namespace: testNS, + Labels: testLabels, + }, + Status: corev1.PodStatus{ + Phase: corev1.PodRunning, + }, + }, + }, + } + clientset := fake.NewSimpleClientset(&testSSList, &testPods) + // TODO: Come up with good way to write this + retryer = &mockRetryer{err: nil} + logger.NewLogger(logger.LevelInfo) + namespaces := []string{testNS} + statefulsetsByNamespace := make(map[string][]appsv1.StatefulSet) + statefulsetsByNamespace[testNS] = testSSList.Items + interval := 1 * time.Second + timeout := 5 * time.Second + err := validateStatefulSetsByNamespace(namespaces, statefulsetsByNamespace, clientset, interval, timeout) + if err != nil { + t.Fatalf("expected nil, got: %v", err) + } +} + +func TestValidateStatefulSetsByNamespaceNoNamespace(t *testing.T) { + testLabels["app"] = "test-app" + testPods := corev1.PodList{ + Items: []corev1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "testPod", + Namespace: testNS, + Labels: testLabels, + }, + Status: corev1.PodStatus{ + Phase: corev1.PodRunning, + }, + }, + }, + } + clientset := fake.NewSimpleClientset(&testSSList, &testPods) + // TODO: Come up with good way to write this + retryer = &mockRetryer{err: nil} + logger.NewLogger(logger.LevelInfo) + namespaces := []string{} + statefulsetsByNamespace := make(map[string][]appsv1.StatefulSet) + interval := 1 * time.Second + timeout := 5 * time.Second + err := validateStatefulSetsByNamespace(namespaces, statefulsetsByNamespace, clientset, interval, timeout) + if err != ErrNamespaceEmpty { + t.Fatalf("expected ErrNamespaceEmpty, got: %v", err) + } + +} + +func TestValidateStatefulSetsByNamespaceInvalidInterval(t *testing.T) { + testLabels["app"] = "test-app" + testPods := corev1.PodList{ + Items: []corev1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "testPod", + Namespace: testNS, + Labels: testLabels, + }, + Status: corev1.PodStatus{ + Phase: corev1.PodRunning, + }, + }, + }, + } + clientset := fake.NewSimpleClientset(&testSSList, &testPods) + // TODO: Come up with good way to write this + retryer = &mockRetryer{err: nil} + logger.NewLogger(logger.LevelInfo) + namespaces := []string{testNS} + statefulsetsByNamespace := make(map[string][]appsv1.StatefulSet) + statefulsetsByNamespace[testNS] = testSSList.Items + interval := -1 * time.Second + timeout := -5 * time.Second + err := validateStatefulSetsByNamespace(namespaces, statefulsetsByNamespace, clientset, interval, timeout) + if err != ErrInvalidInterval { + t.Fatalf("expected ErrInvalidInterval, got: %v", err) + } +} + +func TestValidateStatefulSetsByNamespaceNoStatefulSetByNamespace(t *testing.T) { + clientset := fake.NewSimpleClientset() + // TODO: Come up with good way to write this + retryer = &mockRetryer{err: nil} + logger.NewLogger(logger.LevelInfo) + namespaces := []string{testNS} + statefulsetsByNamespace := make(map[string][]appsv1.StatefulSet) + interval := 1 * time.Second + timeout := 5 * time.Second + err := validateStatefulSetsByNamespace(namespaces, statefulsetsByNamespace, clientset, interval, timeout) + if err != ErrNoStatefulSet { + t.Fatalf("expected ErrNoStatefulSet, got: %v", err) + } +} + +func TestValidateStatefulSetsByNamespaceStatefulSetFailed(t *testing.T) { + testLabels["app"] = "test-app" + faultyPods := corev1.PodList{ + Items: []corev1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "testPod", + Namespace: testNS, + Labels: testLabels, + }, + Status: corev1.PodStatus{ + Phase: corev1.PodUnknown, + }, + }, + }, + } + faultySS := appsv1.StatefulSetList{ + Items: []appsv1.StatefulSet{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: testStset, + Namespace: testNS, + }, + Spec: appsv1.StatefulSetSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: testLabels, + }, + }, + Status: appsv1.StatefulSetStatus{ + AvailableReplicas: 0, + }, + }, + }, + } + + clientset := fake.NewSimpleClientset(&faultySS, &faultyPods) + // TODO: Come up with good way to write this + retryer = &mockRetryer{err: ErrStatefulSetNotHealthy} + logger.NewLogger(logger.LevelInfo) + namespaces := []string{testNS} + statefulsetsByNamespace := make(map[string][]appsv1.StatefulSet) + statefulsetsByNamespace[testNS] = faultySS.Items + interval := 1 * time.Second + timeout := 5 * time.Second + err := validateStatefulSetsByNamespace(namespaces, statefulsetsByNamespace, clientset, interval, timeout) + if err != ErrStatefulSetFailed { + t.Fatalf("expected ErrStatefulSetFailed, got: %v", err) + } +} + +func TestValidateStatefulSetsByNamespacePodFailed(t *testing.T) { + testLabels["app"] = "test-app" + faultyPods := corev1.PodList{ + Items: []corev1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "testPod", + Namespace: testNS, + Labels: testLabels, + }, + Status: corev1.PodStatus{ + Phase: corev1.PodUnknown, + }, + }, + }, + } + + clientset := fake.NewSimpleClientset(&testSSList, &faultyPods) + // TODO: Come up with good way to write this + retryer = &mockRetryer{err: nil} + logger.NewLogger(logger.LevelInfo) + namespaces := []string{testNS} + statefulsetsByNamespace := make(map[string][]appsv1.StatefulSet) + statefulsetsByNamespace[testNS] = testSSList.Items + interval := 1 * time.Second + timeout := 5 * time.Second + err := validateStatefulSetsByNamespace(namespaces, statefulsetsByNamespace, clientset, interval, timeout) + if err != ErrStatefulSetFailed { + t.Fatalf("expected ErrStatefulSetFailed, got: %v", err) + } +} + +func TestCheckStatefulSets(t *testing.T) { + testLabels["app"] = "test-app" + testPods := corev1.PodList{ + Items: []corev1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "testPod", + Namespace: testNS, + Labels: testLabels, + }, + Status: corev1.PodStatus{ + Phase: corev1.PodRunning, + }, + }, + }, + } + clientset := fake.NewSimpleClientset(&testSSList, &testPods) + // TODO: Come up with good way to write this + retryer = &mockRetryer{err: nil} + logger.NewLogger(logger.LevelInfo) + namespaces := []string{testNS} + interval := 1 * time.Second + timeout := 5 * time.Second + err := CheckStatefulSets(namespaces, clientset, interval, timeout) + if err != nil { + t.Fatalf("expected nil, got: %v", err) + } +} + +func TestCheckStatefulSetsNoNamespace(t *testing.T) { + clientset := fake.NewSimpleClientset(&testSSList) + // TODO: Come up with good way to write this + retryer = &mockRetryer{err: nil} + logger.NewLogger(logger.LevelInfo) + namespaces := []string{} + interval := 1 * time.Second + timeout := 5 * time.Second + err := CheckStatefulSets(namespaces, clientset, interval, timeout) + if err != ErrNoNamespace { + t.Fatalf("expected ErrNoNamespace, got: %v", err) + } +} From 6344918744ff7e0af4d284e89877aac1740bdaf7 Mon Sep 17 00:00:00 2001 From: Vibhu Prashar Date: Mon, 7 Aug 2023 17:23:05 +0530 Subject: [PATCH 2/2] incorporate review comments Signed-off-by: Vibhu Prashar --- go.mod | 40 +- go.sum | 82 +-- .../integration_tests/framework/Dockerfile | 2 +- tests/integration_tests/framework/Makefile | 30 +- tests/integration_tests/framework/go.mod | 51 ++ tests/integration_tests/framework/go.sum | 489 ++++++++++++++++++ 6 files changed, 591 insertions(+), 103 deletions(-) rename Dockerfile => tests/integration_tests/framework/Dockerfile (72%) create mode 100644 tests/integration_tests/framework/go.mod create mode 100644 tests/integration_tests/framework/go.sum diff --git a/go.mod b/go.mod index 51a5a4a8ba..d0e1f0590c 100644 --- a/go.mod +++ b/go.mod @@ -7,9 +7,7 @@ require ( github.com/observatorium/api v0.1.3-0.20220621123450-69c5f2661d01 github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.60.1 github.com/pyrra-dev/pyrra v0.5.2 - k8s.io/api v0.27.4 - k8s.io/apimachinery v0.27.4 - k8s.io/client-go v0.27.4 + k8s.io/apimachinery v0.25.3 ) require ( @@ -19,30 +17,18 @@ require ( github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dennwc/varint v1.0.0 // indirect - github.com/emicklei/go-restful/v3 v3.9.0 // indirect - github.com/evanphx/json-patch v4.12.0+incompatible // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/go-logr/logr v1.2.3 // indirect - github.com/go-openapi/jsonpointer v0.19.6 // indirect - github.com/go-openapi/jsonreference v0.20.1 // indirect - github.com/go-openapi/swag v0.22.3 // indirect github.com/gogo/protobuf v1.3.2 // 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/golang/protobuf v1.5.2 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/uuid v1.3.0 // indirect github.com/hashicorp/hcl v1.0.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.7 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // 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/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.13.1 // indirect @@ -51,28 +37,24 @@ require ( github.com/prometheus/procfs v0.8.0 // indirect github.com/prometheus/prometheus v1.8.2-0.20220211202545-56e14463bccf // indirect github.com/rodaine/hclencoder v0.0.1 // indirect - github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/testify v1.8.1 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/goleak v1.1.12 // indirect - golang.org/x/net v0.8.0 // indirect - golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect - golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.6.0 // indirect - golang.org/x/term v0.6.0 // indirect - golang.org/x/text v0.8.0 // indirect - golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect - google.golang.org/appengine v1.6.7 // indirect + golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b // indirect + golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect + golang.org/x/sys v0.0.0-20221010170243-090e33056c14 // indirect + golang.org/x/text v0.3.7 // indirect + golang.org/x/tools v0.1.12 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/alecthomas/kingpin.v2 v2.2.6 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/klog/v2 v2.90.1 // indirect - k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect - k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect + k8s.io/api v0.25.3 // indirect + k8s.io/klog/v2 v2.80.0 // indirect + k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73 // indirect sigs.k8s.io/controller-runtime v0.13.0 // indirect - sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index 010c4d0465..0a56d99168 100644 --- a/go.sum +++ b/go.sum @@ -383,8 +383,6 @@ github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkg github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/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.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -399,8 +397,6 @@ github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= -github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= @@ -475,16 +471,12 @@ github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwds github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= -github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= -github.com/go-openapi/jsonreference v0.20.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTrLC1F86HID8= -github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= @@ -537,8 +529,6 @@ github.com/go-openapi/swag v0.19.12/go.mod h1:eFdyEBkTdoAf/9RXBvj4cr1nH7GD8Kzo5H github.com/go-openapi/swag v0.19.13/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= -github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= github.com/go-openapi/validate v0.19.3/go.mod h1:90Vh6jjkTn+OT1Eefm0ZixWNFjhtOH7vS9k0lo6zwJo= @@ -555,7 +545,6 @@ github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= @@ -638,9 +627,8 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw 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.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -652,8 +640,6 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/flatbuffers v2.0.0+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= -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= @@ -667,7 +653,6 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -692,15 +677,12 @@ github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20211214055906-6f57359322fd h1:1FjCyPC+syAzJ5/2S8fqdZK1R22vvA0J7JZKcuOIQ7Y= github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= @@ -789,7 +771,6 @@ github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/flux v0.65.0/go.mod h1:BwN2XG2lMszOoquQaFdPET8FRQfrXiZsWmcMO9rkaVY= @@ -821,7 +802,6 @@ github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHW github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -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 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= @@ -865,7 +845,6 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB 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/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -891,8 +870,6 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= @@ -962,7 +939,6 @@ github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7P github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= @@ -976,6 +952,7 @@ github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxzi github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= +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/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= @@ -997,7 +974,6 @@ github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo/v2 v2.9.1 h1:zie5Ly042PD3bsCvsSOPvRnFwyo3rKe64TJlD6nu0mk= github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -1005,7 +981,7 @@ github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= -github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E= +github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -1148,7 +1124,6 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/cors v1.8.0/go.mod h1:EBwu+T5AvHOcXwvZIkQFjUN6s8Czyqw12GL/Y0tUyRM= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -1479,8 +1454,8 @@ golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220105145211-5b0dc2dfae98/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.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY= +golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= 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= @@ -1511,8 +1486,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1628,14 +1603,12 @@ golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20221010170243-090e33056c14 h1:k5II8e6QD8mITdi+okbbmR/cIyEbeXLBhy5Ha4nevyc= +golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 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= @@ -1645,9 +1618,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 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= @@ -1658,8 +1630,6 @@ golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220609170525-579cf78fd858 h1:Dpdu/EMxGMFgq0CeYMh4fazTD2vtlZRYE7wyynxJb9U= -golang.org/x/time v0.0.0-20220609170525-579cf78fd858/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1741,7 +1711,8 @@ golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.9-0.20211209172050-90a85b2969be/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= +golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1921,9 +1892,8 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 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/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= @@ -1975,15 +1945,15 @@ k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= k8s.io/api v0.22.4/go.mod h1:Rgs+9gIGYC5laXQSZZ9JqT5NevNgoGiOdVWi1BAB3qk= -k8s.io/api v0.27.4 h1:0pCo/AN9hONazBKlNUdhQymmnfLRbSZjd5H5H3f0bSs= -k8s.io/api v0.27.4/go.mod h1:O3smaaX15NfxjzILfiln1D8Z3+gEYpjEpiNA/1EVK1Y= +k8s.io/api v0.25.3 h1:Q1v5UFfYe87vi5H7NU0p4RXC26PPMT8KOpr1TLQbCMQ= +k8s.io/api v0.25.3/go.mod h1:o42gKscFrEVjHdQnyRenACrMtbuJsVdP+WVjqejfzmI= k8s.io/apimachinery v0.17.5/go.mod h1:ioIo1G/a+uONV7Tv+ZmCbMG1/a3kVw5YcDdncd8ugQ0= k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= k8s.io/apimachinery v0.22.4/go.mod h1:yU6oA6Gnax9RrxGzVvPFFJ+mpnW6PBSqp0sx0I0HHW0= -k8s.io/apimachinery v0.27.4 h1:CdxflD4AF61yewuid0fLl6bM4a3q04jWel0IlP+aYjs= -k8s.io/apimachinery v0.27.4/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E= +k8s.io/apimachinery v0.25.3 h1:7o9ium4uyUOM76t6aunP0nZuex7gDf8VGwkR5RcJnQc= +k8s.io/apimachinery v0.25.3/go.mod h1:jaF9C/iPNM1FuLl7Zuy5b9v+n35HGSh6AQ4HYRkCqwo= k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= @@ -1992,8 +1962,6 @@ k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= k8s.io/client-go v0.22.4/go.mod h1:Yzw4e5e7h1LNHA4uqnMVrpEpUs1hJOiuBsJKIlRCHDA= -k8s.io/client-go v0.27.4 h1:vj2YTtSJ6J4KxaC88P4pMPEQECWMY8gqPqsTgUKzvjk= -k8s.io/client-go v0.27.4/go.mod h1:ragcly7lUlN0SRPk5/ZkGnDjPknzb37TICq07WhI6Xc= k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= @@ -2010,20 +1978,18 @@ k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= k8s.io/klog/v2 v2.40.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= -k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/klog/v2 v2.80.0 h1:lyJt0TWMPaGoODa8B8bUuxgHS3W/m/bNr2cca3brA/g= +k8s.io/klog/v2 v2.80.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20200316234421-82d701f24f9d/go.mod h1:F+5wygcW0wmRTnM3cOgIqGivxkwSWIWT5YdsDbeAOaU= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg= -k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20200414100711-2df71ebbae66/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20230209194617-a36077c30491 h1:r0BAOLElQnnFhE/ApUsg3iHdVYYPBjNSSOMowRZxxsY= -k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73 h1:H9TCJUUx+2VA0ZiD9lvtaX8fthFsMoD+Izn93E/hm8U= +k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= @@ -2032,8 +1998,8 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyz sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/controller-runtime v0.13.0 h1:iqa5RNciy7ADWnIc8QxCbOX5FEKVR3uxVxKHRMc2WIQ= sigs.k8s.io/controller-runtime v0.13.0/go.mod h1:Zbz+el8Yg31jubvAEyglRZGdLAjplZl+PgtYNI6WNTI= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= +sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/structured-merge-diff/v2 v2.0.1/go.mod h1:Wb7vfKAodbKgf6tn1Kl0VvGj7mRH6DGaRcixXEJXTsE= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= @@ -2044,4 +2010,4 @@ sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= -sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= +sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= \ No newline at end of file diff --git a/Dockerfile b/tests/integration_tests/framework/Dockerfile similarity index 72% rename from Dockerfile rename to tests/integration_tests/framework/Dockerfile index 49f169592f..abdc522e9e 100644 --- a/Dockerfile +++ b/tests/integration_tests/framework/Dockerfile @@ -8,5 +8,5 @@ RUN go mod download COPY . . -RUN go build ./tests/integration_tests/framework/cmd/rhobs-test +RUN go build ./cmd/rhobs-test ENTRYPOINT [ "./rhobs-test" ] diff --git a/tests/integration_tests/framework/Makefile b/tests/integration_tests/framework/Makefile index 63504cb506..17776e70c7 100644 --- a/tests/integration_tests/framework/Makefile +++ b/tests/integration_tests/framework/Makefile @@ -8,6 +8,9 @@ EXAMPLES := examples OCP_MANIFESTS := $(EXAMPLES)/manifests/openshift DEV_MANIFESTS := $(EXAMPLES)/manifests/dev XARGS ?= $(shell which gxargs 2>/dev/null || which xargs) +# Setting GOENV +GOOS := $(shell go env GOOS) +GOARCH := $(shell go env GOARCH) all: build build: rhobs-test @@ -26,9 +29,8 @@ go-fmt: .PHONY: container-dev container-dev: kind @docker build \ - -f ../../../Dockerfile \ -t $(IMG):$(BRANCH)-$(BUILD_DATE)-$(TAG) \ - ../../../ + . docker tag $(IMG):$(BRANCH)-$(BUILD_DATE)-$(TAG) localhost:5001/rhobs-test:latest docker push localhost:5001/rhobs-test:latest @@ -45,14 +47,14 @@ test: .PHONY: local local: kind container-dev kubectl apply -f $(DEV_MANIFESTS)/test-deployment.yaml - kubectl apply -f $(DEV_MANIFESTS)/rbac.yaml - kubectl apply -f $(DEV_MANIFESTS)/job.yaml + kubectl apply -f $(DEV_MANIFESTS)/test-rbac.yaml + kubectl apply -f $(DEV_MANIFESTS)/test-job.yaml .PHONY: local-faulty local-faulty: kind container-dev - kubectl apply -f $(DEV_MANIFESTS)/test-deployment-faulty-template.yaml - kubectl apply -f $(DEV_MANIFESTS)/rbac-template.yaml - kubectl apply -f $(DEV_MANIFESTS)/job-template.yaml + kubectl apply -f $(DEV_MANIFESTS)/test-deployment-faulty.yaml + kubectl apply -f $(DEV_MANIFESTS)/test-rbac.yaml + kubectl apply -f $(DEV_MANIFESTS)/test-job.yaml .PHONY: clean clean: @@ -77,22 +79,20 @@ clean-test: .PHONY: container-build container-build: - docker buildx build \ - -f ../../../Dockerfile \ - --platform linux/amd64,linux/arm64 \ + docker build \ + --platform linux/$(GOARCH) \ --build-arg DOCKERFILE_PATH="/Dockerfile" \ -t $(IMG):$(BRANCH)-$(BUILD_DATE)-$(TAG) \ -t $(IMG):$(TAG) \ - ../../../ + . .PHONY: container-build-push container-build-push: - @docker buildx build \ - -f ../../../Dockerfile \ + @docker build \ --push \ - --platform linux/amd64,linux/arm64 \ + --platform linux/$(GOARCH) \ -t $(IMG):$(BRANCH)-$(BUILD_DATE)-$(TAG) \ -t $(IMG):$(TAG) \ - ../../../ + . .PHONY: ocp-manifests ocp-manifests: $(JSONNET) $(JSONNETFMT) $(GOJSONTOYAML) echo "ocp manifests" diff --git a/tests/integration_tests/framework/go.mod b/tests/integration_tests/framework/go.mod new file mode 100644 index 0000000000..286e73162e --- /dev/null +++ b/tests/integration_tests/framework/go.mod @@ -0,0 +1,51 @@ +module github.com/rhobs/configuration/tests/integration_tests/framework + +go 1.19 + +require ( + k8s.io/api v0.27.4 + k8s.io/apimachinery v0.27.4 + k8s.io/client-go v0.27.4 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/emicklei/go-restful/v3 v3.9.0 // indirect + github.com/evanphx/json-patch v4.12.0+incompatible // indirect + github.com/go-logr/logr v1.2.3 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.1 // indirect + github.com/go-openapi/swag v0.22.3 // indirect + github.com/gogo/protobuf v1.3.2 // 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.2.0 // indirect + github.com/google/uuid v1.3.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.7 // 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/pkg/errors v0.9.1 // indirect + github.com/spf13/pflag v1.0.5 // indirect + golang.org/x/net v0.8.0 // indirect + golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect + golang.org/x/sys v0.6.0 // indirect + golang.org/x/term v0.6.0 // indirect + golang.org/x/text v0.8.0 // indirect + golang.org/x/time v0.3.0 // indirect + google.golang.org/appengine v1.6.7 // 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 + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/klog/v2 v2.90.1 // indirect + k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect + k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect +) diff --git a/tests/integration_tests/framework/go.sum b/tests/integration_tests/framework/go.sum new file mode 100644 index 0000000000..65ed009d7f --- /dev/null +++ b/tests/integration_tests/framework/go.sum @@ -0,0 +1,489 @@ +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/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +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= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +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 v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= +github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +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-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.20.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTrLC1F86HID8= +github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= +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/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= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +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.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.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.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.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/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= +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/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/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +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/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/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/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/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +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.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +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 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/onsi/ginkgo/v2 v2.9.1 h1:zie5Ly042PD3bsCvsSOPvRnFwyo3rKe64TJlD6nu0mk= +github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E= +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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +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.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.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.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/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= +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/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-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-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-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +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-20220223155221-ee480838109b h1:clP8eMhB30EHdc0bd2Twtq6kgU7yl5ub2cQLSdrv1Dg= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +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/sys v0.0.0-20180830151530-49385e6e1522/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-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-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-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-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.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +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.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +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-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.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= +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= +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/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/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-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +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.2/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= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +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= +k8s.io/api v0.27.4 h1:0pCo/AN9hONazBKlNUdhQymmnfLRbSZjd5H5H3f0bSs= +k8s.io/api v0.27.4/go.mod h1:O3smaaX15NfxjzILfiln1D8Z3+gEYpjEpiNA/1EVK1Y= +k8s.io/apimachinery v0.27.4 h1:CdxflD4AF61yewuid0fLl6bM4a3q04jWel0IlP+aYjs= +k8s.io/apimachinery v0.27.4/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E= +k8s.io/client-go v0.27.4 h1:vj2YTtSJ6J4KxaC88P4pMPEQECWMY8gqPqsTgUKzvjk= +k8s.io/client-go v0.27.4/go.mod h1:ragcly7lUlN0SRPk5/ZkGnDjPknzb37TICq07WhI6Xc= +k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= +k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg= +k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg= +k8s.io/utils v0.0.0-20230209194617-a36077c30491 h1:r0BAOLElQnnFhE/ApUsg3iHdVYYPBjNSSOMowRZxxsY= +k8s.io/utils v0.0.0-20230209194617-a36077c30491/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/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=