diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..a3aab7af7 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +# More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file +# Ignore build and test binaries. +bin/ diff --git a/.github/workflows/chart.yml b/.github/workflows/chart.yml index 0210ee5eb..f7fcbd852 100644 --- a/.github/workflows/chart.yml +++ b/.github/workflows/chart.yml @@ -4,7 +4,8 @@ on: push: branches: - master - - fix/chart-release + paths: + - charts/* jobs: release: diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index 8d546edd2..287e75e32 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -22,7 +22,6 @@ jobs: uses: docker/build-push-action@v4 with: context: . - file: ./build/Dockerfile.dist push: false load: true tags: | diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1af48b183..57d209c08 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,7 +12,7 @@ jobs: with: go-version: "1.24.2" - run: | - go test ./... + make test build: runs-on: ubuntu-latest name: Go build @@ -22,5 +22,5 @@ jobs: with: go-version: "1.24.2" - run: | - go build -o operator github.com/movetokube/postgres-operator/cmd/manager - file operator + make build + file bin/manager diff --git a/.gitignore b/.gitignore index 9c55478cf..9f2e6d9ae 100644 --- a/.gitignore +++ b/.gitignore @@ -1,88 +1,35 @@ -# Temporary Build Files -build/_output -build/_test -# Created by https://www.gitignore.io/api/go,vim,emacs,visualstudiocode -### Emacs ### -# -*- mode: gitignore; -*- -*~ -\#*\# -/.emacs.desktop -/.emacs.desktop.lock -*.elc -auto-save-list -tramp -.\#* -# Org-mode -.org-id-locations -*_archive -# flymake-mode -*_flymake.* -# eshell files -/eshell/history -/eshell/lastdir -# elpa packages -/elpa/ -# reftex files -*.rel -# AUCTeX auto folder -/auto/ -# cask packages -.cask/ -dist/ -# Flycheck -flycheck_*.el -# server auth directory -/server/ -# projectiles files -.projectile -projectile-bookmarks.eld -# directory configuration -.dir-locals.el -# saveplace -places -# url cache -url/cache/ -# cedet -ede-projects.el -# smex -smex-items -# company-statistics -company-statistics-cache.el -# anaconda-mode -anaconda-mode/ -### Go ### # Binaries for programs and plugins *.exe *.exe~ *.dll *.so *.dylib -# Test binary, build with 'go test -c' +bin/* +Dockerfile.cross + +# Test binary, built with `go test -c` *.test + # Output of the go coverage tool, specifically when used with LiteIDE *.out -### Vim ### -# swap -.sw[a-p] -.*.sw[a-p] -# session -Session.vim -# temporary -.netrwhist -# auto-generated tag files -tags -### VisualStudioCode ### -.vscode/* -.history -# End of https://www.gitignore.io/api/go,vim,emacs,visualstudiocode -### MacOS -.DS_Store +# Go workspace file +go.work + +# Kubernetes Generated files - skip generated files, except for vendored files +!vendor/**/zz_generated.* +# editor and IDE paraphernalia .idea +.vscode +*.swp +*.swo +*~ + +#MacOS +.DS_Store + deploy/secret.yaml -# build artifact -operator # kuttl/kind tests/kind-logs-*/ kubeconfig diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..57032e4a9 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,34 @@ +# Build the manager binary +FROM golang:1.24 AS builder +ARG TARGETOS +ARG TARGETARCH + +WORKDIR /workspace +# Copy the Go Modules manifests +COPY go.mod go.mod +COPY go.sum go.sum +# cache deps before building and copying source so that we don't need to re-download as much +# and so that source changes don't invalidate our downloaded layer +RUN go mod download + +# Copy the go source +COPY cmd/main.go cmd/main.go +COPY api/ api/ +COPY internal/controller/ internal/controller/ +COPY pkg/ pkg/ + +# Build +# the GOARCH has not a default value to allow the binary be built according to the host where the command +# was called. For example, if we call make docker-build in a local env which has the Apple Silicon M1 SO +# the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore, +# by leaving it empty we can ensure that the container and binary shipped on it will have the same platform. +RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager cmd/main.go + +# Use distroless as minimal base image to package the manager binary +# Refer to https://github.com/GoogleContainerTools/distroless for more details +FROM gcr.io/distroless/static:nonroot +WORKDIR / +COPY --from=builder /workspace/manager . +USER 65532:65532 + +ENTRYPOINT ["/manager"] diff --git a/Makefile b/Makefile index 52d5a901b..e57e40e5c 100644 --- a/Makefile +++ b/Makefile @@ -1,23 +1,319 @@ -.PHONY: gen build e2e e2e-build - -gen: - operator-sdk generate k8s - operator-sdk generate crds -build: - operator-sdk build movetokube/postgres-operator - docker push movetokube/postgres-operator -unit-test: - go test ./... -coverprofile coverage.out - go tool cover -func coverage.out -unit-test-coverage: unit-test - go tool cover -html coverage.out -linux-docker: - @docker run -ti -v $(PWD):/work golang:1.24-bookworm /bin/bash -linux-build: - @GOBIN=/work/bin GO111MODULE=on GOOS=linux GOARC=x86_64 go build -o operator github.com/movetokube/postgres-operator/cmd/manager -docker-build: - docker run -ti -v $(PWD):/work -w /work golang:1.24-bookworm make linux-build -e2e-build: - docker buildx build -t postgres-operator:build -f ./build/Dockerfile.dist . -e2e: e2e-build - kubectl kuttl test --config ./tests/kuttl-test-self-hosted-postgres.yaml +# VERSION defines the project version for the bundle. +# Update this value when you upgrade the version of your project. +# To re-generate a bundle for another specific version without changing the standard setup, you can: +# - use the VERSION as arg of the bundle target (e.g make bundle VERSION=0.0.2) +# - use environment variables to overwrite this value (e.g export VERSION=0.0.2) +VERSION ?= 0.0.1 + +# CHANNELS define the bundle channels used in the bundle. +# Add a new line here if you would like to change its default config. (E.g CHANNELS = "candidate,fast,stable") +# To re-generate a bundle for other specific channels without changing the standard setup, you can: +# - use the CHANNELS as arg of the bundle target (e.g make bundle CHANNELS=candidate,fast,stable) +# - use environment variables to overwrite this value (e.g export CHANNELS="candidate,fast,stable") +ifneq ($(origin CHANNELS), undefined) +BUNDLE_CHANNELS := --channels=$(CHANNELS) +endif + +# DEFAULT_CHANNEL defines the default channel used in the bundle. +# Add a new line here if you would like to change its default config. (E.g DEFAULT_CHANNEL = "stable") +# To re-generate a bundle for any other default channel without changing the default setup, you can: +# - use the DEFAULT_CHANNEL as arg of the bundle target (e.g make bundle DEFAULT_CHANNEL=stable) +# - use environment variables to overwrite this value (e.g export DEFAULT_CHANNEL="stable") +ifneq ($(origin DEFAULT_CHANNEL), undefined) +BUNDLE_DEFAULT_CHANNEL := --default-channel=$(DEFAULT_CHANNEL) +endif +BUNDLE_METADATA_OPTS ?= $(BUNDLE_CHANNELS) $(BUNDLE_DEFAULT_CHANNEL) + +# IMAGE_TAG_BASE defines the docker.io namespace and part of the image name for remote images. +# This variable is used to construct full image tags for bundle and catalog images. +# +# For example, running 'make bundle-build bundle-push catalog-build catalog-push' will build and push both +# movetokube.com/postgres-operator-bundle:$VERSION and movetokube.com/postgres-operator-catalog:$VERSION. +IMAGE_TAG_BASE ?= movetokube.com/postgres-operator + +# BUNDLE_IMG defines the image:tag used for the bundle. +# You can use it as an arg. (E.g make bundle-build BUNDLE_IMG=/:) +BUNDLE_IMG ?= $(IMAGE_TAG_BASE)-bundle:v$(VERSION) + +# BUNDLE_GEN_FLAGS are the flags passed to the operator-sdk generate bundle command +BUNDLE_GEN_FLAGS ?= -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS) + +# USE_IMAGE_DIGESTS defines if images are resolved via tags or digests +# You can enable this value if you would like to use SHA Based Digests +# To enable set flag to true +USE_IMAGE_DIGESTS ?= false +ifeq ($(USE_IMAGE_DIGESTS), true) + BUNDLE_GEN_FLAGS += --use-image-digests +endif + +# Set the Operator SDK version to use. By default, what is installed on the system is used. +# This is useful for CI or a project to utilize a specific version of the operator-sdk toolkit. +OPERATOR_SDK_VERSION ?= v1.39.2 +# Image URL to use all building/pushing image targets +IMG ?= controller:latest +# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. +ENVTEST_K8S_VERSION = 1.31.0 + +# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) +ifeq (,$(shell go env GOBIN)) +GOBIN=$(shell go env GOPATH)/bin +else +GOBIN=$(shell go env GOBIN) +endif + +# CONTAINER_TOOL defines the container tool to be used for building images. +# Be aware that the target commands are only tested with Docker which is +# scaffolded by default. However, you might want to replace it to use other +# tools. (i.e. podman) +CONTAINER_TOOL ?= docker + +# Setting SHELL to bash allows bash commands to be executed by recipes. +# Options are set to exit when a recipe line exits non-zero or a piped command fails. +SHELL = /usr/bin/env bash -o pipefail +.SHELLFLAGS = -ec + +.PHONY: all +all: build + +##@ General + +# The help target prints out all targets with their descriptions organized +# beneath their categories. The categories are represented by '##@' and the +# target descriptions by '##'. The awk command is responsible for reading the +# entire set of makefiles included in this invocation, looking for lines of the +# file as xyz: ## something, and then pretty-format the target and help. Then, +# if there's a line with ##@ something, that gets pretty-printed as a category. +# More info on the usage of ANSI control characters for terminal formatting: +# https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters +# More info on the awk command: +# http://linuxcommand.org/lc3_adv_awk.php + +.PHONY: help +help: ## Display this help. + @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) + +##@ Development + +.PHONY: manifests +manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. + $(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases + +.PHONY: generate +generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. + $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." + +.PHONY: fmt +fmt: ## Run go fmt against code. + go fmt ./... + +.PHONY: vet +vet: ## Run go vet against code. + go vet ./... + +.PHONY: test +test: manifests generate fmt vet envtest ## Run tests. + KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $$(go list ./... | grep -v /e2e) -coverprofile cover.out + +.PHONY: lint +lint: golangci-lint ## Run golangci-lint linter + $(GOLANGCI_LINT) run + +.PHONY: lint-fix +lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes + $(GOLANGCI_LINT) run --fix + +##@ Build + +.PHONY: build +build: manifests generate fmt vet ## Build manager binary. + go build -o bin/manager cmd/main.go + +.PHONY: run +run: manifests generate fmt vet ## Run a controller from your host. + go run ./cmd/main.go + +# If you wish to build the manager image targeting other platforms you can use the --platform flag. +# (i.e. docker build --platform linux/arm64). However, you must enable docker buildKit for it. +# More info: https://docs.docker.com/develop/develop-images/build_enhancements/ +.PHONY: docker-build +docker-build: ## Build docker image with the manager. + $(CONTAINER_TOOL) build -t ${IMG} . + +.PHONY: docker-push +docker-push: ## Push docker image with the manager. + $(CONTAINER_TOOL) push ${IMG} + +# PLATFORMS defines the target platforms for the manager image be built to provide support to multiple +# architectures. (i.e. make docker-buildx IMG=myregistry/mypoperator:0.0.1). To use this option you need to: +# - be able to use docker buildx. More info: https://docs.docker.com/build/buildx/ +# - have enabled BuildKit. More info: https://docs.docker.com/develop/develop-images/build_enhancements/ +# - be able to push the image to your registry (i.e. if you do not set a valid value via IMG=> then the export will fail) +# To adequately provide solutions that are compatible with multiple platforms, you should consider using this option. +PLATFORMS ?= linux/arm64,linux/amd64,linux/s390x,linux/ppc64le +.PHONY: docker-buildx +docker-buildx: ## Build and push docker image for the manager for cross-platform support + # copy existing Dockerfile and insert --platform=${BUILDPLATFORM} into Dockerfile.cross, and preserve the original Dockerfile + sed -e '1 s/\(^FROM\)/FROM --platform=\$$\{BUILDPLATFORM\}/; t' -e ' 1,// s//FROM --platform=\$$\{BUILDPLATFORM\}/' Dockerfile > Dockerfile.cross + - $(CONTAINER_TOOL) buildx create --name postgres-operator-builder + $(CONTAINER_TOOL) buildx use postgres-operator-builder + - $(CONTAINER_TOOL) buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile.cross . + - $(CONTAINER_TOOL) buildx rm postgres-operator-builder + rm Dockerfile.cross + +.PHONY: build-installer +build-installer: manifests generate kustomize ## Generate a consolidated YAML with CRDs and deployment. + mkdir -p dist + cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} + $(KUSTOMIZE) build config/default > dist/install.yaml + +##@ Deployment + +ifndef ignore-not-found + ignore-not-found = false +endif + +.PHONY: install +install: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config. + $(KUSTOMIZE) build config/crd | $(KUBECTL) apply -f - + +.PHONY: uninstall +uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. + $(KUSTOMIZE) build config/crd | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f - + +.PHONY: deploy +deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config. + cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} + $(KUSTOMIZE) build config/default | $(KUBECTL) apply -f - + +.PHONY: undeploy +undeploy: kustomize ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. + $(KUSTOMIZE) build config/default | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f - + +##@ Dependencies + +## Location to install dependencies to +LOCALBIN ?= $(shell pwd)/bin +$(LOCALBIN): + mkdir -p $(LOCALBIN) + +## Tool Binaries +KUBECTL ?= kubectl +KUSTOMIZE ?= $(LOCALBIN)/kustomize +CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen +ENVTEST ?= $(LOCALBIN)/setup-envtest +GOLANGCI_LINT = $(LOCALBIN)/golangci-lint + +## Tool Versions +KUSTOMIZE_VERSION ?= v5.4.3 +CONTROLLER_TOOLS_VERSION ?= v0.16.1 +ENVTEST_VERSION ?= release-0.19 +GOLANGCI_LINT_VERSION ?= v1.59.1 + +.PHONY: kustomize +kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. +$(KUSTOMIZE): $(LOCALBIN) + $(call go-install-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v5,$(KUSTOMIZE_VERSION)) + +.PHONY: controller-gen +controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary. +$(CONTROLLER_GEN): $(LOCALBIN) + $(call go-install-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen,$(CONTROLLER_TOOLS_VERSION)) + +.PHONY: envtest +envtest: $(ENVTEST) ## Download setup-envtest locally if necessary. +$(ENVTEST): $(LOCALBIN) + $(call go-install-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest,$(ENVTEST_VERSION)) + +.PHONY: golangci-lint +golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary. +$(GOLANGCI_LINT): $(LOCALBIN) + $(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION)) + +# go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist +# $1 - target path with name of binary +# $2 - package url which can be installed +# $3 - specific version of package +define go-install-tool +@[ -f "$(1)-$(3)" ] || { \ +set -e; \ +package=$(2)@$(3) ;\ +echo "Downloading $${package}" ;\ +rm -f $(1) || true ;\ +GOBIN=$(LOCALBIN) go install $${package} ;\ +mv $(1) $(1)-$(3) ;\ +} ;\ +ln -sf $(1)-$(3) $(1) +endef + +.PHONY: operator-sdk +OPERATOR_SDK ?= $(LOCALBIN)/operator-sdk +operator-sdk: ## Download operator-sdk locally if necessary. +ifeq (,$(wildcard $(OPERATOR_SDK))) +ifeq (, $(shell which operator-sdk 2>/dev/null)) + @{ \ + set -e ;\ + mkdir -p $(dir $(OPERATOR_SDK)) ;\ + OS=$(shell go env GOOS) && ARCH=$(shell go env GOARCH) && \ + curl -sSLo $(OPERATOR_SDK) https://github.com/operator-framework/operator-sdk/releases/download/$(OPERATOR_SDK_VERSION)/operator-sdk_$${OS}_$${ARCH} ;\ + chmod +x $(OPERATOR_SDK) ;\ + } +else +OPERATOR_SDK = $(shell which operator-sdk) +endif +endif + +.PHONY: bundle +bundle: manifests kustomize operator-sdk ## Generate bundle manifests and metadata, then validate generated files. + $(OPERATOR_SDK) generate kustomize manifests -q + cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG) + $(KUSTOMIZE) build config/manifests | $(OPERATOR_SDK) generate bundle $(BUNDLE_GEN_FLAGS) + $(OPERATOR_SDK) bundle validate ./bundle + +.PHONY: bundle-build +bundle-build: ## Build the bundle image. + docker build -f bundle.Dockerfile -t $(BUNDLE_IMG) . + +.PHONY: bundle-push +bundle-push: ## Push the bundle image. + $(MAKE) docker-push IMG=$(BUNDLE_IMG) + +.PHONY: opm +OPM = $(LOCALBIN)/opm +opm: ## Download opm locally if necessary. +ifeq (,$(wildcard $(OPM))) +ifeq (,$(shell which opm 2>/dev/null)) + @{ \ + set -e ;\ + mkdir -p $(dir $(OPM)) ;\ + OS=$(shell go env GOOS) && ARCH=$(shell go env GOARCH) && \ + curl -sSLo $(OPM) https://github.com/operator-framework/operator-registry/releases/download/v1.23.0/$${OS}-$${ARCH}-opm ;\ + chmod +x $(OPM) ;\ + } +else +OPM = $(shell which opm) +endif +endif + +# A comma-separated list of bundle images (e.g. make catalog-build BUNDLE_IMGS=example.com/operator-bundle:v0.1.0,example.com/operator-bundle:v0.2.0). +# These images MUST exist in a registry and be pull-able. +BUNDLE_IMGS ?= $(BUNDLE_IMG) + +# The image tag given to the resulting catalog image (e.g. make catalog-build CATALOG_IMG=example.com/operator-catalog:v0.2.0). +CATALOG_IMG ?= $(IMAGE_TAG_BASE)-catalog:v$(VERSION) + +# Set CATALOG_BASE_IMG to an existing catalog image tag to add $BUNDLE_IMGS to that image. +ifneq ($(origin CATALOG_BASE_IMG), undefined) +FROM_INDEX_OPT := --from-index $(CATALOG_BASE_IMG) +endif + +# Build a catalog image by adding bundle images to an empty catalog using the operator package manager tool, 'opm'. +# This recipe invokes 'opm' in 'semver' bundle add mode. For more information on add modes, see: +# https://github.com/operator-framework/community-operators/blob/7f1438c/docs/packaging-operator.md#updating-your-existing-operator +.PHONY: catalog-build +catalog-build: opm ## Build a catalog image. + $(OPM) index add --container-tool docker --mode semver --tag $(CATALOG_IMG) --bundles $(BUNDLE_IMGS) $(FROM_INDEX_OPT) + +# Push the catalog image. +.PHONY: catalog-push +catalog-push: ## Push a catalog image. + $(MAKE) docker-push IMG=$(CATALOG_IMG) diff --git a/PROJECT b/PROJECT new file mode 100644 index 000000000..c2b17e01a --- /dev/null +++ b/PROJECT @@ -0,0 +1,32 @@ +# Code generated by tool. DO NOT EDIT. +# This file is used to track the info used to scaffold your project +# and allow the plugins properly work. +# More info: https://book.kubebuilder.io/reference/project-config.html +domain: movetokube.com +layout: +- go.kubebuilder.io/v4 +plugins: + manifests.sdk.operatorframework.io/v2: {} + scorecard.sdk.operatorframework.io/v2: {} +projectName: postgres-operator +repo: github.com/movetokube/postgres-operator +resources: +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: movetokube.com + group: db + kind: Postgres + path: github.com/movetokube/postgres-operator/api/v1alpha1 + version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: movetokube.com + group: db + kind: PostgresUser + path: github.com/movetokube/postgres-operator/api/v1alpha1 + version: v1alpha1 +version: "3" diff --git a/api/v1alpha1/groupversion_info.go b/api/v1alpha1/groupversion_info.go new file mode 100644 index 000000000..3eb1feb64 --- /dev/null +++ b/api/v1alpha1/groupversion_info.go @@ -0,0 +1,20 @@ +// Package v1alpha1 contains API Schema definitions for the db v1alpha1 API group +// +kubebuilder:object:generate=true +// +groupName=db.movetokube.com +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects + GroupVersion = schema.GroupVersion{Group: "db.movetokube.com", Version: "v1alpha1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) diff --git a/pkg/apis/db/v1alpha1/postgres_types.go b/api/v1alpha1/postgres_types.go similarity index 77% rename from pkg/apis/db/v1alpha1/postgres_types.go rename to api/v1alpha1/postgres_types.go index c67e2fe36..8ce68189e 100644 --- a/pkg/apis/db/v1alpha1/postgres_types.go +++ b/api/v1alpha1/postgres_types.go @@ -8,7 +8,6 @@ import ( // NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. // PostgresSpec defines the desired state of Postgres -// +k8s:openapi-gen=true type PostgresSpec struct { Database string `json:"database"` // +optional @@ -24,7 +23,6 @@ type PostgresSpec struct { } // PostgresStatus defines the observed state of Postgres -// +k8s:openapi-gen=true type PostgresStatus struct { Succeeded bool `json:"succeeded"` Roles PostgresRoles `json:"roles"` @@ -34,25 +32,20 @@ type PostgresStatus struct { // +optional // +listType=set Extensions []string `json:"extensions,omitempty"` - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file - // Add custom validation using kubebuilder tags: https://book.kubebuilder.io/beyond_basics/generating_crd.html } // PostgresRoles stores the different group roles for database -// +k8s:openapi-gen=true type PostgresRoles struct { Owner string `json:"owner"` Reader string `json:"reader"` Writer string `json:"writer"` } -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// Postgres is the Schema for the postgres API -// +k8s:openapi-gen=true +// +kubebuilder:object:root=true // +kubebuilder:subresource:status // +kubebuilder:resource:scope=Namespaced + +// Postgres is the Schema for the postgres API type Postgres struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` @@ -61,7 +54,7 @@ type Postgres struct { Status PostgresStatus `json:"status,omitempty"` } -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:object:root=true // PostgresList contains a list of Postgres type PostgresList struct { diff --git a/pkg/apis/db/v1alpha1/postgresuser_types.go b/api/v1alpha1/postgresuser_types.go similarity index 78% rename from pkg/apis/db/v1alpha1/postgresuser_types.go rename to api/v1alpha1/postgresuser_types.go index 29c0fded5..14988760b 100644 --- a/pkg/apis/db/v1alpha1/postgresuser_types.go +++ b/api/v1alpha1/postgresuser_types.go @@ -8,7 +8,6 @@ import ( // NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. // PostgresUserSpec defines the desired state of PostgresUser -// +k8s:openapi-gen=true type PostgresUserSpec struct { Role string `json:"role"` Database string `json:"database"` @@ -24,24 +23,19 @@ type PostgresUserSpec struct { } // PostgresUserStatus defines the observed state of PostgresUser -// +k8s:openapi-gen=true type PostgresUserStatus struct { Succeeded bool `json:"succeeded"` PostgresRole string `json:"postgresRole"` PostgresLogin string `json:"postgresLogin"` PostgresGroup string `json:"postgresGroup"` DatabaseName string `json:"databaseName"` - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file - // Add custom validation using kubebuilder tags: https://book.kubebuilder.io/beyond_basics/generating_crd.html } -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// PostgresUser is the Schema for the postgresusers API -// +k8s:openapi-gen=true +// +kubebuilder:object:root=true // +kubebuilder:subresource:status // +kubebuilder:resource:scope=Namespaced + +// PostgresUser is the Schema for the postgresusers API type PostgresUser struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` @@ -50,7 +44,7 @@ type PostgresUser struct { Status PostgresUserStatus `json:"status,omitempty"` } -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:object:root=true // PostgresUserList contains a list of PostgresUser type PostgresUserList struct { diff --git a/pkg/apis/db/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go similarity index 97% rename from pkg/apis/db/v1alpha1/zz_generated.deepcopy.go rename to api/v1alpha1/zz_generated.deepcopy.go index c0f138f04..9d8d57b6a 100644 --- a/pkg/apis/db/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -1,6 +1,6 @@ -// +build !ignore_autogenerated +//go:build !ignore_autogenerated -// Code generated by operator-sdk. DO NOT EDIT. +// Code generated by controller-gen. DO NOT EDIT. package v1alpha1 @@ -15,7 +15,6 @@ func (in *Postgres) DeepCopyInto(out *Postgres) { in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) in.Status.DeepCopyInto(&out.Status) - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Postgres. @@ -48,7 +47,6 @@ func (in *PostgresList) DeepCopyInto(out *PostgresList) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgresList. @@ -72,7 +70,6 @@ func (in *PostgresList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PostgresRoles) DeepCopyInto(out *PostgresRoles) { *out = *in - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgresRoles. @@ -98,7 +95,6 @@ func (in *PostgresSpec) DeepCopyInto(out *PostgresSpec) { *out = make([]string, len(*in)) copy(*out, *in) } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgresSpec. @@ -125,7 +121,6 @@ func (in *PostgresStatus) DeepCopyInto(out *PostgresStatus) { *out = make([]string, len(*in)) copy(*out, *in) } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgresStatus. @@ -145,7 +140,6 @@ func (in *PostgresUser) DeepCopyInto(out *PostgresUser) { in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) out.Status = in.Status - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgresUser. @@ -178,7 +172,6 @@ func (in *PostgresUserList) DeepCopyInto(out *PostgresUserList) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgresUserList. @@ -223,7 +216,6 @@ func (in *PostgresUserSpec) DeepCopyInto(out *PostgresUserSpec) { (*out)[key] = val } } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgresUserSpec. @@ -239,7 +231,6 @@ func (in *PostgresUserSpec) DeepCopy() *PostgresUserSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PostgresUserStatus) DeepCopyInto(out *PostgresUserStatus) { *out = *in - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgresUserStatus. diff --git a/build/Dockerfile b/build/Dockerfile deleted file mode 100644 index d2aa5ea1f..000000000 --- a/build/Dockerfile +++ /dev/null @@ -1,15 +0,0 @@ -FROM registry.access.redhat.com/ubi8/ubi-minimal:latest - -ENV OPERATOR=/usr/local/bin/postgres-operator \ - USER_UID=1001 \ - USER_NAME=postgres-operator - -# install operator binary -COPY build/_output/bin/postgres-operator ${OPERATOR} - -COPY build/bin /usr/local/bin -RUN /usr/local/bin/user_setup - -ENTRYPOINT ["/usr/local/bin/entrypoint"] - -USER ${USER_UID} diff --git a/build/Dockerfile.dist b/build/Dockerfile.dist deleted file mode 100644 index 477190ae1..000000000 --- a/build/Dockerfile.dist +++ /dev/null @@ -1,35 +0,0 @@ -# syntax=docker/dockerfile:1 -FROM --platform=${BUILDPLATFORM} golang:1.24-bookworm AS build - -ARG TARGETPLATFORM -ARG BUILDPLATFORM -ARG TARGETOS -ARG TARGETARCH - -WORKDIR /src -COPY go.mod go.sum ./ -RUN go mod download - -COPY . . - -RUN --mount=target=. \ - --mount=type=cache,target=/root/.cache/go-build \ - --mount=type=cache,target=/go/pkg \ - CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \ - go build -o /usr/local/bin/postgres-operator cmd/manager/main.go - -FROM --platform=${TARGETPLATFORM} registry.access.redhat.com/ubi8/ubi-minimal:latest - -ENV OPERATOR=/usr/local/bin/postgres-operator \ - USER_UID=1001 \ - USER_NAME=postgres-operator - -# install operator binary -COPY --from=0 /usr/local/bin/postgres-operator ${OPERATOR} - -COPY build/bin /usr/local/bin -RUN /usr/local/bin/user_setup - -ENTRYPOINT ["/usr/local/bin/entrypoint"] - -USER ${USER_UID} diff --git a/build/bin/entrypoint b/build/bin/entrypoint deleted file mode 100755 index 94b60477d..000000000 --- a/build/bin/entrypoint +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh -e - -# This is documented here: -# https://docs.openshift.com/container-platform/3.11/creating_images/guidelines.html#openshift-specific-guidelines - -if ! whoami &>/dev/null; then - if [ -w /etc/passwd ]; then - echo "${USER_NAME:-postgres-operator}:x:$(id -u):$(id -g):${USER_NAME:-postgres-operator} user:${HOME}:/sbin/nologin" >> /etc/passwd - fi -fi - -exec ${OPERATOR} $@ diff --git a/build/bin/user_setup b/build/bin/user_setup deleted file mode 100755 index 1e36064cb..000000000 --- a/build/bin/user_setup +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh -set -x - -# ensure $HOME exists and is accessible by group 0 (we don't know what the runtime UID will be) -mkdir -p ${HOME} -chown ${USER_UID}:0 ${HOME} -chmod ug+rwx ${HOME} - -# runtime user will need to be able to self-insert in /etc/passwd -chmod g+rw /etc/passwd - -# no need for this script to remain in the image after running -rm $0 diff --git a/charts/ext-postgres-operator/templates/operator.yaml b/charts/ext-postgres-operator/templates/operator.yaml index fa2a33a8c..0a2441913 100644 --- a/charts/ext-postgres-operator/templates/operator.yaml +++ b/charts/ext-postgres-operator/templates/operator.yaml @@ -34,8 +34,6 @@ spec: securityContext: {{- toYaml .Values.securityContext | nindent 12 }} image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" - command: - - postgres-operator imagePullPolicy: {{ .Values.image.pullPolicy }} envFrom: - secretRef: @@ -51,8 +49,6 @@ spec: valueFrom: fieldRef: fieldPath: metadata.name - - name: OPERATOR_NAME - value: {{ include "chart.fullname" . }} {{- range $key, $value := .Values.env }} - name: {{ $key }} value: {{ $value | quote }} diff --git a/charts/ext-postgres-operator/values.yaml b/charts/ext-postgres-operator/values.yaml index 90f9ff0cf..0422e05aa 100644 --- a/charts/ext-postgres-operator/values.yaml +++ b/charts/ext-postgres-operator/values.yaml @@ -29,10 +29,18 @@ podAnnotations: {} # Additionnal labels to add to the pod. podLabels: {} -podSecurityContext: {} +podSecurityContext: + runAsNonRoot: true # fsGroup: 2000 -resources: {} +securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - "ALL" + +resources: + {} # We usually recommend not to specify default resources and to leave this as a conscious # choice for the user. This also increases chances charts run on environments with little # resources, such as Minikube. If you do want to specify resources, uncomment the following diff --git a/cmd/main.go b/cmd/main.go new file mode 100644 index 000000000..c445a64a2 --- /dev/null +++ b/cmd/main.go @@ -0,0 +1,182 @@ +package main + +import ( + "crypto/tls" + "flag" + "fmt" + "os" + + // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) + // to ensure that exec-entrypoint and run can make use of them. + _ "k8s.io/client-go/plugin/pkg/client/auth" + + "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/healthz" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + "sigs.k8s.io/controller-runtime/pkg/metrics/filters" + metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" + "sigs.k8s.io/controller-runtime/pkg/webhook" + + dbv1alpha1 "github.com/movetokube/postgres-operator/api/v1alpha1" + "github.com/movetokube/postgres-operator/internal/controller" + "github.com/movetokube/postgres-operator/pkg/config" + "github.com/movetokube/postgres-operator/pkg/postgres" + "sigs.k8s.io/controller-runtime/pkg/cache" + // +kubebuilder:scaffold:imports +) + +var ( + scheme = runtime.NewScheme() + setupLog = ctrl.Log.WithName("setup") +) + +func init() { + utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + + utilruntime.Must(dbv1alpha1.AddToScheme(scheme)) + // +kubebuilder:scaffold:scheme +} + +func main() { + var metricsAddr string + var enableLeaderElection bool + var probeAddr string + var secureMetrics bool + var enableHTTP2 bool + var tlsOpts []func(*tls.Config) + flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+ + "Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.") + flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") + flag.BoolVar(&enableLeaderElection, "leader-elect", false, + "Enable leader election for controller manager. "+ + "Enabling this will ensure there is only one active controller manager.") + flag.BoolVar(&secureMetrics, "metrics-secure", true, + "If set, the metrics endpoint is served securely via HTTPS. Use --metrics-secure=false to use HTTP instead.") + flag.BoolVar(&enableHTTP2, "enable-http2", false, + "If set, HTTP/2 will be enabled for the metrics and webhook servers") + opts := zap.Options{ + Development: true, + } + opts.BindFlags(flag.CommandLine) + flag.Parse() + + ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) + + // if the enable-http2 flag is false (the default), http/2 should be disabled + // due to its vulnerabilities. More specifically, disabling http/2 will + // prevent from being vulnerable to the HTTP/2 Stream Cancellation and + // Rapid Reset CVEs. For more information see: + // - https://github.com/advisories/GHSA-qppj-fm5r-hxr3 + // - https://github.com/advisories/GHSA-4374-p667-p6c8 + disableHTTP2 := func(c *tls.Config) { + setupLog.Info("disabling http/2") + c.NextProtos = []string{"http/1.1"} + } + + if !enableHTTP2 { + tlsOpts = append(tlsOpts, disableHTTP2) + } + + webhookServer := webhook.NewServer(webhook.Options{ + TLSOpts: tlsOpts, + }) + + // Metrics endpoint is enabled in 'config/default/kustomization.yaml'. The Metrics options configure the server. + // More info: + // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.0/pkg/metrics/server + // - https://book.kubebuilder.io/reference/metrics.html + metricsServerOptions := metricsserver.Options{ + BindAddress: metricsAddr, + SecureServing: secureMetrics, + // TODO(user): TLSOpts is used to allow configuring the TLS config used for the server. If certificates are + // not provided, self-signed certificates will be generated by default. This option is not recommended for + // production environments as self-signed certificates do not offer the same level of trust and security + // as certificates issued by a trusted Certificate Authority (CA). The primary risk is potentially allowing + // unauthorized access to sensitive metrics data. Consider replacing with CertDir, CertName, and KeyName + // to provide certificates, ensuring the server communicates using trusted and secure certificates. + TLSOpts: tlsOpts, + } + + if secureMetrics { + // FilterProvider is used to protect the metrics endpoint with authn/authz. + // These configurations ensure that only authorized users and service accounts + // can access the metrics endpoint. The RBAC are configured in 'config/rbac/kustomization.yaml'. More info: + // https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.0/pkg/metrics/filters#WithAuthenticationAndAuthorization + metricsServerOptions.FilterProvider = filters.WithAuthenticationAndAuthorization + } + + cfg := config.Get() + lockName := "lock" + if cfg.AnnotationFilter == "" { + setupLog.Info("No POSTGRES_INSTANCE set, this instance will only process CRs without an annotation") + } else { + setupLog.Info("POSTGRES_INSTANCE is set, this instance will only process CRs with the correct annotation", "annotation", cfg.AnnotationFilter) + lockName += "-" + cfg.AnnotationFilter + } + cacheOpts := cache.Options{} + namespace, found := os.LookupEnv("WATCH_NAMESPACE") + if found { + cacheOpts.DefaultNamespaces = map[string]cache.Config{ + namespace: {}, + } + } + + mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ + Scheme: scheme, + Metrics: metricsServerOptions, + WebhookServer: webhookServer, + HealthProbeBindAddress: probeAddr, + LeaderElection: enableLeaderElection, + LeaderElectionID: fmt.Sprintf("%s.db.movetokube.com", lockName), + Cache: cacheOpts, + // LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily + // when the Manager ends. This requires the binary to immediately end when the + // Manager is stopped, otherwise, this setting is unsafe. Setting this significantly + // speeds up voluntary leader transitions as the new leader don't have to wait + // LeaseDuration time first. + // + // In the default scaffold provided, the program ends immediately after + // the manager stops, so would be fine to enable this option. However, + // if you are doing or is intended to do any operation such as perform cleanups + // after the manager stops then its usage might be unsafe. + // LeaderElectionReleaseOnCancel: true, + }) + if err != nil { + setupLog.Error(err, "unable to start manager") + os.Exit(1) + } + + pg, err := postgres.NewPG(cfg, ctrl.Log) + if err != nil { + setupLog.Error(err, "DB-Connection failed", "cfg", cfg) + os.Exit(1) + } + + if err = (controller.NewPostgresReconciler(mgr, cfg, pg)).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "Postgres") + os.Exit(1) + } + if err = (controller.NewPostgresUserReconciler(mgr, cfg, pg)).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "PostgresUser") + os.Exit(1) + } + // +kubebuilder:scaffold:builder + + if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { + setupLog.Error(err, "unable to set up health check") + os.Exit(1) + } + if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil { + setupLog.Error(err, "unable to set up ready check") + os.Exit(1) + } + + setupLog.Info("starting manager") + if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { + setupLog.Error(err, "problem running manager") + os.Exit(1) + } +} diff --git a/cmd/manager/main.go b/cmd/manager/main.go deleted file mode 100644 index 9c8e4cbdd..000000000 --- a/cmd/manager/main.go +++ /dev/null @@ -1,224 +0,0 @@ -package main - -import ( - "context" - "errors" - "flag" - "fmt" - "os" - "runtime" - "strings" - - kubemetrics "github.com/operator-framework/operator-sdk/pkg/kube-metrics" - "github.com/operator-framework/operator-sdk/pkg/metrics" - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/client-go/rest" - "sigs.k8s.io/controller-runtime/pkg/cache" - - // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) - _ "k8s.io/client-go/plugin/pkg/client/auth" - - "github.com/movetokube/postgres-operator/pkg/apis" - operatorconfig "github.com/movetokube/postgres-operator/pkg/config" - "github.com/movetokube/postgres-operator/pkg/controller" - "github.com/movetokube/postgres-operator/pkg/utils" - "github.com/operator-framework/operator-sdk/pkg/k8sutil" - "github.com/operator-framework/operator-sdk/pkg/leader" - "github.com/operator-framework/operator-sdk/pkg/log/zap" - sdkVersion "github.com/operator-framework/operator-sdk/version" - "github.com/spf13/pflag" - "sigs.k8s.io/controller-runtime/pkg/client/config" - "sigs.k8s.io/controller-runtime/pkg/manager" - logf "sigs.k8s.io/controller-runtime/pkg/runtime/log" - "sigs.k8s.io/controller-runtime/pkg/runtime/signals" -) - -// Change below variables to serve metrics on different host or port. -var ( - metricsHost = "0.0.0.0" - metricsPort int32 = 8383 - operatorMetricsPort int32 = 8686 -) -var log = logf.Log.WithName("cmd") - -func printVersion() { - log.Info(fmt.Sprintf("Go Version: %s", runtime.Version())) - log.Info(fmt.Sprintf("Go OS/Arch: %s/%s", runtime.GOOS, runtime.GOARCH)) - log.Info(fmt.Sprintf("Version of operator-sdk: %v", sdkVersion.Version)) -} - -func main() { - // Add the zap logger flag set to the CLI. The flag set must - // be added before calling pflag.Parse(). - pflag.CommandLine.AddFlagSet(zap.FlagSet()) - - // Add flags registered by imported packages (e.g. glog and - // controller-runtime) - pflag.CommandLine.AddGoFlagSet(flag.CommandLine) - - pflag.Parse() - - // Use a zap logr.Logger implementation. If none of the zap - // flags are configured (or if the zap flag set is not being - // used), this defaults to a production zap logger. - // - // The logger instantiated here can be changed to any logger - // implementing the logr.Logger interface. This logger will - // be propagated through the whole operator, generating - // uniform and structured logs. - logf.SetLogger(zap.Logger()) - - printVersion() - - operatorConfig := operatorconfig.Get() - var lockName string - if operatorConfig.AnnotationFilter == "" { - log.Info("No POSTGRES_INSTANCE set, this instance will only process CR's without an annotation") - lockName = "postgres-operator-lock" - } else { - log.Info(fmt.Sprintf("POSTGRES_INSTANCE is set, this instance will only process CR's with the annotation: %s: %s", utils.INSTANCE_ANNOTATION, operatorConfig.AnnotationFilter)) - lockName = fmt.Sprintf("postgres-operator-lock-%s", operatorConfig.AnnotationFilter) - } - - namespace, err := k8sutil.GetWatchNamespace() - if err != nil { - log.Error(err, "Failed to get watch namespace") - os.Exit(1) - } - - // Get a config to talk to the apiserver - cfg, err := config.GetConfig() - if err != nil { - log.Error(err, "") - os.Exit(1) - } - - ctx := context.TODO() - - // Become the leader before proceeding - err = leader.Become(ctx, lockName) - if err != nil { - log.Error(err, "") - os.Exit(1) - } - - // Set default manager options - options := manager.Options{ - Namespace: namespace, - MetricsBindAddress: fmt.Sprintf("%s:%d", metricsHost, metricsPort), - } - - // Add support for MultiNamespace set in WATCH_NAMESPACE (e.g ns1,ns2) - // Note that this is not intended to be used for excluding namespaces, this is better done via a Predicate - // Also note that you may face performance issues when using this with a high number of namespaces. - // More Info: https://godoc.org/github.com/kubernetes-sigs/controller-runtime/pkg/cache#MultiNamespacedCacheBuilder - if strings.Contains(namespace, ",") { - options.Namespace = "" - options.NewCache = cache.MultiNamespacedCacheBuilder(strings.Split(namespace, ",")) - } - - // Create a new manager to provide shared dependencies and start components - mgr, err := manager.New(cfg, options) - if err != nil { - log.Error(err, "") - os.Exit(1) - } - - log.Info("Registering Components.") - - // Setup Scheme for all resources - if err := apis.AddToScheme(mgr.GetScheme()); err != nil { - log.Error(err, "") - os.Exit(1) - } - - // Setup all Controllers - if err := controller.AddToManager(mgr); err != nil { - log.Error(err, "") - os.Exit(1) - } - - // Add the Metrics Service - addMetrics(ctx, cfg) - - log.Info("Starting the Cmd.") - - // Start the Cmd - if err := mgr.Start(signals.SetupSignalHandler()); err != nil { - log.Error(err, "Manager exited non-zero") - os.Exit(1) - } -} - -// addMetrics will create the Services and Service Monitors to allow the operator export the metrics by using -// the Prometheus operator -// addMetrics will create the Services and Service Monitors to allow the operator export the metrics by using -// the Prometheus operator -func addMetrics(ctx context.Context, cfg *rest.Config) { - // Get the namespace the operator is currently deployed in. - operatorNs, err := k8sutil.GetOperatorNamespace() - if err != nil { - if errors.Is(err, k8sutil.ErrRunLocal) { - log.Info("Skipping CR metrics server creation; not running in a cluster.") - return - } - } - - if err := serveCRMetrics(cfg, operatorNs); err != nil { - log.Info("Could not generate and serve custom resource metrics", "error", err.Error()) - } - - // Add to the below struct any other metrics ports you want to expose. - servicePorts := []v1.ServicePort{ - {Port: metricsPort, Name: metrics.OperatorPortName, Protocol: v1.ProtocolTCP, TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: metricsPort}}, - {Port: operatorMetricsPort, Name: metrics.CRPortName, Protocol: v1.ProtocolTCP, TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: operatorMetricsPort}}, - } - - // Create Service object to expose the metrics port(s). - service, err := metrics.CreateMetricsService(ctx, cfg, servicePorts) - if err != nil { - log.Info("Could not create metrics Service", "error", err.Error()) - } - - // CreateServiceMonitors will automatically create the prometheus-operator ServiceMonitor resources - // necessary to configure Prometheus to scrape metrics from this operator. - services := []*v1.Service{service} - - // The ServiceMonitor is created in the same namespace where the operator is deployed - _, err = metrics.CreateServiceMonitors(cfg, operatorNs, services) - if err != nil { - log.Info("Could not create ServiceMonitor object", "error", err.Error()) - // If this operator is deployed to a cluster without the prometheus-operator running, it will return - // ErrServiceMonitorNotPresent, which can be used to safely skip ServiceMonitor creation. - if err == metrics.ErrServiceMonitorNotPresent { - log.Info("Install prometheus-operator in your cluster to create ServiceMonitor objects", "error", err.Error()) - } - } -} - -// serveCRMetrics gets the Operator/CustomResource GVKs and generates metrics based on those types. -// It serves those metrics on "http://metricsHost:operatorMetricsPort". -func serveCRMetrics(cfg *rest.Config, operatorNs string) error { - // The function below returns a list of filtered operator/CR specific GVKs. For more control, override the GVK list below - // with your own custom logic. Note that if you are adding third party API schemas, probably you will need to - // customize this implementation to avoid permissions issues. - filteredGVK, err := k8sutil.GetGVKsFromAddToScheme(apis.AddToScheme) - if err != nil { - return err - } - - // The metrics will be generated from the namespaces which are returned here. - // NOTE that passing nil or an empty list of namespaces in GenerateAndServeCRMetrics will result in an error. - ns, err := kubemetrics.GetNamespacesForMetrics(operatorNs) - if err != nil { - return err - } - - // Generate and serve custom resource specific metrics. - err = kubemetrics.GenerateAndServeCRMetrics(cfg, ns, filteredGVK, metricsHost, operatorMetricsPort) - if err != nil { - return err - } - return nil -} diff --git a/deploy/crds/db.movetokube.com_postgres_crd.yaml b/config/crd/bases/db.movetokube.com_postgres.yaml similarity index 73% rename from deploy/crds/db.movetokube.com_postgres_crd.yaml rename to config/crd/bases/db.movetokube.com_postgres.yaml index 4977deff6..a7de6e1fd 100644 --- a/deploy/crds/db.movetokube.com_postgres_crd.yaml +++ b/config/crd/bases/db.movetokube.com_postgres.yaml @@ -1,6 +1,9 @@ +--- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 name: postgres.db.movetokube.com spec: group: db.movetokube.com @@ -17,14 +20,19 @@ spec: description: Postgres is the Schema for the postgres API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object diff --git a/deploy/crds/db.movetokube.com_postgresusers_crd.yaml b/config/crd/bases/db.movetokube.com_postgresusers.yaml similarity index 71% rename from deploy/crds/db.movetokube.com_postgresusers_crd.yaml rename to config/crd/bases/db.movetokube.com_postgresusers.yaml index 3b45527c7..478eb378c 100644 --- a/deploy/crds/db.movetokube.com_postgresusers_crd.yaml +++ b/config/crd/bases/db.movetokube.com_postgresusers.yaml @@ -1,6 +1,9 @@ +--- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 name: postgresusers.db.movetokube.com spec: group: db.movetokube.com @@ -17,14 +20,19 @@ spec: description: PostgresUser is the Schema for the postgresusers API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml new file mode 100644 index 000000000..1c98cf680 --- /dev/null +++ b/config/crd/kustomization.yaml @@ -0,0 +1,24 @@ +# This kustomization.yaml is not intended to be run by itself, +# since it depends on service name and namespace that are out of this kustomize package. +# It should be run by config/default +resources: +- bases/db.movetokube.com_postgres.yaml +- bases/db.movetokube.com_postgresusers.yaml +# +kubebuilder:scaffold:crdkustomizeresource + +patches: +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. +# patches here are for enabling the conversion webhook for each CRD +# +kubebuilder:scaffold:crdkustomizewebhookpatch + +# [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. +# patches here are for enabling the CA injection for each CRD +#- path: patches/cainjection_in_postgres.yaml +#- path: patches/cainjection_in_postgresusers.yaml +# +kubebuilder:scaffold:crdkustomizecainjectionpatch + +# [WEBHOOK] To enable webhook, uncomment the following section +# the following config is for teaching kustomize how to do kustomization for CRDs. + +#configurations: +#- kustomizeconfig.yaml diff --git a/config/crd/kustomizeconfig.yaml b/config/crd/kustomizeconfig.yaml new file mode 100644 index 000000000..ec5c150a9 --- /dev/null +++ b/config/crd/kustomizeconfig.yaml @@ -0,0 +1,19 @@ +# This file is for teaching kustomize how to substitute name and namespace reference in CRD +nameReference: +- kind: Service + version: v1 + fieldSpecs: + - kind: CustomResourceDefinition + version: v1 + group: apiextensions.k8s.io + path: spec/conversion/webhook/clientConfig/service/name + +namespace: +- kind: CustomResourceDefinition + version: v1 + group: apiextensions.k8s.io + path: spec/conversion/webhook/clientConfig/service/namespace + create: false + +varReference: +- path: metadata/annotations diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml new file mode 100644 index 000000000..cd8360341 --- /dev/null +++ b/config/default/kustomization.yaml @@ -0,0 +1,7 @@ +namespace: operators + +resources: + - namespace.yaml + - ../crd + - ../rbac + - ../manager diff --git a/deploy/namespace.yaml b/config/default/namespace.yaml similarity index 69% rename from deploy/namespace.yaml rename to config/default/namespace.yaml index bd800882c..8269bfee2 100644 --- a/deploy/namespace.yaml +++ b/config/default/namespace.yaml @@ -1,4 +1,4 @@ apiVersion: v1 kind: Namespace metadata: - name: operators \ No newline at end of file + name: operators diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml new file mode 100644 index 000000000..6041fc268 --- /dev/null +++ b/config/manager/kustomization.yaml @@ -0,0 +1,2 @@ +resources: + - operator.yaml diff --git a/deploy/operator.yaml b/config/manager/operator.yaml similarity index 51% rename from deploy/operator.yaml rename to config/manager/operator.yaml index 0bcf0502b..9f9ed4032 100644 --- a/deploy/operator.yaml +++ b/config/manager/operator.yaml @@ -13,11 +13,13 @@ spec: name: ext-postgres-operator spec: serviceAccountName: ext-postgres-operator + securityContext: + runAsNonRoot: true containers: - name: ext-postgres-operator image: movetokube/postgres-operator command: - - postgres-operator + - postgres-operator imagePullPolicy: Always envFrom: - secretRef: @@ -31,5 +33,28 @@ spec: valueFrom: fieldRef: fieldPath: metadata.name - - name: OPERATOR_NAME - value: "ext-postgres-operator" + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - "ALL" + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 10m + memory: 64Mi + terminationGracePeriodSeconds: 10 diff --git a/config/manifests/kustomization.yaml b/config/manifests/kustomization.yaml new file mode 100644 index 000000000..386c6a623 --- /dev/null +++ b/config/manifests/kustomization.yaml @@ -0,0 +1,3 @@ +resources: + - ../default + - ../samples diff --git a/deploy/cluster_role.yaml b/config/rbac/cluster_role.yaml similarity index 100% rename from deploy/cluster_role.yaml rename to config/rbac/cluster_role.yaml diff --git a/deploy/cluster_role_binding.yaml b/config/rbac/cluster_role_binding.yaml similarity index 100% rename from deploy/cluster_role_binding.yaml rename to config/rbac/cluster_role_binding.yaml diff --git a/config/rbac/kustomization.yaml b/config/rbac/kustomization.yaml new file mode 100644 index 000000000..fb70d0dc3 --- /dev/null +++ b/config/rbac/kustomization.yaml @@ -0,0 +1,6 @@ +resources: + - service_account.yaml + - cluster_role.yaml + - cluster_role_binding.yaml + - role.yaml + - role_binding.yaml diff --git a/deploy/role.yaml b/config/rbac/role.yaml similarity index 100% rename from deploy/role.yaml rename to config/rbac/role.yaml diff --git a/deploy/role_binding.yaml b/config/rbac/role_binding.yaml similarity index 100% rename from deploy/role_binding.yaml rename to config/rbac/role_binding.yaml diff --git a/deploy/service_account.yaml b/config/rbac/service_account.yaml similarity index 100% rename from deploy/service_account.yaml rename to config/rbac/service_account.yaml diff --git a/deploy/crds/db_v1alpha1_postgres_cr.yaml b/config/samples/db_v1alpha1_postgres.yaml similarity index 76% rename from deploy/crds/db_v1alpha1_postgres_cr.yaml rename to config/samples/db_v1alpha1_postgres.yaml index 16a39a7f2..a1d0525c3 100644 --- a/deploy/crds/db_v1alpha1_postgres_cr.yaml +++ b/config/samples/db_v1alpha1_postgres.yaml @@ -1,12 +1,14 @@ apiVersion: db.movetokube.com/v1alpha1 kind: Postgres metadata: + labels: + app.kubernetes.io/name: postgres-operator + app.kubernetes.io/managed-by: kustomize name: my-db - namespace: app spec: database: test-db # Name of database created in PostgreSQL dropOnDelete: false # Set to true if you want the operator to drop the database and role when this CR is deleted masterRole: test-db-group schemas: # List of schemas the operator should create in database - stores - - customers \ No newline at end of file + - customers diff --git a/deploy/crds/db_v1alpha1_postgresuser_cr.yaml b/config/samples/db_v1alpha1_postgresuser.yaml similarity index 69% rename from deploy/crds/db_v1alpha1_postgresuser_cr.yaml rename to config/samples/db_v1alpha1_postgresuser.yaml index 4dd7b1a59..9341cc855 100644 --- a/deploy/crds/db_v1alpha1_postgresuser_cr.yaml +++ b/config/samples/db_v1alpha1_postgresuser.yaml @@ -1,12 +1,12 @@ apiVersion: db.movetokube.com/v1alpha1 kind: PostgresUser metadata: + labels: + app.kubernetes.io/name: postgres-operator + app.kubernetes.io/managed-by: kustomize name: my-db-user - namespace: app spec: role: username database: my-db # This references the Postgres CR secretName: my-secret privileges: OWNER # Can be OWNER/READ/WRITE - labels: # optional labels to propagate to the secret - custom-label: custom-value diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml new file mode 100644 index 000000000..538c4d395 --- /dev/null +++ b/config/samples/kustomization.yaml @@ -0,0 +1,5 @@ +## Append samples of your project ## +resources: +- db_v1alpha1_postgres.yaml +- db_v1alpha1_postgresuser.yaml +# +kubebuilder:scaffold:manifestskustomizesamples diff --git a/deploy/secret.yaml b/config/samples/secret.yaml similarity index 81% rename from deploy/secret.yaml rename to config/samples/secret.yaml index bb4a62b4e..090c3e785 100644 --- a/deploy/secret.yaml +++ b/config/samples/secret.yaml @@ -7,4 +7,4 @@ data: POSTGRES_HOST: cG9zdGdyZXMuZGF0YWJhc2Vz POSTGRES_USER: cG9zdGdyZXM= POSTGRES_PASS: YWRtaW4xMjM= - POSTGRES_URI_ARGS: c3NsbW9kZT1kaXNhYmxl \ No newline at end of file + POSTGRES_URI_ARGS: c3NsbW9kZT1kaXNhYmxl diff --git a/deploy/dev/postgres.yaml b/deploy/dev/postgres.yaml deleted file mode 100644 index 4534d30ad..000000000 --- a/deploy/dev/postgres.yaml +++ /dev/null @@ -1,50 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: postgres - namespace: databases -spec: - selector: - app: postgres - ports: - - protocol: TCP - port: 5432 - targetPort: 5432 - ---- - -apiVersion: v1 -kind: ConfigMap -metadata: - name: postgres-config - namespace: databases - labels: - app: postgres -data: - POSTGRES_DB: postgres - POSTGRES_USER: postgres - POSTGRES_PASSWORD: admin123 - ---- - -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - name: postgres - namespace: databases -spec: - replicas: 1 - template: - metadata: - labels: - app: postgres - spec: - containers: - - name: postgres - image: postgres:10.4 - imagePullPolicy: "IfNotPresent" - ports: - - containerPort: 5432 - envFrom: - - configMapRef: - name: postgres-config \ No newline at end of file diff --git a/deploy/dev/psql.yaml b/deploy/dev/psql.yaml deleted file mode 100644 index 5fd371291..000000000 --- a/deploy/dev/psql.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: psql -spec: - containers: - - name: psql - image: governmentpaas/psql - command: - - sleep - - "3600" \ No newline at end of file diff --git a/deploy/kustomization.yaml b/deploy/kustomization.yaml deleted file mode 100644 index 72753570d..000000000 --- a/deploy/kustomization.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization - -namespace: operators - -resources: - - crds/db.movetokube.com_postgres_crd.yaml - - crds/db.movetokube.com_postgresusers_crd.yaml - - operator.yaml - - cluster_role.yaml - - cluster_role_binding.yaml - - role.yaml - - role_binding.yaml - - service_account.yaml diff --git a/deploy/olm-catalog/ext-postgres-operator/0.4.1/ext-postgres-operator.v0.4.1.clusterserviceversion.yaml b/deploy/olm-catalog/ext-postgres-operator/0.4.1/ext-postgres-operator.v0.4.1.clusterserviceversion.yaml deleted file mode 100644 index 320d03fde..000000000 --- a/deploy/olm-catalog/ext-postgres-operator/0.4.1/ext-postgres-operator.v0.4.1.clusterserviceversion.yaml +++ /dev/null @@ -1,160 +0,0 @@ -apiVersion: operators.coreos.com/v1alpha1 -kind: ClusterServiceVersion -metadata: - annotations: - alm-examples: |- - [ - { - "apiVersion": "db.movetokube.com/v1alpha1", - "kind": "Postgres", - "metadata": { - "name": "my-db", - "namespace": "app" - }, - "spec": { - "database": "test-db", - "dropOnDelete": false, - "masterRole": "test-db-group", - "schemas": [ - "stores", - "customers" - ] - } - }, - { - "apiVersion": "db.movetokube.com/v1alpha1", - "kind": "PostgresUser", - "metadata": { - "name": "my-db-user", - "namespace": "app" - }, - "spec": { - "database": "my-db", - "privileges": "OWNER", - "role": "username", - "secretName": "my-secret" - } - } - ] - capabilities: Basic Install - categories: Database - containerImage: movetokube/postgres-operator:0.3 - description: Manage databases and roles in external PostgreSQL server or cluster - repository: https://github.com/movetokube/postgres-operator - createdAt: '2019-12-19T14:12:00Z' - name: ext-postgres-operator.v0.4.1 - namespace: placeholder -spec: - apiservicedefinitions: {} - customresourcedefinitions: - owned: - - description: Represents a resource for managing external PostgreSQL database - and associated group role - displayName: postgres.db.movetokube.com - kind: Postgres - name: postgres.db.movetokube.com - version: v1alpha1 - - description: Represents a resource for managing external PostgreSQL user role - displayName: postgresusers.db.movetokube.com - kind: PostgresUser - name: postgresusers.db.movetokube.com - version: v1alpha1 - description: "ext-postgres-operator is an external PostgreSQL database operator. This is a very light-weight basic operator which\ndoes not provide a PostgreSQL server, but rather manages databases inside an existing PostgreSQL database server (or cluster). \n## Features\r\n* Creates a database from a CR\r\n* Creates a role with random username and password from a CR\r\n* If the database exist, it will only create a role\r\n* Multiple user roles can own one database\r\n* Creates Kubernetes secret with postgres_uri in the same namespace as CR\r\n* Support for AWS RDS and Azure Database for PostgresSQL\r\n\r\n\r\n## Cloud specific configuration\r\n### AWS\r\nIn order for this operator to work correctly with AWS RDS, you need to set `POSTGRES_CLOUD_PROVIDER` to `AWS` either in \r\nthe ext-postgres-operator kubernetes secret or directly in the deployment manifest (`operator.yaml`).\r\n\r\n### Azure Database for PostgreSQL\r\nIn order for this operator to work correctly with Azure managed PostgreSQL database, two env variables needs to be provided for the operator:\r\n* `POSTGRES_CLOUD_PROVIDER` set to `Azure`\r\n* `POSTGRES_DEFAULT_DATABASE` set to your default database, i.e. `postgres`\r\n\r\n## CRs\r\n\r\n### Postgres\r\n```yaml\r\napiVersion: db.movetokube.com\/v1alpha1\r\nkind: Postgres\r\nmetadata:\r\n name: my-db\r\n namespace: app\r\nspec:\r\n database: test-db # Name of database created in PostgreSQL\r\n dropOnDelete: false # Set to true if you want the operator to drop the database and role when this CR is deleted\r\n masterRole: test-db-group\r\n schemas: # List of schemas the operator should create in database\r\n - stores\r\n - customers\r\n```\r\n\r\nThis creates a database called `test-db` and a role `test-db-group` that is set as the owner of the database.\r\nReader and writer roles are also created. These roles have read and write permissions to all tables in the schemas created by the operator, if any.\r\n\r\n### PostgresUser\r\n```yaml\r\napiVersion: db.movetokube.com\/v1alpha1\r\nkind: PostgresUser\r\nmetadata:\r\n name: my-db-user\r\n namespace: app\r\nspec:\r\n role: username\r\n database: my-db # This references the Postgres CR\r\n secretName: my-secret\r\n privileges: OWNER # Can be OWNER\/READ\/WRITE\r\n```\r\n\r\nThis creates a user role `username-` and grants role `test-db-group`, `test-db-writer` or `test-db-reader` depending on `privileges` property. Its credentials are put in secret `my-secret-my-db-user`.\r\n\r\n`PostgresUser` needs to reference a `Postgres` in the same namespace.\r\n\r\nTwo `Postgres` referencing the same database can exist in more than one namespace. The last CR referencing a database will drop the group role and transfer database ownership to the role used by the operator." - displayName: Ext Postgres Operator - install: - spec: - clusterPermissions: - - rules: - - apiGroups: - - "" - resources: - - pods - - services - - endpoints - - persistentvolumeclaims - - events - - configmaps - - secrets - verbs: - - '*' - - apiGroups: - - apps - resources: - - deployments - - daemonsets - - replicasets - - statefulsets - verbs: - - '*' - - apiGroups: - - apps - resourceNames: - - ext-postgres-operator - resources: - - deployments/finalizers - verbs: - - update - - apiGroups: - - db.movetokube.com - resources: - - '*' - verbs: - - '*' - serviceAccountName: ext-postgres-operator - deployments: - - name: ext-postgres-operator - spec: - replicas: 1 - selector: - matchLabels: - name: ext-postgres-operator - strategy: {} - template: - metadata: - labels: - name: ext-postgres-operator - spec: - containers: - - command: - - postgres-operator - env: - - name: WATCH_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.annotations['olm.targetNamespaces'] - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: OPERATOR_NAME - value: ext-postgres-operator - envFrom: - - secretRef: - name: ext-postgres-operator - image: movetokube/postgres-operator:0.2 - imagePullPolicy: Always - name: ext-postgres-operator - resources: {} - serviceAccountName: ext-postgres-operator - strategy: deployment - installModes: - - supported: true - type: OwnNamespace - - supported: true - type: SingleNamespace - - supported: false - type: MultiNamespace - - supported: true - type: AllNamespaces - - links: - - name: movetokube.com - url: https://movetokube.com - maintainers: - - email: tomas@movetokube.com - name: Tomas Adomavicius - maturity: alpha - provider: - name: movetokube.com - replaces: ext-postgres-operator.v0.3.0 - version: 0.4.1 \ No newline at end of file diff --git a/deploy/olm-catalog/ext-postgres-operator/0.4.3/ext-postgres-operator.v0.4.3.clusterserviceversion.yaml b/deploy/olm-catalog/ext-postgres-operator/0.4.3/ext-postgres-operator.v0.4.3.clusterserviceversion.yaml deleted file mode 100644 index 5d714d36f..000000000 --- a/deploy/olm-catalog/ext-postgres-operator/0.4.3/ext-postgres-operator.v0.4.3.clusterserviceversion.yaml +++ /dev/null @@ -1,144 +0,0 @@ -apiVersion: operators.coreos.com/v1alpha1 -kind: ClusterServiceVersion -metadata: - annotations: - alm-examples: |- - [ - { - "apiVersion": "db.movetokube.com/v1alpha1", - "kind": "Postgres", - "metadata": { - "name": "my-db", - "namespace": "app" - }, - "spec": { - "database": "test-db", - "dropOnDelete": false, - "masterRole": "test-db-group", - "schemas": [ - "stores", - "customers" - ] - } - }, - { - "apiVersion": "db.movetokube.com/v1alpha1", - "kind": "PostgresUser", - "metadata": { - "name": "my-db-user", - "namespace": "app" - }, - "spec": { - "database": "my-db", - "privileges": "OWNER", - "role": "username", - "secretName": "my-secret" - } - } - ] - capabilities: Basic Install - name: ext-postgres-operator.v0.4.3 - namespace: placeholder -spec: - apiservicedefinitions: {} - customresourcedefinitions: - owned: - - description: Postgres is the Schema for the postgres API - kind: Postgres - name: postgres.db.movetokube.com - version: v1alpha1 - - description: PostgresUser is the Schema for the postgresusers API - kind: PostgresUser - name: postgresusers.db.movetokube.com - version: v1alpha1 - description: Placeholder description - displayName: Ext Postgres Operator - install: - spec: - clusterPermissions: - - rules: - - apiGroups: - - "" - resources: - - pods - - services - - endpoints - - persistentvolumeclaims - - events - - configmaps - - secrets - verbs: - - '*' - - apiGroups: - - apps - resources: - - deployments - - daemonsets - - replicasets - - statefulsets - verbs: - - '*' - - apiGroups: - - apps - resourceNames: - - ext-postgres-operator - resources: - - deployments/finalizers - verbs: - - update - - apiGroups: - - db.movetokube.com - resources: - - '*' - verbs: - - '*' - serviceAccountName: ext-postgres-operator - deployments: - - name: ext-postgres-operator - spec: - replicas: 1 - selector: - matchLabels: - name: ext-postgres-operator - strategy: {} - template: - metadata: - labels: - name: ext-postgres-operator - spec: - containers: - - command: - - postgres-operator - env: - - name: WATCH_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.annotations['olm.targetNamespaces'] - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: OPERATOR_NAME - value: ext-postgres-operator - envFrom: - - secretRef: - name: ext-postgres-operator - image: movetokube/postgres-operator - imagePullPolicy: Always - name: ext-postgres-operator - resources: {} - serviceAccountName: ext-postgres-operator - strategy: deployment - installModes: - - supported: true - type: OwnNamespace - - supported: true - type: SingleNamespace - - supported: false - type: MultiNamespace - - supported: true - type: AllNamespaces - maturity: alpha - provider: {} - replaces: ext-postgres-operator.v0.4.1 - version: 0.4.3 diff --git a/deploy/olm-catalog/ext-postgres-operator/ext-postgres-operator.package.yaml b/deploy/olm-catalog/ext-postgres-operator/ext-postgres-operator.package.yaml deleted file mode 100644 index e16a02bce..000000000 --- a/deploy/olm-catalog/ext-postgres-operator/ext-postgres-operator.package.yaml +++ /dev/null @@ -1,5 +0,0 @@ -channels: -- currentCSV: ext-postgres-operator.v0.4.1 - name: alpha -defaultChannel: alpha -packageName: ext-postgres-operator diff --git a/deploy/olm-catalog/postgres-operator/0.2.0/ext-postgres-operator.crd.yaml b/deploy/olm-catalog/postgres-operator/0.2.0/ext-postgres-operator.crd.yaml deleted file mode 100644 index ca5857b24..000000000 --- a/deploy/olm-catalog/postgres-operator/0.2.0/ext-postgres-operator.crd.yaml +++ /dev/null @@ -1,36 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: postgres.db.movetokube.com -spec: - group: db.movetokube.com - names: - kind: Postgres - listKind: PostgresList - plural: postgres - singular: postgres - scope: Namespaced - validation: - openAPIV3Schema: - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - type: object - status: - type: object - version: v1alpha1 - versions: - - name: v1alpha1 - served: true - storage: true diff --git a/deploy/olm-catalog/postgres-operator/0.2.0/ext-postgres-operator.package.yaml b/deploy/olm-catalog/postgres-operator/0.2.0/ext-postgres-operator.package.yaml deleted file mode 100644 index 475917d21..000000000 --- a/deploy/olm-catalog/postgres-operator/0.2.0/ext-postgres-operator.package.yaml +++ /dev/null @@ -1,4 +0,0 @@ -packageName: ext-postgres-operator -channels: - - name: stable - currentCSV: ext-postgres-operator.v0.2.0 \ No newline at end of file diff --git a/deploy/olm-catalog/postgres-operator/0.2.0/postgres-operator.v0.2.0.clusterserviceversion.yaml b/deploy/olm-catalog/postgres-operator/0.2.0/postgres-operator.v0.2.0.clusterserviceversion.yaml deleted file mode 100644 index e1875d999..000000000 --- a/deploy/olm-catalog/postgres-operator/0.2.0/postgres-operator.v0.2.0.clusterserviceversion.yaml +++ /dev/null @@ -1,182 +0,0 @@ -apiVersion: operators.coreos.com/v1alpha1 -kind: ClusterServiceVersion -metadata: - annotations: - alm-examples: '[{"apiVersion":"db.movetokube.com/v1alpha1","kind":"Postgres","metadata":{"name":"my-db","namespace":"app"},"spec":{"database":"test-db","secretName":"my-secret"}}]' - capabilities: Basic Install - containerImage: movetokube/postgres-operator - description: Manage databases and roles in external PostgreSQL server or cluster - repository: https://github.com/movetokube/postgres-operator - name: ext-postgres-operator.v0.2.0 - namespace: placeholder -spec: - apiservicedefinitions: {} - customresourcedefinitions: - owned: - - description: Represents a resource for managing PostgreSQL database associated - role - displayName: postgres.db.movetokube.com - kind: Postgres - name: postgres.db.movetokube.com - version: v1alpha1 - description: |- - ext-postgres-operator is an external PostgreSQL database operator. This is a very light-weight basic operator which - does not provide a PostgreSQL server, but rather manages databases inside an existing PostgreSQL database server (or cluster). - ### Features - This operator enables you to create roles and databases easily by defining simple Custom Resources. Operator's - features are as follows: - - * Creates a database - * Creates a role which has admin access to the database - * Creates a secret role, password and postgres uri - * on CR deletion, reassigns all objects owned by role to **postgres** role, removes role and secret - - ### Installation - This operator requires a Kubernetes Secret to be created in the **same namespace** as operator itself. Secret should - contain these keys: POSTGRES\_HOST, POSTGRES\_USER, POSTGRES\_PASS, POSTGRES\_URI\_ARGS. - - Example - ```yaml - apiVersion: v1 - kind: Secret - metadata: - name: ext-postgres-operator - namespace: operators - type: Opaque - data: - POSTGRES_HOST: postgres - POSTGRES_USER: postgres - POSTGRES_PASS: admin - POSTGRES_URI_ARGS: - ``` - - ### Custom Resources - Custom Resource is very simple for this operator, it only supports two keys in specification, which are defined below - * `spec.database` - name of PostgreSQL database that should be created (if it does not exist) - * `spec.secretName` - name of the secret that will contain `ROLE`, `PASSWORD` and `POSTGRES_URL`. The final secret - name will be format: **spec.secretName**-**CR.metadata.name** - displayName: Ext Postgres Operator - install: - spec: - clusterPermissions: - - rules: - - apiGroups: - - "" - resources: - - pods - - services - - endpoints - - persistentvolumeclaims - - events - - configmaps - - secrets - verbs: - - '*' - - apiGroups: - - apps - resources: - - deployments - - daemonsets - - replicasets - - statefulsets - verbs: - - '*' - - apiGroups: - - monitoring.coreos.com - resources: - - servicemonitors - verbs: - - get - - create - - apiGroups: - - apps - resourceNames: - - postgres-operator - resources: - - deployments/finalizers - verbs: - - update - - apiGroups: - - db.movetokube.com - resources: - - '*' - verbs: - - '*' - serviceAccountName: ext-postgres-operator - deployments: - - name: ext-postgres-operator - spec: - replicas: 1 - selector: - matchLabels: - name: ext-postgres-operator - strategy: {} - template: - metadata: - labels: - name: ext-postgres-operator - spec: - containers: - - command: - - postgres-operator - env: - - name: WATCH_NAMESPACE - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: OPERATOR_NAME - value: ext-postgres-operator - - name: POSTGRES_HOST - valueFrom: - secretKeyRef: - key: POSTGRES_HOST - name: ext-postgres-operator - - name: POSTGRES_USER - valueFrom: - secretKeyRef: - key: POSTGRES_USER - name: ext-postgres-operator - - name: POSTGRES_PASS - valueFrom: - secretKeyRef: - key: POSTGRES_PASS - name: ext-postgres-operator - - name: POSTGRES_URI_ARGS - valueFrom: - secretKeyRef: - key: POSTGRES_URI_ARGS - name: ext-postgres-operator - image: movetokube/postgres-operator:0.2 - imagePullPolicy: Always - name: ext-postgres-operator - resources: {} - serviceAccountName: ext-postgres-operator - strategy: deployment - installModes: - - supported: true - type: OwnNamespace - - supported: true - type: SingleNamespace - - supported: false - type: MultiNamespace - - supported: true - type: AllNamespaces - keywords: - - postgres-operator - - postgres - - postgresql - - kubernetes - - database - labels: - name: ext-postgres-operator - links: - - name: movetokube.com - url: https://movetokube.com - maintainers: - - email: tomas@movetokube.com - name: Tomas Adomavicius - maturity: alpha - provider: - name: movetokube.com - version: 0.2.0 diff --git a/deploy/olm-catalog/postgres-operator/0.3.0/db_v1alpha1_postgres_crd.yaml b/deploy/olm-catalog/postgres-operator/0.3.0/db_v1alpha1_postgres_crd.yaml deleted file mode 100644 index 207c530db..000000000 --- a/deploy/olm-catalog/postgres-operator/0.3.0/db_v1alpha1_postgres_crd.yaml +++ /dev/null @@ -1,49 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: postgres.db.movetokube.com -spec: - group: db.movetokube.com - names: - kind: Postgres - listKind: PostgresList - plural: postgres - singular: postgres - scope: Namespaced - validation: - openAPIV3Schema: - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - properties: - database: - type: string - required: - - database - type: object - status: - properties: - postgresRole: - type: string - succeeded: - type: boolean - required: - - succeeded - - postgresRole - type: object - version: v1alpha1 - versions: - - name: v1alpha1 - served: true - storage: true diff --git a/deploy/olm-catalog/postgres-operator/0.3.0/db_v1alpha1_postgresuser_crd.yaml b/deploy/olm-catalog/postgres-operator/0.3.0/db_v1alpha1_postgresuser_crd.yaml deleted file mode 100644 index 88840b582..000000000 --- a/deploy/olm-catalog/postgres-operator/0.3.0/db_v1alpha1_postgresuser_crd.yaml +++ /dev/null @@ -1,61 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: postgresusers.db.movetokube.com -spec: - group: db.movetokube.com - names: - kind: PostgresUser - listKind: PostgresUserList - plural: postgresusers - singular: postgresuser - scope: Namespaced - validation: - openAPIV3Schema: - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - properties: - database: - type: string - role: - type: string - secretName: - type: string - required: - - role - - database - - secretName - type: object - status: - properties: - databaseName: - type: string - postgresGroup: - type: string - postgresRole: - type: string - succeeded: - type: boolean - required: - - succeeded - - postgresRole - - postgresGroup - - databaseName - type: object - version: v1alpha1 - versions: - - name: v1alpha1 - served: true - storage: true diff --git a/deploy/olm-catalog/postgres-operator/0.3.0/postgres-operator.v0.3.0.clusterserviceversion.yaml b/deploy/olm-catalog/postgres-operator/0.3.0/postgres-operator.v0.3.0.clusterserviceversion.yaml deleted file mode 100644 index c86b7e962..000000000 --- a/deploy/olm-catalog/postgres-operator/0.3.0/postgres-operator.v0.3.0.clusterserviceversion.yaml +++ /dev/null @@ -1,190 +0,0 @@ -apiVersion: operators.coreos.com/v1alpha1 -kind: ClusterServiceVersion -metadata: - annotations: - alm-examples: '[{"apiVersion":"db.movetokube.com/v1alpha1","kind":"Postgres","metadata":{"name":"my-db","namespace":"app"},"spec":{"database":"test-db"}},{"apiVersion":"db.movetokube.com/v1alpha1","kind":"PostgresUser","metadata":{"name":"my-db-user","namespace":"app"},"spec":{"database":"my-db","role":"username","secretName":"my-credential-secret"}}]' - capabilities: Basic Install - containerImage: movetokube/postgres-operator:0.3 - description: Manage databases and roles in external PostgreSQL server or cluster - repository: https://github.com/movetokube/postgres-operator - categories: Database - name: ext-postgres-operator.v0.3.0 - namespace: placeholder -spec: - apiservicedefinitions: {} - customresourcedefinitions: - owned: - - kind: Postgres - description: Represents a resource for managing external PostgreSQL database and associated group role - displayName: postgres.db.movetokube.com - name: postgres.db.movetokube.com - version: v1alpha1 - - kind: PostgresUser - description: Represents a resource for managing external PostgreSQL user role - displayName: postgresusers.db.movetokube.com - name: postgresusers.db.movetokube.com - version: v1alpha1 - description: |- - ext-postgres-operator is an external PostgreSQL database operator. This is a very light-weight basic operator which - does not provide a PostgreSQL server, but rather manages databases inside an existing PostgreSQL database server (or cluster). - ### Features - This operator enables you to create roles and databases easily by defining simple Custom Resources. Operator's - features are as follows: - - * Creates a database - * Creates a group role which has full access to the database - * Can create multiple user roles which are granted the group role permissions - * Creates a secret for each user role, which includes role, password and postgres uri - * on PostgresUser CR deletion - deletes the user role and associated Kubernetes secret - * on Postgres CR deletion - checks if there are no other Postgres CRs using the group role, reassigns all objects to - **POSTGRES_USER** role - - ### Installation - This operator requires a Kubernetes Secret to be created in the **same namespace** as operator itself. Secret should - contain these keys: POSTGRES\_HOST, POSTGRES\_USER, POSTGRES\_PASS, POSTGRES\_URI\_ARGS. - - Example - ```yaml - apiVersion: v1 - kind: Secret - metadata: - name: ext-postgres-operator - namespace: operators - type: Opaque - data: - POSTGRES_HOST: postgres - POSTGRES_USER: postgres - POSTGRES_PASS: admin - POSTGRES_URI_ARGS: - ``` - - ### Custom Resources - Custom Resources are very simple for this operator. For `postgres.db.movetokube.com` CR, the spec is: - * `spec.database` - name of PostgreSQL database that should be created (if it does not exist). The group role name - will be **spec.database-group** - - For `postgresusers.db.movetokube.com` CR, this needs to reference `postgres.db.movetokube.com` CR in the same namespace, the spec is: - * `spec.role` - user role prefix, the final user role will be **spec.role-** - * `spec.database` - same as in `Postgres` CR in the same namespace - * `spec.secretName` - name of the secret that will contain `ROLE`, `PASSWORD` and `POSTGRES_URL`. The final secret - name will be format: **spec.secretName**-**CR.metadata.name** - - displayName: Ext Postgres Operator - install: - spec: - clusterPermissions: - - rules: - - apiGroups: - - "" - resources: - - pods - - services - - endpoints - - persistentvolumeclaims - - events - - configmaps - - secrets - verbs: - - '*' - - apiGroups: - - apps - resources: - - deployments - - daemonsets - - replicasets - - statefulsets - verbs: - - '*' - - apiGroups: - - apps - resourceNames: - - ext-postgres-operator - resources: - - deployments/finalizers - verbs: - - update - - apiGroups: - - db.movetokube.com - resources: - - '*' - verbs: - - '*' - serviceAccountName: ext-postgres-operator - deployments: - - name: ext-postgres-operator - spec: - replicas: 1 - selector: - matchLabels: - name: ext-postgres-operator - strategy: {} - template: - metadata: - labels: - name: ext-postgres-operator - spec: - containers: - - command: - - postgres-operator - env: - - name: WATCH_NAMESPACE - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: OPERATOR_NAME - value: ext-postgres-operator - - name: POSTGRES_HOST - valueFrom: - secretKeyRef: - key: POSTGRES_HOST - name: ext-postgres-operator - - name: POSTGRES_USER - valueFrom: - secretKeyRef: - key: POSTGRES_USER - name: ext-postgres-operator - - name: POSTGRES_PASS - valueFrom: - secretKeyRef: - key: POSTGRES_PASS - name: ext-postgres-operator - - name: POSTGRES_URI_ARGS - valueFrom: - secretKeyRef: - key: POSTGRES_URI_ARGS - name: ext-postgres-operator - image: movetokube/postgres-operator:0.2 - imagePullPolicy: Always - name: ext-postgres-operator - resources: {} - serviceAccountName: ext-postgres-operator - strategy: deployment - installModes: - - supported: true - type: OwnNamespace - - supported: true - type: SingleNamespace - - supported: false - type: MultiNamespace - - supported: true - type: AllNamespaces - keywords: - - postgres-operator - - postgres - - postgresql - - kubernetes - - database - labels: - name: ext-postgres-operator - links: - - name: movetokube.com - url: https://movetokube.com - maintainers: - - email: tomas@movetokube.com - name: Tomas Adomavicius - maturity: alpha - provider: - name: movetokube.com - replaces: postgres-operator.v0.2.0 - version: 0.3.0 diff --git a/go.mod b/go.mod index 30616ec4a..5678e407c 100644 --- a/go.mod +++ b/go.mod @@ -1,88 +1,101 @@ module github.com/movetokube/postgres-operator -go 1.18 +go 1.24 require ( - github.com/go-logr/logr v0.1.0 - github.com/go-openapi/spec v0.21.0 - github.com/golang/mock v1.6.0 + github.com/go-logr/logr v1.4.2 github.com/lib/pq v1.10.9 - github.com/onsi/ginkgo v1.12.0 - github.com/onsi/gomega v1.9.0 - github.com/operator-framework/operator-sdk v0.18.0 - github.com/spf13/pflag v1.0.6 - k8s.io/api v0.18.2 - k8s.io/apimachinery v0.18.2 - k8s.io/client-go v12.0.0+incompatible - k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c - sigs.k8s.io/controller-runtime v0.6.0 + github.com/onsi/ginkgo/v2 v2.19.0 + github.com/onsi/gomega v1.33.1 + go.uber.org/mock v0.5.1 + k8s.io/api v0.31.0 + k8s.io/apimachinery v0.31.0 + k8s.io/client-go v0.31.0 + sigs.k8s.io/controller-runtime v0.19.0 ) require ( - cloud.google.com/go v0.49.0 // indirect - github.com/Azure/go-autorest/autorest v0.9.3-0.20191028180845-3492b2aff503 // indirect - github.com/Azure/go-autorest/autorest/adal v0.8.1-0.20191028180845-3492b2aff503 // indirect - github.com/Azure/go-autorest/autorest/date v0.2.0 // indirect - github.com/Azure/go-autorest/logger v0.1.0 // indirect - github.com/Azure/go-autorest/tracing v0.5.0 // indirect + github.com/antlr4-go/antlr/v4 v4.13.0 // indirect + github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.1.1 // indirect - github.com/coreos/prometheus-operator v0.38.1-0.20200424145508-7e176fda06cc // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect - github.com/emicklei/go-restful v2.11.1+incompatible // indirect - github.com/evanphx/json-patch v4.5.0+incompatible // indirect - github.com/go-logr/zapr v0.1.1 // indirect - github.com/go-openapi/jsonpointer v0.21.1 // indirect - github.com/go-openapi/jsonreference v0.21.0 // indirect - github.com/go-openapi/swag v0.23.1 // indirect - github.com/gogo/protobuf v1.3.1 // indirect - github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9 // indirect - github.com/golang/protobuf v1.3.2 // indirect - github.com/google/go-cmp v0.4.0 // indirect - github.com/google/gofuzz v1.1.0 // indirect - github.com/google/uuid v1.1.1 // indirect - github.com/googleapis/gnostic v0.3.1 // indirect - github.com/gophercloud/gophercloud v0.6.0 // indirect - github.com/hashicorp/golang-lru v0.5.3 // indirect - github.com/hpcloud/tail v1.0.0 // indirect - github.com/imdario/mergo v0.3.8 // indirect + github.com/blang/semver/v4 v4.0.0 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/evanphx/json-patch/v5 v5.9.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-logr/zapr v1.3.0 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.4 // indirect + github.com/go-task/slim-sprig/v3 v3.0.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/cel-go v0.20.1 // indirect + github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect + github.com/imdario/mergo v0.3.6 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect - github.com/json-iterator/go v1.1.9 // indirect - github.com/mailru/easyjson v0.9.0 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.1 // 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.1 // 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/prometheus/client_golang v1.5.1 // indirect - github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.9.1 // indirect - github.com/prometheus/procfs v0.0.8 // indirect - go.uber.org/atomic v1.6.0 // indirect - go.uber.org/multierr v1.5.0 // indirect - go.uber.org/zap v1.14.1 // indirect - golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 // indirect - golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect - golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 // indirect - golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64 // indirect - golang.org/x/text v0.3.3 // indirect - golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect - gomodules.xyz/jsonpatch/v2 v2.0.1 // indirect - google.golang.org/appengine v1.6.5 // indirect - gopkg.in/fsnotify.v1 v1.4.7 // indirect + github.com/prometheus/client_golang v1.19.1 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/spf13/cobra v1.8.1 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/stoewer/go-strcase v1.2.0 // indirect + github.com/x448/float16 v0.8.4 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect + go.opentelemetry.io/otel v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 // indirect + go.opentelemetry.io/otel/metric v1.28.0 // indirect + go.opentelemetry.io/otel/sdk v1.28.0 // indirect + go.opentelemetry.io/otel/trace v1.28.0 // indirect + go.opentelemetry.io/proto/otlp v1.3.1 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.26.0 // indirect + golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // indirect + golang.org/x/net v0.26.0 // indirect + golang.org/x/oauth2 v0.21.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/term v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect + golang.org/x/time v0.3.0 // indirect + golang.org/x/tools v0.22.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect + google.golang.org/grpc v1.65.0 // indirect + google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect - gopkg.in/yaml.v2 v2.2.8 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/klog v1.0.0 // indirect - k8s.io/kube-state-metrics v1.7.2 // indirect - k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89 // indirect - sigs.k8s.io/structured-merge-diff/v3 v3.0.0 // indirect - sigs.k8s.io/yaml v1.2.0 // indirect -) - -replace ( - github.com/Azure/go-autorest => github.com/Azure/go-autorest v13.3.2+incompatible // Required by OLM - k8s.io/client-go => k8s.io/client-go v0.18.2 // Required by prometheus-operator + k8s.io/apiextensions-apiserver v0.31.0 // indirect + k8s.io/apiserver v0.31.0 // indirect + k8s.io/component-base v0.31.0 // indirect + k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect + k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect + sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/go.sum b/go.sum index c2ecdcc9a..cbad5fd98 100644 --- a/go.sum +++ b/go.sum @@ -1,1290 +1,255 @@ -bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= -bou.ke/monkey v1.0.1/go.mod h1:FgHuK96Rv2Nlf+0u1OOVDpCMdsWyOFmeeketDHE7LIg= -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.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw= -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.49.0 h1:CH+lkubJzcPYB1Ggupcq0+k8Ni2ILdG2lYjDIgavDBQ= -cloud.google.com/go v0.49.0/go.mod h1:hGvAdzcWNbyuxS3nWhD7H2cIJxjRRTRLQVB0bdputVY= -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/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.3.0/go.mod h1:9IAwXhoyBJ7z9LcAwkj0/7NnPzYaPeZxxVp3zm+5IqA= -contrib.go.opencensus.io/exporter/ocagent v0.6.0/go.mod h1:zmKjrJcdo0aYcVS7bmEeSEBLPA9YJp5bjrofdU3pIXs= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= -github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc= -github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v23.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v36.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-storage-blob-go v0.8.0/go.mod h1:lPI3aLPpuLTeUwh1sViKXFxwl2B6teiRqI0deQUvsw0= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-autorest v13.3.2+incompatible h1:VxzPyuhtnlBOzc4IWCZHqpyH2d+QMLQEuy3wREyY4oc= -github.com/Azure/go-autorest v13.3.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= -github.com/Azure/go-autorest/autorest v0.9.3-0.20191028180845-3492b2aff503 h1:uUhdsDMg2GbFLF5GfQPtLMWd5vdDZSfqvqQp3waafxQ= -github.com/Azure/go-autorest/autorest v0.9.3-0.20191028180845-3492b2aff503/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= -github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= -github.com/Azure/go-autorest/autorest/adal v0.8.1-0.20191028180845-3492b2aff503 h1:Hxqlh1uAA8aGpa1dFhDNhll7U/rkWtG8ZItFvRMr7l0= -github.com/Azure/go-autorest/autorest/adal v0.8.1-0.20191028180845-3492b2aff503/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc= -github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= -github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSWlm5Ew6bxipnr/tbM= -github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= -github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.3.0 h1:qJumjCaCudz+OcqE9/XtEPfvtOjOmKaui4EOpFI6zZc= -github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= -github.com/Azure/go-autorest/autorest/to v0.3.1-0.20191028180845-3492b2aff503/go.mod h1:MgwOyqaIuKdG4TL/2ywSsIWKAfJfgHDo8ObuUk3t5sA= -github.com/Azure/go-autorest/autorest/validation v0.2.1-0.20191028180845-3492b2aff503/go.mod h1:3EEqHnBxQGHXRYq3HT1WyXAvT7LLY3tl70hw6tQIbjI= -github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY= -github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= -github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k= -github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= -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/DATA-DOG/go-sqlmock v1.4.1/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= -github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= -github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/Masterminds/sprig/v3 v3.1.0/go.mod h1:ONGMf7UfYGAbMXCZmQLy8x3lCDIPrEZE/rU8pmrbihA= -github.com/Masterminds/squirrel v1.2.0/go.mod h1:yaPeOnPG5ZRwL9oKdTsO/prlkPbXWZlRVMQ/gGlzIuA= -github.com/Masterminds/vcs v1.13.1/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA= -github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= -github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= -github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= -github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/OneOfOne/xxhash v1.2.6/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= -github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= -github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= -github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= -github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/aliyun/aliyun-oss-go-sdk v2.0.4+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= -github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= -github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q= -github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-metrics v0.3.0/go.mod h1:zXjbSimjXTd7vOpY8B0/2LpvNvDoXBuplAD+gJD3GYs= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= +github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= -github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= -github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.25.48/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= -github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= -github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= -github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= -github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= -github.com/brancz/gojsontoyaml v0.0.0-20191212081931-bf2969bbd742/go.mod h1:IyUJYN1gvWjtLF5ZuygmxbnsAyP3aJS6cHzIuZY50B0= -github.com/brancz/kube-rbac-proxy v0.5.0/go.mod h1:cL2VjiIFGS90Cjh5ZZ8+It6tMcBt8rwvuw2J6Mamnl0= -github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= -github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= -github.com/bugsnag/bugsnag-go v1.5.3/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= -github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= -github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= -github.com/bugsnag/panicwrap v1.2.0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= -github.com/campoy/embedmd v1.0.0/go.mod h1:oxyr9RCiSXg0M3VJ3ks0UGfp98BpSSGr0kpiX3MzVl8= -github.com/cenkalti/backoff v0.0.0-20181003080854-62661b46c409/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v0.0.0-20181017004759-096ff4a8a059/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.0/go.mod h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tjxl5dIMyVM= -github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= -github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= -github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk= -github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= -github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= -github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= -github.com/containerd/containerd v1.2.7/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20200107194136-26c1120b8d41/go.mod h1:Dq467ZllaHgAtVp4p1xUQWBrFXR9s/wyoTpG8zOJGkY= -github.com/containerd/continuity v0.0.0-20200228182428-0f16d7a0959c/go.mod h1:Dq467ZllaHgAtVp4p1xUQWBrFXR9s/wyoTpG8zOJGkY= -github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= -github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= -github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= -github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= -github.com/coreos/bbolt v1.3.1-coreos.6/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/etcd v3.3.15+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/etcd v3.3.17+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= -github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/coreos/prometheus-operator v0.38.1-0.20200424145508-7e176fda06cc h1:nMbUjGuF7UzVluucix/vsy4973BNdEiT/aX6kFtskKM= -github.com/coreos/prometheus-operator v0.38.1-0.20200424145508-7e176fda06cc/go.mod h1:erio69w1R/aC14D5nfvAXSlE8FT8jt2Hnavc50Dp33A= -github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= -github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= -github.com/cznic/b v0.0.0-20180115125044-35e9bbe41f07/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8= -github.com/cznic/fileutil v0.0.0-20180108211300-6a051e75936f/go.mod h1:8S58EK26zhXSxzv7NQFpnliaOQsmDUxvoQO3rt154Vg= -github.com/cznic/golex v0.0.0-20170803123110-4ab7c5e190e4/go.mod h1:+bmmJDNmKlhWNG+gwWCkaBoTy39Fs+bzRxVBzoTQbIc= -github.com/cznic/internal v0.0.0-20180608152220-f44710a21d00/go.mod h1:olo7eAdKwJdXxb55TKGLiJ6xt1H0/tiiRCWKVLmtjY4= -github.com/cznic/lldb v1.1.0/go.mod h1:FIZVUmYUVhPwRiPzL8nD/mpFcJ/G7SSXjjXYG4uRI3A= -github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= -github.com/cznic/ql v1.2.0/go.mod h1:FbpzhyZrqr0PVlK6ury+PoW3T0ODUV22OeWIxcaOrSE= -github.com/cznic/sortutil v0.0.0-20150617083342-4c7342852e65/go.mod h1:q2w6Bg5jeox1B+QkJ6Wp/+Vn0G/bo3f1uY7Fn3vivIQ= -github.com/cznic/strutil v0.0.0-20171016134553-529a34b1c186/go.mod h1:AHHPPPXTw0h6pVabbcbyGRK1DckRn7r/STdZEeIDzZc= -github.com/cznic/zappy v0.0.0-20160723133515-2533cb5b45cc/go.mod h1:Y1SNZ4dRUOKXshKUbwUapqNncRrho4mkjQebgEHZLj8= -github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +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/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE= -github.com/deislabs/oras v0.8.1/go.mod h1:Mx0rMSbBNaNfY9hjpccEnxkOqJL6KGjtxNHPLC4G4As= -github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM= -github.com/denisenkom/go-mssqldb v0.0.0-20191001013358-cfbb681360f0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= -github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= -github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/dgryski/go-sip13 v0.0.0-20190329191031-25c5027a8c7b/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/dhui/dktest v0.3.0/go.mod h1:cyzIUfGsBEbZ6BT7tnXqAShHSXCZhSNmFl70sZ7c1yc= -github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= -github.com/docker/cli v0.0.0-20200130152716-5d0cf8839492/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/distribution v0.0.0-20191216044856-a8371794149d/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= -github.com/docker/distribution v2.7.0+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v0.7.3-0.20190103212154-2b7e084dc98b/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v0.7.3-0.20190817195342-4760db040282/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v1.4.2-0.20200203170920-46ec8731fbce/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= -github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= -github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= -github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= -github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= -github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= -github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= -github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= -github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= -github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= -github.com/elastic/go-sysinfo v1.0.1/go.mod h1:O/D5m1VpYLwGjCYzEt63g3Z1uO3jXfwyzzjiW90t8cY= -github.com/elastic/go-sysinfo v1.1.1/go.mod h1:i1ZYdU10oLNfRzq4vq62BEwD2fH8KaWh6eh0ikPT9F0= -github.com/elastic/go-windows v1.0.0/go.mod h1:TsU0Nrp7/y3+VwE82FoZF8gC/XFg/Elz6CcloAxnPgU= -github.com/elastic/go-windows v1.0.1/go.mod h1:FoVvqWSun28vaDQPbj2Elfc0JahhPB7WQEGa3c814Ss= -github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -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 v2.11.1+incompatible h1:CjKsv3uWcCMvySPQYKxO8XX3f9zD4FeZRsW4G0B4ffE= -github.com/emicklei/go-restful v2.11.1+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -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/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v4.1.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M= -github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= -github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb/go.mod h1:bH6Xx7IW64qjjJq8M2u4dxNaBiDfKK+z/3eGDpXEQhc= -github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/structtag v1.1.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= -github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= -github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsouza/fake-gcs-server v1.7.0/go.mod h1:5XIRs4YvwNbNoz+1JF8j6KLAyDh7RHGAyAK3EP2EsNk= -github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= -github.com/garyburd/redigo v1.6.0/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= -github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= -github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= -github.com/go-bindata/go-bindata/v3 v3.1.3/go.mod h1:1/zrpXsLD8YDIbhZRqXzm1Ghc7NhEvIN9+Z6R5/xH4I= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= -github.com/go-logr/zapr v0.1.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk= -github.com/go-logr/zapr v0.1.1 h1:qXBXPDdNncunGs7XeEpsJt8wCjYBygluzfdLO0G5baE= -github.com/go-logr/zapr v0.1.1/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk= -github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= -github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= -github.com/go-openapi/analysis v0.17.2/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= -github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= -github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= -github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU= -github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= -github.com/go-openapi/errors v0.17.2/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= -github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= -github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= -github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= -github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= -github.com/go-openapi/jsonpointer v0.17.2/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= -github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= -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.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic= -github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk= -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.17.2/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.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= -github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= -github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.17.2/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= -github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= -github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk= -github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= -github.com/go-openapi/runtime v0.18.0/go.mod h1:uI6pHuxWYTy94zZxgcwJkUWa9wbIlhteGfloI10GD4U= -github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= -github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= -github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= -github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= -github.com/go-openapi/spec v0.17.2/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= -github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= -github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= -github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= -github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY= -github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= -github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= -github.com/go-openapi/strfmt v0.17.2/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= -github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= -github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= -github.com/go-openapi/strfmt v0.19.2/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= -github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= -github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= -github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= -github.com/go-openapi/swag v0.17.2/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= -github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= -github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU= -github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0= -github.com/go-openapi/validate v0.17.2/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= -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.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= -github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gobuffalo/envy v1.6.5/go.mod h1:N+GkhhZ/93bGZc6ZKhJLP6+m+tCNPKwgSpH9kaifseQ= -github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= -github.com/gobuffalo/envy v1.7.1/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w= -github.com/gobuffalo/flect v0.1.5/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80= -github.com/gobuffalo/flect v0.2.0/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80= -github.com/gobuffalo/flect v0.2.1/go.mod h1:vmkQwuZYhN5Pc4ljYQZzP+1sq+NEkK+lh20jmEmX3jc= -github.com/gobuffalo/logger v1.0.1/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs= -github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q= -github.com/gobuffalo/packr/v2 v2.7.1/go.mod h1:qYEvAazPaVxy7Y7KR0W8qYEE+RymX74kETFqjFoFlOc= -github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= -github.com/gocql/gocql v0.0.0-20190301043612-f6df8288f9b4/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0= -github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= -github.com/gofrs/flock v0.7.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= -github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.2.2-0.20190730201129-28a6bbf47e48/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/golang-migrate/migrate/v4 v4.6.2/go.mod h1:JYi6reN3+Z734VZ0akNuyOJNcrg45ZL7LDBMW3WGJL0= -github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9 h1:uHTyIjqVhYRhLbJ8nIiOJHkEZZ+5YoOsAbD3sk82NiE= -github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/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.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -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/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAONeMXrxql8uvOKuAZSu8aM5RUGv+1C6IJaEho= -github.com/golangplus/fmt v0.0.0-20150411045040-2a5d6d7d2995/go.mod h1:lJgMEyOkYFkPcDKwRXegd+iM6E7matEszMG5HhwytU8= -github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk= -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/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 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= -github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k= +github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= +github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= +github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= +github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= +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.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= +github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/cel-go v0.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84= +github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= -github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/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-20190723021845-34ac40c74b70/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go v2.0.2+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= -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/gnostic v0.0.0-20170426233943-68f4ded48ba9/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk= -github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= -github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= -github.com/gophercloud/gophercloud v0.2.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= -github.com/gophercloud/gophercloud v0.3.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= -github.com/gophercloud/gophercloud v0.6.0 h1:Xb2lcqZtml1XjgYZxbeayEemq7ASbeTp09m36gQFpEU= -github.com/gophercloud/gophercloud v0.6.0/go.mod h1:GICNByuaEBibcjmjvI7QvYJSZEbGkcYwAR7EZK2WMqM= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gopherjs/gopherjs v0.0.0-20191106031601-ce3c9ade29de/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= -github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= -github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-middleware v0.0.0-20190222133341-cfaf5686ec79/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.3.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.9.4/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.12.1/go.mod h1:8XEsbTttt/W+VvjtQhLACqCisSPWTxCZ7sBRjU6iH9c= -github.com/grpc-ecosystem/grpc-health-probe v0.2.1-0.20181220223928-2bf0a5b182db/go.mod h1:uBKkC2RbarFsvS5jMJHpVhTLvGlGQj9JJwkaePE3FWI= -github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-immutable-radix v1.1.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk= -github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/memberlist v0.1.4/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/memberlist v0.1.5/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hashicorp/serf v0.8.5/go.mod h1:UpNcs7fFbpKIyZaUuSW6EPiH+eZC7OuyFD+wc1oal+k= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/iancoleman/strcase v0.0.0-20190422225806-e506e3ef7365/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE= -github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM= +github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= +github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= -github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/influxdata/influxdb v1.7.7/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= -github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ= -github.com/jackc/pgx v3.2.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= -github.com/jessevdk/go-flags v0.0.0-20180331124232-1c38ed7ad0cc/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= -github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8= -github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak= -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/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/jsonnet-bundler/jsonnet-bundler v0.3.1/go.mod h1:/by7P/OoohkI3q4CgSFqcoFsVY+IaNbzOVDknEsKDeU= -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/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +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/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 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= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kshvakov/clickhouse v1.3.5/go.mod h1:DMzX7FxRymoNkVgizH0DWAL8Cur7wHLgx3MUnGwJqpE= -github.com/kylelemons/godebug v0.0.0-20160406211939-eadb3ce320cb/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= -github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= -github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= -github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= -github.com/leanovate/gopter v0.2.4/go.mod h1:gNcbPWNEWRe4lm+bycKqxUYoH5uoVje5SkOJ3uoLer8= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= -github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= -github.com/lightstep/lightstep-tracer-go v0.18.0/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= -github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= -github.com/lovoo/gcloud-opentracing v0.3.0/go.mod h1:ZFqk2y38kMDDikZPAK7ynTTGuyt17nSPdS3K5e+ZTBY= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= -github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= -github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= -github.com/markbates/inflect v1.0.4/go.mod h1:1fR9+pO2KHEO9ZRtto13gDwwZaAKstQzferVeWqbgNs= -github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= -github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= -github.com/mattn/go-ieproxy v0.0.0-20191113090002-7c0f6868bffe/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.6/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= -github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/go-sqlite3 v1.12.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.15/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.22/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= -github.com/mikefarah/yaml/v2 v2.4.0/go.mod h1:ahVqZF4n1W4NqwvVnZzC4es67xsW9uR/RRf2RRxieJU= -github.com/mikefarah/yq/v2 v2.4.1/go.mod h1:i8SYf1XdgUvY2OFwSqGAtWOOgimD2McJ6iutoxRm4k0= -github.com/minio/minio-go/v6 v6.0.49/go.mod h1:qD0lajrGW49lKZLtXKtCB4X/qkMf0a5tBvN2PaZg7Gg= -github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/hashstructure v0.0.0-20170609045927-2bca23e0e452/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= -github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +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 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/mozillazg/go-cos v0.13.0/go.mod h1:Zp6DvvXn0RUOXGJ2chmWt2bLEqRAnJnS3DnAZsJsoaE= -github.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISeBAdw6E61aqQma60= -github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/nakagami/firebirdsql v0.0.0-20190310045651-3c02a58cfed8/go.mod h1:86wM1zFnC6/uDBfZGNwB65O+pR2OFi5q/YQaEUid1qA= -github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= -github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/oklog/ulid v0.0.0-20170117200651-66bb6560562f/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/olekukonko/tablewriter v0.0.2/go.mod h1:rSAaSIOAGT9odnlyGlUfAJaoc5w2fSBUmeGDbRWPxyQ= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU= -github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= -github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg= -github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= -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= -github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v0.1.2-0.20190618234442-a950415649c7/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= -github.com/openshift/origin v0.0.0-20160503220234-8f127d736703/go.mod h1:0Rox5r9C8aQn6j1oAOQ0c1uC86mYbUFObzjBRvUKHII= -github.com/openshift/prom-label-proxy v0.1.1-0.20191016113035-b8153a7f39f1/go.mod h1:p5MuxzsYP1JPsNGwtjtcgRHHlGziCJJfztff91nNixw= -github.com/opentracing-contrib/go-stdlib v0.0.0-20190519235532-cf7a6c988dc9/go.mod h1:PLldrQSroqzH70Xl+1DQcGnefIbqsKR7UDaiux3zV+w= -github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= -github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= -github.com/operator-framework/api v0.1.1/go.mod h1:yzNYR7qyJqRGOOp+bT6Z/iYSbSPNxeh3Si93Gx/3OBY= -github.com/operator-framework/api v0.3.4/go.mod h1:TmRmw+8XOUaDPq6SP9gA8cIexNf/Pq8LMFY7YaKQFTs= -github.com/operator-framework/api v0.3.7-0.20200528122852-759ca0d84007/go.mod h1:Xbje9x0SHmh0nihE21kpesB38vk3cyxnE6JdDS8Jo1Q= -github.com/operator-framework/operator-registry v1.5.3/go.mod h1:agrQlkWOo1q8U1SAaLSS2WQ+Z9vswNT2M2HFib9iuLY= -github.com/operator-framework/operator-registry v1.12.1/go.mod h1:rf4b/h77GUv1+geiej2KzGRQr8iBLF4dXNwr5AuGkrQ= -github.com/operator-framework/operator-registry v1.12.4/go.mod h1:JChIivJVLE1wRbgIhDFzYQYT9yosa2wd6qiTyMuG5mg= -github.com/operator-framework/operator-sdk v0.18.0 h1:YdtgXvjHu+f0hE/Nzvw9JIU3XvOZyp2Kd2cOLW486rU= -github.com/operator-framework/operator-sdk v0.18.0/go.mod h1:xP/DNvnYnIoGK1bLKiD0s7aYZp2fa4AI6t1v3INaoZg= -github.com/otiai10/copy v1.0.1/go.mod h1:8bMCJrAqOtN/d9oyh5HR7HhLQMvcGMpGdwRDYsfOCHc= -github.com/otiai10/copy v1.0.2/go.mod h1:c7RpqBkwMom4bYTSkLSym4VSJz/XtncWRAj/J4PEIMY= -github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= -github.com/otiai10/curr v0.0.0-20190513014714-f5a3d24e5776/go.mod h1:3HNVkVOU7vZeFXocWuvtcS0XSFLcf2XUSDHkq9t1jU4= -github.com/otiai10/mint v1.2.3/go.mod h1:YnfyPNhBvnY8bW4SGQHCs/aAFhkgySlMZbrF5U0bOVw= -github.com/otiai10/mint v1.2.4/go.mod h1:d+b7n/0R3tdyUYYylALXpWQ/kTN+QobSq/4SRGBkR3M= -github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= -github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA= +github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= +github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= +github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= 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 v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= -github.com/prometheus/alertmanager v0.18.0/go.mod h1:WcxHBl40VSPuOaqWae6l6HpnEOVRIycEJ7i9iYkadEE= -github.com/prometheus/alertmanager v0.20.0/go.mod h1:9g2i48FAyZW6BtbsnvHtMHQXl2aVtrORKwKVCQ+nbrg= -github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= -github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= -github.com/prometheus/client_golang v1.2.0/go.mod h1:XMU6Z2MjaRKVu/dC1qupJI9SiNkDYzz3xecMgSW/F+U= -github.com/prometheus/client_golang v1.2.1/go.mod h1:XMU6Z2MjaRKVu/dC1qupJI9SiNkDYzz3xecMgSW/F+U= -github.com/prometheus/client_golang v1.5.1 h1:bdHYieyGlH+6OLEk2YQha8THib30KP0/yD0YH9m6xcA= -github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= -github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= -github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U= -github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= -github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190425082905-87a4384529e0/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.0.6/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8= -github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/prometheus/prometheus v0.0.0-20180315085919-58e2a31db8de/go.mod h1:oAIUtOny2rjMX0OWN5vPR5/q/twIROJvdqnQKDdil/s= -github.com/prometheus/prometheus v1.8.2-0.20200110114423-1e64d757f711/go.mod h1:7U90zPoLkWjEIQcy/rweQla82OCTUzxVHE51G3OhJbI= -github.com/prometheus/prometheus v2.3.2+incompatible/go.mod h1:oAIUtOny2rjMX0OWN5vPR5/q/twIROJvdqnQKDdil/s= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= -github.com/robfig/cron v0.0.0-20170526150127-736158dc09e1/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.4.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.5.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -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/rubenv/sql-migrate v0.0.0-20200212082348-64f95ea68aa3/go.mod h1:rtQlpHw+eR6UrqaS3kX1VYeaCxzCVdimDS7g5Ln4pPc= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/samuel/go-zookeeper v0.0.0-20190810000440-0ceca61e4d75/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= -github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= -github.com/santhosh-tekuri/jsonschema v1.2.4/go.mod h1:TEAUOeZSmIxTTuHatJzrvARHiuO9LYd+cIxzgEHCQI4= -github.com/satori/go.uuid v0.0.0-20160603004225-b111a074d5ef/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= -github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= -github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/shurcooL/vfsgen v0.0.0-20180825020608-02ddb050ef6b/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= -github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= -github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= -github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/soheilhy/cmux v0.1.3/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= -github.com/spf13/cobra v0.0.7/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= +github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= +github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= +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/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= -github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/thanos-io/thanos v0.11.0/go.mod h1:N/Yes7J68KqvmY+xM6J5CJqEvWIvKSR5sqGtmuD6wDc= -github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= -github.com/uber/jaeger-client-go v2.20.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= -github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= -github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs= -github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= -github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= -github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= -github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8= -github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +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/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= -github.com/yvasiyarov/go-metrics v0.0.0-20150112132944-c25f46c4b940/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= -github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= -github.com/yvasiyarov/gorelic v0.0.7/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= -github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= -github.com/yvasiyarov/newrelic_platform_go v0.0.0-20160601141957-9c099fbc30e9/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= -github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= -gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b/go.mod h1:T3BPAOm2cqquPa0MKWeNkmOM5RQsRhkrwMWonFMN7fE= -go.elastic.co/apm v1.5.0/go.mod h1:OdB9sPtM6Vt7oz3VXt7+KR96i9li74qrxBGHTQygFvk= -go.elastic.co/apm/module/apmhttp v1.5.0/go.mod h1:1FbmNuyD3ddauwzgVwFB0fqY6KbZt3JkV187tGCYYhY= -go.elastic.co/apm/module/apmot v1.5.0/go.mod h1:d2KYwhJParTpyw2WnTNy8geNlHKKFX+4oK3YLlsesWE= -go.elastic.co/fastjson v1.0.0/go.mod h1:PmeUOMMtLHQr9ZS9J9owrAVg0FkaZDRZJEFTTGHtchs= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= -go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= -go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.mongodb.org/mongo-driver v1.1.0/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -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.uber.org/atomic v0.0.0-20181018215023-8dc6146f7569/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= -go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/automaxprocs v1.2.0/go.mod h1:YfO3fm683kQpzETxlTGZhGIVmXAhaw3gxeBADbpZtnU= -go.uber.org/multierr v0.0.0-20180122172545-ddea229ff1df/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= -go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= -go.uber.org/zap v0.0.0-20180814183419-67bc79d13d15/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.14.1 h1:nYDKopTbvAPq/NrUVZwT15y2lpROBiLLyoRTbXOYWOo= -go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= -golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= +go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= +go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= +go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= +go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= +go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= +go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= +go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= +go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/mock v0.5.1 h1:ASgazW/qBmR+A32MYFDB6E2POoTgOwT509VP0CT/fjs= +go.uber.org/mock v0.5.1/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 h1:bXoxMPcSLOq08zI3/c5dEBT6lE4eh+jOh886GHrn6V8= -golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/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-20190125153040-c74c464bbbf2/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-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -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/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 h1:J5lckAjkw6qYlOZNj90mLYNTEKDvWeuc1yieZ8qUzUE= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -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/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc h1:mCRnTeVUjcrhlRmO0VK8a6k6Rrf6TF9htwo2pJVSjIU= +golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181108082009-03003ca0c849/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/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-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190206173232-65e2d4e15006/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-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190424112056-4829fb13d2c6/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-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191112182307-2180aed22343/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-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -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/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= +golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= 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-20210220032951-036812b2e83c/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= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190102155601-82a175fd1598/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190310054646-10058d7d4faa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190425145619-16072639606e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190426135247-a129542de9ae/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-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191025021431-6c3a3bfe00ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/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-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64 h1:D1v9ucDTYBtbz5vNuBbAhIMAGhQhJ6Ym5ah3maMVNX4= -golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= +golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180805044716-cb6730876b98/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.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -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= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/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-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +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-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/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-20190118193359-16909d206f00/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/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-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190425222832-ad9eeb80039a/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-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190617190820-da514acc4774/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-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= -golang.org/x/tools v0.0.0-20190813034749-528a2984e271/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190918214516-5a1a30219888/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3/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-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191030203535-5e247c9ad0a0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191111182352-50fa39b762bc/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-20200403190813-44a64ad78b9b/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.1.1 h1:wGiQel/hW0NnEkJUk8lbzkX2gFJU6PFxf1v5OlCfuOs= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= +golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gomodules.xyz/jsonpatch/v2 v2.0.1 h1:xyiBuvkD2g5n7cYzx6u2sxQvsAy4QJsZFCzGVdzOXZ0= -gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU= -gomodules.xyz/jsonpatch/v3 v3.0.1/go.mod h1:CBhndykehEwTOlEfnsfJwvkFQbSN8YZFr9M+cIHAJto= -gomodules.xyz/orderedmap v0.1.0/go.mod h1:g9/TPUCm1t2gwD3j3zfV8uylyYhVdCNSi+xCEIu7yTU= -gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= -google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= -google.golang.org/api v0.3.2/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= -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/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -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 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= -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-20190404172233-64821d5d2107/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-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= -google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -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-20190927181202-20e1ac93f88c/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/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= -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.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= -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= -gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= +gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 h1:7whR9kGa5LUwFtpLm2ArCEejtnxlGeLbAyjFY8sGNFw= +google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -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/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 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/fsnotify/fsnotify.v1 v1.4.7/go.mod h1:Fyux9zXlo4rWoMSIzpn9fDAYjalPqJ/K1qJ27s+7ltE= -gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= -gopkg.in/gorp.v1 v1.7.2/go.mod h1:Wo3h+DBQZIxATwftsglhdD/62zRFPhGhTiu5jUJmCaw= -gopkg.in/imdario/mergo.v0 v0.3.7/go.mod h1:9qPP6AGrlC1G2PTNXko614FwGZvorN7MiBU0Eppok+U= -gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= +gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473/go.mod h1:N1eN2tsCx0Ydtgjl4cqmbRCsY4/+z4cYDeqwZTk6zog= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.1.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20190905181640-827449938966/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= -gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= -helm.sh/helm/v3 v3.2.0/go.mod h1:ZaXz/vzktgwjyGGFbUWtIQkscfE7WYoRGP2szqAFHR0= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -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 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0= -k8s.io/api v0.0.0-20190620084959-7cf5895f2711/go.mod h1:TBhBqb1AWbBQbW3XRusr7n7E4v2+5ZY8r8sAMnyFC5A= -k8s.io/api v0.0.0-20190813020757-36bff7324fb7/go.mod h1:3Iy+myeAORNCLgjd/Xu9ebwN7Vh59Bw0vh9jhoX+V58= -k8s.io/api v0.0.0-20190918155943-95b840bb6a1f/go.mod h1:uWuOHnjmNrtQomJrvEBg0c0HRNyQ+8KTEERVsK0PW48= -k8s.io/api v0.0.0-20191115095533-47f6de673b26/go.mod h1:iA/8arsvelvo4IDqIhX4IbjTEKBGgvsf2OraTuRtLFU= -k8s.io/api v0.0.0-20191122220107-b5267f2975e0/go.mod h1:vYpRfxYkMrmPPSesoHEkGNHxNKTk96REAwqm/inQbs0= -k8s.io/api v0.16.7/go.mod h1:oUAiGRgo4t+5yqcxjOu5LoHT3wJ8JSbgczkaFYS5L7I= -k8s.io/api v0.18.0/go.mod h1:q2HRQkfDzHMBZL9l/y9rH63PkQl4vae0xRT+8prbrK8= -k8s.io/api v0.18.2 h1:wG5g5ZmSVgm5B+eHMIbI9EGATS2L8Z72rda19RIEgY8= -k8s.io/api v0.18.2/go.mod h1:SJCWI7OLzhZSvbY7U8zwNl9UA4o1fizoug34OV/2r78= -k8s.io/apiextensions-apiserver v0.0.0-20190918161926-8f644eb6e783/go.mod h1:xvae1SZB3E17UpV59AWc271W/Ph25N+bjPyR63X6tPY= -k8s.io/apiextensions-apiserver v0.16.7/go.mod h1:6xYRp4trGp6eT5WZ6tPi/TB2nfWQCzwUvBlpg8iswe0= -k8s.io/apiextensions-apiserver v0.18.0/go.mod h1:18Cwn1Xws4xnWQNC00FLq1E350b9lUF+aOdIWDOZxgo= -k8s.io/apiextensions-apiserver v0.18.2 h1:I4v3/jAuQC+89L3Z7dDgAiN4EOjN6sbm6iBqQwHTah8= -k8s.io/apiextensions-apiserver v0.18.2/go.mod h1:q3faSnRGmYimiocj6cHQ1I3WpLqmDgJFlKL37fC4ZvY= -k8s.io/apimachinery v0.0.0-20190612205821-1799e75a0719/go.mod h1:I4A+glKBHiTgiEjQiCCQfCAIcIMFGt291SmsvcrFzJA= -k8s.io/apimachinery v0.0.0-20190809020650-423f5d784010/go.mod h1:Waf/xTS2FGRrgXCkO5FP3XxTOWh0qLf2QhL1qFZZ/R8= -k8s.io/apimachinery v0.0.0-20190913080033-27d36303b655/go.mod h1:nL6pwRT8NgfF8TT68DBI8uEePRt89cSvoXUVqbkWHq4= -k8s.io/apimachinery v0.0.0-20191115015347-3c7067801da2/go.mod h1:dXFS2zaQR8fyzuvRdJDHw2Aerij/yVGJSre0bZQSVJA= -k8s.io/apimachinery v0.0.0-20191121175448-79c2a76c473a/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg= -k8s.io/apimachinery v0.16.7/go.mod h1:Xk2vD2TRRpuWYLQNM6lT9R7DSFZUYG03SarNkbGrnKE= -k8s.io/apimachinery v0.18.0/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA= -k8s.io/apimachinery v0.18.2 h1:44CmtbmkzVDAhCpRVSiP2R5PPrC2RtlIv/MoB8xpdRA= -k8s.io/apimachinery v0.18.2/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA= -k8s.io/apiserver v0.0.0-20190918160949-bfa5e2e684ad/go.mod h1:XPCXEwhjaFN29a8NldXA901ElnKeKLrLtREO9ZhFyhg= -k8s.io/apiserver v0.0.0-20191122221311-9d521947b1e1/go.mod h1:RbsZY5zzBIWnz4KbctZsTVjwIuOpTp4Z8oCgFHN4kZQ= -k8s.io/apiserver v0.16.7/go.mod h1:/5zSatF30/L9zYfMTl55jzzOnx7r/gGv5a5wtRp8yAw= -k8s.io/apiserver v0.18.0/go.mod h1:3S2O6FeBBd6XTo0njUrLxiqk8GNy6wWOftjhJcXYnjw= -k8s.io/apiserver v0.18.2/go.mod h1:Xbh066NqrZO8cbsoenCwyDJ1OSi8Ag8I2lezeHxzwzw= -k8s.io/autoscaler v0.0.0-20190607113959-1b4f1855cb8e/go.mod h1:QEXezc9uKPT91dwqhSJq3GNI3B1HxFRQHiku9kmrsSA= -k8s.io/cli-runtime v0.18.0/go.mod h1:1eXfmBsIJosjn9LjEBUd2WVPoPAY9XGTqTFcPMIBsUQ= -k8s.io/cli-runtime v0.18.2/go.mod h1:yfFR2sQQzDsV0VEKGZtrJwEy4hLZ2oj4ZIfodgxAHWQ= -k8s.io/client-go v0.18.2 h1:aLB0iaD4nmwh7arT2wIn+lMnAq7OswjaejkQ8p9bBYE= -k8s.io/client-go v0.18.2/go.mod h1:Xcm5wVGXX9HAA2JJ2sSBUn3tCJ+4SVlCbl2MNNv+CIU= -k8s.io/code-generator v0.0.0-20190912054826-cd179ad6a269/go.mod h1:V5BD6M4CyaN5m+VthcclXWsVcT1Hu+glwa1bi3MIsyE= -k8s.io/code-generator v0.16.7/go.mod h1:wFdrXdVi/UC+xIfLi+4l9elsTT/uEF61IfcN2wOLULQ= -k8s.io/code-generator v0.18.0/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc= -k8s.io/code-generator v0.18.2/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc= -k8s.io/component-base v0.0.0-20190918160511-547f6c5d7090/go.mod h1:933PBGtQFJky3TEwYx4aEPZ4IxqhWh3R6DCmzqIn1hA= -k8s.io/component-base v0.0.0-20191122220729-2684fb322cb9/go.mod h1:NFuUusy/X4Tk21m21tcNUihnmp4OI7lXU7/xA+rYXkc= -k8s.io/component-base v0.16.7/go.mod h1:ikdyfezOFMu5O0qJjy/Y9eXwj+fV3pVwdmt0ulVcIR0= -k8s.io/component-base v0.18.0/go.mod h1:u3BCg0z1uskkzrnAKFzulmYaEpZF7XC9Pf/uFyb1v2c= -k8s.io/component-base v0.18.2/go.mod h1:kqLlMuhJNHQ9lz8Z7V5bxUUtjFZnrypArGl58gmDfUM= -k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v0.3.1/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v0.3.3/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v0.4.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= -k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= -k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= -k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= -k8s.io/kube-openapi v0.0.0-20190320154901-5e45bb682580/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= -k8s.io/kube-openapi v0.0.0-20190709113604-33be087ad058/go.mod h1:nfDlWeOsu3pUf4yWGL+ERqohP4YsZcBJXWMK+gkzOA4= -k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= -k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= -k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c h1:/KUFqjjqAcY4Us6luF5RDNZ16KJtb49HfR3ZHB9qYXM= -k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= -k8s.io/kube-state-metrics v1.7.2 h1:6vdtgXrrRRMSgnyDmgua+qvgCYv954JNfxXAtDkeLVQ= -k8s.io/kube-state-metrics v1.7.2/go.mod h1:U2Y6DRi07sS85rmVPmBFlmv+2peBcL8IWGjM+IjYA/E= -k8s.io/kubectl v0.18.0/go.mod h1:LOkWx9Z5DXMEg5KtOjHhRiC1fqJPLyCr3KtQgEolCkU= -k8s.io/kubectl v0.18.2/go.mod h1:OdgFa3AlsPKRpFFYE7ICTwulXOcMGXHTc+UKhHKvrb4= -k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= -k8s.io/metrics v0.18.0/go.mod h1:8aYTW18koXqjLVKL7Ds05RPMX9ipJZI3mywYvBOxXd4= -k8s.io/metrics v0.18.2/go.mod h1:qga8E7QfYNR9Q89cSCAjinC9pTZ7yv1XSVGUB0vJypg= -k8s.io/utils v0.0.0-20190308190857-21c4ce38f2a7/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0= -k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= -k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= -k8s.io/utils v0.0.0-20191114200735-6ca3b61696b6/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= -k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89 h1:d4vVOjXm687F1iLSP2q3lyPPuyvTUt3aVoBpi2DqRsU= -k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= -modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= -modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= -modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= -modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= -modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/letsencrypt v0.0.3/go.mod h1:buyQKZ6IXrRnB7TdkHP0RyEybLx18HHyOSoTyoOLqNY= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0= -sigs.k8s.io/controller-runtime v0.6.0 h1:Fzna3DY7c4BIP6KwfSlrfnj20DJ+SeMBK8HSFvOk9NM= -sigs.k8s.io/controller-runtime v0.6.0/go.mod h1:CpYf5pdNY/B352A1TFLAS2JVSlnGQ5O2cftPHndTroo= -sigs.k8s.io/controller-tools v0.2.4/go.mod h1:m/ztfQNocGYBgTTCmFdnK94uVvgxeZeE3LtJvd/jIzA= -sigs.k8s.io/controller-tools v0.3.0/go.mod h1:enhtKGfxZD1GFEoMgP8Fdbu+uKQ/cq1/WGJhdVChfvI= -sigs.k8s.io/kubebuilder v1.0.9-0.20200513134826-f07a0146a40b/go.mod h1:FGPx0hvP73+bapzWoy5ePuhAJYgJjrFbPxgvWyortM0= -sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU= -sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= -sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA= -sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18= -sigs.k8s.io/structured-merge-diff v1.0.2/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA= -sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= -sigs.k8s.io/structured-merge-diff/v3 v3.0.0 h1:dOmIZBMfhcHS09XZkMyUgkq5trg3/jRyJYFZUiaOp8E= -sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= -vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI= +k8s.io/api v0.31.0 h1:b9LiSjR2ym/SzTOlfMHm1tr7/21aD7fSkqgD/CVJBCo= +k8s.io/api v0.31.0/go.mod h1:0YiFF+JfFxMM6+1hQei8FY8M7s1Mth+z/q7eF1aJkTE= +k8s.io/apiextensions-apiserver v0.31.0 h1:fZgCVhGwsclj3qCw1buVXCV6khjRzKC5eCFt24kyLSk= +k8s.io/apiextensions-apiserver v0.31.0/go.mod h1:b9aMDEYaEe5sdK+1T0KU78ApR/5ZVp4i56VacZYEHxk= +k8s.io/apimachinery v0.31.0 h1:m9jOiSr3FoSSL5WO9bjm1n6B9KROYYgNZOb4tyZ1lBc= +k8s.io/apimachinery v0.31.0/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/apiserver v0.31.0 h1:p+2dgJjy+bk+B1Csz+mc2wl5gHwvNkC9QJV+w55LVrY= +k8s.io/apiserver v0.31.0/go.mod h1:KI9ox5Yu902iBnnyMmy7ajonhKnkeZYJhTZ/YI+WEMk= +k8s.io/client-go v0.31.0 h1:QqEJzNjbN2Yv1H79SsS+SWnXkBgVu4Pj3CJQgbx0gI8= +k8s.io/client-go v0.31.0/go.mod h1:Y9wvC76g4fLjmU0BA+rV+h2cncoadjvjjkkIGoTLcGU= +k8s.io/component-base v0.31.0 h1:/KIzGM5EvPNQcYgwq5NwoQBaOlVFrghoVGr8lG6vNRs= +k8s.io/component-base v0.31.0/go.mod h1:TYVuzI1QmN4L5ItVdMSXKvH7/DtvIuas5/mm8YT3rTo= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 h1:2770sDpzrjjsAtVhSeUFseziht227YAWYHLGNM8QPwY= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= +sigs.k8s.io/controller-runtime v0.19.0 h1:nWVM7aq+Il2ABxwiCizrVDSlmDcshi9llbaFbC0ji/Q= +sigs.k8s.io/controller-runtime v0.19.0/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4= +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.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/hack/boilerplate.go.txt b/hack/boilerplate.go.txt new file mode 100644 index 000000000..e69de29bb diff --git a/pkg/controller/postgres/postgres_controller.go b/internal/controller/postgres_controller.go similarity index 53% rename from pkg/controller/postgres/postgres_controller.go rename to internal/controller/postgres_controller.go index 37f0d8a6a..eae793f35 100644 --- a/pkg/controller/postgres/postgres_controller.go +++ b/internal/controller/postgres_controller.go @@ -1,150 +1,134 @@ -package postgres +package controller import ( "context" - goerr "errors" "fmt" + "slices" - "github.com/movetokube/postgres-operator/pkg/config" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/manager" "github.com/go-logr/logr" - dbv1alpha1 "github.com/movetokube/postgres-operator/pkg/apis/db/v1alpha1" + dbv1alpha1 "github.com/movetokube/postgres-operator/api/v1alpha1" + "github.com/movetokube/postgres-operator/pkg/config" "github.com/movetokube/postgres-operator/pkg/postgres" "github.com/movetokube/postgres-operator/pkg/utils" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" kerrors "k8s.io/apimachinery/pkg/util/errors" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller" - "sigs.k8s.io/controller-runtime/pkg/handler" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - logf "sigs.k8s.io/controller-runtime/pkg/runtime/log" - "sigs.k8s.io/controller-runtime/pkg/source" ) -var log = logf.Log.WithName("controller_postgres") - -// Add creates a new Postgres Controller and adds it to the Manager. The Manager will set fields on the Controller -// and Start it when the Manager is Started. -func Add(mgr manager.Manager) error { - return add(mgr, newReconciler(mgr)) +// PostgresReconciler reconciles a Postgres object +type PostgresReconciler struct { + client.Client + Scheme *runtime.Scheme + pg postgres.PG + // pgHost string + instanceFilter string } -// newReconciler returns a new reconcile.Reconciler -func newReconciler(mgr manager.Manager) reconcile.Reconciler { - c := config.Get() - pg, err := postgres.NewPG(c.PostgresHost, c.PostgresUser, c.PostgresPass, c.PostgresUriArgs, c.PostgresDefaultDb, c.CloudProvider, log.WithName("postgres")) - if err != nil { - return nil - } - - return &ReconcilePostgres{ - client: mgr.GetClient(), - scheme: mgr.GetScheme(), +// NewPostgresReconciler returns a new reconcile.Reconciler +func NewPostgresReconciler(mgr manager.Manager, c *config.Cfg, pg postgres.PG) *PostgresReconciler { + return &PostgresReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), pg: pg, - pgHost: c.PostgresHost, instanceFilter: c.AnnotationFilter, } } -// add adds a new Controller to mgr with r as the reconcile.Reconciler -func add(mgr manager.Manager, r reconcile.Reconciler) error { - if r == nil { - return errors.NewInternalError(goerr.New("failed to get reconciler")) - } - // Create a new controller - c, err := controller.New("postgres-controller", mgr, controller.Options{Reconciler: r}) - if err != nil { - return err - } - - // Watch for changes to primary resource Postgres - err = c.Watch(&source.Kind{Type: &dbv1alpha1.Postgres{}}, &handler.EnqueueRequestForObject{}) - if err != nil { - return err - } - - return nil -} - -var _ reconcile.Reconciler = &ReconcilePostgres{} +// +kubebuilder:rbac:groups=db.movetokube.com,resources=postgres,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=db.movetokube.com,resources=postgres/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=db.movetokube.com,resources=postgres/finalizers,verbs=update -// ReconcilePostgres reconciles a Postgres object -type ReconcilePostgres struct { - // This client, initialized using mgr.Client() above, is a split client - // that reads objects from the cache and writes to the apiserver - client client.Client - scheme *runtime.Scheme - pg postgres.PG - pgHost string - instanceFilter string -} +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// TODO(user): Modify the Reconcile function to compare the state specified by +// the Postgres object against the actual cluster state, and then +// perform operations to make the cluster state reflect the state specified by +// the user. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.0/pkg/reconcile +func (r *PostgresReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + log := log.FromContext(ctx) -// The Controller will requeue the Request to be processed again if the returned error is non-nil or -// Result.Requeue is true, otherwise upon completion it will remove the work from the queue. -func (r *ReconcilePostgres) Reconcile(request reconcile.Request) (_ reconcile.Result, reterr error) { - reqLogger := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name) + reqLogger := log.WithValues("Request.Namespace", req.Namespace, "Request.Name", req.Name) reqLogger.Info("Reconciling Postgres") instance := &dbv1alpha1.Postgres{} // Fetch the Postgres instance - err := r.client.Get(context.TODO(), request.NamespacedName, instance) + err := r.Get(ctx, req.NamespacedName, instance) if err != nil { if errors.IsNotFound(err) { // Request object not found, could have been deleted after reconcile request. // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers. // Return and don't requeue - return reconcile.Result{}, nil + return ctrl.Result{}, nil } // Error reading the object - requeue the request. - return reconcile.Result{}, err + return ctrl.Result{}, err } if !utils.MatchesInstanceAnnotation(instance.Annotations, r.instanceFilter) { - return reconcile.Result{}, nil + return ctrl.Result{}, nil } - before := instance.DeepCopyObject() - // Patch after every reconcile loop, if needed - defer func() { - err = utils.Patch(r.client, context.TODO(), before, instance) - if err != nil { - reterr = kerrors.NewAggregate([]error{reterr, err}) - } - }() + before := instance.DeepCopy() // deletion logic if !instance.GetDeletionTimestamp().IsZero() { - if r.shouldDropDB(instance, reqLogger) && instance.Status.Succeeded { + if r.shouldDropDB(ctx, instance, reqLogger) && instance.Status.Succeeded { if instance.Status.Roles.Owner != "" { err := r.pg.DropRole(instance.Status.Roles.Owner, r.pg.GetUser(), instance.Spec.Database, reqLogger) if err != nil { - return reconcile.Result{}, err + return ctrl.Result{}, err } instance.Status.Roles.Owner = "" } if instance.Status.Roles.Reader != "" { err = r.pg.DropRole(instance.Status.Roles.Reader, r.pg.GetUser(), instance.Spec.Database, reqLogger) if err != nil { - return reconcile.Result{}, err + return ctrl.Result{}, err } instance.Status.Roles.Reader = "" } if instance.Status.Roles.Writer != "" { err = r.pg.DropRole(instance.Status.Roles.Writer, r.pg.GetUser(), instance.Spec.Database, reqLogger) if err != nil { - return reconcile.Result{}, err + return ctrl.Result{}, err } instance.Status.Roles.Writer = "" } err = r.pg.DropDatabase(instance.Spec.Database, reqLogger) if err != nil { - return reconcile.Result{}, err + return ctrl.Result{}, err } } - instance.SetFinalizers(nil) + err = r.Status().Patch(ctx, instance, client.MergeFrom(before)) + if err != nil { + reqLogger.Error(err, "could not update db status") + } + controllerutil.RemoveFinalizer(instance, "finalizer.db.movetokube.com") + err = r.Patch(ctx, instance, client.MergeFrom(before)) + if err != nil { + reqLogger.Error(err, "could not remove finalizer") + } + + return ctrl.Result{}, nil + } - return reconcile.Result{}, nil + // Patch after every reconcile loop, if needed + requeue := func(err error) (ctrl.Result, error) { + reqLogger.Error(err, "Requeuing...") + instance.Status.Succeeded = false + updateErr := r.Status().Patch(ctx, instance, client.MergeFrom(before)) + if updateErr != nil { + err = kerrors.NewAggregate([]error{err, updateErr}) + } + return ctrl.Result{Requeue: true}, err } // creation logic @@ -156,37 +140,38 @@ func (r *ReconcilePostgres) Reconcile(request reconcile.Request) (_ reconcile.Re // Create owner role err = r.pg.CreateGroupRole(owner) if err != nil { - return r.requeue(instance, errors.NewInternalError(err)) + return requeue(errors.NewInternalError(err)) } + instance.Status.Roles.Owner = owner + // Create database err = r.pg.CreateDB(instance.Spec.Database, owner) if err != nil { - return r.requeue(instance, errors.NewInternalError(err)) + reqLogger.Error(err, "Could not create DB") + return requeue(errors.NewInternalError(err)) } // Create reader role reader := fmt.Sprintf("%s-reader", instance.Spec.Database) err = r.pg.CreateGroupRole(reader) if err != nil { - return r.requeue(instance, errors.NewInternalError(err)) + return requeue(errors.NewInternalError(err)) } + instance.Status.Roles.Reader = reader // Create writer role writer := fmt.Sprintf("%s-writer", instance.Spec.Database) err = r.pg.CreateGroupRole(writer) if err != nil { - return r.requeue(instance, errors.NewInternalError(err)) + return requeue(errors.NewInternalError(err)) } - - instance.Status.Roles.Owner = owner - instance.Status.Roles.Reader = reader instance.Status.Roles.Writer = writer instance.Status.Succeeded = true } // create extensions for _, extension := range instance.Spec.Extensions { // Check if extension is already added. Skip if already is added. - if utils.ListContains(instance.Status.Extensions, extension) { + if slices.Contains(instance.Status.Extensions, extension) { continue } // Execute create extension SQL statement @@ -208,7 +193,7 @@ func (r *ReconcilePostgres) Reconcile(request reconcile.Request) (_ reconcile.Re ) for _, schema := range instance.Spec.Schemas { // Schema was previously created - if utils.ListContains(instance.Status.Schemas, schema) { + if slices.Contains(instance.Status.Schemas, schema) { continue } @@ -220,19 +205,40 @@ func (r *ReconcilePostgres) Reconcile(request reconcile.Request) (_ reconcile.Re } // Set privileges on schema - schemaPrivilegesReader := postgres.PostgresSchemaPrivileges{database, owner, reader, schema, readerPrivs, false} + schemaPrivilegesReader := postgres.PostgresSchemaPrivileges{ + DB: database, + Creator: owner, + Role: reader, + Schema: schema, + Privs: readerPrivs, + CreateSchema: false, + } err = r.pg.SetSchemaPrivileges(schemaPrivilegesReader, reqLogger) if err != nil { reqLogger.Error(err, fmt.Sprintf("Could not give %s permissions \"%s\"", reader, readerPrivs)) continue } - schemaPrivilegesWriter := postgres.PostgresSchemaPrivileges{database, owner, writer, schema, readerPrivs, true} + schemaPrivilegesWriter := postgres.PostgresSchemaPrivileges{ + DB: database, + Creator: owner, + Role: writer, + Schema: schema, + Privs: writerPrivs, + CreateSchema: true, + } err = r.pg.SetSchemaPrivileges(schemaPrivilegesWriter, reqLogger) if err != nil { reqLogger.Error(err, fmt.Sprintf("Could not give %s permissions \"%s\"", writer, writerPrivs)) continue } - schemaPrivilegesOwner := postgres.PostgresSchemaPrivileges{database, owner, owner, schema, readerPrivs, true} + schemaPrivilegesOwner := postgres.PostgresSchemaPrivileges{ + DB: database, + Creator: owner, + Role: owner, + Schema: schema, + Privs: writerPrivs, + CreateSchema: true, + } err = r.pg.SetSchemaPrivileges(schemaPrivilegesOwner, reqLogger) if err != nil { reqLogger.Error(err, fmt.Sprintf("Could not give %s permissions \"%s\"", writer, writerPrivs)) @@ -241,37 +247,41 @@ func (r *ReconcilePostgres) Reconcile(request reconcile.Request) (_ reconcile.Re instance.Status.Schemas = append(instance.Status.Schemas, schema) } - - err = r.addFinalizer(reqLogger, instance) + err = r.Status().Patch(ctx, instance, client.MergeFrom(before)) if err != nil { - return r.requeue(instance, err) + return requeue(err) + } + before = instance.DeepCopy() + if controllerutil.AddFinalizer(instance, "finalizer.db.movetokube.com") { + err = r.Patch(ctx, instance, client.MergeFrom(before)) + if err != nil { + return requeue(err) + } } reqLogger.Info("reconciler done", "CR.Namespace", instance.Namespace, "CR.Name", instance.Name) - return reconcile.Result{}, nil + return ctrl.Result{}, nil } - -func (r *ReconcilePostgres) addFinalizer(reqLogger logr.Logger, m *dbv1alpha1.Postgres) error { +func (r *PostgresReconciler) addFinalizer(reqLogger logr.Logger, m *dbv1alpha1.Postgres) error { if len(m.GetFinalizers()) < 1 && m.GetDeletionTimestamp() == nil { reqLogger.Info("adding Finalizer for Postgres") m.SetFinalizers([]string{"finalizer.db.movetokube.com"}) } return nil } - -func (r *ReconcilePostgres) requeue(cr *dbv1alpha1.Postgres, reason error) (reconcile.Result, error) { +func (r *PostgresReconciler) requeue(cr *dbv1alpha1.Postgres, reason error) (ctrl.Result, error) { cr.Status.Succeeded = false - return reconcile.Result{}, reason + return ctrl.Result{}, reason } -func (r *ReconcilePostgres) shouldDropDB(cr *dbv1alpha1.Postgres, logger logr.Logger) bool { +func (r *PostgresReconciler) shouldDropDB(ctx context.Context, cr *dbv1alpha1.Postgres, logger logr.Logger) bool { // If DropOnDelete is false we don't need to check any further if !cr.Spec.DropOnDelete { return false } // Get a list of all Postgres dbs := dbv1alpha1.PostgresList{} - err := r.client.List(context.TODO(), &dbs, &client.ListOptions{}) + err := r.List(ctx, &dbs, &client.ListOptions{}) if err != nil { logger.Info(fmt.Sprintf("%v", err)) return true @@ -291,3 +301,10 @@ func (r *ReconcilePostgres) shouldDropDB(cr *dbv1alpha1.Postgres, logger logr.Lo return true } + +// SetupWithManager sets up the controller with the Manager. +func (r *PostgresReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&dbv1alpha1.Postgres{}). + Complete(r) +} diff --git a/pkg/controller/postgres/postgres_controller_test.go b/internal/controller/postgres_controller_test.go similarity index 52% rename from pkg/controller/postgres/postgres_controller_test.go rename to internal/controller/postgres_controller_test.go index 4c45db3b0..86e2bfa3e 100644 --- a/pkg/controller/postgres/postgres_controller_test.go +++ b/internal/controller/postgres_controller_test.go @@ -1,44 +1,90 @@ -package postgres +package controller import ( "context" "fmt" - "time" - . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - - "github.com/golang/mock/gomock" - "github.com/movetokube/postgres-operator/pkg/apis/db/v1alpha1" - mockpg "github.com/movetokube/postgres-operator/pkg/postgres/mock" - "github.com/movetokube/postgres-operator/pkg/utils" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "go.uber.org/mock/gomock" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes/scheme" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/fake" "sigs.k8s.io/controller-runtime/pkg/reconcile" + + "github.com/movetokube/postgres-operator/api/v1alpha1" + mockpg "github.com/movetokube/postgres-operator/pkg/postgres/mock" + "github.com/movetokube/postgres-operator/pkg/utils" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -var _ = Describe("ReconcilePostgres", func() { - var ( +var _ = Describe("PostgresReconciler", func() { + const ( name = "test-db" namespace = "operator" - sc *runtime.Scheme - req reconcile.Request - mockCtrl *gomock.Controller - pg *mockpg.MockPG ) + var ( + sc *runtime.Scheme + req reconcile.Request + mockCtrl *gomock.Controller + pg *mockpg.MockPG + rp *PostgresReconciler + cl client.Client + ) + + initClient := func(pg *v1alpha1.Postgres, markAsDeleted bool) { + statusCopy := pg.Status.DeepCopy() + if markAsDeleted { + pg.SetFinalizers([]string{"finalizer.db.movetokube.com"}) + } + Expect(cl.Create(ctx, pg)).To(BeNil()) + statusCopy.DeepCopyInto(&pg.Status) + // create status separately, because it is a subresource that is + // omitted and zeroed by default + Expect(cl.Status().Update(ctx, pg)).To(BeNil()) + if markAsDeleted { + Expect(cl.Delete(ctx, pg, &client.DeleteOptions{GracePeriodSeconds: new(int64)})).To(BeNil()) + } + } + + listPGs := func() { + l := v1alpha1.PostgresList{} + Expect(cl.List(ctx, &l)).NotTo(HaveOccurred()) + + for i, el := range l.Items { + GinkgoWriter.Println(i, el) + } + } + + runReconcile := func(rp *PostgresReconciler, ctx context.Context, req reconcile.Request) (err error) { + _, err = rp.Reconcile(ctx, req) + if k8sManager != nil { + k8sManager.GetCache().WaitForCacheSync(ctx) + } + return err + } BeforeEach(func() { // Gomock mockCtrl = gomock.NewController(GinkgoT()) pg = mockpg.NewMockPG(mockCtrl) + cl = k8sClient // Create runtime scheme sc = scheme.Scheme - sc.AddKnownTypes(v1alpha1.SchemeGroupVersion, &v1alpha1.Postgres{}) - sc.AddKnownTypes(v1alpha1.SchemeGroupVersion, &v1alpha1.PostgresList{}) + sc.AddKnownTypes(v1alpha1.GroupVersion, &v1alpha1.Postgres{}) + sc.AddKnownTypes(v1alpha1.GroupVersion, &v1alpha1.PostgresList{}) + // Create PostgresReconciler + rp = &PostgresReconciler{ + Client: managerClient, + Scheme: sc, + pg: pg, + } + if k8sManager != nil { + rp.SetupWithManager(k8sManager) + } // Create mock reconcile request req = reconcile.Request{ NamespacedName: types.NamespacedName{ @@ -49,23 +95,48 @@ var _ = Describe("ReconcilePostgres", func() { }) AfterEach(func() { + Expect(clearPgs(namespace)).To(BeNil()) + if k8sManager != nil { + k8sManager.GetCache().WaitForCacheSync(ctx) + } mockCtrl.Finish() }) - It("should not requeue if Postgres does not exist", func() { - // Create client - cl := fake.NewFakeClient() - // Create ReconcilePostgres - rp := &ReconcilePostgres{ - client: cl, - scheme: sc, - pg: pg, - pgHost: "postgres.local", + It("should have a working client", func() { + pg := &v1alpha1.Postgres{ + ObjectMeta: metav1.ObjectMeta{ + Name: "clienttest", + Namespace: namespace, + }, + Spec: v1alpha1.PostgresSpec{Database: "clienttest"}, + Status: v1alpha1.PostgresStatus{Succeeded: true}, } + pg2 := pg.DeepCopy() + pg2.Name = "clienttest2" + + initClient(pg, false) + initClient(pg2, true) + + instance := &v1alpha1.Postgres{} + Expect(managerClient.Get(ctx, types.NamespacedName{Name: "clienttest", Namespace: namespace}, instance)).To(BeNil()) + Expect(instance.ObjectMeta.Name).To(Equal("clienttest")) + Expect(instance.Status.Succeeded).To(BeTrue()) + + Expect(k8sClient.Get(ctx, types.NamespacedName{Name: "clienttest2", Namespace: namespace}, instance)).NotTo(HaveOccurred()) + Expect(instance.ObjectMeta.Name).To(Equal("clienttest2")) + + Expect(clearPgs(namespace)).NotTo(HaveOccurred()) + l := v1alpha1.PostgresList{} + Expect(cl.List(ctx, &l)).NotTo(HaveOccurred()) + listPGs() + Expect(l.Items).To(BeEmpty()) + }) + + It("should not requeue if Postgres does not exist", func() { // Call Reconcile - res, err := rp.Reconcile(req) + res, err := rp.Reconcile(ctx, req) // No error should be returned - Expect(err).To(BeNil()) + Expect(err).NotTo(HaveOccurred()) // Request should not be requeued Expect(res.Requeue).To(BeFalse()) }) @@ -74,18 +145,13 @@ var _ = Describe("ReconcilePostgres", func() { var ( postgresCR *v1alpha1.Postgres - cl client.Client - rp *ReconcilePostgres ) BeforeEach(func() { - now := metav1.NewTime(time.Now()) postgresCR = &v1alpha1.Postgres{ ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - DeletionTimestamp: &now, - Finalizers: []string{"finalizer.db.movetokube.com"}, + Name: name, + Namespace: namespace, }, Spec: v1alpha1.PostgresSpec{ Database: name, @@ -93,37 +159,37 @@ var _ = Describe("ReconcilePostgres", func() { Status: v1alpha1.PostgresStatus{ Succeeded: true, Roles: v1alpha1.PostgresRoles{ - Owner: name + "-group", + Owner: name + "-owner", Reader: name + "-reader", Writer: name + "-writer", }, }, } + }) Context("DropOnDelete is unset", func() { BeforeEach(func() { - // Create client - cl = fake.NewFakeClient([]runtime.Object{postgresCR}...) - // Create ReconcilePostgres - rp = &ReconcilePostgres{ - client: cl, - scheme: sc, - pg: pg, - pgHost: "postgres.local", - } + initClient(postgresCR, true) }) It("should remove finalizer", func() { - _, err := rp.Reconcile(req) + err := runReconcile(rp, ctx, req) // No error should be returned - Expect(err).To(BeNil()) + Expect(err).NotTo(HaveOccurred()) + // Check updated Postgres + // somewhat difficult, because it just got deleted and might + // be gone for real after the last finalizer is removed foundPostgres := &v1alpha1.Postgres{} - err = cl.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: namespace}, foundPostgres) - Expect(err).To(BeNil()) - Expect(len(foundPostgres.GetFinalizers())).To(Equal(0)) + err = cl.Get(ctx, types.NamespacedName{Name: name, Namespace: namespace}, foundPostgres) + if err != nil { + Expect(errors.IsNotFound(err)).To(BeTrue()) + } else { + Expect(foundPostgres.GetFinalizers()).To(BeEmpty()) + Expect(foundPostgres.Status.Succeeded).To(BeTrue()) + } }) It("should not try to delete roles or database", func() { @@ -131,7 +197,8 @@ var _ = Describe("ReconcilePostgres", func() { pg.EXPECT().DropRole(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(0) pg.EXPECT().DropDatabase(gomock.Any(), gomock.Any()).Times(0) // Call Reconcile - rp.Reconcile(req) + err := runReconcile(rp, ctx, req) + Expect(err).NotTo(HaveOccurred()) }) }) @@ -148,22 +215,16 @@ var _ = Describe("ReconcilePostgres", func() { BeforeEach(func() { // Expected function calls pg.EXPECT().GetUser().Return("pguser").AnyTimes() - dropGroupRole = pg.EXPECT().DropRole(name+"-group", "pguser", name, gomock.Any()) + dropGroupRole = pg.EXPECT().DropRole(name+"-owner", "pguser", name, gomock.Any()) dropReaderRole = pg.EXPECT().DropRole(name+"-reader", "pguser", name, gomock.Any()) dropWriterRole = pg.EXPECT().DropRole(name+"-writer", "pguser", name, gomock.Any()) dropDatabase = pg.EXPECT().DropDatabase(name, gomock.Any()) // Create Postgres with DropOnDelete == true - dropPostgres := postgresCR.DeepCopy() - dropPostgres.Spec.DropOnDelete = true - // Create client - cl = fake.NewFakeClient([]runtime.Object{dropPostgres}...) - // Create ReconcilePostgres - rp = &ReconcilePostgres{ - client: cl, - scheme: sc, - pg: pg, - pgHost: "postgres.local", - } + anotherPostgres := postgresCR.DeepCopy() + anotherPostgres.Spec.DropOnDelete = true + initClient(anotherPostgres, true) + foundPostgres := &v1alpha1.Postgres{} + Expect(cl.Get(ctx, types.NamespacedName{Name: name, Namespace: namespace}, foundPostgres)).To(BeNil()) }) Context("Deletion is successful", func() { @@ -174,15 +235,23 @@ var _ = Describe("ReconcilePostgres", func() { dropReaderRole.Return(nil) dropWriterRole.Return(nil) dropDatabase.Return(nil) + foundPostgres := &v1alpha1.Postgres{} + Expect(cl.Get(ctx, types.NamespacedName{Name: name, Namespace: namespace}, foundPostgres)).To(BeNil()) // Call Reconcile - _, err := rp.Reconcile(req) - // No error should be returned - Expect(err).To(BeNil()) + err := runReconcile(rp, ctx, req) + // Patching both the object and its status fails when using the the FakeClient + //if testEnv != nil { + Expect(err).NotTo(HaveOccurred()) + // Check updated Postgres - foundPostgres := &v1alpha1.Postgres{} - err = cl.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: namespace}, foundPostgres) - Expect(err).To(BeNil()) - Expect(len(foundPostgres.GetFinalizers())).To(Equal(0)) + err = cl.Get(ctx, types.NamespacedName{Name: name, Namespace: namespace}, foundPostgres) + if err != nil { + Expect(errors.IsNotFound(err)).To(BeTrue()) + } else { + Expect(foundPostgres.GetFinalizers()).To(BeEmpty()) + } + //} + }) }) @@ -193,35 +262,46 @@ var _ = Describe("ReconcilePostgres", func() { // DropDatabase fails dropDatabase.Return(fmt.Errorf("Could not drop database")) // Call Reconcile - _, err := rp.Reconcile(req) + err := runReconcile(rp, ctx, req) // No error should be returned - Expect(err).To(Not(BeNil())) + Expect(err).To(HaveOccurred()) // Check updated Postgres foundPostgres := &v1alpha1.Postgres{} - err = cl.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: namespace}, foundPostgres) - Expect(err).To(BeNil()) - Expect(foundPostgres.GetFinalizers()[0]).To(Equal("finalizer.db.movetokube.com")) + Expect(cl.Get(ctx, types.NamespacedName{Name: name, Namespace: namespace}, foundPostgres)).To(BeNil()) + Expect(foundPostgres.GetFinalizers()).To(ConsistOf("finalizer.db.movetokube.com")) }) }) Context("Another Postgres exists with same database", func() { + var anotherPostgres *v1alpha1.Postgres BeforeEach(func() { - // Create two Postgres with same database name - dropPostgres := postgresCR.DeepCopy() - dropPostgres.Spec.DropOnDelete = true - anotherPostgres := postgresCR.DeepCopy() - anotherPostgres.Namespace = "default" - // Create client - cl = fake.NewFakeClient([]runtime.Object{dropPostgres, anotherPostgres}...) - // Create ReconcilePostgres - rp = &ReconcilePostgres{ - client: cl, - scheme: sc, - pg: pg, - pgHost: "postgres.local", + // Create another Postgres with same database name + Expect(k8sClient.Create(ctx, &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "another-namespace", + }, + })).To(BeNil()) + anotherPostgres = &v1alpha1.Postgres{ + ObjectMeta: metav1.ObjectMeta{ + Name: "another-database", + Namespace: "another-namespace", + Finalizers: []string{"finalizer.db.movetokube.com"}, + }, + Spec: v1alpha1.PostgresSpec{ + Database: name, + }, + Status: v1alpha1.PostgresStatus{ + Succeeded: true, + Roles: v1alpha1.PostgresRoles{ + Owner: name + "-group", + Reader: name + "-reader", + Writer: name + "-writer", + }, + }, } + initClient(anotherPostgres, true) }) It("should not drop roles or database", func() { @@ -231,7 +311,8 @@ var _ = Describe("ReconcilePostgres", func() { dropReaderRole.Times(0) dropWriterRole.Times(0) // Call Reconcile - rp.Reconcile(req) + err := runReconcile(rp, ctx, req) + Expect(err).NotTo(HaveOccurred()) }) }) @@ -241,33 +322,25 @@ var _ = Describe("ReconcilePostgres", func() { }) Describe("Checking creation logic", func() { + var postgresCR *v1alpha1.Postgres - var ( - cl client.Client - rp *ReconcilePostgres - ) - var postgresCR *v1alpha1.Postgres = &v1alpha1.Postgres{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Spec: v1alpha1.PostgresSpec{ - Database: name, - }, - } + BeforeEach(func() { + postgresCR = &v1alpha1.Postgres{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: v1alpha1.PostgresSpec{ + Database: name, + }, + Status: v1alpha1.PostgresStatus{}, + } + }) Context("MasterRole is unset", func() { BeforeEach(func() { - // Create client - cl = fake.NewFakeClient([]runtime.Object{postgresCR}...) - // Create ReconcilePostgres - rp = &ReconcilePostgres{ - client: cl, - scheme: sc, - pg: pg, - pgHost: "postgres.local", - } + initClient(postgresCR, false) }) It("should pick a default role name", func() { @@ -279,7 +352,8 @@ var _ = Describe("ReconcilePostgres", func() { // CreateDB call pg.EXPECT().CreateDB(name, expectedName).Return(nil) // Call Reconcile - rp.Reconcile(req) + err := runReconcile(rp, ctx, req) + Expect(err).NotTo(HaveOccurred()) }) }) @@ -290,14 +364,7 @@ var _ = Describe("ReconcilePostgres", func() { // Create client modPostgres := postgresCR.DeepCopy() modPostgres.Spec.MasterRole = "my-master-role" - cl = fake.NewFakeClient([]runtime.Object{modPostgres}...) - // Create ReconcilePostgres - rp = &ReconcilePostgres{ - client: cl, - scheme: sc, - pg: pg, - pgHost: "postgres.local", - } + initClient(modPostgres, false) }) It("should use name in MasterRole", func() { @@ -309,7 +376,8 @@ var _ = Describe("ReconcilePostgres", func() { // CreateDB call pg.EXPECT().CreateDB(name, expectedName).Return(nil) // Call Reconcile - rp.Reconcile(req) + err := runReconcile(rp, ctx, req) + Expect(err).NotTo(HaveOccurred()) }) }) @@ -322,22 +390,21 @@ var _ = Describe("ReconcilePostgres", func() { modPostgres.Annotations = map[string]string{ utils.INSTANCE_ANNOTATION: "my-instance", } - cl = fake.NewFakeClient([]runtime.Object{modPostgres}...) - // Create ReconcilePostgres - rp = &ReconcilePostgres{ - client: cl, - scheme: sc, + rp = &PostgresReconciler{ + Client: managerClient, + Scheme: sc, pg: pg, - pgHost: "postgres.local", instanceFilter: "my-instance", } + initClient(modPostgres, false) }) It("should create the database", func() { pg.EXPECT().CreateGroupRole(gomock.Any()).Return(nil).Times(3) pg.EXPECT().CreateDB(name, gomock.Any()).Return(nil) // Call Reconcile - rp.Reconcile(req) + err := runReconcile(rp, ctx, req) + Expect(err).NotTo(HaveOccurred()) }) }) @@ -349,35 +416,20 @@ var _ = Describe("ReconcilePostgres", func() { modPostgres.Annotations = map[string]string{ utils.INSTANCE_ANNOTATION: "my-instance", } - cl = fake.NewFakeClient([]runtime.Object{modPostgres}...) - // Create ReconcilePostgres - rp = &ReconcilePostgres{ - client: cl, - scheme: sc, - pg: pg, - pgHost: "postgres.local", - instanceFilter: "my-other-instance", - } + initClient(modPostgres, false) }) - It("should create the database", func() { + It("should not create the database", func() { // Call Reconcile - rp.Reconcile(req) + err := runReconcile(rp, ctx, req) + Expect(err).NotTo(HaveOccurred()) }) }) Context("Creation is successful", func() { BeforeEach(func() { - // Create client - cl = fake.NewFakeClient([]runtime.Object{postgresCR}...) - // Create ReconcilePostgres - rp = &ReconcilePostgres{ - client: cl, - scheme: sc, - pg: pg, - pgHost: "postgres.local", - } + initClient(postgresCR, false) // Expected function calls pg.EXPECT().CreateGroupRole(gomock.Any()).Return(nil).Times(3) pg.EXPECT().CreateDB(name, gomock.Any()).Return(nil) @@ -390,12 +442,12 @@ var _ = Describe("ReconcilePostgres", func() { Writer: name + "-writer", } // Call Reconcile - _, err := rp.Reconcile(req) + err := runReconcile(rp, ctx, req) // No error should be returned - Expect(err).To(BeNil()) + Expect(err).NotTo(HaveOccurred()) // Check updated Postgres foundPostgres := &v1alpha1.Postgres{} - err = cl.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: namespace}, foundPostgres) + Expect(cl.Get(ctx, types.NamespacedName{Name: name, Namespace: namespace}, foundPostgres)).To(BeNil()) Expect(foundPostgres.Status.Roles).To(Equal(expectedRoles)) Expect(foundPostgres.Status.Succeeded).To(BeTrue()) }) @@ -403,13 +455,12 @@ var _ = Describe("ReconcilePostgres", func() { It("should set a finalizer", func() { expectedFinalizer := "finalizer.db.movetokube.com" // Call Reconcile - _, err := rp.Reconcile(req) - // No error should be returned - Expect(err).To(BeNil()) + err := runReconcile(rp, ctx, req) + Expect(err).NotTo(HaveOccurred()) // Check updated Postgres foundPostgres := &v1alpha1.Postgres{} - err = cl.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: namespace}, foundPostgres) - Expect(foundPostgres.GetFinalizers()[0]).To(Equal(expectedFinalizer)) + Expect(cl.Get(ctx, types.NamespacedName{Name: name, Namespace: namespace}, foundPostgres)).To(BeNil()) + Expect(foundPostgres.GetFinalizers()).To(ContainElement(expectedFinalizer)) }) }) @@ -417,31 +468,24 @@ var _ = Describe("ReconcilePostgres", func() { Context("Creation is not successful", func() { BeforeEach(func() { - // Create client - cl = fake.NewFakeClient([]runtime.Object{postgresCR}...) - // Create ReconcilePostgres - rp = &ReconcilePostgres{ - client: cl, - scheme: sc, - pg: pg, - pgHost: "postgres.local", - } + initClient(postgresCR.DeepCopy(), false) // Expected function calls pg.EXPECT().CreateGroupRole(gomock.Any()).Return(nil).Times(1) pg.EXPECT().CreateDB(name, gomock.Any()).Return(fmt.Errorf("Could not create database")) }) - It("should not update status", func() { + It("should not mark status as successful", func() { expectedRoles := v1alpha1.PostgresRoles{ - Owner: "", + Owner: name + "-group", Reader: "", Writer: "", } // Call Reconcile - rp.Reconcile(req) + _, err := rp.Reconcile(ctx, req) + Expect(err).To(HaveOccurred()) // Check updated Postgres foundPostgres := &v1alpha1.Postgres{} - cl.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: namespace}, foundPostgres) + Expect(cl.Get(ctx, types.NamespacedName{Name: name, Namespace: namespace}, foundPostgres)).To(BeNil()) Expect(foundPostgres.Status.Roles).To(Equal(expectedRoles)) Expect(foundPostgres.Status.Succeeded).To(BeFalse()) }) @@ -451,53 +495,45 @@ var _ = Describe("ReconcilePostgres", func() { }) Describe("Checking extensions logic", func() { - - var ( - cl client.Client - rp *ReconcilePostgres - ) - var postgresCR *v1alpha1.Postgres = &v1alpha1.Postgres{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Spec: v1alpha1.PostgresSpec{ - Database: name, - }, - Status: v1alpha1.PostgresStatus{ - // So it doesn't run creation logic - Succeeded: true, - }, - } + var postgresCR *v1alpha1.Postgres + BeforeEach(func() { + postgresCR = &v1alpha1.Postgres{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: v1alpha1.PostgresSpec{ + Database: name, + }, + Status: v1alpha1.PostgresStatus{ + // So it doesn't run creation logic + Succeeded: true, + }, + } + }) Context("Postgres has no extensions", func() { BeforeEach(func() { - // Create client - cl = fake.NewFakeClient([]runtime.Object{postgresCR}...) - // Create ReconcilePostgres - rp = &ReconcilePostgres{ - client: cl, - scheme: sc, - pg: pg, - pgHost: "postgres.local", - } + initClient(postgresCR, false) }) It("should not try to create extensions", func() { // CreateExtension should not be called pg.EXPECT().CreateExtension(name, gomock.Any(), gomock.Any()).Times(0) // Call Reconcile - rp.Reconcile(req) + err := runReconcile(rp, ctx, req) + Expect(err).NotTo(HaveOccurred()) }) It("should not set status", func() { // Call reconcile - rp.Reconcile(req) + err := runReconcile(rp, ctx, req) + Expect(err).NotTo(HaveOccurred()) // Check updated Postgres foundPostgres := &v1alpha1.Postgres{} - cl.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: namespace}, foundPostgres) - Expect(len(foundPostgres.Status.Extensions)).To(Equal(0)) + Expect(cl.Get(ctx, types.NamespacedName{Name: name, Namespace: namespace}, foundPostgres)).To(BeNil()) + Expect(foundPostgres.Status.Extensions).To(BeEmpty()) }) }) @@ -508,15 +544,7 @@ var _ = Describe("ReconcilePostgres", func() { // Add extensions to Postgres object extPostgres := postgresCR.DeepCopy() extPostgres.Spec.Extensions = []string{"pg_stat_statements", "hstore"} - // Create client - cl = fake.NewFakeClient([]runtime.Object{extPostgres}...) - // Create ReconcilePostgres - rp = &ReconcilePostgres{ - client: cl, - scheme: sc, - pg: pg, - pgHost: "postgres.local", - } + initClient(extPostgres, false) }) Context("Creation is successful", func() { @@ -529,13 +557,12 @@ var _ = Describe("ReconcilePostgres", func() { It("should update status", func() { // Call reconcile - rp.Reconcile(req) + err := runReconcile(rp, ctx, req) + Expect(err).NotTo(HaveOccurred()) // Check updated Postgres foundPostgres := &v1alpha1.Postgres{} - cl.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: namespace}, foundPostgres) - Expect(len(foundPostgres.Status.Extensions)).To(Equal(2)) - Expect(foundPostgres.Status.Extensions[0]).To(Equal("pg_stat_statements")) - Expect(foundPostgres.Status.Extensions[1]).To(Equal("hstore")) + Expect(cl.Get(ctx, types.NamespacedName{Name: name, Namespace: namespace}, foundPostgres)).To(BeNil()) + Expect(foundPostgres.Status.Extensions).To(ConsistOf("pg_stat_statements", "hstore")) }) }) @@ -550,12 +577,12 @@ var _ = Describe("ReconcilePostgres", func() { It("should update status", func() { // Call reconcile - rp.Reconcile(req) + err := runReconcile(rp, ctx, req) + Expect(err).NotTo(HaveOccurred()) // Check updated Postgres foundPostgres := &v1alpha1.Postgres{} - cl.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: namespace}, foundPostgres) - Expect(len(foundPostgres.Status.Extensions)).To(Equal(1)) - Expect(foundPostgres.Status.Extensions[0]).To(Equal("hstore")) + Expect(cl.Get(ctx, types.NamespacedName{Name: name, Namespace: namespace}, foundPostgres)).To(BeNil()) + Expect(foundPostgres.Status.Extensions).To(ConsistOf("hstore")) }) }) @@ -569,31 +596,22 @@ var _ = Describe("ReconcilePostgres", func() { extPostgres := postgresCR.DeepCopy() extPostgres.Spec.Extensions = []string{"pg_stat_statements", "hstore"} extPostgres.Status.Extensions = []string{"hstore"} - // Create client - cl = fake.NewFakeClient([]runtime.Object{extPostgres}...) - // Create ReconcilePostgres - rp = &ReconcilePostgres{ - client: cl, - scheme: sc, - pg: pg, - pgHost: "postgres.local", - } + initClient(extPostgres, false) }) Context("Creation is successful", func() { - It("should not recreate extisting extension", func() { + It("should not recreate existing extension", func() { // Expected method calls pg.EXPECT().CreateExtension(name, "pg_stat_statements", gomock.Any()).Return(nil).Times(1) pg.EXPECT().CreateExtension(name, "hstore", gomock.Any()).Times(0) // Call reconcile - rp.Reconcile(req) + err := runReconcile(rp, ctx, req) + Expect(err).NotTo(HaveOccurred()) // Check updated Postgres foundPostgres := &v1alpha1.Postgres{} - cl.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: namespace}, foundPostgres) - Expect(len(foundPostgres.Status.Extensions)).To(Equal(2)) - Expect(foundPostgres.Status.Extensions[0]).To(Equal("hstore")) - Expect(foundPostgres.Status.Extensions[1]).To(Equal("pg_stat_statements")) + Expect(cl.Get(ctx, types.NamespacedName{Name: name, Namespace: namespace}, foundPostgres)).To(BeNil()) + Expect(foundPostgres.Status.Extensions).To(ConsistOf("hstore", "pg_stat_statements")) }) }) @@ -603,58 +621,50 @@ var _ = Describe("ReconcilePostgres", func() { }) Describe("Checking schemas logic", func() { - - var ( - cl client.Client - rp *ReconcilePostgres - ) - var postgresCR *v1alpha1.Postgres = &v1alpha1.Postgres{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Spec: v1alpha1.PostgresSpec{ - Database: name, - }, - Status: v1alpha1.PostgresStatus{ - // So it doesn't run creation logic - Succeeded: true, - Roles: v1alpha1.PostgresRoles{ - Owner: name + "-group", - Reader: name + "-reader", - Writer: name + "-writer", + var postgresCR *v1alpha1.Postgres + BeforeEach(func() { + postgresCR = &v1alpha1.Postgres{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, }, - }, - } + Spec: v1alpha1.PostgresSpec{ + Database: name, + }, + Status: v1alpha1.PostgresStatus{ + // So it doesn't run creation logic + Succeeded: true, + Roles: v1alpha1.PostgresRoles{ + Owner: name + "-group", + Reader: name + "-reader", + Writer: name + "-writer", + }, + }, + } + }) Context("Postgres has no schemas", func() { BeforeEach(func() { - // Create client - cl = fake.NewFakeClient([]runtime.Object{postgresCR}...) - // Create ReconcilePostgres - rp = &ReconcilePostgres{ - client: cl, - scheme: sc, - pg: pg, - pgHost: "postgres.local", - } + initClient(postgresCR, false) }) It("should not try to create schemas", func() { // CreateSchema should not be called pg.EXPECT().CreateSchema(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(0) // Call Reconcile - rp.Reconcile(req) + err := runReconcile(rp, ctx, req) + Expect(err).NotTo(HaveOccurred()) }) It("should not set status", func() { // Call reconcile - rp.Reconcile(req) + err := runReconcile(rp, ctx, req) + Expect(err).NotTo(HaveOccurred()) // Check updated Postgres foundPostgres := &v1alpha1.Postgres{} - cl.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: namespace}, foundPostgres) - Expect(len(foundPostgres.Status.Schemas)).To(Equal(0)) + Expect(cl.Get(ctx, types.NamespacedName{Name: name, Namespace: namespace}, foundPostgres)).To(BeNil()) + Expect(foundPostgres.Status.Schemas).To(BeEmpty()) }) }) @@ -665,15 +675,7 @@ var _ = Describe("ReconcilePostgres", func() { // Add schemas to Postgres object schemaPostgres := postgresCR.DeepCopy() schemaPostgres.Spec.Schemas = []string{"customers", "stores"} - // Create client - cl = fake.NewFakeClient([]runtime.Object{schemaPostgres}...) - // Create ReconcilePostgres - rp = &ReconcilePostgres{ - client: cl, - scheme: sc, - pg: pg, - pgHost: "postgres.local", - } + initClient(schemaPostgres, false) }) Context("Creation is successful", func() { @@ -682,21 +684,20 @@ var _ = Describe("ReconcilePostgres", func() { // Expected method calls // customers schema pg.EXPECT().CreateSchema(name, name+"-group", "customers", gomock.Any()).Return(nil).Times(1) - pg.EXPECT().SetSchemaPrivileges(name, name+"-group", gomock.Any(), "customers", gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(3) + pg.EXPECT().SetSchemaPrivileges(gomock.Any(), gomock.Any()).Return(nil).Times(3) // stores schema pg.EXPECT().CreateSchema(name, name+"-group", "stores", gomock.Any()).Return(nil).Times(1) - pg.EXPECT().SetSchemaPrivileges(name, name+"-group", gomock.Any(), "stores", gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(3) + pg.EXPECT().SetSchemaPrivileges(gomock.Any(), gomock.Any()).Return(nil).Times(3) }) It("should update status", func() { // Call reconcile - rp.Reconcile(req) + err := runReconcile(rp, ctx, req) + Expect(err).NotTo(HaveOccurred()) // Check updated Postgres foundPostgres := &v1alpha1.Postgres{} - cl.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: namespace}, foundPostgres) - Expect(len(foundPostgres.Status.Schemas)).To(Equal(2)) - Expect(foundPostgres.Status.Schemas[0]).To(Equal("customers")) - Expect(foundPostgres.Status.Schemas[1]).To(Equal("stores")) + Expect(cl.Get(ctx, types.NamespacedName{Name: name, Namespace: namespace}, foundPostgres)).To(BeNil()) + Expect(foundPostgres.Status.Schemas).To(ConsistOf("customers", "stores")) }) }) @@ -707,22 +708,20 @@ var _ = Describe("ReconcilePostgres", func() { // Expected method calls // customers schema errors pg.EXPECT().CreateSchema(name, name+"-group", "customers", gomock.Any()).Return(fmt.Errorf("Could not create schema")).Times(1) - pg.EXPECT().SetSchemaPrivileges(name, name+"-group", gomock.Any(), "customers", gomock.Any(), gomock.Any() ,gomock.Any()).Return(nil).Times(0) + pg.EXPECT().SetSchemaPrivileges(gomock.Any(), gomock.Any()).Return(nil).Times(0) // stores schema pg.EXPECT().CreateSchema(name, name+"-group", "stores", gomock.Any()).Return(nil).Times(1) - pg.EXPECT().SetSchemaPrivileges(name, name+"-group", name+"-reader", "stores", gomock.Any(), false, gomock.Any()).Return(nil).Times(1) - pg.EXPECT().SetSchemaPrivileges(name, name+"-group", name+"-writer", "stores", gomock.Any(), true, gomock.Any()).Return(nil).Times(1) - pg.EXPECT().SetSchemaPrivileges(name, name+"-group", name+"-group", "stores", gomock.Any(), true, gomock.Any()).Return(nil).Times(1) + pg.EXPECT().SetSchemaPrivileges(gomock.Any(), gomock.Any()).Return(nil).Times(3) }) It("should update status", func() { // Call reconcile - rp.Reconcile(req) + err := runReconcile(rp, ctx, req) + Expect(err).NotTo(HaveOccurred()) // Check updated Postgres foundPostgres := &v1alpha1.Postgres{} - cl.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: namespace}, foundPostgres) - Expect(len(foundPostgres.Status.Schemas)).To(Equal(1)) - Expect(foundPostgres.Status.Schemas[0]).To(Equal("stores")) + Expect(cl.Get(ctx, types.NamespacedName{Name: name, Namespace: namespace}, foundPostgres)).To(BeNil()) + Expect(foundPostgres.Status.Schemas).To(ConsistOf("stores")) }) }) @@ -736,34 +735,25 @@ var _ = Describe("ReconcilePostgres", func() { schemaPostgres := postgresCR.DeepCopy() schemaPostgres.Spec.Schemas = []string{"customers", "stores"} schemaPostgres.Status.Schemas = []string{"stores"} - // Create client - cl = fake.NewFakeClient([]runtime.Object{schemaPostgres}...) - // Create ReconcilePostgres - rp = &ReconcilePostgres{ - client: cl, - scheme: sc, - pg: pg, - pgHost: "postgres.local", - } + initClient(schemaPostgres, false) }) Context("Creation is successful", func() { - It("should not recreate extisting schema", func() { - // Expected method calls + It("should not recreate existing schema", func() { // customers schema pg.EXPECT().CreateSchema(name, name+"-group", "customers", gomock.Any()).Return(nil).Times(1) - pg.EXPECT().SetSchemaPrivileges(name, name+"-group", gomock.Any(), "customers", gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(3) + pg.EXPECT().SetSchemaPrivileges(gomock.Any(), gomock.Any()).Return(nil).Times(3) // stores schema already exists pg.EXPECT().CreateSchema(name, name+"-group", "stores", gomock.Any()).Times(0) + pg.EXPECT().SetSchemaPrivileges(gomock.Any(), gomock.Any()).Return(nil).Times(0) // Call reconcile - rp.Reconcile(req) + err := runReconcile(rp, ctx, req) + Expect(err).NotTo(HaveOccurred()) // Check updated Postgres foundPostgres := &v1alpha1.Postgres{} - cl.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: namespace}, foundPostgres) - Expect(len(foundPostgres.Status.Schemas)).To(Equal(2)) - Expect(foundPostgres.Status.Schemas[0]).To(Equal("stores")) - Expect(foundPostgres.Status.Schemas[1]).To(Equal("customers")) + Expect(cl.Get(ctx, types.NamespacedName{Name: name, Namespace: namespace}, foundPostgres)).To(BeNil()) + Expect(foundPostgres.Status.Schemas).To(ConsistOf("stores", "customers")) }) }) diff --git a/internal/controller/postgresuser_controller.go b/internal/controller/postgresuser_controller.go new file mode 100644 index 000000000..735ade207 --- /dev/null +++ b/internal/controller/postgresuser_controller.go @@ -0,0 +1,339 @@ +package controller + +import ( + "context" + "fmt" + "maps" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/manager" + + "github.com/go-logr/logr" + dbv1alpha1 "github.com/movetokube/postgres-operator/api/v1alpha1" + "github.com/movetokube/postgres-operator/pkg/config" + "github.com/movetokube/postgres-operator/pkg/postgres" + "github.com/movetokube/postgres-operator/pkg/utils" +) + +// PostgresUserReconciler reconciles a PostgresUser object +type PostgresUserReconciler struct { + client.Client + Scheme *runtime.Scheme + pg postgres.PG + pgHost string + instanceFilter string + keepSecretName bool // use secret name as defined in PostgresUserSpec +} + +// NewPostgresUserReconciler returns a new reconcile.Reconciler +func NewPostgresUserReconciler(mgr manager.Manager, cfg *config.Cfg, pg postgres.PG) *PostgresUserReconciler { + return &PostgresUserReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + pg: pg, + instanceFilter: cfg.AnnotationFilter, + keepSecretName: cfg.KeepSecretName, + } +} + +// +kubebuilder:rbac:groups=db.movetokube.com,resources=postgresusers,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=db.movetokube.com,resources=postgresusers/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=db.movetokube.com,resources=postgresusers/finalizers,verbs=update + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.0/pkg/reconcile +func (r *PostgresUserReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + log := log.FromContext(ctx) + + reqLogger := log.WithValues("Request.Namespace", req.Namespace, "Request.Name", req.Name) + reqLogger.Info("Reconciling PostgresUser") + + // Fetch the PostgresUser instance + instance := &dbv1alpha1.PostgresUser{} + err := r.Get(ctx, req.NamespacedName, instance) + if err != nil { + if errors.IsNotFound(err) { + // Request object not found, could have been deleted after reconcile request. + // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers. + // Return and don't requeue + return ctrl.Result{}, nil + } + // Error reading the object - requeue the request. + return ctrl.Result{}, err + } + + if !utils.MatchesInstanceAnnotation(instance.Annotations, r.instanceFilter) { + return ctrl.Result{}, nil + } + + // Deletion logic + if instance.GetDeletionTimestamp() != nil { + if instance.Status.Succeeded && instance.Status.PostgresRole != "" { + // Initialize database name for connection with default database + // in case postgres cr isn't here anymore + db := r.pg.GetDefaultDatabase() + // Search Postgres CR + postgres, err := r.getPostgresCR(ctx, instance) + // Check if error exists and not a not found error + if err != nil && !errors.IsNotFound(err) { + return ctrl.Result{}, err + } + // Check if postgres cr is found and not in deletion state + if postgres != nil && postgres.GetDeletionTimestamp().IsZero() { + db = instance.Status.DatabaseName + } + err = r.pg.DropRole(instance.Status.PostgresRole, instance.Status.PostgresGroup, + db, reqLogger) + if err != nil { + return ctrl.Result{}, err + } + } + controllerutil.RemoveFinalizer(instance, "finalizer.db.movetokube.com") + + // Update CR + err = r.Update(ctx, instance) + if err != nil { + return ctrl.Result{}, err + } + return ctrl.Result{}, nil + } + + // Creation logic + var role, login string + password, err := utils.GetSecureRandomString(15) + + if err != nil { + return r.requeue(ctx, instance, err) + } + + if instance.Status.PostgresRole == "" { + // We need to get the Postgres CR to get the group role name + database, err := r.getPostgresCR(ctx, instance) + if err != nil { + return r.requeue(ctx, instance, errors.NewInternalError(err)) + } + // Create user role + suffix := utils.GetRandomString(6) + role = fmt.Sprintf("%s-%s", instance.Spec.Role, suffix) + login, err = r.pg.CreateUserRole(role, password) + if err != nil { + return r.requeue(ctx, instance, errors.NewInternalError(err)) + } + + // Grant group role to user role + var groupRole string + switch instance.Spec.Privileges { + case "READ": + groupRole = database.Status.Roles.Reader + case "WRITE": + groupRole = database.Status.Roles.Writer + default: + groupRole = database.Status.Roles.Owner + } + + err = r.pg.GrantRole(groupRole, role) + if err != nil { + return r.requeue(ctx, instance, errors.NewInternalError(err)) + } + + // Alter default set role to group role + // This is so that objects created by user gets owned by group role + err = r.pg.AlterDefaultLoginRole(role, groupRole) + if err != nil { + return r.requeue(ctx, instance, errors.NewInternalError(err)) + } + + instance.Status.PostgresRole = role + instance.Status.PostgresGroup = groupRole + instance.Status.PostgresLogin = login + instance.Status.DatabaseName = database.Spec.Database + err = r.Status().Update(ctx, instance) + if err != nil { + return r.requeue(ctx, instance, err) + } + } else { + role = instance.Status.PostgresRole + login = instance.Status.PostgresLogin + } + + err = r.addFinalizer(ctx, reqLogger, instance) + if err != nil { + return r.requeue(ctx, instance, err) + } + err = r.addOwnerRef(ctx, reqLogger, instance) + if err != nil { + return r.requeue(ctx, instance, err) + } + + secret, err := r.newSecretForCR(instance, role, password, login) + if err != nil { + return r.requeue(ctx, instance, err) + } + + // Set PostgresUser instance as the owner and controller + if err := controllerutil.SetControllerReference(instance, secret, r.Scheme); err != nil { + return r.requeue(ctx, instance, err) + } + + // Check if this Secret already exists + found := &corev1.Secret{} + err = r.Get(ctx, types.NamespacedName{Name: secret.Name, Namespace: secret.Namespace}, found) + if err != nil && errors.IsNotFound(err) { + // if role is already created, update password + if instance.Status.Succeeded { + err := r.pg.UpdatePassword(role, password) + if err != nil { + return r.requeue(ctx, instance, err) + } + } + reqLogger.Info("Creating secret", "Secret.Namespace", secret.Namespace, "Secret.Name", secret.Name) + err = r.Create(ctx, secret) + if err != nil { + return ctrl.Result{}, err + } + + // Secret created successfully - don't requeue + return r.finish(ctx, instance) + } else if err != nil { + return r.requeue(ctx, instance, err) + } + + reqLogger.Info("reconciler done", "CR.Namespace", instance.Namespace, "CR.Name", instance.Name) + return ctrl.Result{}, nil +} + +func (r *PostgresUserReconciler) getPostgresCR(ctx context.Context, instance *dbv1alpha1.PostgresUser) (*dbv1alpha1.Postgres, error) { + database := dbv1alpha1.Postgres{} + err := r.Get(ctx, + types.NamespacedName{Namespace: instance.Namespace, Name: instance.Spec.Database}, &database) + if err != nil { + return nil, err + } + if !utils.MatchesInstanceAnnotation(database.Annotations, r.instanceFilter) { + err = fmt.Errorf("database \"%s\" is not managed by this operator", database.Name) + return nil, err + } + if !database.Status.Succeeded { + err = fmt.Errorf("database \"%s\" is not ready", database.Name) + return nil, err + } + return &database, nil +} + +func (r *PostgresUserReconciler) newSecretForCR(cr *dbv1alpha1.PostgresUser, role, password, login string) (*corev1.Secret, error) { + pgUserUrl := fmt.Sprintf("postgresql://%s:%s@%s/%s", role, password, r.pgHost, cr.Status.DatabaseName) + pgJDBCUrl := fmt.Sprintf("jdbc:postgresql://%s/%s", r.pgHost, cr.Status.DatabaseName) + pgDotnetUrl := fmt.Sprintf("User ID=%s;Password=%s;Host=%s;Port=5432;Database=%s;", role, password, r.pgHost, cr.Status.DatabaseName) + labels := map[string]string{ + "app": cr.Name, + } + // Merge in user-defined secret labels + maps.Copy(labels, cr.Spec.Labels) + + annotations := cr.Spec.Annotations + name := fmt.Sprintf("%s-%s", cr.Spec.SecretName, cr.Name) + if r.keepSecretName { + name = cr.Spec.SecretName + } + + templateData, err := utils.RenderTemplate(cr.Spec.SecretTemplate, utils.TemplateContext{ + Role: role, + Host: r.pgHost, + Database: cr.Status.DatabaseName, + Password: password, + }) + if err != nil { + return nil, fmt.Errorf("render templated keys: %w", err) + } + + data := map[string][]byte{ + "POSTGRES_URL": []byte(pgUserUrl), + "POSTGRES_JDBC_URL": []byte(pgJDBCUrl), + "POSTGRES_DOTNET_URL": []byte(pgDotnetUrl), + "HOST": []byte(r.pgHost), + "DATABASE_NAME": []byte(cr.Status.DatabaseName), + "ROLE": []byte(role), + "PASSWORD": []byte(password), + "LOGIN": []byte(login), + } + // templates may override standard keys + if len(templateData) > 0 { + maps.Copy(data, templateData) + } + + return &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: cr.Namespace, + Labels: labels, + Annotations: annotations, + }, + Data: data, + }, nil +} + +func (r *PostgresUserReconciler) addFinalizer(ctx context.Context, reqLogger logr.Logger, m *dbv1alpha1.PostgresUser) error { + if len(m.GetFinalizers()) < 1 && m.GetDeletionTimestamp() == nil { + reqLogger.Info("adding Finalizer for Postgres") + m.SetFinalizers([]string{"finalizer.db.movetokube.com"}) + + // Update CR + err := r.Update(ctx, m) + if err != nil { + reqLogger.Error(err, "failed to update PosgresUser with finalizer") + return err + } + } + return nil +} +func (r *PostgresUserReconciler) addOwnerRef(ctx context.Context, reqLogger logr.Logger, instance *dbv1alpha1.PostgresUser) error { + // Search postgres database CR + pg, err := r.getPostgresCR(ctx, instance) + if err != nil { + return err + } + // Update owners + err = controllerutil.SetControllerReference(pg, instance, r.Scheme) + if err != nil { + return err + } + // Update CR + err = r.Update(ctx, instance) + return err +} + +func (r *PostgresUserReconciler) requeue(ctx context.Context, cr *dbv1alpha1.PostgresUser, reason error) (ctrl.Result, error) { + cr.Status.Succeeded = false + err := r.Status().Update(ctx, cr) + if err != nil { + return ctrl.Result{}, err + } + return ctrl.Result{}, reason +} + +func (r *PostgresUserReconciler) finish(ctx context.Context, cr *dbv1alpha1.PostgresUser) (ctrl.Result, error) { + cr.Status.Succeeded = true + err := r.Status().Update(ctx, cr) + if err != nil { + return ctrl.Result{}, err + } + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *PostgresUserReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&dbv1alpha1.PostgresUser{}). + Complete(r) +} diff --git a/internal/controller/postgresuser_controller_test.go b/internal/controller/postgresuser_controller_test.go new file mode 100644 index 000000000..fc08091d2 --- /dev/null +++ b/internal/controller/postgresuser_controller_test.go @@ -0,0 +1,618 @@ +package controller + +import ( + "context" + "fmt" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "go.uber.org/mock/gomock" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/kubernetes/scheme" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + dbv1alpha1 "github.com/movetokube/postgres-operator/api/v1alpha1" + mockpg "github.com/movetokube/postgres-operator/pkg/postgres/mock" + "github.com/movetokube/postgres-operator/pkg/utils" +) + +var _ = Describe("PostgresUser Controller", func() { + const ( + name = "test-user" + namespace = "operator" + databaseName = "test-db" + secretName = "db-credentials" + roleName = "app" + ) + + var ( + sc *runtime.Scheme + req reconcile.Request + mockCtrl *gomock.Controller + pg *mockpg.MockPG + rp *PostgresUserReconciler + cl client.Client + ) + + initClient := func(postgres *dbv1alpha1.Postgres, user *dbv1alpha1.PostgresUser, markAsDeleted bool) { + if postgres != nil { + pgStatusCopy := postgres.Status.DeepCopy() + Expect(cl.Create(ctx, postgres)).To(BeNil()) + pgStatusCopy.DeepCopyInto(&postgres.Status) + Expect(cl.Status().Update(ctx, postgres)).To(BeNil()) + } + + if user != nil { + userStatusCopy := user.Status.DeepCopy() + if markAsDeleted { + user.SetFinalizers([]string{"finalizer.db.movetokube.com"}) + } + Expect(cl.Create(ctx, user)).To(BeNil()) + userStatusCopy.DeepCopyInto(&user.Status) + Expect(cl.Status().Update(ctx, user)).To(BeNil()) + if markAsDeleted { + Expect(cl.Delete(ctx, user, &client.DeleteOptions{GracePeriodSeconds: new(int64)})).To(BeNil()) + } + } + } + + runReconcile := func(rp *PostgresUserReconciler, ctx context.Context, req reconcile.Request) (err error) { + _, err = rp.Reconcile(ctx, req) + if k8sManager != nil { + k8sManager.GetCache().WaitForCacheSync(ctx) + } + return err + } + + clearUsers := func(namespace string) error { + l := dbv1alpha1.PostgresUserList{} + err := k8sClient.List(ctx, &l, client.InNamespace(namespace)) + Expect(err).ToNot(HaveOccurred()) + for _, el := range l.Items { + org := el.DeepCopy() + el.SetFinalizers(nil) + err = k8sClient.Patch(ctx, &el, client.MergeFrom(org)) + if err != nil { + return err + } + } + return k8sClient.DeleteAllOf(ctx, &dbv1alpha1.PostgresUser{}, client.InNamespace(namespace)) + } + + BeforeEach(func() { + // Gomock + mockCtrl = gomock.NewController(GinkgoT()) + pg = mockpg.NewMockPG(mockCtrl) + cl = k8sClient + // Create runtime scheme + sc = scheme.Scheme + sc.AddKnownTypes(dbv1alpha1.GroupVersion, &dbv1alpha1.Postgres{}) + sc.AddKnownTypes(dbv1alpha1.GroupVersion, &dbv1alpha1.PostgresList{}) + sc.AddKnownTypes(dbv1alpha1.GroupVersion, &dbv1alpha1.PostgresUser{}) + sc.AddKnownTypes(dbv1alpha1.GroupVersion, &dbv1alpha1.PostgresUserList{}) + // Create PostgresUserReconciler + rp = &PostgresUserReconciler{ + Client: managerClient, + Scheme: sc, + pg: pg, + pgHost: "postgres.local", + } + if k8sManager != nil { + rp.SetupWithManager(k8sManager) + } + // Create mock reconcile request + req = reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: name, + Namespace: namespace, + }, + } + }) + + AfterEach(func() { + Expect(clearPgs(namespace)).To(BeNil()) + Expect(clearUsers(namespace)).To(BeNil()) + if k8sManager != nil { + k8sManager.GetCache().WaitForCacheSync(ctx) + } + mockCtrl.Finish() + }) + + It("should not requeue if PostgresUser does not exist", func() { + // Call Reconcile + res, err := rp.Reconcile(ctx, req) + // No error should be returned + Expect(err).NotTo(HaveOccurred()) + // Request should not be requeued + Expect(res.Requeue).To(BeFalse()) + }) + + Describe("Checking deletion logic", func() { + var ( + postgresDB *dbv1alpha1.Postgres + postgresUser *dbv1alpha1.PostgresUser + ) + + BeforeEach(func() { + postgresDB = &dbv1alpha1.Postgres{ + ObjectMeta: metav1.ObjectMeta{ + Name: databaseName, + Namespace: namespace, + }, + Spec: dbv1alpha1.PostgresSpec{ + Database: databaseName, + }, + Status: dbv1alpha1.PostgresStatus{ + Succeeded: true, + Roles: dbv1alpha1.PostgresRoles{ + Owner: databaseName + "-group", + Reader: databaseName + "-reader", + Writer: databaseName + "-writer", + }, + }, + } + + postgresUser = &dbv1alpha1.PostgresUser{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: dbv1alpha1.PostgresUserSpec{ + Database: databaseName, + SecretName: secretName, + Role: roleName, + Privileges: "WRITE", + }, + Status: dbv1alpha1.PostgresUserStatus{ + Succeeded: true, + PostgresGroup: databaseName + "-writer", + PostgresRole: "mockuser", + DatabaseName: databaseName, + }, + } + }) + + Context("User deletion", func() { + BeforeEach(func() { + initClient(postgresDB, postgresUser, true) + }) + + It("should drop the role and remove finalizer", func() { + // Expect DropRole to be called + pg.EXPECT().GetDefaultDatabase().Return("postgres") + pg.EXPECT().DropRole(postgresUser.Status.PostgresRole, postgresUser.Status.PostgresGroup, + databaseName, gomock.Any()).Return(nil) + + // Call Reconcile + err := runReconcile(rp, ctx, req) + Expect(err).NotTo(HaveOccurred()) + + // Check if PostgresUser was properly deleted + foundUser := &dbv1alpha1.PostgresUser{} + err = cl.Get(ctx, types.NamespacedName{Name: name, Namespace: namespace}, foundUser) + if err != nil { + Expect(errors.IsNotFound(err)).To(BeTrue()) + } else { + Expect(foundUser.GetFinalizers()).To(BeEmpty()) + } + }) + + It("should return an error if role dropping fails", func() { + // Expect DropRole to fail + pg.EXPECT().GetDefaultDatabase().Return("postgres") + pg.EXPECT().DropRole(postgresUser.Status.PostgresRole, postgresUser.Status.PostgresGroup, + databaseName, gomock.Any()).Return(fmt.Errorf("failed to drop role")) + // Call Reconcile + err := runReconcile(rp, ctx, req) + Expect(err).To(HaveOccurred()) + + // Check if PostgresUser still has finalizer + foundUser := &dbv1alpha1.PostgresUser{} + err = cl.Get(ctx, types.NamespacedName{Name: name, Namespace: namespace}, foundUser) + Expect(err).NotTo(HaveOccurred()) + Expect(foundUser.GetFinalizers()).NotTo(BeEmpty()) + }) + }) + }) + + Describe("Checking creation logic", func() { + var ( + postgresDB *dbv1alpha1.Postgres + postgresUser *dbv1alpha1.PostgresUser + ) + + BeforeEach(func() { + postgresDB = &dbv1alpha1.Postgres{ + ObjectMeta: metav1.ObjectMeta{ + Name: databaseName, + Namespace: namespace, + }, + Spec: dbv1alpha1.PostgresSpec{ + Database: databaseName, + }, + Status: dbv1alpha1.PostgresStatus{ + Succeeded: true, + Roles: dbv1alpha1.PostgresRoles{ + Owner: databaseName + "-group", + Reader: databaseName + "-reader", + Writer: databaseName + "-writer", + }, + }, + } + + postgresUser = &dbv1alpha1.PostgresUser{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: dbv1alpha1.PostgresUserSpec{ + Database: databaseName, + SecretName: secretName, + Role: roleName, + Privileges: "WRITE", + }, + } + }) + + Context("New PostgresUser creation", func() { + BeforeEach(func() { + // Create database but not the user yet + initClient(postgresDB, nil, false) + + // Do not create the user yet, the reconciler will do it + Expect(cl.Create(ctx, postgresUser)).To(Succeed()) + }) + + AfterEach(func() { + // Clean up any created secrets + secretList := &corev1.SecretList{} + Expect(cl.List(ctx, secretList, client.InNamespace(namespace))).To(Succeed()) + for _, secret := range secretList.Items { + Expect(cl.Delete(ctx, &secret)).To(Succeed()) + } + }) + + It("should create user role, grant privileges, and create a secret", func() { + var capturedRole string + // Mock expected calls + pg.EXPECT().GetDefaultDatabase().Return("postgres").AnyTimes() + pg.EXPECT().CreateUserRole(gomock.Any(), gomock.Any()).DoAndReturn( + func(role, password string) (string, error) { + Expect(role).To(HavePrefix(roleName + "-")) + capturedRole = role + return role, nil + }) + pg.EXPECT().GrantRole(databaseName+"-writer", gomock.Any()).DoAndReturn( + func(groupRole, role string) error { + Expect(role).To(Equal(capturedRole)) + return nil + }) + pg.EXPECT().AlterDefaultLoginRole(gomock.Any(), gomock.Any()).DoAndReturn( + func(role, groupRole string) error { + Expect(role).To(Equal(capturedRole)) + Expect(groupRole).To(Equal(databaseName + "-writer")) + return nil + }) + + // Call Reconcile + err := runReconcile(rp, ctx, req) + Expect(err).NotTo(HaveOccurred()) + + // Check if PostgresUser status was properly updated + foundUser := &dbv1alpha1.PostgresUser{} + err = cl.Get(ctx, types.NamespacedName{Name: name, Namespace: namespace}, foundUser) + Expect(err).NotTo(HaveOccurred()) + Expect(foundUser.Status.Succeeded).To(BeTrue()) + Expect(foundUser.Status.PostgresRole).To(HavePrefix(roleName + "-")) + Expect(foundUser.Status.PostgresGroup).To(Equal(databaseName + "-writer")) + Expect(foundUser.Status.DatabaseName).To(Equal(databaseName)) + + // Check if secret was created + foundSecret := &corev1.Secret{} + err = cl.Get(ctx, types.NamespacedName{Name: secretName + "-" + name, Namespace: namespace}, foundSecret) + Expect(err).NotTo(HaveOccurred()) + Expect(foundSecret.Data).To(HaveKey("ROLE")) + Expect(foundSecret.Data).To(HaveKey("PASSWORD")) + Expect(foundSecret.Data).To(HaveKey("POSTGRES_URL")) + }) + + It("should fail if the database does not exist", func() { + // Delete the postgres DB + Expect(cl.Delete(ctx, postgresDB)).To(Succeed()) + + // Set up a new PostgresUser with a non-existent database + nonExistentUser := &dbv1alpha1.PostgresUser{ + ObjectMeta: metav1.ObjectMeta{ + Name: "nonexistent-user", + Namespace: namespace, + }, + Spec: dbv1alpha1.PostgresUserSpec{ + Database: "nonexistent-db", + SecretName: secretName, + Role: roleName, + Privileges: "WRITE", + }, + } + Expect(cl.Create(ctx, nonExistentUser)).To(Succeed()) + + // Call Reconcile + req := reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: "nonexistent-user", + Namespace: namespace, + }, + } + _, err := rp.Reconcile(ctx, req) + Expect(err).To(HaveOccurred()) + + // Check status + foundUser := &dbv1alpha1.PostgresUser{} + err = cl.Get(ctx, types.NamespacedName{Name: "nonexistent-user", Namespace: namespace}, foundUser) + Expect(err).NotTo(HaveOccurred()) + Expect(foundUser.Status.Succeeded).To(BeFalse()) + }) + }) + + Context("Instance filter", func() { + BeforeEach(func() { + // Set up annotated resources + postgresDBWithAnnotation := postgresDB.DeepCopy() + postgresDBWithAnnotation.Annotations = map[string]string{ + utils.INSTANCE_ANNOTATION: "my-instance", + } + + postgresUserWithAnnotation := postgresUser.DeepCopy() + postgresUserWithAnnotation.Annotations = map[string]string{ + utils.INSTANCE_ANNOTATION: "my-instance", + } + + initClient(postgresDBWithAnnotation, postgresUserWithAnnotation, false) + + // Set up the reconciler with instance filter + rp.instanceFilter = "my-instance" + }) + AfterEach(func() { + // Clean up any created secrets + secretList := &corev1.SecretList{} + Expect(cl.List(ctx, secretList, client.InNamespace(namespace))).To(Succeed()) + for _, secret := range secretList.Items { + Expect(cl.Delete(ctx, &secret)).To(Succeed()) + } + }) + + It("should process users with matching instance annotation", func() { + // Mock expected calls for a successful reconciliation + pg.EXPECT().GetDefaultDatabase().Return("postgres").AnyTimes() + pg.EXPECT().CreateUserRole(gomock.Any(), gomock.Any()).Return(roleName+"-mockrole", nil) + pg.EXPECT().GrantRole(gomock.Any(), gomock.Any()).Return(nil) + pg.EXPECT().AlterDefaultLoginRole(gomock.Any(), gomock.Any()).Return(nil) + + // Call Reconcile + err := runReconcile(rp, ctx, req) + Expect(err).NotTo(HaveOccurred()) + }) + + It("should not process users with non-matching instance annotation", func() { + // Create a user with different annotation + userWithDifferentAnnotation := postgresUser.DeepCopy() + userWithDifferentAnnotation.Name = "different-annotation-user" + userWithDifferentAnnotation.Annotations = map[string]string{ + utils.INSTANCE_ANNOTATION: "different-instance", + } + Expect(cl.Create(ctx, userWithDifferentAnnotation)).To(Succeed()) + + // Call Reconcile with the different user + reqDifferent := reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: "different-annotation-user", + Namespace: namespace, + }, + } + err := runReconcile(rp, ctx, reqDifferent) + Expect(err).NotTo(HaveOccurred()) + + // Verify that the user wasn't processed (status.PostgresRole should be empty) + foundUser := &dbv1alpha1.PostgresUser{} + err = cl.Get(ctx, types.NamespacedName{Name: "different-annotation-user", Namespace: namespace}, foundUser) + Expect(err).NotTo(HaveOccurred()) + Expect(foundUser.Status.PostgresRole).To(Equal("")) + }) + }) + + Context("Secret template", func() { + BeforeEach(func() { + userWithTemplate := postgresUser.DeepCopy() + userWithTemplate.Spec.SecretTemplate = map[string]string{ + "CUSTOM_KEY": "User: {{.Role}}, DB: {{.Database}}", + "PGPASSWORD": "{{.Password}}", + } + + initClient(postgresDB, userWithTemplate, false) + }) + AfterEach(func() { + // Clean up any created secrets + secretList := &corev1.SecretList{} + Expect(cl.List(ctx, secretList, client.InNamespace(namespace))).To(Succeed()) + for _, secret := range secretList.Items { + Expect(cl.Delete(ctx, &secret)).To(Succeed()) + } + }) + + It("should render templates in the secret", func() { + // Mock expected calls + pg.EXPECT().GetDefaultDatabase().Return("postgres").AnyTimes() + pg.EXPECT().CreateUserRole(gomock.Any(), gomock.Any()).Return("app-mockedRole", nil) + pg.EXPECT().GrantRole(gomock.Any(), gomock.Any()).Return(nil) + pg.EXPECT().AlterDefaultLoginRole(gomock.Any(), gomock.Any()).Return(nil) + + // Call Reconcile + err := runReconcile(rp, ctx, req) + Expect(err).NotTo(HaveOccurred()) + + // Let's update the user status manually to mark it as succeeded + // This should trigger creation of the secret with templates in our second reconcile + foundUser := &dbv1alpha1.PostgresUser{} + err = cl.Get(ctx, types.NamespacedName{Name: name, Namespace: namespace}, foundUser) + Expect(err).NotTo(HaveOccurred()) + + // Set the status to succeeded + foundUser.Status.Succeeded = true + err = cl.Status().Update(ctx, foundUser) + Expect(err).NotTo(HaveOccurred()) + + // Run another reconcile which should update the secret with the correct templates + err = runReconcile(rp, ctx, req) + Expect(err).NotTo(HaveOccurred()) + + // Now check if the secret was created with the templated values + foundSecret := &corev1.Secret{} + name := fmt.Sprintf("%s-%s", secretName, name) + GinkgoWriter.Printf("Getting secret %s\n", name) + err = cl.Get(ctx, types.NamespacedName{Name: name, Namespace: namespace}, foundSecret) + Expect(err).NotTo(HaveOccurred()) + + // Get the role from the actual secret (since it might differ from what we expect) + actualRole := string(foundSecret.Data["ROLE"]) + GinkgoWriter.Printf("Actual role: %s\n", actualRole) + + // Check if POSTGRES_URL contains the actual role from the secret + pgUrl := string(foundSecret.Data["POSTGRES_URL"]) + Expect(pgUrl).To(ContainSubstring(actualRole)) + + // Check if the template was applied using the data in the actual secret + // Directly check the custom keys we're expecting + Expect(foundSecret.Data).To(HaveKey("CUSTOM_KEY")) + customKey := string(foundSecret.Data["CUSTOM_KEY"]) + Expect(customKey).To(ContainSubstring("User: " + actualRole)) + Expect(customKey).To(ContainSubstring("DB: " + databaseName)) + + // Check PGPASSWORD is present (should be generated from template) + Expect(foundSecret.Data).To(HaveKey("PGPASSWORD")) + pgPassword := string(foundSecret.Data["PGPASSWORD"]) + Expect(pgPassword).NotTo(BeEmpty()) + }) + }) + }) + Context("Secret creation with user-defined labels and annotations", func() { + It("should create a secret with user-defined labels and annotations", func() { + // Set up the reconciler with host and keepSecretName setting + rp.pgHost = "localhost" + rp.keepSecretName = false + + // Create a PostgresUser with custom labels and annotations + cr := &dbv1alpha1.PostgresUser{ + ObjectMeta: metav1.ObjectMeta{ + Name: "myuser", + Namespace: "myns", + }, + Spec: dbv1alpha1.PostgresUserSpec{ + SecretName: "mysecret", + Labels: map[string]string{ + "custom": "label", + "foo": "bar", + }, + }, + Status: dbv1alpha1.PostgresUserStatus{ + DatabaseName: "somedb", + }, + } + + // Call newSecretForCR with test values + secret, err := rp.newSecretForCR(cr, "role1", "pass1", "login1") + + // Verify results + Expect(err).NotTo(HaveOccurred()) + + // Check labels + expectedLabels := map[string]string{ + "app": "myuser", + "custom": "label", + "foo": "bar", + } + Expect(secret.Labels).To(Equal(expectedLabels)) + + // Check name and namespace + Expect(secret.Name).To(Equal("mysecret-myuser")) + Expect(secret.Namespace).To(Equal("myns")) + + // Check secret data + Expect(string(secret.Data["ROLE"])).To(Equal("role1")) + Expect(string(secret.Data["PASSWORD"])).To(Equal("pass1")) + Expect(string(secret.Data["LOGIN"])).To(Equal("login1")) + Expect(string(secret.Data["DATABASE_NAME"])).To(Equal("somedb")) + Expect(string(secret.Data["HOST"])).To(Equal("localhost")) + }) + + It("should handle empty labels map correctly", func() { + // Set up the reconciler + rp.pgHost = "localhost" + rp.keepSecretName = false + + // Create a PostgresUser with empty labels + cr := &dbv1alpha1.PostgresUser{ + ObjectMeta: metav1.ObjectMeta{ + Name: "myuser2", + Namespace: "myns2", + }, + Spec: dbv1alpha1.PostgresUserSpec{ + SecretName: "mysecret2", + Labels: map[string]string{}, + }, + Status: dbv1alpha1.PostgresUserStatus{ + DatabaseName: "somedb2", + }, + } + + // Call newSecretForCR + secret, err := rp.newSecretForCR(cr, "role2", "pass2", "login2") + + // Verify results + Expect(err).NotTo(HaveOccurred()) + + // Check that default labels are applied + expectedLabels := map[string]string{ + "app": "myuser2", + } + Expect(secret.Labels).To(Equal(expectedLabels)) + + // Check name and namespace + Expect(secret.Name).To(Equal("mysecret2-myuser2")) + Expect(secret.Namespace).To(Equal("myns2")) + }) + + It("should respect keepSecretName setting when true", func() { + // Set up the reconciler with keepSecretName=true + rp.pgHost = "localhost" + rp.keepSecretName = true + + // Create a PostgresUser + cr := &dbv1alpha1.PostgresUser{ + ObjectMeta: metav1.ObjectMeta{ + Name: "myuser3", + Namespace: "myns3", + }, + Spec: dbv1alpha1.PostgresUserSpec{ + SecretName: "mysecret3", + Labels: map[string]string{}, + }, + Status: dbv1alpha1.PostgresUserStatus{ + DatabaseName: "somedb3", + }, + } + + // Call newSecretForCR + secret, err := rp.newSecretForCR(cr, "role3", "pass3", "login3") + + // Verify results + Expect(err).NotTo(HaveOccurred()) + + // Check that the original secret name is kept without appending the CR name + Expect(secret.Name).To(Equal("mysecret3")) + }) + }) +}) diff --git a/internal/controller/suite_test.go b/internal/controller/suite_test.go new file mode 100644 index 000000000..65cd2ae95 --- /dev/null +++ b/internal/controller/suite_test.go @@ -0,0 +1,130 @@ +package controller + +import ( + "context" + "fmt" + "os" + "path/filepath" + "runtime" + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/controller-runtime/pkg/envtest" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + "sigs.k8s.io/controller-runtime/pkg/manager" + + "github.com/movetokube/postgres-operator/api/v1alpha1" + dbv1alpha1 "github.com/movetokube/postgres-operator/api/v1alpha1" + // +kubebuilder:scaffold:imports +) + +// These tests use Ginkgo (BDD-style Go testing framework). Refer to +// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. + +var cfg *rest.Config +var k8sClient client.Client +var managerClient client.Client +var testEnv *envtest.Environment +var ctx context.Context +var cancel context.CancelFunc + +var k8sManager manager.Manager +var realClient bool + +func TestControllers(t *testing.T) { + RegisterFailHandler(Fail) + + RunSpecs(t, "Controller Suite") +} + +func clearPgs(namespace string) (err error) { + l := dbv1alpha1.PostgresList{} + err = k8sClient.List(ctx, &l, client.InNamespace(namespace)) + Expect(err).ToNot(HaveOccurred()) + for _, el := range l.Items { + org := el.DeepCopy() + el.SetFinalizers(nil) + err = k8sClient.Patch(ctx, &el, client.MergeFrom(org)) + if err != nil { + return + } + } + return k8sClient.DeleteAllOf(ctx, &dbv1alpha1.Postgres{}, client.InNamespace(namespace)) +} + +var _ = BeforeSuite(func() { + logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) + + ctx, cancel = context.WithCancel(context.TODO()) + + By("bootstrapping test environment") + _, realClient = os.LookupEnv("ENVTEST_K8S_VERSION") + var err error + if realClient { + testEnv = &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")}, + ErrorIfCRDPathMissing: true, + + // The BinaryAssetsDirectory is only required if you want to run the tests directly + // without call the makefile target test. If not informed it will look for the + // default path defined in controller-runtime which is /usr/local/kubebuilder/. + // Note that you must have the required binaries setup under the bin directory to perform + // the tests directly. When we run make test it will be setup and used automatically. + BinaryAssetsDirectory: filepath.Join("..", "..", "bin", "k8s", + fmt.Sprintf("1.31.0-%s-%s", runtime.GOOS, runtime.GOARCH)), + } + + var err error + // cfg is defined in this file globally. + cfg, err = testEnv.Start() + Expect(err).NotTo(HaveOccurred()) + Expect(cfg).NotTo(BeNil()) + } + + err = dbv1alpha1.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + + // +kubebuilder:scaffold:scheme + + if realClient { + k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{ + Scheme: scheme.Scheme, + }) + Expect(err).NotTo(HaveOccurred()) + go func() { + defer GinkgoRecover() + err = k8sManager.Start(ctx) + Expect(err).ToNot(HaveOccurred(), "failed to run manager") + }() + managerClient = k8sManager.GetClient() + k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) + Expect(k8sClient).NotTo(BeNil()) + } else { + k8sClient = fake.NewClientBuilder().WithScheme(scheme.Scheme).WithStatusSubresource(&v1alpha1.Postgres{}, &v1alpha1.PostgresUser{}).Build() + managerClient = k8sClient + } + Expect(k8sClient.Create(ctx, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{ + Name: "operator", + }})).NotTo(HaveOccurred()) + +}) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + cancel() + if testEnv != nil { + err := testEnv.Stop() + Expect(err).NotTo(HaveOccurred()) + } +}) diff --git a/pkg/apis/addtoscheme_db_v1alpha1.go b/pkg/apis/addtoscheme_db_v1alpha1.go deleted file mode 100644 index 98321b429..000000000 --- a/pkg/apis/addtoscheme_db_v1alpha1.go +++ /dev/null @@ -1,10 +0,0 @@ -package apis - -import ( - "github.com/movetokube/postgres-operator/pkg/apis/db/v1alpha1" -) - -func init() { - // Register the types with the Scheme so the components can map objects to GroupVersionKinds and back - AddToSchemes = append(AddToSchemes, v1alpha1.SchemeBuilder.AddToScheme) -} diff --git a/pkg/apis/apis.go b/pkg/apis/apis.go deleted file mode 100644 index 07dc96164..000000000 --- a/pkg/apis/apis.go +++ /dev/null @@ -1,13 +0,0 @@ -package apis - -import ( - "k8s.io/apimachinery/pkg/runtime" -) - -// AddToSchemes may be used to add all resources defined in the project to a Scheme -var AddToSchemes runtime.SchemeBuilder - -// AddToScheme adds all Resources to the Scheme -func AddToScheme(s *runtime.Scheme) error { - return AddToSchemes.AddToScheme(s) -} diff --git a/pkg/apis/db/v1alpha1/doc.go b/pkg/apis/db/v1alpha1/doc.go deleted file mode 100644 index 44d90a35d..000000000 --- a/pkg/apis/db/v1alpha1/doc.go +++ /dev/null @@ -1,4 +0,0 @@ -// Package v1alpha1 contains API Schema definitions for the db v1alpha1 API group -// +k8s:deepcopy-gen=package,register -// +groupName=db.movetokube.com -package v1alpha1 diff --git a/pkg/apis/db/v1alpha1/register.go b/pkg/apis/db/v1alpha1/register.go deleted file mode 100644 index 280299b77..000000000 --- a/pkg/apis/db/v1alpha1/register.go +++ /dev/null @@ -1,19 +0,0 @@ -// NOTE: Boilerplate only. Ignore this file. - -// Package v1alpha1 contains API Schema definitions for the db v1alpha1 API group -// +k8s:deepcopy-gen=package,register -// +groupName=db.movetokube.com -package v1alpha1 - -import ( - "k8s.io/apimachinery/pkg/runtime/schema" - "sigs.k8s.io/controller-runtime/pkg/runtime/scheme" -) - -var ( - // SchemeGroupVersion is group version used to register these objects - SchemeGroupVersion = schema.GroupVersion{Group: "db.movetokube.com", Version: "v1alpha1"} - - // SchemeBuilder is used to add go types to the GroupVersionKind scheme - SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} -) diff --git a/pkg/apis/db/v1alpha1/zz_generated.openapi.go b/pkg/apis/db/v1alpha1/zz_generated.openapi.go deleted file mode 100644 index a6a20527c..000000000 --- a/pkg/apis/db/v1alpha1/zz_generated.openapi.go +++ /dev/null @@ -1,355 +0,0 @@ -// +build !ignore_autogenerated - -// This file was autogenerated by openapi-gen. Do not edit it manually! - -package v1alpha1 - -import ( - spec "github.com/go-openapi/spec" - common "k8s.io/kube-openapi/pkg/common" -) - -func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { - return map[string]common.OpenAPIDefinition{ - "./pkg/apis/db/v1alpha1.Postgres": schema_pkg_apis_db_v1alpha1_Postgres(ref), - "./pkg/apis/db/v1alpha1.PostgresRoles": schema_pkg_apis_db_v1alpha1_PostgresRoles(ref), - "./pkg/apis/db/v1alpha1.PostgresSpec": schema_pkg_apis_db_v1alpha1_PostgresSpec(ref), - "./pkg/apis/db/v1alpha1.PostgresStatus": schema_pkg_apis_db_v1alpha1_PostgresStatus(ref), - "./pkg/apis/db/v1alpha1.PostgresUser": schema_pkg_apis_db_v1alpha1_PostgresUser(ref), - "./pkg/apis/db/v1alpha1.PostgresUserSpec": schema_pkg_apis_db_v1alpha1_PostgresUserSpec(ref), - "./pkg/apis/db/v1alpha1.PostgresUserStatus": schema_pkg_apis_db_v1alpha1_PostgresUserStatus(ref), - } -} - -func schema_pkg_apis_db_v1alpha1_Postgres(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Postgres is the Schema for the postgres API", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), - }, - }, - "spec": { - SchemaProps: spec.SchemaProps{ - Ref: ref("./pkg/apis/db/v1alpha1.PostgresSpec"), - }, - }, - "status": { - SchemaProps: spec.SchemaProps{ - Ref: ref("./pkg/apis/db/v1alpha1.PostgresStatus"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "./pkg/apis/db/v1alpha1.PostgresSpec", "./pkg/apis/db/v1alpha1.PostgresStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, - } -} - -func schema_pkg_apis_db_v1alpha1_PostgresRoles(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "PostgresRoles stores the different group roles for database", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "owner": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "reader": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "writer": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"owner", "reader", "writer"}, - }, - }, - } -} - -func schema_pkg_apis_db_v1alpha1_PostgresSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "PostgresSpec defines the desired state of Postgres", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "database": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "masterRole": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "dropOnDelete": { - SchemaProps: spec.SchemaProps{ - Type: []string{"boolean"}, - Format: "", - }, - }, - "schemas": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-list-type": "set", - }, - }, - SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "extensions": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-list-type": "set", - }, - }, - SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - }, - Required: []string{"database"}, - }, - }, - } -} - -func schema_pkg_apis_db_v1alpha1_PostgresStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "PostgresStatus defines the observed state of Postgres", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "succeeded": { - SchemaProps: spec.SchemaProps{ - Type: []string{"boolean"}, - Format: "", - }, - }, - "roles": { - SchemaProps: spec.SchemaProps{ - Ref: ref("./pkg/apis/db/v1alpha1.PostgresRoles"), - }, - }, - "schemas": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-list-type": "set", - }, - }, - SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "extensions": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-list-type": "set", - }, - }, - SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - }, - Required: []string{"succeeded", "roles"}, - }, - }, - Dependencies: []string{ - "./pkg/apis/db/v1alpha1.PostgresRoles"}, - } -} - -func schema_pkg_apis_db_v1alpha1_PostgresUser(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "PostgresUser is the Schema for the postgresusers API", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), - }, - }, - "spec": { - SchemaProps: spec.SchemaProps{ - Ref: ref("./pkg/apis/db/v1alpha1.PostgresUserSpec"), - }, - }, - "status": { - SchemaProps: spec.SchemaProps{ - Ref: ref("./pkg/apis/db/v1alpha1.PostgresUserStatus"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "./pkg/apis/db/v1alpha1.PostgresUserSpec", "./pkg/apis/db/v1alpha1.PostgresUserStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, - } -} - -func schema_pkg_apis_db_v1alpha1_PostgresUserSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "PostgresUserSpec defines the desired state of PostgresUser", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "role": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "database": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "secretName": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "privileges": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"role", "database", "secretName"}, - }, - }, - } -} - -func schema_pkg_apis_db_v1alpha1_PostgresUserStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "PostgresUserStatus defines the observed state of PostgresUser", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "succeeded": { - SchemaProps: spec.SchemaProps{ - Type: []string{"boolean"}, - Format: "", - }, - }, - "postgresRole": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "postgresLogin": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "postgresGroup": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "databaseName": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"succeeded", "postgresRole", "postgresLogin", "postgresGroup", "databaseName"}, - }, - }, - } -} diff --git a/pkg/config/config.go b/pkg/config/config.go index e3cb922c1..5bd44a45d 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -8,7 +8,7 @@ import ( "github.com/movetokube/postgres-operator/pkg/utils" ) -type cfg struct { +type Cfg struct { PostgresHost string PostgresUser string PostgresPass string @@ -20,11 +20,11 @@ type cfg struct { } var doOnce sync.Once -var config *cfg +var config *Cfg -func Get() *cfg { +func Get() *Cfg { doOnce.Do(func() { - config = &cfg{} + config = &Cfg{} config.PostgresHost = utils.MustGetEnv("POSTGRES_HOST") config.PostgresUser = url.PathEscape(utils.MustGetEnv("POSTGRES_USER")) config.PostgresPass = url.PathEscape(utils.MustGetEnv("POSTGRES_PASS")) diff --git a/pkg/controller/add_postgres.go b/pkg/controller/add_postgres.go deleted file mode 100644 index 585d1fb44..000000000 --- a/pkg/controller/add_postgres.go +++ /dev/null @@ -1,10 +0,0 @@ -package controller - -import ( - "github.com/movetokube/postgres-operator/pkg/controller/postgres" -) - -func init() { - // AddToManagerFuncs is a list of functions to create controllers and add them to a manager. - AddToManagerFuncs = append(AddToManagerFuncs, postgres.Add) -} diff --git a/pkg/controller/add_postgresuser.go b/pkg/controller/add_postgresuser.go deleted file mode 100644 index 63b40b7f4..000000000 --- a/pkg/controller/add_postgresuser.go +++ /dev/null @@ -1,10 +0,0 @@ -package controller - -import ( - "github.com/movetokube/postgres-operator/pkg/controller/postgresuser" -) - -func init() { - // AddToManagerFuncs is a list of functions to create controllers and add them to a manager. - AddToManagerFuncs = append(AddToManagerFuncs, postgresuser.Add) -} diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go deleted file mode 100644 index 7c069f3ee..000000000 --- a/pkg/controller/controller.go +++ /dev/null @@ -1,18 +0,0 @@ -package controller - -import ( - "sigs.k8s.io/controller-runtime/pkg/manager" -) - -// AddToManagerFuncs is a list of functions to add all Controllers to the Manager -var AddToManagerFuncs []func(manager.Manager) error - -// AddToManager adds all Controllers to the Manager -func AddToManager(m manager.Manager) error { - for _, f := range AddToManagerFuncs { - if err := f(m); err != nil { - return err - } - } - return nil -} diff --git a/pkg/controller/postgres/postgres_controller_suite_test.go b/pkg/controller/postgres/postgres_controller_suite_test.go deleted file mode 100644 index ba10a7fb8..000000000 --- a/pkg/controller/postgres/postgres_controller_suite_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package postgres - -import ( - "testing" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -func TestPostgres(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "ReconcilePostgres Suite") -} diff --git a/pkg/controller/postgresuser/postgresuser_controller.go b/pkg/controller/postgresuser/postgresuser_controller.go deleted file mode 100644 index 3759c5a5a..000000000 --- a/pkg/controller/postgresuser/postgresuser_controller.go +++ /dev/null @@ -1,411 +0,0 @@ -package postgresuser - -import ( - "bytes" - "context" - goerr "errors" - "fmt" - "text/template" - - "github.com/movetokube/postgres-operator/pkg/config" - - "github.com/go-logr/logr" - dbv1alpha1 "github.com/movetokube/postgres-operator/pkg/apis/db/v1alpha1" - "github.com/movetokube/postgres-operator/pkg/postgres" - "github.com/movetokube/postgres-operator/pkg/utils" - - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - "sigs.k8s.io/controller-runtime/pkg/handler" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - logf "sigs.k8s.io/controller-runtime/pkg/runtime/log" - "sigs.k8s.io/controller-runtime/pkg/source" -) - -var log = logf.Log.WithName("controller_postgresuser") - -/** -* USER ACTION REQUIRED: This is a scaffold file intended for the user to modify with their own Controller -* business logic. Delete these comments after modifying this file.* - */ - -// Add creates a new PostgresUser Controller and adds it to the Manager. The Manager will set fields on the Controller -// and Start it when the Manager is Started. -func Add(mgr manager.Manager) error { - return add(mgr, newReconciler(mgr)) -} - -// newReconciler returns a new reconcile.Reconciler -func newReconciler(mgr manager.Manager) reconcile.Reconciler { - c := config.Get() - pg, err := postgres.NewPG(c.PostgresHost, c.PostgresUser, c.PostgresPass, c.PostgresUriArgs, c.PostgresDefaultDb, c.CloudProvider, log.WithName("postgresuser")) - if err != nil { - return nil - } - - return &ReconcilePostgresUser{ - client: mgr.GetClient(), - scheme: mgr.GetScheme(), - pg: pg, - pgHost: c.PostgresHost, - instanceFilter: c.AnnotationFilter, - keepSecretName: c.KeepSecretName, - } -} - -// add adds a new Controller to mgr with r as the reconcile.Reconciler -func add(mgr manager.Manager, r reconcile.Reconciler) error { - if r == nil { - return errors.NewInternalError(goerr.New("failed to get reconciler")) - } - // Create a new controller - c, err := controller.New("postgresuser-controller", mgr, controller.Options{Reconciler: r}) - if err != nil { - return err - } - - // Watch for changes to primary resource PostgresUser - err = c.Watch(&source.Kind{Type: &dbv1alpha1.PostgresUser{}}, &handler.EnqueueRequestForObject{}) - if err != nil { - return err - } - - // Watch for changes to the generated secret - err = c.Watch(&source.Kind{Type: &corev1.Secret{}}, &handler.EnqueueRequestForOwner{ - IsController: true, - OwnerType: &dbv1alpha1.PostgresUser{}, - }) - if err != nil { - return err - } - - return nil -} - -// blank assignment to verify that ReconcilePostgresUser implements reconcile.Reconciler -var _ reconcile.Reconciler = &ReconcilePostgresUser{} - -// ReconcilePostgresUser reconciles a PostgresUser object -type ReconcilePostgresUser struct { - // This client, initialized using mgr.Client() above, is a split client - // that reads objects from the cache and writes to the apiserver - client client.Client - scheme *runtime.Scheme - pg postgres.PG - pgHost string - instanceFilter string - keepSecretName bool // use secret name as defined in PostgresUserSpec -} - -// The Controller will requeue the Request to be processed again if the returned error is non-nil or -// Result.Requeue is true, otherwise upon completion it will remove the work from the queue. -func (r *ReconcilePostgresUser) Reconcile(request reconcile.Request) (reconcile.Result, error) { - reqLogger := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name) - reqLogger.Info("Reconciling PostgresUser") - - // Fetch the PostgresUser instance - instance := &dbv1alpha1.PostgresUser{} - err := r.client.Get(context.TODO(), request.NamespacedName, instance) - if err != nil { - if errors.IsNotFound(err) { - // Request object not found, could have been deleted after reconcile request. - // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers. - // Return and don't requeue - return reconcile.Result{}, nil - } - // Error reading the object - requeue the request. - return reconcile.Result{}, err - } - - if !utils.MatchesInstanceAnnotation(instance.Annotations, r.instanceFilter) { - return reconcile.Result{}, nil - } - - // Deletion logic - if instance.GetDeletionTimestamp() != nil { - if instance.Status.Succeeded && instance.Status.PostgresRole != "" { - // Initialize database name for connection with default database - // in case postgres cr isn't here anymore - db := r.pg.GetDefaultDatabase() - // Search Postgres CR - postgres, err := r.getPostgresCR(instance) - // Check if error exists and not a not found error - if err != nil && !errors.IsNotFound(err) { - return reconcile.Result{}, err - } - // Check if postgres cr is found and not in deletion state - if postgres != nil && !postgres.GetDeletionTimestamp().IsZero() { - db = instance.Status.DatabaseName - } - err = r.pg.DropRole(instance.Status.PostgresRole, instance.Status.PostgresGroup, - db, reqLogger) - if err != nil { - return reconcile.Result{}, err - } - } - instance.SetFinalizers(nil) - - // Update CR - err = r.client.Update(context.TODO(), instance) - if err != nil { - return reconcile.Result{}, err - } - return reconcile.Result{}, nil - } - - // Creation logic - var role, login string - password, err := utils.GetSecureRandomString(15) - - if err != nil { - return r.requeue(instance, err) - } - - if instance.Status.PostgresRole == "" { - // We need to get the Postgres CR to get the group role name - database, err := r.getPostgresCR(instance) - if err != nil { - return r.requeue(instance, errors.NewInternalError(err)) - } - // Create user role - suffix := utils.GetRandomString(6) - role = fmt.Sprintf("%s-%s", instance.Spec.Role, suffix) - login, err = r.pg.CreateUserRole(role, password) - if err != nil { - return r.requeue(instance, errors.NewInternalError(err)) - } - - // Grant group role to user role - var groupRole string - switch instance.Spec.Privileges { - case "READ": - groupRole = database.Status.Roles.Reader - case "WRITE": - groupRole = database.Status.Roles.Writer - default: - groupRole = database.Status.Roles.Owner - } - - err = r.pg.GrantRole(groupRole, role) - if err != nil { - return r.requeue(instance, errors.NewInternalError(err)) - } - - // Alter default set role to group role - // This is so that objects created by user gets owned by group role - err = r.pg.AlterDefaultLoginRole(role, groupRole) - if err != nil { - return r.requeue(instance, errors.NewInternalError(err)) - } - - instance.Status.PostgresRole = role - instance.Status.PostgresGroup = groupRole - instance.Status.PostgresLogin = login - instance.Status.DatabaseName = database.Spec.Database - err = r.client.Status().Update(context.TODO(), instance) - if err != nil { - return r.requeue(instance, err) - } - } else { - role = instance.Status.PostgresRole - login = instance.Status.PostgresLogin - } - - err = r.addFinalizer(reqLogger, instance) - if err != nil { - return r.requeue(instance, err) - } - err = r.addOwnerRef(reqLogger, instance) - if err != nil { - return r.requeue(instance, err) - } - - secret, err := r.newSecretForCR(instance, role, password, login) - if err != nil { - return r.requeue(instance, err) - } - - // Set PostgresUser instance as the owner and controller - if err := controllerutil.SetControllerReference(instance, secret, r.scheme); err != nil { - return r.requeue(instance, err) - } - - // Check if this Secret already exists - found := &corev1.Secret{} - err = r.client.Get(context.TODO(), types.NamespacedName{Name: secret.Name, Namespace: secret.Namespace}, found) - if err != nil && errors.IsNotFound(err) { - // if role is already created, update password - if instance.Status.Succeeded { - err := r.pg.UpdatePassword(role, password) - if err != nil { - return r.requeue(instance, err) - } - } - reqLogger.Info("Creating secret", "Secret.Namespace", secret.Namespace, "Secret.Name", secret.Name) - err = r.client.Create(context.TODO(), secret) - if err != nil { - return reconcile.Result{}, err - } - - // Secret created successfully - don't requeue - return r.finish(instance) - } else if err != nil { - return r.requeue(instance, err) - } - - reqLogger.Info("reconciler done", "CR.Namespace", instance.Namespace, "CR.Name", instance.Name) - return reconcile.Result{}, nil -} - -func (r *ReconcilePostgresUser) addFinalizer(reqLogger logr.Logger, m *dbv1alpha1.PostgresUser) error { - if len(m.GetFinalizers()) < 1 && m.GetDeletionTimestamp() == nil { - reqLogger.Info("adding Finalizer for Postgres") - m.SetFinalizers([]string{"finalizer.db.movetokube.com"}) - - // Update CR - err := r.client.Update(context.TODO(), m) - if err != nil { - reqLogger.Error(err, "failed to update PosgresUser with finalizer") - return err - } - } - return nil -} - -func (r *ReconcilePostgresUser) newSecretForCR(cr *dbv1alpha1.PostgresUser, role, password, login string) (*corev1.Secret, error) { - pgUserUrl := fmt.Sprintf("postgresql://%s:%s@%s/%s", role, password, r.pgHost, cr.Status.DatabaseName) - pgJDBCUrl := fmt.Sprintf("jdbc:postgresql://%s/%s", r.pgHost, cr.Status.DatabaseName) - pgDotnetUrl := fmt.Sprintf("User ID=%s;Password=%s;Host=%s;Port=5432;Database=%s;", role, password, r.pgHost, cr.Status.DatabaseName) - labels := map[string]string{ - "app": cr.Name, - } - // Merge in user-defined secret labels - for k, v := range cr.Spec.Labels { - labels[k] = v - } - annotations := cr.Spec.Annotations - name := fmt.Sprintf("%s-%s", cr.Spec.SecretName, cr.Name) - if r.keepSecretName { - name = cr.Spec.SecretName - } - - templateData, err := renderTemplate(cr.Spec.SecretTemplate, templateContext{ - Role: role, - Host: r.pgHost, - Database: cr.Status.DatabaseName, - Password: password, - }) - if err != nil { - return nil, fmt.Errorf("render templated keys: %w", err) - } - - data := map[string][]byte{ - "POSTGRES_URL": []byte(pgUserUrl), - "POSTGRES_JDBC_URL": []byte(pgJDBCUrl), - "POSTGRES_DOTNET_URL": []byte(pgDotnetUrl), - "HOST": []byte(r.pgHost), - "DATABASE_NAME": []byte(cr.Status.DatabaseName), - "ROLE": []byte(role), - "PASSWORD": []byte(password), - "LOGIN": []byte(login), - } - // templates may override standard keys - for k, v := range templateData { - data[k] = v - } - - return &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: cr.Namespace, - Labels: labels, - Annotations: annotations, - }, - Data: data, - }, nil -} - -func (r *ReconcilePostgresUser) requeue(cr *dbv1alpha1.PostgresUser, reason error) (reconcile.Result, error) { - cr.Status.Succeeded = false - err := r.client.Status().Update(context.TODO(), cr) - if err != nil { - return reconcile.Result{}, err - } - return reconcile.Result{}, reason -} - -func (r *ReconcilePostgresUser) finish(cr *dbv1alpha1.PostgresUser) (reconcile.Result, error) { - cr.Status.Succeeded = true - err := r.client.Status().Update(context.TODO(), cr) - if err != nil { - return reconcile.Result{}, err - } - return reconcile.Result{}, nil -} - -func (r *ReconcilePostgresUser) getPostgresCR(instance *dbv1alpha1.PostgresUser) (*dbv1alpha1.Postgres, error) { - database := dbv1alpha1.Postgres{} - err := r.client.Get(context.TODO(), - types.NamespacedName{Namespace: instance.Namespace, Name: instance.Spec.Database}, &database) - if err != nil { - return nil, err - } - if !utils.MatchesInstanceAnnotation(database.Annotations, r.instanceFilter) { - err = fmt.Errorf("database \"%s\" is not managed by this operator", database.Name) - return nil, err - } - if !database.Status.Succeeded { - err = fmt.Errorf("database \"%s\" is not ready", database.Name) - return nil, err - } - return &database, nil -} - -func (r *ReconcilePostgresUser) addOwnerRef(reqLogger logr.Logger, instance *dbv1alpha1.PostgresUser) error { - // Search postgres database CR - pg, err := r.getPostgresCR(instance) - if err != nil { - return err - } - // Update owners - err = controllerutil.SetControllerReference(pg, instance, r.scheme) - if err != nil { - return err - } - // Update CR - err = r.client.Update(context.TODO(), instance) - return err -} - -type templateContext struct { - Host string - Role string - Database string - Password string -} - -func renderTemplate(data map[string]string, tc templateContext) (map[string][]byte, error) { - if len(data) == 0 { - return nil, nil - } - var out = make(map[string][]byte, len(data)) - for key, templ := range data { - parsed, err := template.New("").Parse(templ) - if err != nil { - return nil, fmt.Errorf("parse template %q: %w", key, err) - } - var content bytes.Buffer - if err := parsed.Execute(&content, tc); err != nil { - return nil, fmt.Errorf("execute template %q: %w", key, err) - } - out[key] = content.Bytes() - } - return out, nil -} diff --git a/pkg/controller/postgresuser/postgresuser_controller_test.go b/pkg/controller/postgresuser/postgresuser_controller_test.go deleted file mode 100644 index b30c7314a..000000000 --- a/pkg/controller/postgresuser/postgresuser_controller_test.go +++ /dev/null @@ -1,138 +0,0 @@ -package postgresuser - -import ( - "reflect" - "testing" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - dbv1alpha1 "github.com/movetokube/postgres-operator/pkg/apis/db/v1alpha1" -) - -func TestNewSecretForCR_UserDefinedLabels(t *testing.T) { - r := &ReconcilePostgresUser{ - pgHost: "localhost", - keepSecretName: false, - } - cr := &dbv1alpha1.PostgresUser{ - ObjectMeta: metav1.ObjectMeta{ - Name: "myuser", - Namespace: "myns", - }, - Spec: dbv1alpha1.PostgresUserSpec{ - SecretName: "mysecret", - Labels: map[string]string{ - "custom": "label", - "foo": "bar", - }, - Annotations: map[string]string{ - "anno": "value", - }, - }, - Status: dbv1alpha1.PostgresUserStatus{ - DatabaseName: "somedb", - }, - } - secret, err := r.newSecretForCR(cr, "role1", "pass1", "login1") - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - expectedLabels := map[string]string{ - "app": "myuser", - "custom": "label", - "foo": "bar", - } - if !reflect.DeepEqual(secret.Labels, expectedLabels) { - t.Errorf("labels mismatch: got %v, want %v", secret.Labels, expectedLabels) - } - if secret.Annotations["anno"] != "value" { - t.Errorf("annotations mismatch: got %v", secret.Annotations) - } - expectedName := "mysecret-myuser" - if secret.Name != expectedName { - t.Errorf("secret name mismatch: got %s, want %s", secret.Name, expectedName) - } - if secret.Namespace != "myns" { - t.Errorf("secret namespace mismatch: got %s", secret.Namespace) - } - if string(secret.Data["ROLE"]) != "role1" { - t.Errorf("secret data ROLE mismatch: got %s", secret.Data["ROLE"]) - } - if string(secret.Data["PASSWORD"]) != "pass1" { - t.Errorf("secret data PASSWORD mismatch: got %s", secret.Data["PASSWORD"]) - } - if string(secret.Data["LOGIN"]) != "login1" { - t.Errorf("secret data LOGIN mismatch: got %s", secret.Data["LOGIN"]) - } - if string(secret.Data["DATABASE_NAME"]) != "somedb" { - t.Errorf("secret data DATABASE_NAME mismatch: got %s", secret.Data["DATABASE_NAME"]) - } - if string(secret.Data["HOST"]) != "localhost" { - t.Errorf("secret data HOST mismatch: got %s", secret.Data["HOST"]) - } -} - -func TestNewSecretForCR_EmptyLabels(t *testing.T) { - r := &ReconcilePostgresUser{ - pgHost: "localhost", - keepSecretName: false, - } - cr := &dbv1alpha1.PostgresUser{ - ObjectMeta: metav1.ObjectMeta{ - Name: "myuser2", - Namespace: "myns2", - }, - Spec: dbv1alpha1.PostgresUserSpec{ - SecretName: "mysecret2", - Labels: map[string]string{}, - }, - Status: dbv1alpha1.PostgresUserStatus{ - DatabaseName: "somedb2", - }, - } - secret, err := r.newSecretForCR(cr, "role2", "pass2", "login2") - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - expectedLabels := map[string]string{ - "app": "myuser2", - } - if !reflect.DeepEqual(secret.Labels, expectedLabels) { - t.Errorf("labels mismatch: got %v, want %v", secret.Labels, expectedLabels) - } - expectedName := "mysecret2-myuser2" - if secret.Name != expectedName { - t.Errorf("secret name mismatch: got %s, want %s", secret.Name, expectedName) - } - if secret.Namespace != "myns2" { - t.Errorf("secret namespace mismatch: got %s", secret.Namespace) - } -} - -func TestNewSecretForCR_KeepSecretName(t *testing.T) { - r := &ReconcilePostgresUser{ - pgHost: "localhost", - keepSecretName: true, - } - cr := &dbv1alpha1.PostgresUser{ - ObjectMeta: metav1.ObjectMeta{ - Name: "myuser3", - Namespace: "myns3", - }, - Spec: dbv1alpha1.PostgresUserSpec{ - SecretName: "mysecret3", - Labels: map[string]string{}, - }, - Status: dbv1alpha1.PostgresUserStatus{ - DatabaseName: "somedb3", - }, - } - secret, err := r.newSecretForCR(cr, "role3", "pass3", "login3") - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - expectedName := "mysecret3" - if secret.Name != expectedName { - t.Errorf("secret name mismatch with keepSecretName: got %s, want %s", secret.Name, expectedName) - } -} diff --git a/pkg/postgres/database.go b/pkg/postgres/database.go index db856e7f3..45e393c8e 100644 --- a/pkg/postgres/database.go +++ b/pkg/postgres/database.go @@ -17,9 +17,9 @@ const ( GRANT_CREATE_TABLE = `GRANT CREATE ON SCHEMA "%s" TO "%s"` GRANT_ALL_TABLES = `GRANT %s ON ALL TABLES IN SCHEMA "%s" TO "%s"` DEFAULT_PRIVS_SCHEMA = `ALTER DEFAULT PRIVILEGES FOR ROLE "%s" IN SCHEMA "%s" GRANT %s ON TABLES TO "%s"` - REVOKE_CONNECT = `REVOKE CONNECT ON DATABASE "%s" FROM public` - TERMINATE_BACKEND = `SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE pg_stat_activity.datname = '%s' AND pid <> pg_backend_pid()` - GET_DB_OWNER = `SELECT pg_catalog.pg_get_userbyid(d.datdba) FROM pg_catalog.pg_database d WHERE d.datname = '%s'` + REVOKE_CONNECT = `REVOKE CONNECT ON DATABASE "%s" FROM public` + TERMINATE_BACKEND = `SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE pg_stat_activity.datname = '%s' AND pid <> pg_backend_pid()` + GET_DB_OWNER = `SELECT pg_catalog.pg_get_userbyid(d.datdba) FROM pg_catalog.pg_database d WHERE d.datname = '%s'` GRANT_CREATE_SCHEMA = `GRANT CREATE ON DATABASE "%s" TO "%s"` ) @@ -125,7 +125,7 @@ func (c *pg) SetSchemaPrivileges(schemaPrivileges PostgresSchemaPrivileges, logg _, err = tmpDb.Exec(fmt.Sprintf(GRANT_CREATE_TABLE, schemaPrivileges.Schema, schemaPrivileges.Role)) if err != nil { return err - } + } } return nil diff --git a/pkg/postgres/gcp.go b/pkg/postgres/gcp.go index 9ebb1ec61..1531ffb84 100644 --- a/pkg/postgres/gcp.go +++ b/pkg/postgres/gcp.go @@ -55,10 +55,10 @@ func (c *gcppg) CreateDB(dbname, role string) error { } func (c *gcppg) DropRole(role, newOwner, database string, logger logr.Logger) error { - + tmpDb, err := GetConnection(c.user, c.pass, c.host, database, c.args, logger) q := fmt.Sprintf(GET_DB_OWNER, database) - logger.Info("Checking master role: "+ q) + logger.Info("Checking master role: " + q) rows, err := tmpDb.Query(q) if err != nil { return err @@ -68,9 +68,9 @@ func (c *gcppg) DropRole(role, newOwner, database string, logger logr.Logger) er rows.Scan(&masterRole) } - if( role != masterRole){ + if role != masterRole { q = fmt.Sprintf(DROP_ROLE, role) - logger.Info("GCP Drop Role: "+ q) + logger.Info("GCP Drop Role: " + q) _, err = tmpDb.Exec(q) // Check if error exists and if different from "ROLE NOT FOUND" => 42704 if err != nil && err.(*pq.Error).Code != "42704" { @@ -79,7 +79,7 @@ func (c *gcppg) DropRole(role, newOwner, database string, logger logr.Logger) er defer tmpDb.Close() } else { - logger.Info(fmt.Sprintf("GCP refusing DropRole on master role " + masterRole)) + logger.Info(fmt.Sprintf("GCP refusing DropRole on master role: %s", masterRole)) } return nil } diff --git a/pkg/postgres/mock/postgres.go b/pkg/postgres/mock/postgres.go index c14e541f1..4f53e59ec 100644 --- a/pkg/postgres/mock/postgres.go +++ b/pkg/postgres/mock/postgres.go @@ -1,69 +1,75 @@ // Code generated by MockGen. DO NOT EDIT. // Source: pkg/postgres/postgres.go +// +// Generated by this command: +// +// mockgen -source pkg/postgres/postgres.go +// -// Package mock is a generated GoMock package. -package mock +// Package mock_postgres is a generated GoMock package. +package mock_postgres import ( reflect "reflect" logr "github.com/go-logr/logr" - gomock "github.com/golang/mock/gomock" - "github.com/movetokube/postgres-operator/pkg/postgres" + postgres "github.com/movetokube/postgres-operator/pkg/postgres" + gomock "go.uber.org/mock/gomock" ) -// MockPG is a mock of PG interface +// MockPG is a mock of PG interface. type MockPG struct { ctrl *gomock.Controller recorder *MockPGMockRecorder + isgomock struct{} } -// MockPGMockRecorder is the mock recorder for MockPG +// MockPGMockRecorder is the mock recorder for MockPG. type MockPGMockRecorder struct { mock *MockPG } -// NewMockPG creates a new mock instance +// NewMockPG creates a new mock instance. func NewMockPG(ctrl *gomock.Controller) *MockPG { mock := &MockPG{ctrl: ctrl} mock.recorder = &MockPGMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use +// EXPECT returns an object that allows the caller to indicate expected use. func (m *MockPG) EXPECT() *MockPGMockRecorder { return m.recorder } -// CreateDB mocks base method -func (m *MockPG) CreateDB(dbname, username string) error { +// AlterDefaultLoginRole mocks base method. +func (m *MockPG) AlterDefaultLoginRole(role, setRole string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateDB", dbname, username) + ret := m.ctrl.Call(m, "AlterDefaultLoginRole", role, setRole) ret0, _ := ret[0].(error) return ret0 } -// CreateDB indicates an expected call of CreateDB -func (mr *MockPGMockRecorder) CreateDB(dbname, username interface{}) *gomock.Call { +// AlterDefaultLoginRole indicates an expected call of AlterDefaultLoginRole. +func (mr *MockPGMockRecorder) AlterDefaultLoginRole(role, setRole any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateDB", reflect.TypeOf((*MockPG)(nil).CreateDB), dbname, username) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AlterDefaultLoginRole", reflect.TypeOf((*MockPG)(nil).AlterDefaultLoginRole), role, setRole) } -// CreateSchema mocks base method -func (m *MockPG) CreateSchema(db, role, schema string, logger logr.Logger) error { +// CreateDB mocks base method. +func (m *MockPG) CreateDB(dbname, username string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateSchema", db, role, schema, logger) + ret := m.ctrl.Call(m, "CreateDB", dbname, username) ret0, _ := ret[0].(error) return ret0 } -// CreateSchema indicates an expected call of CreateSchema -func (mr *MockPGMockRecorder) CreateSchema(db, role, schema, logger interface{}) *gomock.Call { +// CreateDB indicates an expected call of CreateDB. +func (mr *MockPGMockRecorder) CreateDB(dbname, username any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSchema", reflect.TypeOf((*MockPG)(nil).CreateSchema), db, role, schema, logger) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateDB", reflect.TypeOf((*MockPG)(nil).CreateDB), dbname, username) } -// CreateExtension mocks base method +// CreateExtension mocks base method. func (m *MockPG) CreateExtension(db, extension string, logger logr.Logger) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateExtension", db, extension, logger) @@ -71,13 +77,13 @@ func (m *MockPG) CreateExtension(db, extension string, logger logr.Logger) error return ret0 } -// CreateExtension indicates an expected call of CreateExtension -func (mr *MockPGMockRecorder) CreateExtension(db, extension, logger interface{}) *gomock.Call { +// CreateExtension indicates an expected call of CreateExtension. +func (mr *MockPGMockRecorder) CreateExtension(db, extension, logger any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateExtension", reflect.TypeOf((*MockPG)(nil).CreateExtension), db, extension, logger) } -// CreateGroupRole mocks base method +// CreateGroupRole mocks base method. func (m *MockPG) CreateGroupRole(role string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateGroupRole", role) @@ -85,13 +91,27 @@ func (m *MockPG) CreateGroupRole(role string) error { return ret0 } -// CreateGroupRole indicates an expected call of CreateGroupRole -func (mr *MockPGMockRecorder) CreateGroupRole(role interface{}) *gomock.Call { +// CreateGroupRole indicates an expected call of CreateGroupRole. +func (mr *MockPGMockRecorder) CreateGroupRole(role any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateGroupRole", reflect.TypeOf((*MockPG)(nil).CreateGroupRole), role) } -// CreateUserRole mocks base method +// CreateSchema mocks base method. +func (m *MockPG) CreateSchema(db, role, schema string, logger logr.Logger) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateSchema", db, role, schema, logger) + ret0, _ := ret[0].(error) + return ret0 +} + +// CreateSchema indicates an expected call of CreateSchema. +func (mr *MockPGMockRecorder) CreateSchema(db, role, schema, logger any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSchema", reflect.TypeOf((*MockPG)(nil).CreateSchema), db, role, schema, logger) +} + +// CreateUserRole mocks base method. func (m *MockPG) CreateUserRole(role, password string) (string, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateUserRole", role, password) @@ -100,134 +120,120 @@ func (m *MockPG) CreateUserRole(role, password string) (string, error) { return ret0, ret1 } -// CreateUserRole indicates an expected call of CreateUserRole -func (mr *MockPGMockRecorder) CreateUserRole(role, password interface{}) *gomock.Call { +// CreateUserRole indicates an expected call of CreateUserRole. +func (mr *MockPGMockRecorder) CreateUserRole(role, password any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateUserRole", reflect.TypeOf((*MockPG)(nil).CreateUserRole), role, password) } -// UpdatePassword mocks base method -func (m *MockPG) UpdatePassword(role, password string) error { +// DropDatabase mocks base method. +func (m *MockPG) DropDatabase(db string, logger logr.Logger) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UpdatePassword", role, password) + ret := m.ctrl.Call(m, "DropDatabase", db, logger) ret0, _ := ret[0].(error) return ret0 } -// UpdatePassword indicates an expected call of UpdatePassword -func (mr *MockPGMockRecorder) UpdatePassword(role, password interface{}) *gomock.Call { +// DropDatabase indicates an expected call of DropDatabase. +func (mr *MockPGMockRecorder) DropDatabase(db, logger any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdatePassword", reflect.TypeOf((*MockPG)(nil).UpdatePassword), role, password) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DropDatabase", reflect.TypeOf((*MockPG)(nil).DropDatabase), db, logger) } -// GrantRole mocks base method -func (m *MockPG) GrantRole(role, grantee string) error { +// DropRole mocks base method. +func (m *MockPG) DropRole(role, newOwner, database string, logger logr.Logger) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GrantRole", role, grantee) + ret := m.ctrl.Call(m, "DropRole", role, newOwner, database, logger) ret0, _ := ret[0].(error) return ret0 } -// GrantRole indicates an expected call of GrantRole -func (mr *MockPGMockRecorder) GrantRole(role, grantee interface{}) *gomock.Call { +// DropRole indicates an expected call of DropRole. +func (mr *MockPGMockRecorder) DropRole(role, newOwner, database, logger any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GrantRole", reflect.TypeOf((*MockPG)(nil).GrantRole), role, grantee) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DropRole", reflect.TypeOf((*MockPG)(nil).DropRole), role, newOwner, database, logger) } -// SetSchemaPrivileges mocks base method -func (m *MockPG) SetSchemaPrivileges(privileges postgres.PostgresSchemaPrivileges, logger logr.Logger) error { +// GetDefaultDatabase mocks base method. +func (m *MockPG) GetDefaultDatabase() string { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SetSchemaPrivileges", privileges.DB, privileges.Creator, privileges.Role, privileges.Schema, privileges.Privs, privileges.CreateSchema, logger) - ret0, _ := ret[0].(error) + ret := m.ctrl.Call(m, "GetDefaultDatabase") + ret0, _ := ret[0].(string) return ret0 } -// SetSchemaPrivileges indicates an expected call of SetSchemaPrivileges -func (mr *MockPGMockRecorder) SetSchemaPrivileges(db, creator, role, schema, privs, createSchema, logger interface{}) *gomock.Call { +// GetDefaultDatabase indicates an expected call of GetDefaultDatabase. +func (mr *MockPGMockRecorder) GetDefaultDatabase() *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetSchemaPrivileges", reflect.TypeOf((*MockPG)(nil).SetSchemaPrivileges), db, creator, role, schema, privs, createSchema, logger) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDefaultDatabase", reflect.TypeOf((*MockPG)(nil).GetDefaultDatabase)) } -// RevokeRole mocks base method -func (m *MockPG) RevokeRole(role, revoked string) error { +// GetUser mocks base method. +func (m *MockPG) GetUser() string { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RevokeRole", role, revoked) - ret0, _ := ret[0].(error) + ret := m.ctrl.Call(m, "GetUser") + ret0, _ := ret[0].(string) return ret0 } -// RevokeRole indicates an expected call of RevokeRole -func (mr *MockPGMockRecorder) RevokeRole(role, revoked interface{}) *gomock.Call { +// GetUser indicates an expected call of GetUser. +func (mr *MockPGMockRecorder) GetUser() *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RevokeRole", reflect.TypeOf((*MockPG)(nil).RevokeRole), role, revoked) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUser", reflect.TypeOf((*MockPG)(nil).GetUser)) } -// AlterDefaultLoginRole mocks base method -func (m *MockPG) AlterDefaultLoginRole(role, setRole string) error { +// GrantRole mocks base method. +func (m *MockPG) GrantRole(role, grantee string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AlterDefaultLoginRole", role, setRole) + ret := m.ctrl.Call(m, "GrantRole", role, grantee) ret0, _ := ret[0].(error) return ret0 } -// AlterDefaultLoginRole indicates an expected call of AlterDefaultLoginRole -func (mr *MockPGMockRecorder) AlterDefaultLoginRole(role, setRole interface{}) *gomock.Call { +// GrantRole indicates an expected call of GrantRole. +func (mr *MockPGMockRecorder) GrantRole(role, grantee any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AlterDefaultLoginRole", reflect.TypeOf((*MockPG)(nil).AlterDefaultLoginRole), role, setRole) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GrantRole", reflect.TypeOf((*MockPG)(nil).GrantRole), role, grantee) } -// DropDatabase mocks base method -func (m *MockPG) DropDatabase(db string, logger logr.Logger) error { +// RevokeRole mocks base method. +func (m *MockPG) RevokeRole(role, revoked string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DropDatabase", db, logger) + ret := m.ctrl.Call(m, "RevokeRole", role, revoked) ret0, _ := ret[0].(error) return ret0 } -// DropDatabase indicates an expected call of DropDatabase -func (mr *MockPGMockRecorder) DropDatabase(db, logger interface{}) *gomock.Call { +// RevokeRole indicates an expected call of RevokeRole. +func (mr *MockPGMockRecorder) RevokeRole(role, revoked any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DropDatabase", reflect.TypeOf((*MockPG)(nil).DropDatabase), db, logger) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RevokeRole", reflect.TypeOf((*MockPG)(nil).RevokeRole), role, revoked) } -// DropRole mocks base method -func (m *MockPG) DropRole(role, newOwner, database string, logger logr.Logger) error { +// SetSchemaPrivileges mocks base method. +func (m *MockPG) SetSchemaPrivileges(schemaPrivileges postgres.PostgresSchemaPrivileges, logger logr.Logger) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DropRole", role, newOwner, database, logger) + ret := m.ctrl.Call(m, "SetSchemaPrivileges", schemaPrivileges, logger) ret0, _ := ret[0].(error) return ret0 } -// DropRole indicates an expected call of DropRole -func (mr *MockPGMockRecorder) DropRole(role, newOwner, database, logger interface{}) *gomock.Call { +// SetSchemaPrivileges indicates an expected call of SetSchemaPrivileges. +func (mr *MockPGMockRecorder) SetSchemaPrivileges(schemaPrivileges, logger any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DropRole", reflect.TypeOf((*MockPG)(nil).DropRole), role, newOwner, database, logger) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetSchemaPrivileges", reflect.TypeOf((*MockPG)(nil).SetSchemaPrivileges), schemaPrivileges, logger) } -// GetUser mocks base method -func (m *MockPG) GetUser() string { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetUser") - ret0, _ := ret[0].(string) - return ret0 -} - -// GetUser indicates an expected call of GetUser -func (mr *MockPGMockRecorder) GetUser() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUser", reflect.TypeOf((*MockPG)(nil).GetUser)) -} - -// GetDefaultDatabase mocks base method -func (m *MockPG) GetDefaultDatabase() string { +// UpdatePassword mocks base method. +func (m *MockPG) UpdatePassword(role, password string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetDefaultDatabase") - ret0, _ := ret[0].(string) + ret := m.ctrl.Call(m, "UpdatePassword", role, password) + ret0, _ := ret[0].(error) return ret0 } -// GetDefaultDatabase indicates an expected call of GetDefaultDatabase -func (mr *MockPGMockRecorder) GetDefaultDatabase() *gomock.Call { +// UpdatePassword indicates an expected call of UpdatePassword. +func (mr *MockPGMockRecorder) UpdatePassword(role, password any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDefaultDatabase", reflect.TypeOf((*MockPG)(nil).GetUser)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdatePassword", reflect.TypeOf((*MockPG)(nil).UpdatePassword), role, password) } diff --git a/pkg/postgres/postgres.go b/pkg/postgres/postgres.go index a5c66b0af..35f314bce 100644 --- a/pkg/postgres/postgres.go +++ b/pkg/postgres/postgres.go @@ -6,6 +6,7 @@ import ( "log" "github.com/go-logr/logr" + "github.com/movetokube/postgres-operator/pkg/config" ) type PG interface { @@ -44,8 +45,14 @@ type PostgresSchemaPrivileges struct { CreateSchema bool } -func NewPG(host, user, password, uri_args, default_database, cloud_type string, logger logr.Logger) (PG, error) { - db, err := GetConnection(user, password, host, default_database, uri_args, logger) +func NewPG(cfg *config.Cfg, logger logr.Logger) (PG, error) { + db, err := GetConnection( + cfg.PostgresUser, + cfg.PostgresPass, + cfg.PostgresHost, + cfg.PostgresDefaultDb, + cfg.PostgresUriArgs, + logger) if err != nil { log.Fatalf("failed to connect to PostgreSQL server: %s", err.Error()) } @@ -53,14 +60,14 @@ func NewPG(host, user, password, uri_args, default_database, cloud_type string, postgres := &pg{ db: db, log: logger, - host: host, - user: user, - pass: password, - args: uri_args, - default_database: default_database, + host: cfg.PostgresHost, + user: cfg.PostgresUser, + pass: cfg.PostgresPass, + args: cfg.PostgresUriArgs, + default_database: cfg.PostgresDefaultDb, } - switch cloud_type { + switch cfg.CloudProvider { case "AWS": logger.Info("Using AWS wrapper") return newAWSPG(postgres), nil diff --git a/pkg/utils/annotationfilter_test.go b/pkg/utils/annotationfilter_test.go index 7d0de2705..e9ff13658 100644 --- a/pkg/utils/annotationfilter_test.go +++ b/pkg/utils/annotationfilter_test.go @@ -1,67 +1,80 @@ package utils import ( - "testing" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" ) -func TestProcessAnnotationWithNoCorrectKeyAndFilterDefined(t *testing.T) { - annotations := map[string]string{ - "invalidkey": "value", - } - response := MatchesInstanceAnnotation(annotations, "value") - if response { - t.Fail() - } -} +var _ = Describe("MatchesInstanceAnnotation", func() { + Context("when filter is defined", func() { + const filter = "value" -func TestProcessAnnotationWithCorrectKeyAndFilterDefined(t *testing.T) { - annotations := map[string]string{ - INSTANCE_ANNOTATION: "value", - } - response := MatchesInstanceAnnotation(annotations, "value") - if !response { - t.Fail() - } -} + It("should return false when annotations are nil", func() { + Expect(MatchesInstanceAnnotation(nil, filter)).To(BeFalse()) + }) -func TestProcessAnnotationWithNilAnnotationsAndFilterDefined(t *testing.T) { - response := MatchesInstanceAnnotation(nil, "value") - if response { - t.Fail() - } -} + It("should return false when correct key is not present", func() { + annotations := map[string]string{ + "invalidkey": "value", + } + Expect(MatchesInstanceAnnotation(annotations, filter)).To(BeFalse()) + }) -func TestProcessAnnotationWithNilAnnotationAndNoFilterDefined(t *testing.T) { - response := MatchesInstanceAnnotation(nil, "") - if !response { - t.Fail() - } -} + It("should return true when correct key and value match", func() { + annotations := map[string]string{ + INSTANCE_ANNOTATION: "value", + } + Expect(MatchesInstanceAnnotation(annotations, filter)).To(BeTrue()) + }) + }) -func TestProcessAnnotationWithNoCorrectKeyAndNoFilterDefined(t *testing.T) { - annotations := map[string]string{ - "invalidkey": "value", - } - response := MatchesInstanceAnnotation(annotations, "") - if !response { - t.Fail() - } -} + Context("when filter is not defined", func() { + const filter = "" -func TestProcessAnnotationWithEmptyMapAndNoFilterDefined(t *testing.T) { - annotations := map[string]string{} - response := MatchesInstanceAnnotation(annotations, "") - if !response { - t.Fail() - } -} + It("should return true when annotations are nil", func() { + Expect(MatchesInstanceAnnotation(nil, filter)).To(BeTrue()) + }) -func TestProcessAnnotationWithCorrectKeyAndNoFilterDefined(t *testing.T) { - annotations := map[string]string{ - INSTANCE_ANNOTATION: "value", - } - response := MatchesInstanceAnnotation(annotations, "") - if response { - t.Fail() - } -} + It("should return true when annotations map is empty", func() { + annotations := map[string]string{} + Expect(MatchesInstanceAnnotation(annotations, filter)).To(BeTrue()) + }) + + It("should return true when correct key is not present", func() { + annotations := map[string]string{ + "invalidkey": "value", + } + Expect(MatchesInstanceAnnotation(annotations, filter)).To(BeTrue()) + }) + + It("should return false when the instance annotation key is present", func() { + annotations := map[string]string{ + INSTANCE_ANNOTATION: "value", + } + Expect(MatchesInstanceAnnotation(annotations, filter)).To(BeFalse()) + }) + }) + Context("Testing case insensitivity", func() { + It("should match values case-insensitively", func() { + annotations := map[string]string{ + INSTANCE_ANNOTATION: "VALUE", + } + Expect(MatchesInstanceAnnotation(annotations, "value")).To(BeTrue()) + + annotations = map[string]string{ + INSTANCE_ANNOTATION: "value", + } + Expect(MatchesInstanceAnnotation(annotations, "VALUE")).To(BeTrue()) + }) + }) + + Context("Testing with mixed cases", func() { + It("should match values with mixed cases", func() { + annotations := map[string]string{ + INSTANCE_ANNOTATION: "MiXeDcAsE", + } + Expect(MatchesInstanceAnnotation(annotations, "mixedcase")).To(BeTrue()) + Expect(MatchesInstanceAnnotation(annotations, "MIXEDCASE")).To(BeTrue()) + }) + }) +}) diff --git a/pkg/utils/list.go b/pkg/utils/list.go deleted file mode 100644 index ea63ed530..000000000 --- a/pkg/utils/list.go +++ /dev/null @@ -1,10 +0,0 @@ -package utils - -func ListContains(l []string, i string) bool { - for _, item := range l { - if item == i { - return true - } - } - return false -} diff --git a/pkg/utils/patch.go b/pkg/utils/patch.go deleted file mode 100644 index df1bff689..000000000 --- a/pkg/utils/patch.go +++ /dev/null @@ -1,67 +0,0 @@ -package utils - -import ( - "context" - "reflect" - - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/util/errors" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -func Patch(cl client.Client, ctx context.Context, before runtime.Object, after runtime.Object) error { - resourcePatch := client.MergeFrom(before.DeepCopyObject()) - statusPatch := client.MergeFrom(before.DeepCopyObject()) - // Convert resources to unstructured for easier comparison - beforeUnstructured, err := runtime.DefaultUnstructuredConverter.ToUnstructured(before) - if err != nil { - return err - } - afterUnstructured, err := runtime.DefaultUnstructuredConverter.ToUnstructured(after) - if err != nil { - return err - } - - beforeHasStatus := false - afterHasStatus := false - // Attempt to remove the status for easier comparison - beforeStatus, ok, err := unstructured.NestedFieldCopy(beforeUnstructured, "status") - if err != nil { - return err - } - if ok { - beforeHasStatus = true - // Remove status from object so they can patched separately - unstructured.RemoveNestedField(beforeUnstructured, "status") - } - afterStatus, ok, err := unstructured.NestedFieldCopy(afterUnstructured, "status") - if err != nil { - return err - } - if ok { - afterHasStatus = true - // Remove status from object so they can patched separately - unstructured.RemoveNestedField(afterUnstructured, "status") - } - - var errs []error - - // Check if there's any difference to patch - if !reflect.DeepEqual(beforeUnstructured, afterUnstructured) { - err = cl.Patch(ctx, after.DeepCopyObject(), resourcePatch) - if err != nil { - errs = append(errs, err) - } - } - - // Check if there's any difference in status to patch - if (beforeHasStatus || afterHasStatus) && !reflect.DeepEqual(beforeStatus, afterStatus) { - err = cl.Status().Patch(ctx, after.DeepCopyObject(), statusPatch) - if err != nil { - errs = append(errs, err) - } - } - - return errors.NewAggregate(errs) -} diff --git a/pkg/utils/patch_test.go b/pkg/utils/patch_test.go deleted file mode 100644 index e4736e447..000000000 --- a/pkg/utils/patch_test.go +++ /dev/null @@ -1,64 +0,0 @@ -package utils - -import ( - "context" - "strconv" - "testing" - - "github.com/movetokube/postgres-operator/pkg/apis/db/v1alpha1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/kubernetes/scheme" - "sigs.k8s.io/controller-runtime/pkg/client/fake" -) - -var ( - name string = "test-db" - namespace string = "operator" -) - -var postgres *v1alpha1.Postgres = &v1alpha1.Postgres{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Spec: v1alpha1.PostgresSpec{ - Database: name, - }, -} -var objs []runtime.Object = []runtime.Object{postgres} - -func TestPatchUtilShouldPatchIfThereIsDifference(t *testing.T) { - // Create modified postgres - modPostgres := postgres.DeepCopy() - modPostgres.Spec.DropOnDelete = true - modPostgres.Status.Succeeded = true - - // Create runtime scheme - s := scheme.Scheme - s.AddKnownTypes(v1alpha1.SchemeGroupVersion, &v1alpha1.Postgres{}) - - // Create fake client to mock API calls - cl := fake.NewFakeClient(objs...) - - // Patch object - err := Patch(cl, context.TODO(), postgres, modPostgres) - if err != nil { - t.Fatalf("could not patch object: (%v)", err) - } - - // Check if postgres is identical to modified object - foundPostgres := &v1alpha1.Postgres{} - err = cl.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: namespace}, foundPostgres) - if err != nil { - t.Fatalf("could not get postgres: (%v)", err) - } - // Comparison - if foundPostgres.Spec.DropOnDelete != modPostgres.Spec.DropOnDelete { - t.Fatalf("found Postgres is not identical to modified Postgres: DropOnDelete == %s, expected %s", strconv.FormatBool(foundPostgres.Spec.DropOnDelete), strconv.FormatBool(modPostgres.Spec.DropOnDelete)) - } - if foundPostgres.Status.Succeeded != modPostgres.Status.Succeeded { - t.Fatalf("found Postgres is not identical to modified Postgres: Succeeded == %s, expected %s", strconv.FormatBool(foundPostgres.Status.Succeeded), strconv.FormatBool(modPostgres.Status.Succeeded)) - } -} diff --git a/pkg/utils/random.go b/pkg/utils/random.go index 77aebbebd..9efc466d3 100644 --- a/pkg/utils/random.go +++ b/pkg/utils/random.go @@ -26,4 +26,4 @@ func GetSecureRandomString(length int) (string, error) { } return string(b), nil -} \ No newline at end of file +} diff --git a/pkg/utils/random_test.go b/pkg/utils/random_test.go new file mode 100644 index 000000000..418f454b7 --- /dev/null +++ b/pkg/utils/random_test.go @@ -0,0 +1,63 @@ +package utils + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("Random String Utils", func() { + Context("Generating random strings", func() { + const testLength = 10 + + When("using GetRandomString", func() { + It("should return a string of the specified length", func() { + result := GetRandomString(testLength) + Expect(result).To(HaveLen(testLength)) + }) + + It("should only contain valid characters", func() { + result := GetRandomString(testLength) + validChars := "^[a-zA-Z0-9]+$" + Expect(result).To(MatchRegexp(validChars)) + }) + + It("should generate different strings on multiple calls", func() { + result1 := GetRandomString(testLength) + result2 := GetRandomString(testLength) + Expect(result1).NotTo(Equal(result2)) + }) + }) + + When("using GetSecureRandomString", func() { + It("should return a string of the specified length", func() { + result, err := GetSecureRandomString(testLength) + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(HaveLen(testLength)) + }) + + It("should only contain valid characters", func() { + result, err := GetSecureRandomString(testLength) + Expect(err).NotTo(HaveOccurred()) + validChars := "^[a-zA-Z0-9]+$" + Expect(result).To(MatchRegexp(validChars)) + }) + + It("should generate different strings on multiple calls", func() { + result1, err1 := GetSecureRandomString(testLength) + result2, err2 := GetSecureRandomString(testLength) + Expect(err1).NotTo(HaveOccurred()) + Expect(err2).NotTo(HaveOccurred()) + Expect(result1).NotTo(Equal(result2)) + }) + + It("should handle generating strings of different lengths", func() { + result1, err1 := GetSecureRandomString(5) + result2, err2 := GetSecureRandomString(15) + Expect(err1).NotTo(HaveOccurred()) + Expect(err2).NotTo(HaveOccurred()) + Expect(result1).To(HaveLen(5)) + Expect(result2).To(HaveLen(15)) + }) + }) + }) +}) diff --git a/pkg/utils/suite_test.go b/pkg/utils/suite_test.go new file mode 100644 index 000000000..847bc9652 --- /dev/null +++ b/pkg/utils/suite_test.go @@ -0,0 +1,13 @@ +package utils + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestUtils(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Utils Suite") +} diff --git a/pkg/utils/template.go b/pkg/utils/template.go new file mode 100644 index 000000000..14232d94f --- /dev/null +++ b/pkg/utils/template.go @@ -0,0 +1,33 @@ +package utils + +import ( + "bytes" + "fmt" + "text/template" +) + +type TemplateContext struct { + Host string + Role string + Database string + Password string +} + +func RenderTemplate(data map[string]string, tc TemplateContext) (map[string][]byte, error) { + if len(data) == 0 { + return nil, nil + } + var out = make(map[string][]byte, len(data)) + for key, templ := range data { + parsed, err := template.New("").Parse(templ) + if err != nil { + return nil, fmt.Errorf("parse template %q: %w", key, err) + } + var content bytes.Buffer + if err := parsed.Execute(&content, tc); err != nil { + return nil, fmt.Errorf("execute template %q: %w", key, err) + } + out[key] = content.Bytes() + } + return out, nil +} diff --git a/pkg/utils/template_test.go b/pkg/utils/template_test.go new file mode 100644 index 000000000..a90ba9ec1 --- /dev/null +++ b/pkg/utils/template_test.go @@ -0,0 +1,101 @@ +package utils + +import ( + "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" +) + +var _ = ginkgo.Describe("Template Utils", func() { + ginkgo.Context("RenderTemplate function", func() { + var ( + templateContext TemplateContext + templates map[string]string + ) + + ginkgo.BeforeEach(func() { + templateContext = TemplateContext{ + Host: "localhost", + Role: "admin", + Database: "postgres", + Password: "secret", + } + }) + + ginkgo.When("provided with valid templates", func() { + ginkgo.BeforeEach(func() { + templates = map[string]string{ + "simple": "Host: {{.Host}}", + "all-fields": "Host: {{.Host}}, Role: {{.Role}}, DB: {{.Database}}, Password: {{.Password}}", + "multi-line": "Connection Info:\n Host: {{.Host}}\n Database: {{.Database}}", + "empty-templ": "", + } + }) + + ginkgo.It("should render all templates correctly", func() { + result, err := RenderTemplate(templates, templateContext) + + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + gomega.Expect(result).To(gomega.HaveLen(4)) + + gomega.Expect(string(result["simple"])).To(gomega.Equal("Host: localhost")) + gomega.Expect(string(result["all-fields"])).To(gomega.Equal("Host: localhost, Role: admin, DB: postgres, Password: secret")) + gomega.Expect(string(result["multi-line"])).To(gomega.Equal("Connection Info:\n Host: localhost\n Database: postgres")) + gomega.Expect(string(result["empty-templ"])).To(gomega.Equal("")) + }) + }) + + ginkgo.When("provided with an invalid template", func() { + ginkgo.BeforeEach(func() { + templates = map[string]string{ + "invalid": "Host: {{.Host}}, Invalid: {{.NonExistent}}", + } + }) + + ginkgo.It("should return an error", func() { + result, err := RenderTemplate(templates, templateContext) + + gomega.Expect(err).To(gomega.HaveOccurred()) + gomega.Expect(err.Error()).To(gomega.ContainSubstring("execute template")) + gomega.Expect(result).To(gomega.BeNil()) + }) + }) + + ginkgo.When("provided with a template with syntax error", func() { + ginkgo.BeforeEach(func() { + templates = map[string]string{ + "syntax-error": "Host: {{.Host}, Missing closing bracket", + } + }) + + ginkgo.It("should return an error", func() { + result, err := RenderTemplate(templates, templateContext) + + gomega.Expect(err).To(gomega.HaveOccurred()) + gomega.Expect(err.Error()).To(gomega.ContainSubstring("parse template")) + gomega.Expect(result).To(gomega.BeNil()) + }) + }) + + ginkgo.When("provided with an empty template map", func() { + ginkgo.BeforeEach(func() { + templates = map[string]string{} + }) + + ginkgo.It("should return nil", func() { + result, err := RenderTemplate(templates, templateContext) + + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + gomega.Expect(result).To(gomega.BeNil()) + }) + }) + + ginkgo.When("provided with a nil template map", func() { + ginkgo.It("should return nil", func() { + result, err := RenderTemplate(nil, templateContext) + + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + gomega.Expect(result).To(gomega.BeNil()) + }) + }) + }) +}) diff --git a/tests/kuttl-test-self-hosted-postgres.yaml b/tests/kuttl-test-self-hosted-postgres.yaml index eca7f69ce..a6d088af3 100644 --- a/tests/kuttl-test-self-hosted-postgres.yaml +++ b/tests/kuttl-test-self-hosted-postgres.yaml @@ -9,7 +9,6 @@ kindContainers: - postgres-operator:build artifactsDir: ./tests/ commands: - - command: helm repo add ext-postgres-operator https://movetokube.github.io/postgres-operator/ - command: >- helm install -n $NAMESPACE postgresql oci://registry-1.docker.io/bitnamicharts/postgresql --version 16.6.0 diff --git a/version/version.go b/version/version.go deleted file mode 100644 index e3e130bf9..000000000 --- a/version/version.go +++ /dev/null @@ -1,5 +0,0 @@ -package version - -var ( - Version = "0.0.1" -)