From cabc020cc660be51780ff8781772230ad9ad94f8 Mon Sep 17 00:00:00 2001 From: Eduard Filip Date: Mon, 14 Jul 2025 19:32:30 +0200 Subject: [PATCH] Upgrade to Operator SDK 1.41.1 (#211) * Add missing improvements from Operator SDK 1.34.1 These were not mentioned in the upgrade documentation for version 1.34.x (https://sdk.operatorframework.io/docs/upgrading-sdk-version/v1.34.0/), but I've found them by compating the release with the previous one (https://github.com/operator-framework/operator-sdk/compare/v1.33.0...v1.34.1). * Upgrade to Operator SDK 1.36.0 Source of upgrade steps: https://sdk.operatorframework.io/docs/upgrading-sdk-version/v1.36.0/ Key differences: - Go packages `k8s.io/*` are already at a version higher than the one in the upgrade. - `ENVTEST_K8S_VERSION` is at a version higher than the one in the upgrade - We didn't have the golangci-lint make command before, thus we only needed to add things. * Upgrade to Operator SDK 1.38.0 Source of upgrade steps: https://sdk.operatorframework.io/docs/upgrading-sdk-version/v1.38.0/ * Upgrade to Operator SDK 1.39.0 Source of upgrade steps: https://sdk.operatorframework.io/docs/upgrading-sdk-version/v1.39.0/ * Upgrade to Operator SDK 1.40.0 Source of upgrade steps: https://sdk.operatorframework.io/docs/upgrading-sdk-version/v1.40.0/ I didn't do the "Add app.kubernetes.io/name label to your manifests" since it seems that we have it already, and it's customized. * Address lint errors * Update golangci-lint version used to support Go 1.24 * Improve workflows - Make workflow targets more specific. - Make build workflow only build (i.e. remove test part of it). - Rearrange steps and improve naming for build workflow. * Add back deleted test Initially the test has been removed due to lint saying that it was duplicate code, but it falsely errored since the values are different. * Improve code and add missing upgrade pieces * Upgrade to Operator SDK 1.41.1 Source of upgrade steps: https://sdk.operatorframework.io/docs/upgrading-sdk-version/v1.41.0/ Upgrading to 1.41.1 from 1.40.0 doesn't have any migration steps. Key elements: - Upgrade to golangci-lint v2 - Made the manifests using the updated controller tools * Address linter errors golanci-lint v2 seems to be more robust than the previous one, which is beneficial. Thus, we address the linter errors thrown by v2 and improve our code even further. * Add Makefile improvements These were brought in by comparing the Makefile of a freshly created operator using the latest operator-sdk with ours. * Add missing default kustomization for 1.40.0 upgrade * Bring default kustomization to latest version This is done by putting the file's content from a newly-generated operator. * Switch metrics-bind-address default value back to 8080 This ensures that the upgrade is backwards-compatible. * Add webhook-related scaffolding This enables us to easily add support for webhooks by running `operator-sdk create webhook` whenever we want to add them. * Fix typo --- .devcontainer/devcontainer.json | 25 ++ .devcontainer/post-install.sh | 23 ++ .github/workflows/build.yml | 25 +- .github/workflows/lint.yml | 24 ++ .github/workflows/test.yml | 24 ++ .gitignore | 6 +- .golangci.yml | 52 +++ Dockerfile | 2 +- Makefile | 151 ++++++--- api/v1/onepassworditem_types.go | 6 +- cmd/main.go | 161 +++++++++- .../onepassword.com_onepassworditems.yaml | 2 +- config/crd/kustomization.yaml | 6 - .../cainjection_in_onepassworditems.yaml | 7 - .../patches/webhook_in_onepassworditems.yaml | 16 - config/default/kustomization.yaml | 302 ++++++++++++------ config/default/manager_auth_proxy_patch.yaml | 41 --- config/default/manager_config_patch.yaml | 10 - config/default/manager_metrics_patch.yaml | 4 + config/default/metrics_service.yaml | 17 + config/manager/manager.yaml | 3 +- .../network-policy/allow-metrics-traffic.yaml | 26 ++ config/network-policy/kustomization.yaml | 2 + config/prometheus/kustomization.yaml | 9 + config/prometheus/monitor.yaml | 14 +- config/prometheus/monitor_tls_patch.yaml | 19 ++ .../rbac/auth_proxy_client_clusterrole.yaml | 16 - config/rbac/auth_proxy_role.yaml | 24 -- config/rbac/auth_proxy_role_binding.yaml | 19 -- config/rbac/auth_proxy_service.yaml | 23 -- config/rbac/kustomization.yaml | 23 +- config/rbac/metrics_auth_role.yaml | 17 + config/rbac/metrics_auth_role_binding.yaml | 12 + config/rbac/metrics_reader_role.yaml | 9 + config/rbac/onepassworditem_admin_role.yaml | 31 ++ config/rbac/onepassworditem_editor_role.yaml | 8 +- config/rbac/onepassworditem_viewer_role.yaml | 8 +- config/rbac/role.yaml | 36 --- go.mod | 97 +++--- go.sum | 204 ++++++++---- internal/controller/deployment_controller.go | 23 +- .../controller/deployment_controller_test.go | 15 +- .../controller/onepassworditem_controller.go | 53 ++- .../onepassworditem_controller_test.go | 46 +-- internal/controller/suite_test.go | 33 +- .../kubernetes_secrets_builder.go | 45 ++- .../kubernetes_secrets_builder_test.go | 67 ++-- pkg/logs/log_levels.go | 2 +- pkg/mocks/mocksecretserver.go | 1 + pkg/onepassword/annotations.go | 11 +- pkg/onepassword/annotations_test.go | 2 +- pkg/onepassword/client/connect/connect.go | 3 +- pkg/onepassword/client/testing/item.go | 2 +- .../client/testing/mock/connect.go | 6 +- pkg/onepassword/client/testing/mock/sdk.go | 28 +- pkg/onepassword/connect_setup.go | 33 +- pkg/onepassword/containers.go | 8 +- pkg/onepassword/deployments.go | 9 +- pkg/onepassword/items.go | 17 +- pkg/onepassword/model/item.go | 4 +- pkg/onepassword/secret_update_handler.go | 69 ++-- pkg/onepassword/secret_update_handler_test.go | 9 +- pkg/onepassword/volumes.go | 11 +- version/version.go | 2 +- 64 files changed, 1317 insertions(+), 686 deletions(-) create mode 100644 .devcontainer/devcontainer.json create mode 100644 .devcontainer/post-install.sh create mode 100644 .github/workflows/lint.yml create mode 100644 .github/workflows/test.yml create mode 100644 .golangci.yml delete mode 100644 config/crd/patches/cainjection_in_onepassworditems.yaml delete mode 100644 config/crd/patches/webhook_in_onepassworditems.yaml delete mode 100644 config/default/manager_auth_proxy_patch.yaml delete mode 100644 config/default/manager_config_patch.yaml create mode 100644 config/default/manager_metrics_patch.yaml create mode 100644 config/default/metrics_service.yaml create mode 100644 config/network-policy/allow-metrics-traffic.yaml create mode 100644 config/network-policy/kustomization.yaml create mode 100644 config/prometheus/monitor_tls_patch.yaml delete mode 100644 config/rbac/auth_proxy_client_clusterrole.yaml delete mode 100644 config/rbac/auth_proxy_role.yaml delete mode 100644 config/rbac/auth_proxy_role_binding.yaml delete mode 100644 config/rbac/auth_proxy_service.yaml create mode 100644 config/rbac/metrics_auth_role.yaml create mode 100644 config/rbac/metrics_auth_role_binding.yaml create mode 100644 config/rbac/metrics_reader_role.yaml create mode 100644 config/rbac/onepassworditem_admin_role.yaml diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..4dafb3c --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,25 @@ +{ + "name": "Kubebuilder DevContainer", + "image": "docker.io/golang:1.24", + "features": { + "ghcr.io/devcontainers/features/docker-in-docker:2": {}, + "ghcr.io/devcontainers/features/git:1": {} + }, + + "runArgs": ["--network=host"], + + "customizations": { + "vscode": { + "settings": { + "terminal.integrated.shell.linux": "/bin/bash" + }, + "extensions": [ + "ms-kubernetes-tools.vscode-kubernetes-tools", + "ms-azuretools.vscode-docker" + ] + } + }, + + "onCreateCommand": "bash .devcontainer/post-install.sh" +} + diff --git a/.devcontainer/post-install.sh b/.devcontainer/post-install.sh new file mode 100644 index 0000000..265c43e --- /dev/null +++ b/.devcontainer/post-install.sh @@ -0,0 +1,23 @@ +#!/bin/bash +set -x + +curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-amd64 +chmod +x ./kind +mv ./kind /usr/local/bin/kind + +curl -L -o kubebuilder https://go.kubebuilder.io/dl/latest/linux/amd64 +chmod +x kubebuilder +mv kubebuilder /usr/local/bin/ + +KUBECTL_VERSION=$(curl -L -s https://dl.k8s.io/release/stable.txt) +curl -LO "https://dl.k8s.io/release/$KUBECTL_VERSION/bin/linux/amd64/kubectl" +chmod +x kubectl +mv kubectl /usr/local/bin/kubectl + +docker network create -d=bridge --subnet=172.19.0.0/24 kind + +kind version +kubebuilder version +docker --version +go version +kubectl version --client diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 306d253..78d80e0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,21 +1,22 @@ -name: Build and Test -on: [push, pull_request] +name: Build + +on: + push: + branches: [main] + pull_request: jobs: build: - name: Build + name: Run on Ubuntu runs-on: ubuntu-latest steps: - - name: Set up Go 1.x - uses: actions/setup-go@v4 - with: - go-version: ^1.21 - - - name: Check out code into the Go module directory + - name: Clone the code uses: actions/checkout@v4 + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + - name: Build run: go build -v ./... - - - name: Test - run: make test diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..764f661 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,24 @@ +name: Lint + +on: + push: + branches: [main] + pull_request: + +jobs: + lint: + name: Run on Ubuntu + runs-on: ubuntu-latest + steps: + - name: Clone the code + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Run linter + uses: golangci/golangci-lint-action@v8 + with: + version: v2.2 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..71724cf --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,24 @@ +name: Tests + +on: + push: + branches: [main] + pull_request: + +jobs: + test: + name: Run on Ubuntu + runs-on: ubuntu-latest + steps: + - name: Clone the code + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Running Tests + run: | + go mod tidy + make test diff --git a/.gitignore b/.gitignore index c0a7a54..e32a86d 100644 --- a/.gitignore +++ b/.gitignore @@ -8,14 +8,16 @@ bin testbin/* -# Test binary, build with `go test -c` +# Test binary, built with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out -# Kubernetes Generated files - skip generated files, except for vendored files +# Go workspace file +go.work +# Kubernetes Generated files - skip generated files, except for vendored files !vendor/**/zz_generated.* # editor and IDE paraphernalia diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..e5b21b0 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,52 @@ +version: "2" +run: + allow-parallel-runners: true +linters: + default: none + enable: + - copyloopvar + - dupl + - errcheck + - ginkgolinter + - goconst + - gocyclo + - govet + - ineffassign + - lll + - misspell + - nakedret + - prealloc + - revive + - staticcheck + - unconvert + - unparam + - unused + settings: + revive: + rules: + - name: comment-spacings + - name: import-shadowing + exclusions: + generated: lax + rules: + - linters: + - lll + path: api/* + - linters: + - dupl + - lll + path: internal/* + paths: + - third_party$ + - builtin$ + - examples$ +formatters: + enable: + - gofmt + - goimports + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ diff --git a/Dockerfile b/Dockerfile index 86e2234..26227fa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Build the manager binary -FROM golang:1.24 as builder +FROM golang:1.24 AS builder ARG TARGETOS ARG TARGETARCH diff --git a/Makefile b/Makefile index 430c145..feffab2 100644 --- a/Makefile +++ b/Makefile @@ -50,12 +50,10 @@ 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.34.1 +OPERATOR_SDK_VERSION ?= v1.41.1 # Image URL to use all building/pushing image targets IMG ?= 1password/onepassword-operator:latest -# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. -ENVTEST_K8S_VERSION = 1.29.1 # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) ifeq (,$(shell go env GOBIN)) @@ -82,7 +80,7 @@ all: build # 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 commands is responsible for reading 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. @@ -114,9 +112,50 @@ vet: ## Run go vet against code. go vet ./... .PHONY: test -test: manifests generate fmt vet envtest ## Run tests. +test: manifests generate fmt vet setup-envtest ## Run tests. KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test ./... -coverprofile cover.out +# TODO(user): To use a different vendor for e2e tests, modify the setup under 'tests/e2e'. +# The default setup assumes Kind is pre-installed and builds/loads the Manager Docker image locally. +# CertManager is installed by default; skip with: +# - CERT_MANAGER_INSTALL_SKIP=true +KIND_CLUSTER ?= onepassword-operator-test-e2e + +.PHONY: setup-test-e2e +setup-test-e2e: ## Set up a Kind cluster for e2e tests if it does not exist + @command -v $(KIND) >/dev/null 2>&1 || { \ + echo "Kind is not installed. Please install Kind manually."; \ + exit 1; \ + } + @case "$$($(KIND) get clusters)" in \ + *"$(KIND_CLUSTER)"*) \ + echo "Kind cluster '$(KIND_CLUSTER)' already exists. Skipping creation." ;; \ + *) \ + echo "Creating Kind cluster '$(KIND_CLUSTER)'..."; \ + $(KIND) create cluster --name $(KIND_CLUSTER) ;; \ + esac + +.PHONY: test-e2e +test-e2e: setup-test-e2e manifests generate fmt vet ## Run the e2e tests. Expected an isolated environment using Kind. + KIND_CLUSTER=$(KIND_CLUSTER) go test ./test/e2e/ -v -ginkgo.v + $(MAKE) cleanup-test-e2e + +.PHONY: cleanup-test-e2e +cleanup-test-e2e: ## Tear down the Kind cluster used for e2e tests + @$(KIND) delete cluster --name $(KIND_CLUSTER) + +.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 + +.PHONY: lint-config +lint-config: golangci-lint ## Verify golangci-lint linter configuration + $(GOLANGCI_LINT) config verify + ##@ Build .PHONY: build @@ -127,30 +166,39 @@ build: manifests generate fmt vet ## Build manager binary. run: manifests generate fmt vet ## Run a controller from your host. go run ./cmd/main.go -# If you wish built 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. +# 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: test ## Build docker image with the manager. +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 build to provide support to multiple +# 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: -# - able to use docker buildx . More info: https://docs.docker.com/build/buildx/ -# - have enable BuildKit, More info: https://docs.docker.com/develop/develop-images/build_enhancements/ -# - be able to push the image for your registry (i.e. if you do not inform a valid value via IMG=> then the export will fail) -# To properly provided solutions that supports more than one platform you should use this option. +# - 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 - - $(CONTAINER_TOOL) buildx create --name project-v3-builder - $(CONTAINER_TOOL) buildx use project-v3-builder - - $(CONTAINER_TOOL) buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile . - - $(CONTAINER_TOOL) buildx rm project-v3-builder + # 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 onepassword-operator-builder + $(CONTAINER_TOOL) buildx use onepassword-operator-builder + - $(CONTAINER_TOOL) buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile.cross . + - $(CONTAINER_TOOL) buildx rm onepassword-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 @@ -176,10 +224,10 @@ deploy: manifests kustomize set-namespace ## Deploy controller to the K8s cluste $(KUSTOMIZE) build config/default | $(KUBECTL) apply -f - .PHONY: undeploy -undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. +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 - -##@ Build Dependencies +##@ Dependencies ## Location to install dependencies to LOCALBIN ?= $(shell pwd)/bin @@ -188,33 +236,64 @@ $(LOCALBIN): ## Tool Binaries KUBECTL ?= kubectl +KIND ?= kind KUSTOMIZE ?= $(LOCALBIN)/kustomize CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen ENVTEST ?= $(LOCALBIN)/setup-envtest +GOLANGCI_LINT = $(LOCALBIN)/golangci-lint ## Tool Versions -KUSTOMIZE_VERSION ?= v5.3.0 -CONTROLLER_TOOLS_VERSION ?= v0.14.0 +KUSTOMIZE_VERSION ?= v5.6.0 +CONTROLLER_TOOLS_VERSION ?= v0.18.0 +# ENVTEST_VERSION is the version of controller-runtime release branch to fetch the envtest setup script (i.e. release-0.20) +ENVTEST_VERSION := $(shell go list -m -f "{{ .Version }}" sigs.k8s.io/controller-runtime | awk -F'[v.]' '{printf "release-%d.%d", $$2, $$3}') +# ENVTEST_K8S_VERSION is the version of Kubernetes to use for setting up ENVTEST binaries (i.e. 1.31) +ENVTEST_K8S_VERSION := $(shell go list -m -f "{{ .Version }}" k8s.io/api | awk -F'[v.]' '{printf "1.%d", $$3}') +GOLANGCI_LINT_VERSION ?= v2.2.0 .PHONY: kustomize -kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. If wrong version is installed, it will be removed before downloading. +kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. $(KUSTOMIZE): $(LOCALBIN) - @if test -x $(LOCALBIN)/kustomize && ! $(LOCALBIN)/kustomize version | grep -q $(KUSTOMIZE_VERSION); then \ - echo "$(LOCALBIN)/kustomize version is not expected $(KUSTOMIZE_VERSION). Removing it before installing."; \ - rm -rf $(LOCALBIN)/kustomize; \ - fi - test -s $(LOCALBIN)/kustomize || GOBIN=$(LOCALBIN) GO111MODULE=on go install sigs.k8s.io/kustomize/kustomize/v5@$(KUSTOMIZE_VERSION) + $(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. If wrong version is installed, it will be overwritten. +controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary. $(CONTROLLER_GEN): $(LOCALBIN) - test -s $(LOCALBIN)/controller-gen && $(LOCALBIN)/controller-gen --version | grep -q $(CONTROLLER_TOOLS_VERSION) || \ - GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_TOOLS_VERSION) + $(call go-install-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen,$(CONTROLLER_TOOLS_VERSION)) + +.PHONY: setup-envtest +setup-envtest: envtest ## Download the binaries required for ENVTEST in the local bin directory. + @echo "Setting up envtest binaries for Kubernetes version $(ENVTEST_K8S_VERSION)..." + @$(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path || { \ + echo "Error: Failed to set up envtest binaries for version $(ENVTEST_K8S_VERSION)."; \ + exit 1; \ + } .PHONY: envtest -envtest: $(ENVTEST) ## Download envtest-setup locally if necessary. +envtest: $(ENVTEST) ## Download setup-envtest locally if necessary. $(ENVTEST): $(LOCALBIN) - test -s $(LOCALBIN)/setup-envtest || GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest + $(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/v2/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 @@ -242,14 +321,14 @@ bundle: manifests kustomize operator-sdk ## Generate bundle manifests and metada .PHONY: bundle-build bundle-build: ## Build the bundle image. - docker build -f bundle.Dockerfile -t $(BUNDLE_IMG) . + $(CONTAINER_TOOL) 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 = ./bin/opm +OPM = $(LOCALBIN)/opm opm: ## Download opm locally if necessary. ifeq (,$(wildcard $(OPM))) ifeq (,$(shell which opm 2>/dev/null)) @@ -257,7 +336,7 @@ 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 ;\ + curl -sSLo $(OPM) https://github.com/operator-framework/operator-registry/releases/download/v1.55.0/$${OS}-$${ARCH}-opm ;\ chmod +x $(OPM) ;\ } else @@ -282,7 +361,7 @@ endif # 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) + $(OPM) index add --container-tool $(CONTAINER_TOOL) --mode semver --tag $(CATALOG_IMG) --bundles $(BUNDLE_IMGS) $(FROM_INDEX_OPT) # Push the catalog image. .PHONY: catalog-push diff --git a/api/v1/onepassworditem_types.go b/api/v1/onepassworditem_types.go index 8c504b2..a6ef2bc 100644 --- a/api/v1/onepassworditem_types.go +++ b/api/v1/onepassworditem_types.go @@ -67,8 +67,8 @@ type OnePasswordItemStatus struct { Conditions []OnePasswordItemCondition `json:"conditions"` } -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status // OnePasswordItem is the Schema for the onepassworditems API type OnePasswordItem struct { @@ -81,7 +81,7 @@ type OnePasswordItem struct { Status OnePasswordItemStatus `json:"status,omitempty"` } -//+kubebuilder:object:root=true +// +kubebuilder:object:root=true // OnePasswordItemList contains a list of OnePasswordItem type OnePasswordItemList struct { diff --git a/cmd/main.go b/cmd/main.go index b1a258c..e9a6e04 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -26,10 +26,12 @@ package main import ( "context" + "crypto/tls" "errors" "flag" "fmt" "os" + "path/filepath" "regexp" "runtime" "strconv" @@ -46,9 +48,12 @@ import ( "k8s.io/client-go/rest" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/cache" + "sigs.k8s.io/controller-runtime/pkg/certwatcher" "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" onepasswordcomv1 "github.com/1Password/onepassword-operator/api/v1" "github.com/1Password/onepassword-operator/internal/controller" @@ -56,7 +61,7 @@ import ( opclient "github.com/1Password/onepassword-operator/pkg/onepassword/client" "github.com/1Password/onepassword-operator/pkg/utils" "github.com/1Password/onepassword-operator/version" - //+kubebuilder:scaffold:imports + // +kubebuilder:scaffold:imports ) var ( @@ -73,13 +78,6 @@ const ( annotationRegExpString = "^operator.1password.io\\/[a-zA-Z\\.]+" ) -// Change below variables to serve metrics on different host or port. -var ( - metricsHost = "0.0.0.0" - metricsPort int32 = 8383 - operatorMetricsPort int32 = 8686 -) - func printVersion() { setupLog.Info(fmt.Sprintf("Operator Version: %s", version.OperatorVersion)) setupLog.Info(fmt.Sprintf("Go Version: %s", runtime.Version())) @@ -91,18 +89,36 @@ func init() { utilruntime.Must(clientgoscheme.AddToScheme(scheme)) utilruntime.Must(onepasswordcomv1.AddToScheme(scheme)) - //+kubebuilder:scaffold:scheme + // +kubebuilder:scaffold:scheme } func main() { var metricsAddr string + var metricsCertPath, metricsCertName, metricsCertKey string + var webhookCertPath, webhookCertName, webhookCertKey string var enableLeaderElection bool var probeAddr string - flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.") - flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") + var secureMetrics bool + var enableHTTP2 bool + var tlsOpts []func(*tls.Config) + flag.StringVar(&metricsAddr, "metrics-bind-address", "8080", + "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.StringVar(&metricsCertPath, "metrics-cert-path", "", + "The directory that contains the metrics server certificate.") + flag.StringVar(&metricsCertName, "metrics-cert-name", "tls.crt", + "The name of the metrics server certificate file.") + flag.StringVar(&metricsCertKey, "metrics-cert-key", "tls.key", + "The name of the metrics server key file.") + flag.BoolVar(&enableHTTP2, "enable-http2", false, + "If set, HTTP/2 will be enabled for the metrics") opts := zap.Options{ Development: true, } @@ -111,6 +127,21 @@ func main() { 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 Cancelation 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) + } + printVersion() // Create a root context that will be cancelled on termination signals @@ -128,12 +159,104 @@ func main() { os.Exit(1) } + // Create watchers for metrics and webhooks certificates + var metricsCertWatcher, webhookCertWatcher *certwatcher.CertWatcher + + // Initial webhook TLS options + webhookTLSOpts := tlsOpts + + if len(webhookCertPath) > 0 { + setupLog.Info("Initializing webhook certificate watcher using provided certificates", + "webhook-cert-path", webhookCertPath, "webhook-cert-name", webhookCertName, "webhook-cert-key", webhookCertKey) + + var err error + webhookCertWatcher, err = certwatcher.New( + filepath.Join(webhookCertPath, webhookCertName), + filepath.Join(webhookCertPath, webhookCertKey), + ) + if err != nil { + setupLog.Error(err, "Failed to initialize webhook certificate watcher") + os.Exit(1) + } + + webhookTLSOpts = append(webhookTLSOpts, func(config *tls.Config) { + config.GetCertificate = webhookCertWatcher.GetCertificate + }) + } + + webhookServer := webhook.NewServer(webhook.Options{ + TLSOpts: webhookTLSOpts, + }) + + // 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.21.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 + } + + // If the certificate is not specified, controller-runtime will automatically + // generate self-signed certificates for the metrics server. While convenient for development and testing, + // this setup is not recommended for production. + // + // TODO(user): If you enable certManager, uncomment the following lines: + // - [METRICS-WITH-CERTS] at config/default/kustomization.yaml to generate and use certificates + // managed by cert-manager for the metrics server. + // - [PROMETHEUS-WITH-CERTS] at config/prometheus/kustomization.yaml for TLS certification. + if len(metricsCertPath) > 0 { + setupLog.Info("Initializing metrics certificate watcher using provided certificates", + "metrics-cert-path", metricsCertPath, "metrics-cert-name", metricsCertName, "metrics-cert-key", metricsCertKey) + + var err error + metricsCertWatcher, err = certwatcher.New( + filepath.Join(metricsCertPath, metricsCertName), + filepath.Join(metricsCertPath, metricsCertKey), + ) + if err != nil { + setupLog.Error(err, "Failed to initialize metrics certificate watcher") + os.Exit(1) + } + + metricsServerOptions.TLSOpts = append(metricsServerOptions.TLSOpts, func(config *tls.Config) { + config.GetCertificate = metricsCertWatcher.GetCertificate + }) + } + options := ctrl.Options{ Scheme: scheme, - Metrics: metricsserver.Options{BindAddress: metricsAddr}, + Metrics: metricsServerOptions, + WebhookServer: webhookServer, HealthProbeBindAddress: probeAddr, LeaderElection: enableLeaderElection, LeaderElectionID: "c26807fd.onepassword.com", + // 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, } // Add support for MultiNamespace set in WATCH_NAMESPACE (e.g ns1,ns2) @@ -184,14 +307,14 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "Deployment") os.Exit(1) } - //+kubebuilder:scaffold:builder + // +kubebuilder:scaffold:builder - //Setup 1PasswordConnect + // Setup 1PasswordConnect if shouldManageConnect() { setupLog.Info("Automated Connect Management Enabled") go func(ctx context.Context) { connectStarted := false - for connectStarted == false { + for !connectStarted { err := op.SetupConnect(ctx, mgr.GetClient(), deploymentNamespace) // Cache Not Started is an acceptable error. Retry until cache is started. if err != nil && !errors.Is(err, &cache.ErrCacheNotStarted{}) { @@ -226,6 +349,14 @@ func main() { } }(ctx) + if metricsCertWatcher != nil { + setupLog.Info("Adding metrics certificate watcher to manager") + if err := mgr.Add(metricsCertWatcher); err != nil { + setupLog.Error(err, "Unable to add metrics certificate watcher to manager") + os.Exit(1) + } + } + if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { setupLog.Error(err, "unable to set up health check") os.Exit(1) diff --git a/config/crd/bases/onepassword.com_onepassworditems.yaml b/config/crd/bases/onepassword.com_onepassworditems.yaml index c6d9599..ad59829 100644 --- a/config/crd/bases/onepassword.com_onepassworditems.yaml +++ b/config/crd/bases/onepassword.com_onepassworditems.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.18.0 name: onepassworditems.onepassword.com spec: group: onepassword.com diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index ecc10c5..e9b3b99 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -11,13 +11,7 @@ patches: #- path: patches/webhook_in_onepassworditems.yaml #+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_onepassworditems.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/patches/cainjection_in_onepassworditems.yaml b/config/crd/patches/cainjection_in_onepassworditems.yaml deleted file mode 100644 index cab4852..0000000 --- a/config/crd/patches/cainjection_in_onepassworditems.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# The following patch adds a directive for certmanager to inject CA into the CRD -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE/CERTIFICATE_NAME - name: onepassworditems.onepassword.com diff --git a/config/crd/patches/webhook_in_onepassworditems.yaml b/config/crd/patches/webhook_in_onepassworditems.yaml deleted file mode 100644 index 583718a..0000000 --- a/config/crd/patches/webhook_in_onepassworditems.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# The following patch enables a conversion webhook for the CRD -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: onepassworditems.onepassword.com -spec: - conversion: - strategy: Webhook - webhook: - clientConfig: - service: - namespace: system - name: webhook-service - path: /convert - conversionReviewVersions: - - v1 diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml index a722a38..f18595d 100644 --- a/config/default/kustomization.yaml +++ b/config/default/kustomization.yaml @@ -25,118 +25,210 @@ resources: #- ../certmanager # [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. #- ../prometheus +# [METRICS] Expose the controller manager metrics service. +- metrics_service.yaml +# [NETWORK POLICY] Protect the /metrics endpoint and Webhook Server with NetworkPolicy. +# Only Pod(s) running a namespace labeled with 'metrics: enabled' will be able to gather the metrics. +# Only CR(s) which requires webhooks and are applied on namespaces labeled with 'webhooks: enabled' will +# be able to communicate with the Webhook Server. +#- ../network-policy +# Uncomment the patches line if you enable Metrics patches: -# Protect the /metrics endpoint by putting it behind auth. -# If you want your controller-manager to expose the /metrics -# endpoint w/o any authn/z, please comment the following line. -- path: manager_auth_proxy_patch.yaml +# [METRICS] The following patch will enable the metrics endpoint using HTTPS and the port :8443. +# More info: https://book.kubebuilder.io/reference/metrics +- path: manager_metrics_patch.yaml + target: + kind: Deployment + +# Uncomment the patches line if you enable Metrics and CertManager +# [METRICS-WITH-CERTS] To enable metrics protected with certManager, uncomment the following line. +# This patch will protect the metrics with certManager self-signed certs. +#- path: cert_metrics_manager_patch.yaml +# target: +# kind: Deployment # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in # crd/kustomization.yaml #- path: manager_webhook_patch.yaml - -# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. -# Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks. -# 'CERTMANAGER' needs to be enabled to use ca injection -#- path: webhookcainjection_patch.yaml +# target: +# kind: Deployment # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. # Uncomment the following replacements to add the cert-manager CA injection annotations #replacements: -# - source: # Add cert-manager annotation to ValidatingWebhookConfiguration, MutatingWebhookConfiguration and CRDs -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert # this name should match the one in certificate.yaml -# fieldPath: .metadata.namespace # namespace of the certificate CR -# targets: -# - select: -# kind: ValidatingWebhookConfiguration -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 0 -# create: true -# - select: -# kind: MutatingWebhookConfiguration -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 0 -# create: true -# - select: -# kind: CustomResourceDefinition -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 0 -# create: true -# - source: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert # this name should match the one in certificate.yaml -# fieldPath: .metadata.name -# targets: -# - select: -# kind: ValidatingWebhookConfiguration -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 1 -# create: true -# - select: -# kind: MutatingWebhookConfiguration -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 1 -# create: true -# - select: -# kind: CustomResourceDefinition -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 1 -# create: true -# - source: # Add cert-manager annotation to the webhook Service -# kind: Service -# version: v1 -# name: webhook-service -# fieldPath: .metadata.name # namespace of the service -# targets: -# - select: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# fieldPaths: -# - .spec.dnsNames.0 -# - .spec.dnsNames.1 -# options: -# delimiter: '.' -# index: 0 -# create: true -# - source: -# kind: Service -# version: v1 -# name: webhook-service -# fieldPath: .metadata.namespace # namespace of the service -# targets: -# - select: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# fieldPaths: -# - .spec.dnsNames.0 -# - .spec.dnsNames.1 -# options: -# delimiter: '.' -# index: 1 -# create: true +# - source: # Uncomment the following block to enable certificates for metrics +# kind: Service +# version: v1 +# name: controller-manager-metrics-service +# fieldPath: metadata.name +# targets: +# - select: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: metrics-certs +# fieldPaths: +# - spec.dnsNames.0 +# - spec.dnsNames.1 +# options: +# delimiter: '.' +# index: 0 +# create: true +# - select: # Uncomment the following to set the Service name for TLS config in Prometheus ServiceMonitor +# kind: ServiceMonitor +# group: monitoring.coreos.com +# version: v1 +# name: controller-manager-metrics-monitor +# fieldPaths: +# - spec.endpoints.0.tlsConfig.serverName +# options: +# delimiter: '.' +# index: 0 +# create: true +# +# - source: +# kind: Service +# version: v1 +# name: controller-manager-metrics-service +# fieldPath: metadata.namespace +# targets: +# - select: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: metrics-certs +# fieldPaths: +# - spec.dnsNames.0 +# - spec.dnsNames.1 +# options: +# delimiter: '.' +# index: 1 +# create: true +# - select: # Uncomment the following to set the Service namespace for TLS in Prometheus ServiceMonitor +# kind: ServiceMonitor +# group: monitoring.coreos.com +# version: v1 +# name: controller-manager-metrics-monitor +# fieldPaths: +# - spec.endpoints.0.tlsConfig.serverName +# options: +# delimiter: '.' +# index: 1 +# create: true +# +# - source: # Uncomment the following block if you have any webhook +# kind: Service +# version: v1 +# name: webhook-service +# fieldPath: .metadata.name # Name of the service +# targets: +# - select: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPaths: +# - .spec.dnsNames.0 +# - .spec.dnsNames.1 +# options: +# delimiter: '.' +# index: 0 +# create: true +# - source: +# kind: Service +# version: v1 +# name: webhook-service +# fieldPath: .metadata.namespace # Namespace of the service +# targets: +# - select: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPaths: +# - .spec.dnsNames.0 +# - .spec.dnsNames.1 +# options: +# delimiter: '.' +# index: 1 +# create: true +# +# - source: # Uncomment the following block if you have a ValidatingWebhook (--programmatic-validation) +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert # This name should match the one in certificate.yaml +# fieldPath: .metadata.namespace # Namespace of the certificate CR +# targets: +# - select: +# kind: ValidatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 0 +# create: true +# - source: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPath: .metadata.name +# targets: +# - select: +# kind: ValidatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 1 +# create: true +# +# - source: # Uncomment the following block if you have a DefaultingWebhook (--defaulting ) +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPath: .metadata.namespace # Namespace of the certificate CR +# targets: +# - select: +# kind: MutatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 0 +# create: true +# - source: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPath: .metadata.name +# targets: +# - select: +# kind: MutatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 1 +# create: true +# +# - source: # Uncomment the following block if you have a ConversionWebhook (--conversion) +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPath: .metadata.namespace # Namespace of the certificate CR +# targets: # Do not remove or uncomment the following scaffold marker; required to generate code for target CRD. +# +kubebuilder:scaffold:crdkustomizecainjectionns +# - source: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPath: .metadata.name +# targets: # Do not remove or uncomment the following scaffold marker; required to generate code for target CRD. +# +kubebuilder:scaffold:crdkustomizecainjectionname diff --git a/config/default/manager_auth_proxy_patch.yaml b/config/default/manager_auth_proxy_patch.yaml deleted file mode 100644 index 65c3d64..0000000 --- a/config/default/manager_auth_proxy_patch.yaml +++ /dev/null @@ -1,41 +0,0 @@ -# This patch inject a sidecar container which is a HTTP proxy for the -# controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews. -apiVersion: apps/v1 -kind: Deployment -metadata: - name: onepassword-connect-operator - namespace: system -spec: - template: - spec: - securityContext: - runAsNonRoot: true - containers: - - name: kube-rbac-proxy - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - "ALL" - image: gcr.io/kubebuilder/kube-rbac-proxy:v0.15.0 - args: - - "--secure-listen-address=0.0.0.0:8443" - - "--upstream=http://127.0.0.1:8080/" - - "--logtostderr=true" - - "--v=0" - ports: - - containerPort: 8443 - protocol: TCP - name: https - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 5m - memory: 64Mi - - name: manager - args: - - "--health-probe-bind-address=:8081" - - "--metrics-bind-address=127.0.0.1:8080" - - "--leader-elect" diff --git a/config/default/manager_config_patch.yaml b/config/default/manager_config_patch.yaml deleted file mode 100644 index 4e6baff..0000000 --- a/config/default/manager_config_patch.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: onepassword-connect-operator - namespace: system -spec: - template: - spec: - containers: - - name: manager diff --git a/config/default/manager_metrics_patch.yaml b/config/default/manager_metrics_patch.yaml new file mode 100644 index 0000000..488f136 --- /dev/null +++ b/config/default/manager_metrics_patch.yaml @@ -0,0 +1,4 @@ +# This patch adds the args to allow exposing the metrics endpoint using HTTPS +- op: add + path: /spec/template/spec/containers/0/args/0 + value: --metrics-bind-address=:8443 \ No newline at end of file diff --git a/config/default/metrics_service.yaml b/config/default/metrics_service.yaml new file mode 100644 index 0000000..3cfbe61 --- /dev/null +++ b/config/default/metrics_service.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + control-plane: controller-manager + app.kubernetes.io/name: onepassword-operator + app.kubernetes.io/managed-by: kustomize + name: controller-manager-metrics-service + namespace: system +spec: + ports: + - name: https + port: 8443 + protocol: TCP + targetPort: 8443 + selector: + control-plane: controller-manager diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index 07a6442..e9602da 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -72,6 +72,7 @@ spec: - /manager args: - --leader-elect + - --health-probe-bind-address=:8081 image: 1password/onepassword-operator:latest name: manager env: @@ -106,7 +107,7 @@ spec: allowPrivilegeEscalation: false capabilities: drop: - - "ALL" + - "ALL" livenessProbe: httpGet: path: /healthz diff --git a/config/network-policy/allow-metrics-traffic.yaml b/config/network-policy/allow-metrics-traffic.yaml new file mode 100644 index 0000000..cc078a1 --- /dev/null +++ b/config/network-policy/allow-metrics-traffic.yaml @@ -0,0 +1,26 @@ +# This NetworkPolicy allows ingress traffic +# with Pods running on namespaces labeled with 'metrics: enabled'. Only Pods on those +# namespaces are able to gathering data from the metrics endpoint. +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + labels: + app.kubernetes.io/name: onepassword-operator + app.kubernetes.io/managed-by: kustomize + name: allow-metrics-traffic + namespace: system +spec: + podSelector: + matchLabels: + control-plane: controller-manager + policyTypes: + - Ingress + ingress: + # This allows ingress traffic from any namespace with the label metrics: enabled + - from: + - namespaceSelector: + matchLabels: + metrics: enabled # Only from namespaces with this label + ports: + - port: 8443 + protocol: TCP diff --git a/config/network-policy/kustomization.yaml b/config/network-policy/kustomization.yaml new file mode 100644 index 0000000..404efe2 --- /dev/null +++ b/config/network-policy/kustomization.yaml @@ -0,0 +1,2 @@ +resources: +- allow-metrics-traffic.yaml \ No newline at end of file diff --git a/config/prometheus/kustomization.yaml b/config/prometheus/kustomization.yaml index ed13716..fdc5481 100644 --- a/config/prometheus/kustomization.yaml +++ b/config/prometheus/kustomization.yaml @@ -1,2 +1,11 @@ resources: - monitor.yaml + +# [PROMETHEUS-WITH-CERTS] The following patch configures the ServiceMonitor in ../prometheus +# to securely reference certificates created and managed by cert-manager. +# Additionally, ensure that you uncomment the [METRICS WITH CERTMANAGER] patch under config/default/kustomization.yaml +# to mount the "metrics-server-cert" secret in the Manager Deployment. +#patches: +# - path: monitor_tls_patch.yaml +# target: +# kind: ServiceMonitor diff --git a/config/prometheus/monitor.yaml b/config/prometheus/monitor.yaml index 94b17bd..def7341 100644 --- a/config/prometheus/monitor.yaml +++ b/config/prometheus/monitor.yaml @@ -5,7 +5,7 @@ metadata: labels: name: onepassword-connect-operator control-plane: onepassword-connect-operator - app.kubernetes.io/name: servicemonitor + app.kubernetes.io/name: onepassword-operator app.kubernetes.io/instance: controller-manager-metrics-monitor app.kubernetes.io/component: metrics app.kubernetes.io/created-by: onepassword-connect-operator @@ -16,12 +16,22 @@ metadata: spec: endpoints: - path: /metrics - port: https + port: https # Ensure this is the name of the port that exposes HTTPS metrics scheme: https bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token tlsConfig: + # TODO(user): The option insecureSkipVerify: true is not recommended for production since it disables + # certificate verification. This poses a significant security risk by making the system vulnerable to + # man-in-the-middle attacks, where an attacker could intercept and manipulate the communication between + # Prometheus and the monitored services. This could lead to unauthorized access to sensitive metrics data, + # compromising the integrity and confidentiality of the information. + # Please use the following options for secure configurations: + # caFile: /etc/metrics-certs/ca.crt + # certFile: /etc/metrics-certs/tls.crt + # keyFile: /etc/metrics-certs/tls.key insecureSkipVerify: true selector: matchLabels: name: onepassword-connect-operator control-plane: onepassword-connect-operator + app.kubernetes.io/name: onepassword-operator diff --git a/config/prometheus/monitor_tls_patch.yaml b/config/prometheus/monitor_tls_patch.yaml new file mode 100644 index 0000000..5bf84ce --- /dev/null +++ b/config/prometheus/monitor_tls_patch.yaml @@ -0,0 +1,19 @@ +# Patch for Prometheus ServiceMonitor to enable secure TLS configuration +# using certificates managed by cert-manager +- op: replace + path: /spec/endpoints/0/tlsConfig + value: + # SERVICE_NAME and SERVICE_NAMESPACE will be substituted by kustomize + serverName: SERVICE_NAME.SERVICE_NAMESPACE.svc + insecureSkipVerify: false + ca: + secret: + name: metrics-server-cert + key: ca.crt + cert: + secret: + name: metrics-server-cert + key: tls.crt + keySecret: + name: metrics-server-cert + key: tls.key diff --git a/config/rbac/auth_proxy_client_clusterrole.yaml b/config/rbac/auth_proxy_client_clusterrole.yaml deleted file mode 100644 index 3c8911a..0000000 --- a/config/rbac/auth_proxy_client_clusterrole.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/name: clusterrole - app.kubernetes.io/instance: metrics-reader - app.kubernetes.io/component: kube-rbac-proxy - app.kubernetes.io/created-by: onepassword-connect-operator - app.kubernetes.io/part-of: onepassword-connect-operator - app.kubernetes.io/managed-by: kustomize - name: metrics-reader -rules: -- nonResourceURLs: - - "/metrics" - verbs: - - get diff --git a/config/rbac/auth_proxy_role.yaml b/config/rbac/auth_proxy_role.yaml deleted file mode 100644 index 405673d..0000000 --- a/config/rbac/auth_proxy_role.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/name: clusterrole - app.kubernetes.io/instance: proxy-role - app.kubernetes.io/component: kube-rbac-proxy - app.kubernetes.io/created-by: onepassword-connect-operator - app.kubernetes.io/part-of: onepassword-connect-operator - app.kubernetes.io/managed-by: kustomize - name: proxy-role -rules: -- apiGroups: - - authentication.k8s.io - resources: - - tokenreviews - verbs: - - create -- apiGroups: - - authorization.k8s.io - resources: - - subjectaccessreviews - verbs: - - create diff --git a/config/rbac/auth_proxy_role_binding.yaml b/config/rbac/auth_proxy_role_binding.yaml deleted file mode 100644 index c38f8f4..0000000 --- a/config/rbac/auth_proxy_role_binding.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - labels: - app.kubernetes.io/name: clusterrolebinding - app.kubernetes.io/instance: proxy-rolebinding - app.kubernetes.io/component: kube-rbac-proxy - app.kubernetes.io/created-by: onepassword-connect-operator - app.kubernetes.io/part-of: onepassword-connect-operator - app.kubernetes.io/managed-by: kustomize - name: proxy-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: proxy-role -subjects: -- kind: ServiceAccount - name: onepassword-connect-operator - namespace: system diff --git a/config/rbac/auth_proxy_service.yaml b/config/rbac/auth_proxy_service.yaml deleted file mode 100644 index 2eff7b6..0000000 --- a/config/rbac/auth_proxy_service.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - labels: - name: onepassword-connect-operator - control-plane: onepassword-connect-operator - app.kubernetes.io/name: service - app.kubernetes.io/instance: controller-manager-metrics-service - app.kubernetes.io/component: kube-rbac-proxy - app.kubernetes.io/created-by: onepassword-connect-operator - app.kubernetes.io/part-of: onepassword-connect-operator - app.kubernetes.io/managed-by: kustomize - name: onepassword-connect-operator-metrics-service - namespace: system -spec: - ports: - - name: https - port: 8443 - protocol: TCP - targetPort: https - selector: - name: onepassword-connect-operator - control-plane: onepassword-connect-operator diff --git a/config/rbac/kustomization.yaml b/config/rbac/kustomization.yaml index 731832a..c405333 100644 --- a/config/rbac/kustomization.yaml +++ b/config/rbac/kustomization.yaml @@ -9,10 +9,19 @@ resources: - role_binding.yaml - leader_election_role.yaml - leader_election_role_binding.yaml -# Comment the following 4 lines if you want to disable -# the auth proxy (https://github.com/brancz/kube-rbac-proxy) -# which protects your /metrics endpoint. -- auth_proxy_service.yaml -- auth_proxy_role.yaml -- auth_proxy_role_binding.yaml -- auth_proxy_client_clusterrole.yaml +# The following RBAC configurations are used to protect +# the metrics endpoint with authn/authz. These configurations +# ensure that only authorized users and service accounts +# can access the metrics endpoint. Comment the following +# permissions if you want to disable this protection. +# More info: https://book.kubebuilder.io/reference/metrics.html +- metrics_auth_role.yaml +- metrics_auth_role_binding.yaml +- metrics_reader_role.yaml +# For each CRD, "Admin", "Editor" and "Viewer" roles are scaffolded by +# default, aiding admins in cluster management. Those roles are +# not used by the {{ .ProjectName }} itself. You can comment the following lines +# if you do not want those helpers be installed with your Project. +- onepassworditem_admin_role.yaml +- onepassworditem_editor_role.yaml +- onepassworditem_viewer_role.yaml diff --git a/config/rbac/metrics_auth_role.yaml b/config/rbac/metrics_auth_role.yaml new file mode 100644 index 0000000..bd419f4 --- /dev/null +++ b/config/rbac/metrics_auth_role.yaml @@ -0,0 +1,17 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: metrics-auth-role +rules: + - apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create + - apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create diff --git a/config/rbac/metrics_auth_role_binding.yaml b/config/rbac/metrics_auth_role_binding.yaml new file mode 100644 index 0000000..eb0c3f5 --- /dev/null +++ b/config/rbac/metrics_auth_role_binding.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: metrics-auth-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: metrics-auth-role +subjects: + - kind: ServiceAccount + name: controller-manager + namespace: system diff --git a/config/rbac/metrics_reader_role.yaml b/config/rbac/metrics_reader_role.yaml new file mode 100644 index 0000000..07f4382 --- /dev/null +++ b/config/rbac/metrics_reader_role.yaml @@ -0,0 +1,9 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: metrics-reader +rules: + - nonResourceURLs: + - "/metrics" + verbs: + - get diff --git a/config/rbac/onepassworditem_admin_role.yaml b/config/rbac/onepassworditem_admin_role.yaml new file mode 100644 index 0000000..851aeeb --- /dev/null +++ b/config/rbac/onepassworditem_admin_role.yaml @@ -0,0 +1,31 @@ +# This rule is not used by the project onepassword-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants full permissions ('*') over onepassword.com. +# This role is intended for users authorized to modify roles and bindings within the cluster, +# enabling them to delegate specific permissions to other users or groups as needed. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: onepassworditem-admin-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: onepassword-connect-operator + app.kubernetes.io/part-of: onepassword-connect-operator + app.kubernetes.io/managed-by: kustomize + name: onepassworditem-admin-role +rules: + - apiGroups: + - onepassword.com + resources: + - onepassworditems + verbs: + - '*' + - apiGroups: + - onepassword.com + resources: + - onepassworditems/status + verbs: + - get diff --git a/config/rbac/onepassworditem_editor_role.yaml b/config/rbac/onepassworditem_editor_role.yaml index 7c9bee9..3b4c056 100644 --- a/config/rbac/onepassworditem_editor_role.yaml +++ b/config/rbac/onepassworditem_editor_role.yaml @@ -1,4 +1,10 @@ -# permissions for end users to edit onepassworditems. +# This rule is not used by the project onepassword-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants permissions to create, update, and delete resources within the onepassword.com. +# This role is intended for users who need to manage these resources +# but should not control RBAC or manage permissions for others. + apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: diff --git a/config/rbac/onepassworditem_viewer_role.yaml b/config/rbac/onepassworditem_viewer_role.yaml index 033617a..f607a61 100644 --- a/config/rbac/onepassworditem_viewer_role.yaml +++ b/config/rbac/onepassworditem_viewer_role.yaml @@ -1,4 +1,10 @@ -# permissions for end users to view onepassworditems. +# This rule is not used by the project onepassword-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants read-only access to onepassword.com resources. +# This role is intended for users who need visibility into these resources +# without permissions to modify them. It is ideal for monitoring purposes and limited-access viewing. + apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index a1f0177..87d74a3 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -24,12 +24,6 @@ rules: - patch - update - watch -- apiGroups: - - "" - resources: - - pods - verbs: - - get - apiGroups: - apps resources: @@ -45,25 +39,6 @@ rules: - patch - update - watch -- apiGroups: - - apps - resources: - - deployments - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - resources: - - deployments - - replicasets - verbs: - - get - apiGroups: - apps resources: @@ -106,17 +81,6 @@ rules: - onepassword.com resources: - '*' - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - onepassword.com - resources: - onepassworditems verbs: - create diff --git a/go.mod b/go.mod index 5cc7b63..941b1a8 100644 --- a/go.mod +++ b/go.mod @@ -1,49 +1,56 @@ module github.com/1Password/onepassword-operator -go 1.24 +go 1.24.0 -toolchain go1.24.4 +toolchain go1.24.5 require ( github.com/1Password/connect-sdk-go v1.5.3 github.com/1password/onepassword-sdk-go v0.3.1 github.com/go-logr/logr v1.4.2 - github.com/onsi/ginkgo/v2 v2.14.0 - github.com/onsi/gomega v1.30.0 + github.com/onsi/ginkgo/v2 v2.22.0 + github.com/onsi/gomega v1.36.1 github.com/stretchr/testify v1.10.0 - k8s.io/api v0.29.3 - k8s.io/apimachinery v0.29.3 - k8s.io/client-go v0.29.3 + k8s.io/api v0.33.0 + k8s.io/apimachinery v0.33.0 + k8s.io/client-go v0.33.0 k8s.io/kubectl v0.29.0 - sigs.k8s.io/controller-runtime v0.17.2 + sigs.k8s.io/controller-runtime v0.21.0 ) require ( + cel.dev/expr v0.19.1 // indirect + github.com/antlr4-go/antlr/v4 v4.13.0 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/davecgh/go-spew v1.1.1 // 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/dylibso/observe-sdk/go v0.0.0-20240819160327-2d926c5d788a // indirect github.com/emicklei/go-restful/v3 v3.12.0 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect - github.com/evanphx/json-patch/v5 v5.9.0 // indirect + github.com/evanphx/json-patch/v5 v5.9.11 // indirect github.com/extism/go-sdk v1.7.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.21.0 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect - github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect + github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/gobwas/glob v0.2.3 // 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/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-20210720184732-4bb14d4b1be1 // indirect + github.com/google/btree v1.1.3 // indirect + github.com/google/cel-go v0.23.2 // indirect + github.com/google/gnostic-models v0.6.9 // indirect + github.com/google/go-cmp v0.7.0 // indirect + github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect github.com/google/uuid v1.6.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 // indirect github.com/ianlancetaylor/demangle v0.0.0-20240805132620-81f5be970eca // indirect - github.com/imdario/mergo v0.3.16 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/mailru/easyjson v0.7.7 // indirect @@ -52,40 +59,58 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.19.0 // indirect - github.com/prometheus/client_model v0.6.0 // indirect - github.com/prometheus/common v0.51.1 // indirect - github.com/prometheus/procfs v0.13.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/client_golang v1.22.0 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.62.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.3.0 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/tetratelabs/wabin v0.0.0-20230304001439-f6f874872834 // indirect github.com/tetratelabs/wazero v1.9.0 // indirect github.com/uber/jaeger-client-go v2.30.0+incompatible // indirect github.com/uber/jaeger-lib v2.4.1+incompatible // indirect - go.opentelemetry.io/proto/otlp v1.3.1 // indirect + github.com/x448/float16 v0.8.4 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect + go.opentelemetry.io/otel v1.33.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 // indirect + go.opentelemetry.io/otel/metric v1.33.0 // indirect + go.opentelemetry.io/otel/sdk v1.33.0 // indirect + go.opentelemetry.io/otel/trace v1.33.0 // indirect + go.opentelemetry.io/proto/otlp v1.4.0 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81 // indirect + golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect golang.org/x/net v0.41.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect + golang.org/x/sync v0.15.0 // indirect golang.org/x/sys v0.33.0 // indirect golang.org/x/term v0.32.0 // indirect golang.org/x/text v0.26.0 // indirect - golang.org/x/time v0.5.0 // indirect + golang.org/x/time v0.9.0 // indirect golang.org/x/tools v0.33.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/protobuf v1.34.2 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect + google.golang.org/grpc v1.68.1 // indirect + google.golang.org/protobuf v1.36.5 // indirect + gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiextensions-apiserver v0.29.3 // indirect - k8s.io/component-base v0.29.3 // indirect - k8s.io/klog/v2 v2.120.1 // indirect - k8s.io/kube-openapi v0.0.0-20240322212309-b815d8309940 // indirect - k8s.io/utils v0.0.0-20240310230437-4693a0247e57 // indirect - sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + k8s.io/apiextensions-apiserver v0.33.0 // indirect + k8s.io/apiserver v0.33.0 // indirect + k8s.io/component-base v0.33.0 // indirect + k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect + k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect + sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect + sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect + sigs.k8s.io/randfill v1.0.0 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/go.sum b/go.sum index b4694f3..9338ad5 100644 --- a/go.sum +++ b/go.sum @@ -1,33 +1,47 @@ +cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4= +cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= github.com/1Password/connect-sdk-go v1.5.3 h1:KyjJ+kCKj6BwB2Y8tPM1Ixg5uIS6HsB0uWA8U38p/Uk= github.com/1Password/connect-sdk-go v1.5.3/go.mod h1:5rSymY4oIYtS4G3t0oMkGAXBeoYiukV3vkqlnEjIDJs= github.com/1password/onepassword-sdk-go v0.3.1 h1:dz0LrYuIh/HrZ7rxr8NMymikNLBIXhyj4NBmo5Tdamc= github.com/1password/onepassword-sdk-go v0.3.1/go.mod h1:kssODrGGqHtniqPR91ZPoCMEo79mKulKat7RaD1bunk= github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM= github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= +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/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/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/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/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/dylibso/observe-sdk/go v0.0.0-20240819160327-2d926c5d788a h1:UwSIFv5g5lIvbGgtf3tVwC7Ky9rmMFBp0RMs+6f6YqE= github.com/dylibso/observe-sdk/go v0.0.0-20240819160327-2d926c5d788a/go.mod h1:C8DzXehI4zAbrdlbtOByKX6pfivJTBiV9Jjqv56Yd9Q= github.com/emicklei/go-restful/v3 v3.12.0 h1:y2DdzBAURM29NFF94q6RaY4vjIH1rtwDapwQtU84iWk= github.com/emicklei/go-restful/v3 v3.12.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -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/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= +github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= github.com/extism/go-sdk v1.7.0 h1:yHbSa2JbcF60kjGsYiGEOcClfbknqCJchyh9TRibFWo= github.com/extism/go-sdk v1.7.0/go.mod h1:Dhuc1qcD0aqjdqJ3ZDyGdkZPEj/EHKVjbE4P+1XRMqc= +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.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= @@ -36,43 +50,50 @@ github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +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/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= 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/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= -github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= +github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/cel-go v0.23.2 h1:UdEe3CvQh3Nv+E/j9r1Y//WO0K0cSyD7/y0bzyLIMI4= +github.com/google/cel-go v0.23.2/go.mod h1:52Pb6QsDbC5kvgxvZhiL9QX1oZEkcUF/ZqaPx1J5Wwo= +github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= +github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= 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/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= 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/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 h1:TmHmbvxPmaegwhDubVz0lICL0J5Ka2vwTzhoePEXsGE= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0/go.mod h1:qztMSjm835F2bXf+5HKAPIS5qsmQDqZna/PgVt4rWtI= github.com/ianlancetaylor/demangle v0.0.0-20240805132620-81f5be970eca h1:T54Ema1DU8ngI+aef9ZhAhNGQhcRTrWxVeG07F+c/Rw= github.com/ianlancetaylor/demangle v0.0.0-20240805132620-81f5be970eca/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= -github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= -github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= +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/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/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 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= @@ -82,33 +103,43 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/onsi/ginkgo/v2 v2.14.0 h1:vSmGj2Z5YPb9JwCWT6z6ihcUvDhuXLc3sJiqd3jMKAY= -github.com/onsi/ginkgo/v2 v2.14.0/go.mod h1:JkUdW7JkN0V6rFvsHcJ478egV3XH9NxpD27Hal/PhZw= -github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= -github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= +github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg= +github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= +github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw= +github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= -github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= -github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos= -github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8= -github.com/prometheus/common v0.51.1 h1:eIjN50Bwglz6a/c3hAgSMcofL3nD+nFQkV6Dd4DsQCw= -github.com/prometheus/common v0.51.1/go.mod h1:lrWtQx+iDfn2mbH5GUzlH9TSHyfZpHkSiG1W7y3sF2Q= -github.com/prometheus/procfs v0.13.0 h1:GqzLlQyfsPbaEHaQkO7tbDlriv/4o5Hudv6OXHGKX7o= -github.com/prometheus/procfs v0.13.0/go.mod h1:cd4PFCR54QLnGKPaKGA6l+cfuNXtht43ZKY6tow0Y1g= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +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.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= +github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= +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.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= +github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= +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.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +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/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= +github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +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.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tetratelabs/wabin v0.0.0-20230304001439-f6f874872834 h1:ZF+QBjOI+tILZjBaFj3HgFonKXUcwgJ4djLb6i42S3Q= @@ -119,10 +150,28 @@ github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaO github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg= github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= +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.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -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.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= +go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= +go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 h1:5pojmb1U1AogINhN3SurB+zm/nIcusopeBNp42f45QM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA= +go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ= +go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M= +go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM= +go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM= +go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= +go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= +go.opentelemetry.io/proto/otlp v1.4.0 h1:TA9WRvW6zMwP+Ssb6fLoUIuirti1gGbP28GcKG1jgeg= +go.opentelemetry.io/proto/otlp v1.4.0/go.mod h1:PPBWZIP98o2ElSqI35IHfu7hIhSwvc5N38Jw8pXuGFY= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -134,8 +183,8 @@ go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81 h1:6R2FC06FonbXQ8pK11/PDFY6N6LWlf9KlzibaCapmqc= -golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -149,9 +198,10 @@ golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKl 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-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= +golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= @@ -161,8 +211,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= -golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= -golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= +golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= @@ -175,42 +225,54 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q= +google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 h1:8ZmaLZE4XWrtU3MyClkYqqtl6Oegr3235h7jxsDyqCY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= +google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0= +google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/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/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.29.3 h1:2ORfZ7+bGC3YJqGpV0KSDDEVf8hdGQ6A03/50vj8pmw= -k8s.io/api v0.29.3/go.mod h1:y2yg2NTyHUUkIoTC+phinTnEa3KFM6RZ3szxt014a80= -k8s.io/apiextensions-apiserver v0.29.3 h1:9HF+EtZaVpFjStakF4yVufnXGPRppWFEQ87qnO91YeI= -k8s.io/apiextensions-apiserver v0.29.3/go.mod h1:po0XiY5scnpJfFizNGo6puNU6Fq6D70UJY2Cb2KwAVc= -k8s.io/apimachinery v0.29.3 h1:2tbx+5L7RNvqJjn7RIuIKu9XTsIZ9Z5wX2G22XAa5EU= -k8s.io/apimachinery v0.29.3/go.mod h1:hx/S4V2PNW4OMg3WizRrHutyB5la0iCUbZym+W0EQIU= -k8s.io/client-go v0.29.3 h1:R/zaZbEAxqComZ9FHeQwOh3Y1ZUs7FaHKZdQtIc2WZg= -k8s.io/client-go v0.29.3/go.mod h1:tkDisCvgPfiRpxGnOORfkljmS+UrW+WtXAy2fTvXJB0= -k8s.io/component-base v0.29.3 h1:Oq9/nddUxlnrCuuR2K/jp6aflVvc0uDvxMzAWxnGzAo= -k8s.io/component-base v0.29.3/go.mod h1:Yuj33XXjuOk2BAaHsIGHhCKZQAgYKhqIxIjIr2UXYio= -k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= -k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20240322212309-b815d8309940 h1:qVoMaQV5t62UUvHe16Q3eb2c5HPzLHYzsi0Tu/xLndo= -k8s.io/kube-openapi v0.0.0-20240322212309-b815d8309940/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= +k8s.io/api v0.33.0 h1:yTgZVn1XEe6opVpP1FylmNrIFWuDqe2H0V8CT5gxfIU= +k8s.io/api v0.33.0/go.mod h1:CTO61ECK/KU7haa3qq8sarQ0biLq2ju405IZAd9zsiM= +k8s.io/apiextensions-apiserver v0.33.0 h1:d2qpYL7Mngbsc1taA4IjJPRJ9ilnsXIrndH+r9IimOs= +k8s.io/apiextensions-apiserver v0.33.0/go.mod h1:VeJ8u9dEEN+tbETo+lFkwaaZPg6uFKLGj5vyNEwwSzc= +k8s.io/apimachinery v0.33.0 h1:1a6kHrJxb2hs4t8EE5wuR/WxKDwGN1FKH3JvDtA0CIQ= +k8s.io/apimachinery v0.33.0/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= +k8s.io/apiserver v0.33.0 h1:QqcM6c+qEEjkOODHppFXRiw/cE2zP85704YrQ9YaBbc= +k8s.io/apiserver v0.33.0/go.mod h1:EixYOit0YTxt8zrO2kBU7ixAtxFce9gKGq367nFmqI8= +k8s.io/client-go v0.33.0 h1:UASR0sAYVUzs2kYuKn/ZakZlcs2bEHaizrrHUZg0G98= +k8s.io/client-go v0.33.0/go.mod h1:kGkd+l/gNGg8GYWAPr0xF1rRKvVWvzh9vmZAMXtaKOg= +k8s.io/component-base v0.33.0 h1:Ot4PyJI+0JAD9covDhwLp9UNkUja209OzsJ4FzScBNk= +k8s.io/component-base v0.33.0/go.mod h1:aXYZLbw3kihdkOPMDhWbjGCO6sg+luw554KP51t8qCU= +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-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= k8s.io/kubectl v0.29.0 h1:Oqi48gXjikDhrBF67AYuZRTcJV4lg2l42GmvsP7FmYI= k8s.io/kubectl v0.29.0/go.mod h1:0jMjGWIcMIQzmUaMgAzhSELv5WtHo2a8pq67DtviAJs= -k8s.io/utils v0.0.0-20240310230437-4693a0247e57 h1:gbqbevonBh57eILzModw6mrkbwM0gQBEuevE/AaBsHY= -k8s.io/utils v0.0.0-20240310230437-4693a0247e57/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -sigs.k8s.io/controller-runtime v0.17.2 h1:FwHwD1CTUemg0pW2otk7/U5/i5m2ymzvOXdbeGOUvw0= -sigs.k8s.io/controller-runtime v0.17.2/go.mod h1:+MngTvIQQQhfXtwfdGw/UOQ/aIaqsYywfCINOtwMO/s= -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= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 h1:jpcvIRr3GLoUoEKRkHKSmGjxb6lWwrBlJsXc+eUYQHM= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= +sigs.k8s.io/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8= +sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= +sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= +sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/structured-merge-diff/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc= +sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= 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/internal/controller/deployment_controller.go b/internal/controller/deployment_controller.go index b299cec..6ce9ed7 100644 --- a/internal/controller/deployment_controller.go +++ b/internal/controller/deployment_controller.go @@ -59,9 +59,9 @@ type DeploymentReconciler struct { OpAnnotationRegExp *regexp.Regexp } -//+kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=apps,resources=deployments/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=apps,resources=deployments/finalizers,verbs=update +// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=apps,resources=deployments/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=apps,resources=deployments/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. @@ -91,12 +91,12 @@ func (r *DeploymentReconciler) Reconcile(ctx context.Context, req ctrl.Request) return ctrl.Result{}, nil } - //If the deployment is not being deleted - if deployment.ObjectMeta.DeletionTimestamp.IsZero() { + // If the deployment is not being deleted + if deployment.DeletionTimestamp.IsZero() { // Adds a finalizer to the deployment if one does not exist. // This is so we can handle cleanup of associated secrets properly - if !utils.ContainsString(deployment.ObjectMeta.Finalizers, finalizer) { - deployment.ObjectMeta.Finalizers = append(deployment.ObjectMeta.Finalizers, finalizer) + if !utils.ContainsString(deployment.Finalizers, finalizer) { + deployment.Finalizers = append(deployment.Finalizers, finalizer) if err = r.Update(ctx, deployment); err != nil { return reconcile.Result{}, err } @@ -114,7 +114,7 @@ func (r *DeploymentReconciler) Reconcile(ctx context.Context, req ctrl.Request) } // The deployment has been marked for deletion. If the one password // finalizer is found there are cleanup tasks to perform - if utils.ContainsString(deployment.ObjectMeta.Finalizers, finalizer) { + if utils.ContainsString(deployment.Finalizers, finalizer) { secretName := annotations[op.NameAnnotation] if err = r.cleanupKubernetesSecretForDeployment(ctx, secretName, deployment); err != nil { @@ -133,13 +133,14 @@ func (r *DeploymentReconciler) Reconcile(ctx context.Context, req ctrl.Request) func (r *DeploymentReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&appsv1.Deployment{}). + Named("onepassword-deployment"). Complete(r) } func (r *DeploymentReconciler) cleanupKubernetesSecretForDeployment(ctx context.Context, secretName string, deletedDeployment *appsv1.Deployment) error { kubernetesSecret := &corev1.Secret{} - kubernetesSecret.ObjectMeta.Name = secretName - kubernetesSecret.ObjectMeta.Namespace = deletedDeployment.Namespace + kubernetesSecret.Name = secretName + kubernetesSecret.Namespace = deletedDeployment.Namespace if len(secretName) == 0 { return nil @@ -185,7 +186,7 @@ func (r *DeploymentReconciler) areMultipleDeploymentsUsingSecret(ctx context.Con } func (r *DeploymentReconciler) removeOnePasswordFinalizerFromDeployment(ctx context.Context, deployment *appsv1.Deployment) error { - deployment.ObjectMeta.Finalizers = utils.RemoveString(deployment.ObjectMeta.Finalizers, finalizer) + deployment.Finalizers = utils.RemoveString(deployment.Finalizers, finalizer) return r.Update(ctx, deployment) } diff --git a/internal/controller/deployment_controller_test.go b/internal/controller/deployment_controller_test.go index 7703fb8..a900725 100644 --- a/internal/controller/deployment_controller_test.go +++ b/internal/controller/deployment_controller_test.go @@ -82,10 +82,7 @@ var _ = Describe("Deployment controller", func() { time.Sleep(time.Millisecond * 100) Eventually(func() bool { err := k8sClient.Get(ctx, secretKey, createdSecret) - if err != nil { - return false - } - return true + return err == nil }, timeout, interval).Should(BeTrue()) Expect(createdSecret.Data).Should(Equal(item1.SecretData)) } @@ -190,10 +187,7 @@ var _ = Describe("Deployment controller", func() { updatedSecret := &v1.Secret{} Eventually(func() bool { err := k8sClient.Get(ctx, secretKey, updatedSecret) - if err != nil { - return false - } - return true + return err == nil }, timeout, interval).Should(BeTrue()) Expect(updatedSecret.Data).Should(Equal(item2.SecretData)) }) @@ -247,10 +241,7 @@ var _ = Describe("Deployment controller", func() { updatedSecret := &v1.Secret{} Eventually(func() bool { err := k8sClient.Get(ctx, secretKey, updatedSecret) - if err != nil { - return false - } - return true + return err == nil }, timeout, interval).Should(BeTrue()) Expect(updatedSecret.Data).Should(Equal(item1.SecretData)) }) diff --git a/internal/controller/onepassworditem_controller.go b/internal/controller/onepassworditem_controller.go index 078af65..fbd5b63 100644 --- a/internal/controller/onepassworditem_controller.go +++ b/internal/controller/onepassworditem_controller.go @@ -57,18 +57,18 @@ type OnePasswordItemReconciler struct { OpClient opclient.Client } -//+kubebuilder:rbac:groups=onepassword.com,resources=onepassworditems,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=onepassword.com,resources=onepassworditems/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=onepassword.com,resources=onepassworditems/finalizers,verbs=update +// +kubebuilder:rbac:groups=onepassword.com,resources=onepassworditems,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=onepassword.com,resources=onepassworditems/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=onepassword.com,resources=onepassworditems/finalizers,verbs=update -//+kubebuilder:rbac:groups="",resources=pods,verbs=get -//+kubebuilder:rbac:groups="",resources=pods;services;services/finalizers;endpoints;persistentvolumeclaims;events;configmaps;secrets;namespaces,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=apps,resources=daemonsets;deployments;replicasets;statefulsets,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=apps,resources=replicasets;deployments,verbs=get -//+kubebuilder:rbac:groups=apps,resourceNames=onepassword-connect-operator,resources=deployments/finalizers,verbs=update -//+kubebuilder:rbac:groups=onepassword.com,resources=*,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=monitoring.coreos.com,resources=servicemonitors,verbs=get;create -//+kubebuilder:rbac:groups=coordination.k8s.io,resources=leases,verbs=get;list;create;update +// +kubebuilder:rbac:groups="",resources=pods,verbs=get +// +kubebuilder:rbac:groups="",resources=pods;services;services/finalizers;endpoints;persistentvolumeclaims;events;configmaps;secrets;namespaces,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=apps,resources=daemonsets;deployments;replicasets;statefulsets,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=apps,resources=replicasets;deployments,verbs=get +// +kubebuilder:rbac:groups=apps,resourceNames=onepassword-connect-operator,resources=deployments/finalizers,verbs=update +// +kubebuilder:rbac:groups=onepassword.com,resources=*,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=monitoring.coreos.com,resources=servicemonitors,verbs=get;create +// +kubebuilder:rbac:groups=coordination.k8s.io,resources=leases,verbs=get;list;create;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. @@ -93,11 +93,11 @@ func (r *OnePasswordItemReconciler) Reconcile(ctx context.Context, req ctrl.Requ } // If the deployment is not being deleted - if onepassworditem.ObjectMeta.DeletionTimestamp.IsZero() { + if onepassworditem.DeletionTimestamp.IsZero() { // Adds a finalizer to the deployment if one does not exist. // This is so we can handle cleanup of associated secrets properly - if !utils.ContainsString(onepassworditem.ObjectMeta.Finalizers, finalizer) { - onepassworditem.ObjectMeta.Finalizers = append(onepassworditem.ObjectMeta.Finalizers, finalizer) + if !utils.ContainsString(onepassworditem.Finalizers, finalizer) { + onepassworditem.Finalizers = append(onepassworditem.Finalizers, finalizer) if err = r.Update(ctx, onepassworditem); err != nil { return ctrl.Result{}, err } @@ -117,7 +117,7 @@ func (r *OnePasswordItemReconciler) Reconcile(ctx context.Context, req ctrl.Requ return ctrl.Result{}, err } // If one password finalizer exists then we must cleanup associated secrets - if utils.ContainsString(onepassworditem.ObjectMeta.Finalizers, finalizer) { + if utils.ContainsString(onepassworditem.Finalizers, finalizer) { // Delete associated kubernetes secret if err = r.cleanupKubernetesSecret(ctx, onepassworditem); err != nil { @@ -125,7 +125,7 @@ func (r *OnePasswordItemReconciler) Reconcile(ctx context.Context, req ctrl.Requ } // Remove finalizer now that cleanup is complete - if err = r.removeFinalizer(ctx, onepassworditem); err != nil { + if err = r.removeOnePasswordFinalizerFromOnePasswordItem(ctx, onepassworditem); err != nil { return ctrl.Result{}, err } } @@ -136,21 +136,14 @@ func (r *OnePasswordItemReconciler) Reconcile(ctx context.Context, req ctrl.Requ func (r *OnePasswordItemReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&onepasswordv1.OnePasswordItem{}). + Named("onepassworditem"). Complete(r) } -func (r *OnePasswordItemReconciler) removeFinalizer(ctx context.Context, onePasswordItem *onepasswordv1.OnePasswordItem) error { - onePasswordItem.ObjectMeta.Finalizers = utils.RemoveString(onePasswordItem.ObjectMeta.Finalizers, finalizer) - if err := r.Update(ctx, onePasswordItem); err != nil { - return err - } - return nil -} - func (r *OnePasswordItemReconciler) cleanupKubernetesSecret(ctx context.Context, onePasswordItem *onepasswordv1.OnePasswordItem) error { kubernetesSecret := &corev1.Secret{} - kubernetesSecret.ObjectMeta.Name = onePasswordItem.Name - kubernetesSecret.ObjectMeta.Namespace = onePasswordItem.Namespace + kubernetesSecret.Name = onePasswordItem.Name + kubernetesSecret.Namespace = onePasswordItem.Namespace if err := r.Delete(ctx, kubernetesSecret); err != nil { if !errors.IsNotFound(err) { @@ -160,12 +153,12 @@ func (r *OnePasswordItemReconciler) cleanupKubernetesSecret(ctx context.Context, return nil } -func (r *OnePasswordItemReconciler) removeOnePasswordFinalizerFromOnePasswordItem(ctx context.Context, opSecret *onepasswordv1.OnePasswordItem) error { - opSecret.ObjectMeta.Finalizers = utils.RemoveString(opSecret.ObjectMeta.Finalizers, finalizer) - return r.Update(ctx, opSecret) +func (r *OnePasswordItemReconciler) removeOnePasswordFinalizerFromOnePasswordItem(ctx context.Context, onePasswordItem *onepasswordv1.OnePasswordItem) error { + onePasswordItem.Finalizers = utils.RemoveString(onePasswordItem.Finalizers, finalizer) + return r.Update(ctx, onePasswordItem) } -func (r *OnePasswordItemReconciler) handleOnePasswordItem(ctx context.Context, resource *onepasswordv1.OnePasswordItem, req ctrl.Request) error { +func (r *OnePasswordItemReconciler) handleOnePasswordItem(ctx context.Context, resource *onepasswordv1.OnePasswordItem, _ ctrl.Request) error { secretName := resource.GetName() labels := resource.Labels secretType := resource.Type diff --git a/internal/controller/onepassworditem_controller_test.go b/internal/controller/onepassworditem_controller_test.go index 701de59..945111f 100644 --- a/internal/controller/onepassworditem_controller_test.go +++ b/internal/controller/onepassworditem_controller_test.go @@ -3,6 +3,7 @@ package controller import ( "context" "fmt" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -60,20 +61,14 @@ var _ = Describe("OnePasswordItem controller", func() { created := &onepasswordv1.OnePasswordItem{} Eventually(func() bool { err := k8sClient.Get(ctx, key, created) - if err != nil { - return false - } - return true + return err == nil }, timeout, interval).Should(BeTrue()) By("Creating the K8s secret successfully") createdSecret := &v1.Secret{} Eventually(func() bool { err := k8sClient.Get(ctx, key, createdSecret) - if err != nil { - return false - } - return true + return err == nil }, timeout, interval).Should(BeTrue()) Expect(createdSecret.Data).Should(Equal(item1.SecretData)) @@ -101,10 +96,7 @@ var _ = Describe("OnePasswordItem controller", func() { updatedSecret := &v1.Secret{} Eventually(func() bool { err := k8sClient.Get(ctx, key, updatedSecret) - if err != nil { - return false - } - return true + return err == nil }, timeout, interval).Should(BeTrue()) Expect(updatedSecret.Data).Should(Equal(newDataByte)) @@ -175,20 +167,14 @@ var _ = Describe("OnePasswordItem controller", func() { created := &onepasswordv1.OnePasswordItem{} Eventually(func() bool { err := k8sClient.Get(ctx, key, created) - if err != nil { - return false - } - return true + return err == nil }, timeout, interval).Should(BeTrue()) By("Creating the K8s secret successfully") createdSecret := &v1.Secret{} Eventually(func() bool { err := k8sClient.Get(ctx, key, createdSecret) - if err != nil { - return false - } - return true + return err == nil }, timeout, interval).Should(BeTrue()) Expect(createdSecret.Data).Should(Equal(expectedData)) @@ -297,10 +283,7 @@ var _ = Describe("OnePasswordItem controller", func() { secret := &v1.Secret{} Eventually(func() bool { err := k8sClient.Get(ctx, key, secret) - if err != nil { - return false - } - return true + return err == nil }, timeout, interval).Should(BeTrue()) Expect(secret.Type).Should(Equal(v1.SecretType(customType))) }) @@ -344,10 +327,7 @@ var _ = Describe("OnePasswordItem controller", func() { createdSecret := &v1.Secret{} Eventually(func() bool { err := k8sClient.Get(ctx, key, createdSecret) - if err != nil { - return false - } - return true + return err == nil }, timeout, interval).Should(BeTrue()) Expect(createdSecret.Data).Should(HaveKeyWithValue("server.crt", fileContent)) @@ -381,20 +361,14 @@ var _ = Describe("OnePasswordItem controller", func() { secret := &v1.Secret{} Eventually(func() bool { err := k8sClient.Get(ctx, key, secret) - if err != nil { - return false - } - return true + return err == nil }, timeout, interval).Should(BeTrue()) By("Failing to update K8s secret") Eventually(func() bool { secret.Type = v1.SecretTypeBasicAuth err := k8sClient.Update(ctx, secret) - if err != nil { - return false - } - return true + return err == nil }, timeout, interval).Should(BeFalse()) }) diff --git a/internal/controller/suite_test.go b/internal/controller/suite_test.go index f8d1232..36f2f1b 100644 --- a/internal/controller/suite_test.go +++ b/internal/controller/suite_test.go @@ -26,6 +26,7 @@ package controller import ( "context" + "os" "path/filepath" "regexp" "testing" @@ -46,7 +47,7 @@ import ( onepasswordcomv1 "github.com/1Password/onepassword-operator/api/v1" "github.com/1Password/onepassword-operator/pkg/mocks" "github.com/1Password/onepassword-operator/pkg/onepassword/model" - //+kubebuilder:scaffold:imports + // +kubebuilder:scaffold:imports ) // These tests use Ginkgo (BDD-style Go testing framework). Refer to @@ -155,6 +156,11 @@ var _ = BeforeSuite(func() { ErrorIfCRDPathMissing: true, } + // Retrieve the first found binary directory to allow running tests from IDEs + if getFirstFoundEnvTestBinaryDir() != "" { + testEnv.BinaryAssetsDirectory = getFirstFoundEnvTestBinaryDir() + } + var err error // cfg is defined in this file globally. cfg, err = testEnv.Start() @@ -164,7 +170,7 @@ var _ = BeforeSuite(func() { err = onepasswordcomv1.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) - //+kubebuilder:scaffold:scheme + // +kubebuilder:scaffold:scheme k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) Expect(err).NotTo(HaveOccurred()) @@ -210,3 +216,26 @@ var _ = AfterSuite(func() { err := testEnv.Stop() Expect(err).NotTo(HaveOccurred()) }) + +// getFirstFoundEnvTestBinaryDir locates the first binary in the specified path. +// ENVTEST-based tests depend on specific binaries, usually located in paths set by +// controller-runtime. When running tests directly (e.g., via an IDE) without using +// Makefile targets, the 'BinaryAssetsDirectory' must be explicitly configured. +// +// This function streamlines the process by finding the required binaries, similar to +// setting the 'KUBEBUILDER_ASSETS' environment variable. To ensure the binaries are +// properly set up, run 'make setup-envtest' beforehand. +func getFirstFoundEnvTestBinaryDir() string { + basePath := filepath.Join("..", "..", "bin", "k8s") + entries, err := os.ReadDir(basePath) + if err != nil { + logf.Log.Error(err, "Failed to read directory", "path", basePath) + return "" + } + for _, entry := range entries { + if entry.IsDir() { + return filepath.Join(basePath, entry.Name()) + } + } + return "" +} diff --git a/pkg/kubernetessecrets/kubernetes_secrets_builder.go b/pkg/kubernetessecrets/kubernetes_secrets_builder.go index dfb282f..28e6e3d 100644 --- a/pkg/kubernetessecrets/kubernetes_secrets_builder.go +++ b/pkg/kubernetessecrets/kubernetes_secrets_builder.go @@ -2,7 +2,7 @@ package kubernetessecrets import ( "context" - errs "errors" + "errors" "fmt" "reflect" "regexp" @@ -11,7 +11,7 @@ import ( "github.com/1Password/onepassword-operator/pkg/onepassword/model" "github.com/1Password/onepassword-operator/pkg/utils" corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" + apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" kubeValidate "k8s.io/apimachinery/pkg/util/validation" @@ -26,11 +26,20 @@ const VersionAnnotation = OnepasswordPrefix + "/item-version" const ItemPathAnnotation = OnepasswordPrefix + "/item-path" const RestartDeploymentsAnnotation = OnepasswordPrefix + "/auto-restart" -var ErrCannotUpdateSecretType = errs.New("Cannot change secret type. Secret type is immutable") +var ErrCannotUpdateSecretType = errors.New("cannot change secret type: secret type is immutable") var log = logf.Log -func CreateKubernetesSecretFromItem(ctx context.Context, kubeClient kubernetesClient.Client, secretName, namespace string, item *model.Item, autoRestart string, labels map[string]string, secretType string, ownerRef *metav1.OwnerReference) error { +func CreateKubernetesSecretFromItem( + ctx context.Context, + kubeClient kubernetesClient.Client, + secretName, namespace string, + item *model.Item, + autoRestart string, + labels map[string]string, + secretType string, + ownerRef *metav1.OwnerReference, +) error { itemVersion := fmt.Sprint(item.Version) secretAnnotations := map[string]string{ VersionAnnotation: itemVersion, @@ -40,17 +49,20 @@ func CreateKubernetesSecretFromItem(ctx context.Context, kubeClient kubernetesCl if autoRestart != "" { _, err := utils.StringToBool(autoRestart) if err != nil { - return fmt.Errorf("Error parsing %v annotation on Secret %v. Must be true or false. Defaulting to false.", RestartDeploymentsAnnotation, secretName) + return fmt.Errorf("error parsing %v annotation on Secret %v. Must be true or false. Defaulting to false", + RestartDeploymentsAnnotation, secretName, + ) } secretAnnotations[RestartDeploymentsAnnotation] = autoRestart } // "Opaque" and "" secret types are treated the same by Kubernetes. - secret := BuildKubernetesSecretFromOnePasswordItem(secretName, namespace, secretAnnotations, labels, secretType, *item, ownerRef) + secret := BuildKubernetesSecretFromOnePasswordItem(secretName, namespace, secretAnnotations, labels, + secretType, *item, ownerRef) currentSecret := &corev1.Secret{} err := kubeClient.Get(ctx, types.NamespacedName{Name: secret.Name, Namespace: secret.Namespace}, currentSecret) - if err != nil && errors.IsNotFound(err) { + if err != nil && apierrors.IsNotFound(err) { log.Info(fmt.Sprintf("Creating Secret %v at namespace '%v'", secret.Name, secret.Namespace)) return kubeClient.Create(ctx, secret) } else if err != nil { @@ -75,20 +87,29 @@ func CreateKubernetesSecretFromItem(ctx context.Context, kubeClient kubernetesCl currentLabels := currentSecret.Labels if !reflect.DeepEqual(currentAnnotations, secretAnnotations) || !reflect.DeepEqual(currentLabels, labels) { log.Info(fmt.Sprintf("Updating Secret %v at namespace '%v'", secret.Name, secret.Namespace)) - currentSecret.ObjectMeta.Annotations = secretAnnotations - currentSecret.ObjectMeta.Labels = labels + currentSecret.Annotations = secretAnnotations + currentSecret.Labels = labels currentSecret.Data = secret.Data if err := kubeClient.Update(ctx, currentSecret); err != nil { - return fmt.Errorf("Kubernetes secret update failed: %w", err) + return fmt.Errorf("kubernetes secret update failed: %w", err) } return nil } - log.Info(fmt.Sprintf("Secret with name %v and version %v already exists", secret.Name, secret.Annotations[VersionAnnotation])) + log.Info(fmt.Sprintf("Secret with name %v and version %v already exists", + secret.Name, secret.Annotations[VersionAnnotation], + )) return nil } -func BuildKubernetesSecretFromOnePasswordItem(name, namespace string, annotations map[string]string, labels map[string]string, secretType string, item model.Item, ownerRef *metav1.OwnerReference) *corev1.Secret { +func BuildKubernetesSecretFromOnePasswordItem( + name, namespace string, + annotations map[string]string, + labels map[string]string, + secretType string, + item model.Item, + ownerRef *metav1.OwnerReference, +) *corev1.Secret { var ownerRefs []metav1.OwnerReference if ownerRef != nil { ownerRefs = []metav1.OwnerReference{*ownerRef} diff --git a/pkg/kubernetessecrets/kubernetes_secrets_builder_test.go b/pkg/kubernetessecrets/kubernetes_secrets_builder_test.go index a3e8039..21677c4 100644 --- a/pkg/kubernetessecrets/kubernetes_secrets_builder_test.go +++ b/pkg/kubernetessecrets/kubernetes_secrets_builder_test.go @@ -3,7 +3,6 @@ package kubernetessecrets import ( "context" "fmt" - "github.com/1Password/onepassword-operator/pkg/onepassword/model" "strings" "testing" @@ -12,26 +11,34 @@ import ( "k8s.io/apimachinery/pkg/types" kubeValidate "k8s.io/apimachinery/pkg/util/validation" "sigs.k8s.io/controller-runtime/pkg/client/fake" + + "github.com/1Password/onepassword-operator/pkg/onepassword/model" ) -const restartDeploymentAnnotation = "false" +const ( + restartDeploymentAnnotation = "false" + testNamespace = "test" + testItemUUID = "h46bb3jddvay7nxopfhvlwg35q" + testVaultUUID = "hfnjvi6aymbsnfc2xeeoheizda" +) func TestCreateKubernetesSecretFromOnePasswordItem(t *testing.T) { ctx := context.Background() secretName := "test-secret-name" - namespace := "test" + namespace := testNamespace item := model.Item{} item.Fields = generateFields(5) item.Version = 123 - item.VaultID = "hfnjvi6aymbsnfc2xeeoheizda" - item.ID = "h46bb3jddvay7nxopfhvlwg35q" + item.VaultID = testVaultUUID + item.ID = testItemUUID kubeClient := fake.NewClientBuilder().Build() secretLabels := map[string]string{} secretType := "" - err := CreateKubernetesSecretFromItem(ctx, kubeClient, secretName, namespace, &item, restartDeploymentAnnotation, secretLabels, secretType, nil) + err := CreateKubernetesSecretFromItem(ctx, kubeClient, secretName, namespace, &item, restartDeploymentAnnotation, + secretLabels, secretType, nil) if err != nil { t.Errorf("Unexpected error: %v", err) } @@ -48,13 +55,13 @@ func TestCreateKubernetesSecretFromOnePasswordItem(t *testing.T) { func TestKubernetesSecretFromOnePasswordItemOwnerReferences(t *testing.T) { ctx := context.Background() secretName := "test-secret-name" - namespace := "test" + namespace := testNamespace item := model.Item{} item.Fields = generateFields(5) item.Version = 123 - item.VaultID = "hfnjvi6aymbsnfc2xeeoheizda" - item.ID = "h46bb3jddvay7nxopfhvlwg35q" + item.VaultID = testVaultUUID + item.ID = testItemUUID kubeClient := fake.NewClientBuilder().Build() secretLabels := map[string]string{} @@ -66,15 +73,19 @@ func TestKubernetesSecretFromOnePasswordItemOwnerReferences(t *testing.T) { Name: "test-deployment", UID: types.UID("test-uid"), } - err := CreateKubernetesSecretFromItem(ctx, kubeClient, secretName, namespace, &item, restartDeploymentAnnotation, secretLabels, secretType, ownerRef) + err := CreateKubernetesSecretFromItem(ctx, kubeClient, secretName, namespace, &item, restartDeploymentAnnotation, + secretLabels, secretType, ownerRef) if err != nil { t.Errorf("Unexpected error: %v", err) } createdSecret := &corev1.Secret{} err = kubeClient.Get(ctx, types.NamespacedName{Name: secretName, Namespace: namespace}, createdSecret) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } // Check owner references. - gotOwnerRefs := createdSecret.ObjectMeta.OwnerReferences + gotOwnerRefs := createdSecret.OwnerReferences if len(gotOwnerRefs) != 1 { t.Errorf("Expected owner references length: 1 but got: %d", len(gotOwnerRefs)) } @@ -94,19 +105,20 @@ func TestKubernetesSecretFromOnePasswordItemOwnerReferences(t *testing.T) { func TestUpdateKubernetesSecretFromOnePasswordItem(t *testing.T) { ctx := context.Background() secretName := "test-secret-update" - namespace := "test" + namespace := testNamespace item := model.Item{} item.Fields = generateFields(5) item.Version = 123 - item.VaultID = "hfnjvi6aymbsnfc2xeeoheizda" - item.ID = "h46bb3jddvay7nxopfhvlwg35q" + item.VaultID = testVaultUUID + item.ID = testItemUUID kubeClient := fake.NewClientBuilder().Build() secretLabels := map[string]string{} secretType := "" - err := CreateKubernetesSecretFromItem(ctx, kubeClient, secretName, namespace, &item, restartDeploymentAnnotation, secretLabels, secretType, nil) + err := CreateKubernetesSecretFromItem(ctx, kubeClient, secretName, namespace, &item, restartDeploymentAnnotation, + secretLabels, secretType, nil) if err != nil { t.Errorf("Unexpected error: %v", err) @@ -116,9 +128,10 @@ func TestUpdateKubernetesSecretFromOnePasswordItem(t *testing.T) { newItem := model.Item{} newItem.Fields = generateFields(6) newItem.Version = 456 - newItem.VaultID = "hfnjvi6aymbsnfc2xeeoheizda" - newItem.ID = "h46bb3jddvay7nxopfhvlwg35q" - err = CreateKubernetesSecretFromItem(ctx, kubeClient, secretName, namespace, &newItem, restartDeploymentAnnotation, secretLabels, secretType, nil) + newItem.VaultID = testVaultUUID + newItem.ID = testItemUUID + err = CreateKubernetesSecretFromItem(ctx, kubeClient, secretName, namespace, &newItem, restartDeploymentAnnotation, + secretLabels, secretType, nil) if err != nil { t.Errorf("Unexpected error: %v", err) } @@ -210,19 +223,20 @@ func TestBuildKubernetesSecretFixesInvalidLabels(t *testing.T) { func TestCreateKubernetesTLSSecretFromOnePasswordItem(t *testing.T) { ctx := context.Background() secretName := "tls-test-secret-name" - namespace := "test" + namespace := testNamespace item := model.Item{} item.Fields = generateFields(5) item.Version = 123 - item.VaultID = "hfnjvi6aymbsnfc2xeeoheizda" - item.ID = "h46bb3jddvay7nxopfhvlwg35q" + item.VaultID = testVaultUUID + item.ID = testItemUUID kubeClient := fake.NewClientBuilder().Build() secretLabels := map[string]string{} secretType := "kubernetes.io/tls" - err := CreateKubernetesSecretFromItem(ctx, kubeClient, secretName, namespace, &item, restartDeploymentAnnotation, secretLabels, secretType, nil) + err := CreateKubernetesSecretFromItem(ctx, kubeClient, secretName, namespace, &item, restartDeploymentAnnotation, + secretLabels, secretType, nil) if err != nil { t.Errorf("Unexpected error: %v", err) } @@ -254,7 +268,9 @@ func compareAnnotationsToItem(annotations map[string]string, item model.Item, t } if annotations[RestartDeploymentsAnnotation] != "false" { - t.Errorf("Expected restart deployments annotation to be %v but was %v", restartDeploymentAnnotation, RestartDeploymentsAnnotation) + t.Errorf("Expected restart deployments annotation to be %v but was %v", + restartDeploymentAnnotation, RestartDeploymentsAnnotation, + ) } } @@ -286,7 +302,10 @@ func ParseVaultIdAndItemIdFromPath(path string) (string, string, error) { if len(splitPath) == 4 && splitPath[0] == "vaults" && splitPath[2] == "items" { return splitPath[1], splitPath[3], nil } - return "", "", fmt.Errorf("%q is not an acceptable path for One Password item. Must be of the format: `vaults/{vault_id}/items/{item_id}`", path) + return "", "", fmt.Errorf( + "%q is not an acceptable path for One Password item. Must be of the format: `vaults/{vault_id}/items/{item_id}`", + path, + ) } func validLabel(v string) bool { diff --git a/pkg/logs/log_levels.go b/pkg/logs/log_levels.go index e2b20f3..cb5ead1 100644 --- a/pkg/logs/log_levels.go +++ b/pkg/logs/log_levels.go @@ -1,7 +1,7 @@ package logs // A Level is a logging priority. Lower levels are more important. -// All levels have been multipled by -1 to ensure compatibilty +// All levels have been multiplied by -1 to ensure compatibility // between zapcore and logr const ( ErrorLevel = -2 diff --git a/pkg/mocks/mocksecretserver.go b/pkg/mocks/mocksecretserver.go index 4fa091f..6ecb46f 100644 --- a/pkg/mocks/mocksecretserver.go +++ b/pkg/mocks/mocksecretserver.go @@ -2,6 +2,7 @@ package mocks import ( "context" + "github.com/stretchr/testify/mock" "github.com/1Password/onepassword-operator/pkg/onepassword/model" diff --git a/pkg/onepassword/annotations.go b/pkg/onepassword/annotations.go index 652f671..d185ef9 100644 --- a/pkg/onepassword/annotations.go +++ b/pkg/onepassword/annotations.go @@ -45,13 +45,14 @@ func FilterAnnotations(annotations map[string]string, regex *regexp.Regexp) map[ func AreAnnotationsUsingSecrets(annotations map[string]string, secrets map[string]*corev1.Secret) bool { _, ok := secrets[annotations[NameAnnotation]] - if ok { - return true - } - return false + return ok } -func AppendAnnotationUpdatedSecret(annotations map[string]string, secrets map[string]*corev1.Secret, updatedDeploymentSecrets map[string]*corev1.Secret) map[string]*corev1.Secret { +func AppendAnnotationUpdatedSecret( + annotations map[string]string, + secrets map[string]*corev1.Secret, + updatedDeploymentSecrets map[string]*corev1.Secret, +) map[string]*corev1.Secret { secret, ok := secrets[annotations[NameAnnotation]] if ok { updatedDeploymentSecrets[secret.Name] = secret diff --git a/pkg/onepassword/annotations_test.go b/pkg/onepassword/annotations_test.go index 3949f78..69781a0 100644 --- a/pkg/onepassword/annotations_test.go +++ b/pkg/onepassword/annotations_test.go @@ -80,7 +80,7 @@ func TestGetNoAnnotationsForDeployment(t *testing.T) { } numAnnotations := len(filteredAnnotations) - if 0 != numAnnotations { + if numAnnotations != 0 { t.Errorf("Expected %v annotations got %v", 0, numAnnotations) } } diff --git a/pkg/onepassword/client/connect/connect.go b/pkg/onepassword/client/connect/connect.go index 0c77a3d..cfb88c3 100644 --- a/pkg/onepassword/client/connect/connect.go +++ b/pkg/onepassword/client/connect/connect.go @@ -58,7 +58,8 @@ func (c *Connect) GetItemsByTitle(ctx context.Context, vaultID, itemTitle string } // GetFileContent retrieves the content of a file from a 1Password item. -// As the Connect has a delay when synchronizing files and returns a 500 error in this case, this function implements a retry mechanism. +// As the Connect has a delay when synchronizing files and returns a 500 error in this case, +// this function implements a retry mechanism. func (c *Connect) GetFileContent(ctx context.Context, vaultID, itemID, fileID string) ([]byte, error) { const maxRetries = 5 const delay = 1 * time.Second diff --git a/pkg/onepassword/client/testing/item.go b/pkg/onepassword/client/testing/item.go index a44dd77..fd59d8d 100644 --- a/pkg/onepassword/client/testing/item.go +++ b/pkg/onepassword/client/testing/item.go @@ -1,7 +1,6 @@ package testing import ( - sdk "github.com/1password/onepassword-sdk-go" "testing" "time" @@ -9,6 +8,7 @@ import ( "github.com/1Password/connect-sdk-go/onepassword" "github.com/1Password/onepassword-operator/pkg/onepassword/model" + sdk "github.com/1password/onepassword-sdk-go" ) func CreateConnectItem() *onepassword.Item { diff --git a/pkg/onepassword/client/testing/mock/connect.go b/pkg/onepassword/client/testing/mock/connect.go index e511c64..c3cffbe 100644 --- a/pkg/onepassword/client/testing/mock/connect.go +++ b/pkg/onepassword/client/testing/mock/connect.go @@ -104,7 +104,11 @@ func (c *ConnectClientMock) GetFileContent(file *onepassword.File) ([]byte, erro return args.Get(0).([]byte), args.Error(1) } -func (c *ConnectClientMock) DownloadFile(file *onepassword.File, targetDirectory string, overwrite bool) (string, error) { +func (c *ConnectClientMock) DownloadFile( + file *onepassword.File, + targetDirectory string, + overwrite bool, +) (string, error) { // Only implement this if mocking is needed panic("implement me") } diff --git a/pkg/onepassword/client/testing/mock/sdk.go b/pkg/onepassword/client/testing/mock/sdk.go index 5940dfd..ebd16d8 100644 --- a/pkg/onepassword/client/testing/mock/sdk.go +++ b/pkg/onepassword/client/testing/mock/sdk.go @@ -23,7 +23,7 @@ type ItemAPIMock struct { } func (i *ItemAPIMock) Create(ctx context.Context, params sdk.ItemCreateParams) (sdk.Item, error) { - //TODO implement me + // TODO implement me panic("implement me") } @@ -33,27 +33,31 @@ func (i *ItemAPIMock) Get(ctx context.Context, vaultID string, itemID string) (s } func (i *ItemAPIMock) Put(ctx context.Context, item sdk.Item) (sdk.Item, error) { - //TODO implement me + // TODO implement me panic("implement me") } func (i *ItemAPIMock) Delete(ctx context.Context, vaultID string, itemID string) error { - //TODO implement me + // TODO implement me panic("implement me") } func (i *ItemAPIMock) Archive(ctx context.Context, vaultID string, itemID string) error { - //TODO implement me + // TODO implement me panic("implement me") } -func (i *ItemAPIMock) List(ctx context.Context, vaultID string, filters ...sdk.ItemListFilter) ([]sdk.ItemOverview, error) { +func (i *ItemAPIMock) List( + ctx context.Context, + vaultID string, + filters ...sdk.ItemListFilter, +) ([]sdk.ItemOverview, error) { args := i.Called(ctx, vaultID, filters) return args.Get(0).([]sdk.ItemOverview), args.Error(1) } func (i *ItemAPIMock) Shares() sdk.ItemsSharesAPI { - //TODO implement me + // TODO implement me panic("implement me") } @@ -66,17 +70,21 @@ type FileAPIMock struct { } func (f *FileAPIMock) Attach(ctx context.Context, item sdk.Item, fileParams sdk.FileCreateParams) (sdk.Item, error) { - //TODO implement me + // TODO implement me panic("implement me") } func (f *FileAPIMock) Delete(ctx context.Context, item sdk.Item, sectionID string, fieldID string) (sdk.Item, error) { - //TODO implement me + // TODO implement me panic("implement me") } -func (f *FileAPIMock) ReplaceDocument(ctx context.Context, item sdk.Item, docParams sdk.DocumentCreateParams) (sdk.Item, error) { - //TODO implement me +func (f *FileAPIMock) ReplaceDocument( + ctx context.Context, + item sdk.Item, + docParams sdk.DocumentCreateParams, +) (sdk.Item, error) { + // TODO implement me panic("implement me") } diff --git a/pkg/onepassword/connect_setup.go b/pkg/onepassword/connect_setup.go index 57bf057..b9e1904 100644 --- a/pkg/onepassword/connect_setup.go +++ b/pkg/onepassword/connect_setup.go @@ -32,11 +32,19 @@ func SetupConnect(ctx context.Context, kubeClient client.Client, deploymentNames return nil } -func setupDeployment(ctx context.Context, kubeClient client.Client, deploymentPath string, deploymentNamespace string) error { +func setupDeployment( + ctx context.Context, + kubeClient client.Client, + deploymentPath string, + deploymentNamespace string, +) error { existingDeployment := &appsv1.Deployment{} // check if deployment has already been created - err := kubeClient.Get(ctx, types.NamespacedName{Name: "onepassword-connect", Namespace: deploymentNamespace}, existingDeployment) + err := kubeClient.Get(ctx, types.NamespacedName{ + Name: "onepassword-connect", + Namespace: deploymentNamespace, + }, existingDeployment) if err != nil { if errors.IsNotFound(err) { logConnectSetup.Info("No existing Connect deployment found. Creating Deployment") @@ -46,7 +54,12 @@ func setupDeployment(ctx context.Context, kubeClient client.Client, deploymentPa return err } -func createDeployment(ctx context.Context, kubeClient client.Client, deploymentPath string, deploymentNamespace string) error { +func createDeployment( + ctx context.Context, + kubeClient client.Client, + deploymentPath string, + deploymentNamespace string, +) error { deployment, err := getDeploymentToCreate(deploymentPath, deploymentNamespace) if err != nil { return err @@ -81,8 +94,11 @@ func getDeploymentToCreate(deploymentPath string, deploymentNamespace string) (* func setupService(ctx context.Context, kubeClient client.Client, servicePath string, deploymentNamespace string) error { existingService := &corev1.Service{} - //check if service has already been created - err := kubeClient.Get(ctx, types.NamespacedName{Name: "onepassword-connect", Namespace: deploymentNamespace}, existingService) + // check if service has already been created + err := kubeClient.Get(ctx, types.NamespacedName{ + Name: "onepassword-connect", + Namespace: deploymentNamespace, + }, existingService) if err != nil { if errors.IsNotFound(err) { logConnectSetup.Info("No existing Connect service found. Creating Service") @@ -92,7 +108,12 @@ func setupService(ctx context.Context, kubeClient client.Client, servicePath str return err } -func createService(ctx context.Context, kubeClient client.Client, servicePath string, deploymentNamespace string) error { +func createService( + ctx context.Context, + kubeClient client.Client, + servicePath string, + deploymentNamespace string, +) error { f, err := os.Open(servicePath) if err != nil { return err diff --git a/pkg/onepassword/containers.go b/pkg/onepassword/containers.go index c0910a8..b19b28c 100644 --- a/pkg/onepassword/containers.go +++ b/pkg/onepassword/containers.go @@ -28,7 +28,11 @@ func AreContainersUsingSecrets(containers []corev1.Container, secrets map[string return false } -func AppendUpdatedContainerSecrets(containers []corev1.Container, secrets map[string]*corev1.Secret, updatedDeploymentSecrets map[string]*corev1.Secret) map[string]*corev1.Secret { +func AppendUpdatedContainerSecrets( + containers []corev1.Container, + secrets map[string]*corev1.Secret, + updatedDeploymentSecrets map[string]*corev1.Secret, +) map[string]*corev1.Secret { for i := 0; i < len(containers); i++ { envVariables := containers[i].Env for j := 0; j < len(envVariables); j++ { @@ -42,7 +46,7 @@ func AppendUpdatedContainerSecrets(containers []corev1.Container, secrets map[st envFromVariables := containers[i].EnvFrom for j := 0; j < len(envFromVariables); j++ { if envFromVariables[j].SecretRef != nil { - secret, ok := secrets[envFromVariables[j].SecretRef.LocalObjectReference.Name] + secret, ok := secrets[envFromVariables[j].SecretRef.Name] if ok { updatedDeploymentSecrets[secret.Name] = secret } diff --git a/pkg/onepassword/deployments.go b/pkg/onepassword/deployments.go index 6c97efb..4b20525 100644 --- a/pkg/onepassword/deployments.go +++ b/pkg/onepassword/deployments.go @@ -9,10 +9,15 @@ func IsDeploymentUsingSecrets(deployment *appsv1.Deployment, secrets map[string] volumes := deployment.Spec.Template.Spec.Volumes containers := deployment.Spec.Template.Spec.Containers containers = append(containers, deployment.Spec.Template.Spec.InitContainers...) - return AreAnnotationsUsingSecrets(deployment.Annotations, secrets) || AreContainersUsingSecrets(containers, secrets) || AreVolumesUsingSecrets(volumes, secrets) + return AreAnnotationsUsingSecrets(deployment.Annotations, secrets) || + AreContainersUsingSecrets(containers, secrets) || + AreVolumesUsingSecrets(volumes, secrets) } -func GetUpdatedSecretsForDeployment(deployment *appsv1.Deployment, secrets map[string]*corev1.Secret) map[string]*corev1.Secret { +func GetUpdatedSecretsForDeployment( + deployment *appsv1.Deployment, + secrets map[string]*corev1.Secret, +) map[string]*corev1.Secret { volumes := deployment.Spec.Template.Spec.Volumes containers := deployment.Spec.Template.Spec.Containers containers = append(containers, deployment.Spec.Template.Spec.InitContainers...) diff --git a/pkg/onepassword/items.go b/pkg/onepassword/items.go index 534c000..a1175e9 100644 --- a/pkg/onepassword/items.go +++ b/pkg/onepassword/items.go @@ -49,7 +49,10 @@ func ParseVaultAndItemFromPath(path string) (string, string, error) { if len(splitPath) == 4 && splitPath[0] == "vaults" && splitPath[2] == "items" { return splitPath[1], splitPath[3], nil } - return "", "", fmt.Errorf("%q is not an acceptable path for One Password item. Must be of the format: `vaults/{vault_id}/items/{item_id}`", path) + return "", "", fmt.Errorf( + "%q is not an acceptable path for One Password item. Must be of the format: `vaults/{vault_id}/items/{item_id}`", + path, + ) } func getVaultID(ctx context.Context, client opclient.Client, vaultNameOrID string) (string, error) { @@ -60,7 +63,7 @@ func getVaultID(ctx context.Context, client opclient.Client, vaultNameOrID strin } if len(vaults) == 0 { - return "", fmt.Errorf("No vaults found with identifier %q", vaultNameOrID) + return "", fmt.Errorf("no vaults found with identifier %q", vaultNameOrID) } oldestVault := vaults[0] @@ -70,7 +73,9 @@ func getVaultID(ctx context.Context, client opclient.Client, vaultNameOrID strin oldestVault = returnedVault } } - logger.Info(fmt.Sprintf("%v 1Password vaults found with the title %q. Will use vault %q as it is the oldest.", len(vaults), vaultNameOrID, oldestVault.ID)) + logger.Info(fmt.Sprintf("%v 1Password vaults found with the title %q. Will use vault %q as it is the oldest.", + len(vaults), vaultNameOrID, oldestVault.ID, + )) } vaultNameOrID = oldestVault.ID } @@ -85,7 +90,7 @@ func getItemID(ctx context.Context, client opclient.Client, vaultId, itemNameOrI } if len(items) == 0 { - return "", fmt.Errorf("No items found with identifier %q", itemNameOrID) + return "", fmt.Errorf("no items found with identifier %q", itemNameOrID) } oldestItem := items[0] @@ -95,7 +100,9 @@ func getItemID(ctx context.Context, client opclient.Client, vaultId, itemNameOrI oldestItem = returnedItem } } - logger.Info(fmt.Sprintf("%v 1Password items found with the title %q. Will use item %q as it is the oldest.", len(items), itemNameOrID, oldestItem.ID)) + logger.Info(fmt.Sprintf("%v 1Password items found with the title %q. Will use item %q as it is the oldest.", + len(items), itemNameOrID, oldestItem.ID, + )) } itemNameOrID = oldestItem.ID } diff --git a/pkg/onepassword/model/item.go b/pkg/onepassword/model/item.go index 9f4af44..cab76ce 100644 --- a/pkg/onepassword/model/item.go +++ b/pkg/onepassword/model/item.go @@ -24,9 +24,7 @@ func (i *Item) FromConnectItem(item *connect.Item) { i.VaultID = item.Vault.ID i.Version = item.Version - for _, tag := range item.Tags { - i.Tags = append(i.Tags, tag) - } + i.Tags = append(i.Tags, item.Tags...) for _, field := range item.Fields { i.Fields = append(i.Fields, ItemField{ diff --git a/pkg/onepassword/secret_update_handler.go b/pkg/onepassword/secret_update_handler.go index a1e0e1e..53f3d32 100644 --- a/pkg/onepassword/secret_update_handler.go +++ b/pkg/onepassword/secret_update_handler.go @@ -18,12 +18,16 @@ import ( logf "sigs.k8s.io/controller-runtime/pkg/log" ) -const envHostVariable = "OP_HOST" +// const envHostVariable = "OP_HOST" const lockTag = "operator.1password.io:ignore-secret" var log = logf.Log.WithName("update_op_kubernetes_secrets_task") -func NewManager(kubernetesClient client.Client, opClient opclient.Client, shouldAutoRestartDeploymentsGlobal bool) *SecretUpdateHandler { +func NewManager( + kubernetesClient client.Client, + opClient opclient.Client, + shouldAutoRestartDeploymentsGlobal bool, +) *SecretUpdateHandler { return &SecretUpdateHandler{ client: kubernetesClient, opClient: opClient, @@ -46,7 +50,10 @@ func (h *SecretUpdateHandler) UpdateKubernetesSecretsTask(ctx context.Context) e return h.restartDeploymentsWithUpdatedSecrets(ctx, updatedKubernetesSecrets) } -func (h *SecretUpdateHandler) restartDeploymentsWithUpdatedSecrets(ctx context.Context, updatedSecretsByNamespace map[string]map[string]*corev1.Secret) error { +func (h *SecretUpdateHandler) restartDeploymentsWithUpdatedSecrets( + ctx context.Context, + updatedSecretsByNamespace map[string]map[string]*corev1.Secret, +) error { // No secrets to update. Exit if len(updatedSecretsByNamespace) == 0 || updatedSecretsByNamespace == nil { return nil @@ -83,14 +90,18 @@ func (h *SecretUpdateHandler) restartDeploymentsWithUpdatedSecrets(ctx context.C } } - log.V(logs.DebugLevel).Info(fmt.Sprintf("Deployment %q at namespace %q is up to date", deployment.GetName(), deployment.Namespace)) + log.V(logs.DebugLevel).Info(fmt.Sprintf("Deployment %q at namespace %q is up to date", + deployment.GetName(), deployment.Namespace, + )) } return nil } func (h *SecretUpdateHandler) restartDeployment(ctx context.Context, deployment *appsv1.Deployment) { - log.Info(fmt.Sprintf("Deployment %q at namespace %q references an updated secret. Restarting", deployment.GetName(), deployment.Namespace)) + log.Info(fmt.Sprintf("Deployment %q at namespace %q references an updated secret. Restarting", + deployment.GetName(), deployment.Namespace, + )) if deployment.Spec.Template.Annotations == nil { deployment.Spec.Template.Annotations = map[string]string{} } @@ -101,7 +112,9 @@ func (h *SecretUpdateHandler) restartDeployment(ctx context.Context, deployment } } -func (h *SecretUpdateHandler) updateKubernetesSecrets(ctx context.Context) (map[string]map[string]*corev1.Secret, error) { +func (h *SecretUpdateHandler) updateKubernetesSecrets(ctx context.Context) ( + map[string]map[string]*corev1.Secret, error, +) { secrets := &corev1.SecretList{} err := h.client.List(ctx, secrets) if err != nil { @@ -123,7 +136,9 @@ func (h *SecretUpdateHandler) updateKubernetesSecrets(ctx context.Context) (map[ item, err := GetOnePasswordItemByPath(ctx, h.opClient, OnePasswordItemPath) if err != nil { - log.Error(err, fmt.Sprintf("failed to retrieve 1Password item at path %s for secret %s", secret.Annotations[ItemPathAnnotation], secret.Name)) + log.Error(err, fmt.Sprintf("failed to retrieve 1Password item at path %s for secret %s", + secret.Annotations[ItemPathAnnotation], secret.Name, + )) continue } @@ -132,7 +147,11 @@ func (h *SecretUpdateHandler) updateKubernetesSecrets(ctx context.Context) (map[ if currentVersion != itemVersion || secret.Annotations[ItemPathAnnotation] != itemPathString { if isItemLockedForForcedRestarts(item) { - log.V(logs.DebugLevel).Info(fmt.Sprintf("Secret '%v' has been updated in 1Password but is set to be ignored. Updates to an ignored secret will not trigger an update to a kubernetes secret or a rolling restart.", secret.GetName())) + log.V(logs.DebugLevel).Info(fmt.Sprintf( + "Secret '%v' has been updated in 1Password but is set to be ignored. "+ + "Updates to an ignored secret will not trigger an update to a kubernetes secret or a rolling restart.", + secret.GetName(), + )) secret.Annotations[VersionAnnotation] = itemVersion secret.Annotations[ItemPathAnnotation] = itemPathString if err := h.client.Update(ctx, &secret); err != nil { @@ -145,7 +164,9 @@ func (h *SecretUpdateHandler) updateKubernetesSecrets(ctx context.Context) (map[ secret.Annotations[VersionAnnotation] = itemVersion secret.Annotations[ItemPathAnnotation] = itemPathString secret.Data = kubeSecrets.BuildKubernetesSecretData(item.Fields, item.Files) - log.V(logs.DebugLevel).Info(fmt.Sprintf("New secret path: %v and version: %v", secret.Annotations[ItemPathAnnotation], secret.Annotations[VersionAnnotation])) + log.V(logs.DebugLevel).Info(fmt.Sprintf("New secret path: %v and version: %v", + secret.Annotations[ItemPathAnnotation], secret.Annotations[VersionAnnotation], + )) if err := h.client.Update(ctx, &secret); err != nil { log.Error(err, fmt.Sprintf("failed to update secret %s to version %s", secret.Name, itemVersion)) continue @@ -171,10 +192,7 @@ func isItemLockedForForcedRestarts(item *model.Item) bool { func isUpdatedSecret(secretName string, updatedSecrets map[string]*corev1.Secret) bool { _, ok := updatedSecrets[secretName] - if ok { - return true - } - return false + return ok } func (h *SecretUpdateHandler) getIsSetForAutoRestartByNamespaceMap(ctx context.Context) (map[string]bool, error) { @@ -209,16 +227,22 @@ func (h *SecretUpdateHandler) getPathFromOnePasswordItem(secret corev1.Secret) s return secret.Annotations[ItemPathAnnotation] } -func isSecretSetForAutoRestart(secret *corev1.Secret, deployment *appsv1.Deployment, setForAutoRestartByNamespace map[string]bool) bool { +func isSecretSetForAutoRestart( + secret *corev1.Secret, + deployment *appsv1.Deployment, + setForAutoRestartByNamespace map[string]bool, +) bool { restartDeployment := secret.Annotations[RestartDeploymentsAnnotation] - //If annotation for auto restarts for deployment is not set. Check for the annotation on its namepsace + // If annotation for auto restarts for deployment is not set. Check for the annotation on its namepsace if restartDeployment == "" { return isDeploymentSetForAutoRestart(deployment, setForAutoRestartByNamespace) } restartDeploymentBool, err := utils.StringToBool(restartDeployment) if err != nil { - log.Error(err, fmt.Sprintf("Error parsing %s annotation on Secret %s. Must be true or false. Defaulting to false.", RestartDeploymentsAnnotation, secret.Name)) + log.Error(err, fmt.Sprintf("Error parsing %s annotation on Secret %s. Must be true or false. Defaulting to false.", + RestartDeploymentsAnnotation, secret.Name, + )) return false } return restartDeploymentBool @@ -226,14 +250,17 @@ func isSecretSetForAutoRestart(secret *corev1.Secret, deployment *appsv1.Deploym func isDeploymentSetForAutoRestart(deployment *appsv1.Deployment, setForAutoRestartByNamespace map[string]bool) bool { restartDeployment := deployment.Annotations[RestartDeploymentsAnnotation] - //If annotation for auto restarts for deployment is not set. Check for the annotation on its namepsace + // If annotation for auto restarts for deployment is not set. Check for the annotation on its namepsace if restartDeployment == "" { return setForAutoRestartByNamespace[deployment.Namespace] } restartDeploymentBool, err := utils.StringToBool(restartDeployment) if err != nil { - log.Error(err, fmt.Sprintf("Error parsing %s annotation on Deployment %s. Must be true or false. Defaulting to false.", RestartDeploymentsAnnotation, deployment.Name)) + log.Error(err, fmt.Sprintf( + "Error parsing %s annotation on Deployment %s. Must be true or false. Defaulting to false.", + RestartDeploymentsAnnotation, deployment.Name, + )) return false } return restartDeploymentBool @@ -241,14 +268,16 @@ func isDeploymentSetForAutoRestart(deployment *appsv1.Deployment, setForAutoRest func (h *SecretUpdateHandler) isNamespaceSetToAutoRestart(namespace *corev1.Namespace) bool { restartDeployment := namespace.Annotations[RestartDeploymentsAnnotation] - //If annotation for auto restarts for deployment is not set. Check environment variable set on the operator + // If annotation for auto restarts for deployment is not set. Check environment variable set on the operator if restartDeployment == "" { return h.shouldAutoRestartDeploymentsGlobal } restartDeploymentBool, err := utils.StringToBool(restartDeployment) if err != nil { - log.Error(err, fmt.Sprintf("Error parsing %s annotation on Namespace %s. Must be true or false. Defaulting to false.", RestartDeploymentsAnnotation, namespace.Name)) + log.Error(err, fmt.Sprintf("Error parsing %s annotation on Namespace %s. Must be true or false. Defaulting to false.", + RestartDeploymentsAnnotation, namespace.Name, + )) return false } return restartDeploymentBool diff --git a/pkg/onepassword/secret_update_handler_test.go b/pkg/onepassword/secret_update_handler_test.go index e3d622f..508b506 100644 --- a/pkg/onepassword/secret_update_handler_test.go +++ b/pkg/onepassword/secret_update_handler_test.go @@ -43,7 +43,6 @@ type testUpdateSecretTask struct { existingSecret *corev1.Secret expectedError error expectedResultSecret *corev1.Secret - expectedEvents []string opItem map[string]string expectedRestart bool globalAutoRestartEnabled bool @@ -63,6 +62,9 @@ var defaultNamespace = &corev1.Namespace{ }, } +// TODO: Refactor test cases to avoid duplication. +// +//nolint:dupl var tests = []testUpdateSecretTask{ { testName: "Test unrelated deployment is not restarted with an updated secret", @@ -838,9 +840,10 @@ func TestUpdateSecretHandler(t *testing.T) { assert.Equal(t, testData.expectedResultSecret.Annotations[VersionAnnotation], secret.Annotations[VersionAnnotation]) } - //check if deployment has been restarted + // check if deployment has been restarted deployment := &appsv1.Deployment{} err = cl.Get(ctx, types.NamespacedName{Name: testData.existingDeployment.Name, Namespace: namespace}, deployment) + assert.NoError(t, err) _, ok := deployment.Spec.Template.Annotations[RestartAnnotation] if ok { @@ -849,7 +852,7 @@ func TestUpdateSecretHandler(t *testing.T) { assert.False(t, testData.expectedRestart, "Deployment was restarted but should not have been.") } - oldPodTemplateAnnotations := testData.existingDeployment.Spec.Template.ObjectMeta.Annotations + oldPodTemplateAnnotations := testData.existingDeployment.Spec.Template.Annotations newPodTemplateAnnotations := deployment.Spec.Template.Annotations for name, expected := range oldPodTemplateAnnotations { actual, ok := newPodTemplateAnnotations[name] diff --git a/pkg/onepassword/volumes.go b/pkg/onepassword/volumes.go index cb1c1b1..739fc1d 100644 --- a/pkg/onepassword/volumes.go +++ b/pkg/onepassword/volumes.go @@ -10,13 +10,14 @@ func AreVolumesUsingSecrets(volumes []corev1.Volume, secrets map[string]*corev1. return false } } - if len(volumes) == 0 { - return false - } - return true + return len(volumes) > 0 } -func AppendUpdatedVolumeSecrets(volumes []corev1.Volume, secrets map[string]*corev1.Secret, updatedDeploymentSecrets map[string]*corev1.Secret) map[string]*corev1.Secret { +func AppendUpdatedVolumeSecrets( + volumes []corev1.Volume, + secrets map[string]*corev1.Secret, + updatedDeploymentSecrets map[string]*corev1.Secret, +) map[string]*corev1.Secret { for i := 0; i < len(volumes); i++ { secret := IsVolumeUsingSecret(volumes[i], secrets) if secret != nil { diff --git a/version/version.go b/version/version.go index c20b8b3..7825064 100644 --- a/version/version.go +++ b/version/version.go @@ -2,5 +2,5 @@ package version var ( OperatorVersion = "1.9.1" - OperatorSDKVersion = "1.34.1" + OperatorSDKVersion = "1.41.1" )