Kubernetes Operator that creates Service Endpoints from Secrets
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

initial commit

Evan Jarrett 67d05e3e

+2650
+3
.dockerignore
··· 1 + # More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file 2 + # Ignore build and test binaries. 3 + bin/
+27
.gitignore
··· 1 + # Binaries for programs and plugins 2 + *.exe 3 + *.exe~ 4 + *.dll 5 + *.so 6 + *.dylib 7 + bin/* 8 + Dockerfile.cross 9 + 10 + # Test binary, built with `go test -c` 11 + *.test 12 + 13 + # Output of the go coverage tool, specifically when used with LiteIDE 14 + *.out 15 + 16 + # Go workspace file 17 + go.work 18 + 19 + # Kubernetes Generated files - skip generated files, except for vendored files 20 + !vendor/**/zz_generated.* 21 + 22 + # editor and IDE paraphernalia 23 + .idea 24 + .vscode 25 + *.swp 26 + *.swo 27 + *~
+40
.golangci.yml
··· 1 + run: 2 + timeout: 5m 3 + allow-parallel-runners: true 4 + 5 + issues: 6 + # don't skip warning about doc comments 7 + # don't exclude the default set of lint 8 + exclude-use-default: false 9 + # restore some of the defaults 10 + # (fill in the rest as needed) 11 + exclude-rules: 12 + - path: "api/*" 13 + linters: 14 + - lll 15 + - path: "internal/*" 16 + linters: 17 + - dupl 18 + - lll 19 + linters: 20 + disable-all: true 21 + enable: 22 + - dupl 23 + - errcheck 24 + - exportloopref 25 + - goconst 26 + - gocyclo 27 + - gofmt 28 + - goimports 29 + - gosimple 30 + - govet 31 + - ineffassign 32 + - lll 33 + - misspell 34 + - nakedret 35 + - prealloc 36 + - staticcheck 37 + - typecheck 38 + - unconvert 39 + - unparam 40 + - unused
+33
Dockerfile
··· 1 + # Build the manager binary 2 + FROM golang:1.21 AS builder 3 + ARG TARGETOS 4 + ARG TARGETARCH 5 + 6 + WORKDIR /workspace 7 + # Copy the Go Modules manifests 8 + COPY go.mod go.mod 9 + COPY go.sum go.sum 10 + # cache deps before building and copying source so that we don't need to re-download as much 11 + # and so that source changes don't invalidate our downloaded layer 12 + RUN go mod download 13 + 14 + # Copy the go source 15 + COPY cmd/main.go cmd/main.go 16 + COPY api/ api/ 17 + COPY internal/controller/ internal/controller/ 18 + 19 + # Build 20 + # the GOARCH has not a default value to allow the binary be built according to the host where the command 21 + # was called. For example, if we call make docker-build in a local env which has the Apple Silicon M1 SO 22 + # the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore, 23 + # by leaving it empty we can ensure that the container and binary shipped on it will have the same platform. 24 + RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager cmd/main.go 25 + 26 + # Use distroless as minimal base image to package the manager binary 27 + # Refer to https://github.com/GoogleContainerTools/distroless for more details 28 + FROM gcr.io/distroless/static:nonroot 29 + WORKDIR / 30 + COPY --from=builder /workspace/manager . 31 + USER 65532:65532 32 + 33 + ENTRYPOINT ["/manager"]
+322
Makefile
··· 1 + # VERSION defines the project version for the bundle. 2 + # Update this value when you upgrade the version of your project. 3 + # To re-generate a bundle for another specific version without changing the standard setup, you can: 4 + # - use the VERSION as arg of the bundle target (e.g make bundle VERSION=0.0.2) 5 + # - use environment variables to overwrite this value (e.g export VERSION=0.0.2) 6 + VERSION ?= 0.0.1 7 + 8 + # CHANNELS define the bundle channels used in the bundle. 9 + # Add a new line here if you would like to change its default config. (E.g CHANNELS = "candidate,fast,stable") 10 + # To re-generate a bundle for other specific channels without changing the standard setup, you can: 11 + # - use the CHANNELS as arg of the bundle target (e.g make bundle CHANNELS=candidate,fast,stable) 12 + # - use environment variables to overwrite this value (e.g export CHANNELS="candidate,fast,stable") 13 + ifneq ($(origin CHANNELS), undefined) 14 + BUNDLE_CHANNELS := --channels=$(CHANNELS) 15 + endif 16 + 17 + # DEFAULT_CHANNEL defines the default channel used in the bundle. 18 + # Add a new line here if you would like to change its default config. (E.g DEFAULT_CHANNEL = "stable") 19 + # To re-generate a bundle for any other default channel without changing the default setup, you can: 20 + # - use the DEFAULT_CHANNEL as arg of the bundle target (e.g make bundle DEFAULT_CHANNEL=stable) 21 + # - use environment variables to overwrite this value (e.g export DEFAULT_CHANNEL="stable") 22 + ifneq ($(origin DEFAULT_CHANNEL), undefined) 23 + BUNDLE_DEFAULT_CHANNEL := --default-channel=$(DEFAULT_CHANNEL) 24 + endif 25 + BUNDLE_METADATA_OPTS ?= $(BUNDLE_CHANNELS) $(BUNDLE_DEFAULT_CHANNEL) 26 + 27 + # IMAGE_TAG_BASE defines the docker.io namespace and part of the image name for remote images. 28 + # This variable is used to construct full image tags for bundle and catalog images. 29 + # 30 + # For example, running 'make bundle-build bundle-push catalog-build catalog-push' will build and push both 31 + # j5t.io/secret-service-operator-bundle:$VERSION and j5t.io/secret-service-operator-catalog:$VERSION. 32 + IMAGE_TAG_BASE ?= j5t.io/secret-service-operator 33 + 34 + # BUNDLE_IMG defines the image:tag used for the bundle. 35 + # You can use it as an arg. (E.g make bundle-build BUNDLE_IMG=<some-registry>/<project-name-bundle>:<tag>) 36 + BUNDLE_IMG ?= $(IMAGE_TAG_BASE)-bundle:v$(VERSION) 37 + 38 + # BUNDLE_GEN_FLAGS are the flags passed to the operator-sdk generate bundle command 39 + BUNDLE_GEN_FLAGS ?= -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS) 40 + 41 + # USE_IMAGE_DIGESTS defines if images are resolved via tags or digests 42 + # You can enable this value if you would like to use SHA Based Digests 43 + # To enable set flag to true 44 + USE_IMAGE_DIGESTS ?= false 45 + ifeq ($(USE_IMAGE_DIGESTS), true) 46 + BUNDLE_GEN_FLAGS += --use-image-digests 47 + endif 48 + 49 + # Set the Operator SDK version to use. By default, what is installed on the system is used. 50 + # This is useful for CI or a project to utilize a specific version of the operator-sdk toolkit. 51 + OPERATOR_SDK_VERSION ?= v1.37.0 52 + # Image URL to use all building/pushing image targets 53 + IMG ?= controller:latest 54 + # ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. 55 + ENVTEST_K8S_VERSION = 1.29.0 56 + 57 + # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) 58 + ifeq (,$(shell go env GOBIN)) 59 + GOBIN=$(shell go env GOPATH)/bin 60 + else 61 + GOBIN=$(shell go env GOBIN) 62 + endif 63 + 64 + # CONTAINER_TOOL defines the container tool to be used for building images. 65 + # Be aware that the target commands are only tested with Docker which is 66 + # scaffolded by default. However, you might want to replace it to use other 67 + # tools. (i.e. podman) 68 + CONTAINER_TOOL ?= podman 69 + 70 + # Setting SHELL to bash allows bash commands to be executed by recipes. 71 + # Options are set to exit when a recipe line exits non-zero or a piped command fails. 72 + SHELL = /usr/bin/env bash -o pipefail 73 + .SHELLFLAGS = -ec 74 + 75 + .PHONY: all 76 + all: build 77 + 78 + ##@ General 79 + 80 + # The help target prints out all targets with their descriptions organized 81 + # beneath their categories. The categories are represented by '##@' and the 82 + # target descriptions by '##'. The awk command is responsible for reading the 83 + # entire set of makefiles included in this invocation, looking for lines of the 84 + # file as xyz: ## something, and then pretty-format the target and help. Then, 85 + # if there's a line with ##@ something, that gets pretty-printed as a category. 86 + # More info on the usage of ANSI control characters for terminal formatting: 87 + # https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters 88 + # More info on the awk command: 89 + # http://linuxcommand.org/lc3_adv_awk.php 90 + 91 + .PHONY: help 92 + help: ## Display this help. 93 + @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) 94 + 95 + ##@ Development 96 + 97 + .PHONY: manifests 98 + manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. 99 + $(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases 100 + 101 + .PHONY: generate 102 + generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. 103 + $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." 104 + 105 + .PHONY: fmt 106 + fmt: ## Run go fmt against code. 107 + go fmt ./... 108 + 109 + .PHONY: vet 110 + vet: ## Run go vet against code. 111 + go vet ./... 112 + 113 + .PHONY: test 114 + test: manifests generate fmt vet envtest ## Run tests. 115 + KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $$(go list ./... | grep -v /e2e) -coverprofile cover.out 116 + 117 + # Utilize Kind or modify the e2e tests to load the image locally, enabling compatibility with other vendors. 118 + .PHONY: test-e2e # Run the e2e tests against a Kind k8s instance that is spun up. 119 + test-e2e: 120 + go test ./test/e2e/ -v -ginkgo.v 121 + 122 + .PHONY: lint 123 + lint: golangci-lint ## Run golangci-lint linter & yamllint 124 + $(GOLANGCI_LINT) run 125 + 126 + .PHONY: lint-fix 127 + lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes 128 + $(GOLANGCI_LINT) run --fix 129 + 130 + ##@ Build 131 + 132 + .PHONY: build 133 + build: manifests generate fmt vet ## Build manager binary. 134 + go build -o bin/manager cmd/main.go 135 + 136 + .PHONY: run 137 + run: manifests generate fmt vet ## Run a controller from your host. 138 + go run ./cmd/main.go 139 + 140 + # If you wish to build the manager image targeting other platforms you can use the --platform flag. 141 + # (i.e. docker build --platform linux/arm64). However, you must enable docker buildKit for it. 142 + # More info: https://docs.docker.com/develop/develop-images/build_enhancements/ 143 + .PHONY: docker-build 144 + docker-build: ## Build docker image with the manager. 145 + $(CONTAINER_TOOL) build -t ${IMG} . 146 + 147 + .PHONY: docker-push 148 + docker-push: ## Push docker image with the manager. 149 + $(CONTAINER_TOOL) push ${IMG} 150 + 151 + # PLATFORMS defines the target platforms for the manager image be built to provide support to multiple 152 + # architectures. (i.e. make docker-buildx IMG=myregistry/mypoperator:0.0.1). To use this option you need to: 153 + # - be able to use docker buildx. More info: https://docs.docker.com/build/buildx/ 154 + # - have enabled BuildKit. More info: https://docs.docker.com/develop/develop-images/build_enhancements/ 155 + # - be able to push the image to your registry (i.e. if you do not set a valid value via IMG=<myregistry/image:<tag>> then the export will fail) 156 + # To adequately provide solutions that are compatible with multiple platforms, you should consider using this option. 157 + PLATFORMS ?= linux/arm64,linux/amd64,linux/s390x,linux/ppc64le 158 + .PHONY: docker-buildx 159 + docker-buildx: ## Build and push docker image for the manager for cross-platform support 160 + # copy existing Dockerfile and insert --platform=${BUILDPLATFORM} into Dockerfile.cross, and preserve the original Dockerfile 161 + sed -e '1 s/\(^FROM\)/FROM --platform=\$$\{BUILDPLATFORM\}/; t' -e ' 1,// s//FROM --platform=\$$\{BUILDPLATFORM\}/' Dockerfile > Dockerfile.cross 162 + - $(CONTAINER_TOOL) buildx create --name project-v3-builder 163 + $(CONTAINER_TOOL) buildx use project-v3-builder 164 + - $(CONTAINER_TOOL) buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile.cross . 165 + - $(CONTAINER_TOOL) buildx rm project-v3-builder 166 + rm Dockerfile.cross 167 + 168 + .PHONY: build-installer 169 + build-installer: manifests generate kustomize ## Generate a consolidated YAML with CRDs and deployment. 170 + mkdir -p dist 171 + cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} 172 + $(KUSTOMIZE) build config/default > dist/install.yaml 173 + 174 + ##@ Deployment 175 + 176 + ifndef ignore-not-found 177 + ignore-not-found = false 178 + endif 179 + 180 + .PHONY: install 181 + install: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config. 182 + $(KUSTOMIZE) build config/crd | $(KUBECTL) apply -f - 183 + 184 + .PHONY: uninstall 185 + uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. 186 + $(KUSTOMIZE) build config/crd | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f - 187 + 188 + .PHONY: deploy 189 + deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config. 190 + cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} 191 + $(KUSTOMIZE) build config/default | $(KUBECTL) apply -f - 192 + 193 + .PHONY: undeploy 194 + 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. 195 + $(KUSTOMIZE) build config/default | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f - 196 + 197 + ##@ Dependencies 198 + 199 + ## Location to install dependencies to 200 + LOCALBIN ?= $(shell pwd)/bin 201 + $(LOCALBIN): 202 + mkdir -p $(LOCALBIN) 203 + 204 + ## Tool Binaries 205 + KUBECTL ?= kubectl 206 + KUSTOMIZE ?= $(LOCALBIN)/kustomize-$(KUSTOMIZE_VERSION) 207 + CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen-$(CONTROLLER_TOOLS_VERSION) 208 + ENVTEST ?= $(LOCALBIN)/setup-envtest-$(ENVTEST_VERSION) 209 + GOLANGCI_LINT = $(LOCALBIN)/golangci-lint-$(GOLANGCI_LINT_VERSION) 210 + 211 + ## Tool Versions 212 + KUSTOMIZE_VERSION ?= v5.3.0 213 + CONTROLLER_TOOLS_VERSION ?= v0.14.0 214 + ENVTEST_VERSION ?= release-0.17 215 + GOLANGCI_LINT_VERSION ?= v1.57.2 216 + 217 + .PHONY: kustomize 218 + kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. 219 + $(KUSTOMIZE): $(LOCALBIN) 220 + $(call go-install-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v5,$(KUSTOMIZE_VERSION)) 221 + 222 + .PHONY: controller-gen 223 + controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary. 224 + $(CONTROLLER_GEN): $(LOCALBIN) 225 + $(call go-install-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen,$(CONTROLLER_TOOLS_VERSION)) 226 + 227 + .PHONY: envtest 228 + envtest: $(ENVTEST) ## Download setup-envtest locally if necessary. 229 + $(ENVTEST): $(LOCALBIN) 230 + $(call go-install-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest,$(ENVTEST_VERSION)) 231 + 232 + .PHONY: golangci-lint 233 + golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary. 234 + $(GOLANGCI_LINT): $(LOCALBIN) 235 + $(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/cmd/golangci-lint,${GOLANGCI_LINT_VERSION}) 236 + 237 + # go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist 238 + # $1 - target path with name of binary (ideally with version) 239 + # $2 - package url which can be installed 240 + # $3 - specific version of package 241 + define go-install-tool 242 + @[ -f $(1) ] || { \ 243 + set -e; \ 244 + package=$(2)@$(3) ;\ 245 + echo "Downloading $${package}" ;\ 246 + GOBIN=$(LOCALBIN) go install $${package} ;\ 247 + mv "$$(echo "$(1)" | sed "s/-$(3)$$//")" $(1) ;\ 248 + } 249 + endef 250 + 251 + .PHONY: operator-sdk 252 + OPERATOR_SDK ?= $(LOCALBIN)/operator-sdk 253 + operator-sdk: ## Download operator-sdk locally if necessary. 254 + ifeq (,$(wildcard $(OPERATOR_SDK))) 255 + ifeq (, $(shell which operator-sdk 2>/dev/null)) 256 + @{ \ 257 + set -e ;\ 258 + mkdir -p $(dir $(OPERATOR_SDK)) ;\ 259 + OS=$(shell go env GOOS) && ARCH=$(shell go env GOARCH) && \ 260 + curl -sSLo $(OPERATOR_SDK) https://github.com/operator-framework/operator-sdk/releases/download/$(OPERATOR_SDK_VERSION)/operator-sdk_$${OS}_$${ARCH} ;\ 261 + chmod +x $(OPERATOR_SDK) ;\ 262 + } 263 + else 264 + OPERATOR_SDK = $(shell which operator-sdk) 265 + endif 266 + endif 267 + 268 + .PHONY: bundle 269 + bundle: manifests kustomize operator-sdk ## Generate bundle manifests and metadata, then validate generated files. 270 + $(OPERATOR_SDK) generate kustomize manifests -q 271 + cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG) 272 + $(KUSTOMIZE) build config/manifests | $(OPERATOR_SDK) generate bundle $(BUNDLE_GEN_FLAGS) 273 + $(OPERATOR_SDK) bundle validate ./bundle 274 + 275 + .PHONY: bundle-build 276 + bundle-build: ## Build the bundle image. 277 + docker build -f bundle.Dockerfile -t $(BUNDLE_IMG) . 278 + 279 + .PHONY: bundle-push 280 + bundle-push: ## Push the bundle image. 281 + $(MAKE) docker-push IMG=$(BUNDLE_IMG) 282 + 283 + .PHONY: opm 284 + OPM = $(LOCALBIN)/opm 285 + opm: ## Download opm locally if necessary. 286 + ifeq (,$(wildcard $(OPM))) 287 + ifeq (,$(shell which opm 2>/dev/null)) 288 + @{ \ 289 + set -e ;\ 290 + mkdir -p $(dir $(OPM)) ;\ 291 + OS=$(shell go env GOOS) && ARCH=$(shell go env GOARCH) && \ 292 + curl -sSLo $(OPM) https://github.com/operator-framework/operator-registry/releases/download/v1.23.0/$${OS}-$${ARCH}-opm ;\ 293 + chmod +x $(OPM) ;\ 294 + } 295 + else 296 + OPM = $(shell which opm) 297 + endif 298 + endif 299 + 300 + # A comma-separated list of bundle images (e.g. make catalog-build BUNDLE_IMGS=example.com/operator-bundle:v0.1.0,example.com/operator-bundle:v0.2.0). 301 + # These images MUST exist in a registry and be pull-able. 302 + BUNDLE_IMGS ?= $(BUNDLE_IMG) 303 + 304 + # The image tag given to the resulting catalog image (e.g. make catalog-build CATALOG_IMG=example.com/operator-catalog:v0.2.0). 305 + CATALOG_IMG ?= $(IMAGE_TAG_BASE)-catalog:v$(VERSION) 306 + 307 + # Set CATALOG_BASE_IMG to an existing catalog image tag to add $BUNDLE_IMGS to that image. 308 + ifneq ($(origin CATALOG_BASE_IMG), undefined) 309 + FROM_INDEX_OPT := --from-index $(CATALOG_BASE_IMG) 310 + endif 311 + 312 + # Build a catalog image by adding bundle images to an empty catalog using the operator package manager tool, 'opm'. 313 + # This recipe invokes 'opm' in 'semver' bundle add mode. For more information on add modes, see: 314 + # https://github.com/operator-framework/community-operators/blob/7f1438c/docs/packaging-operator.md#updating-your-existing-operator 315 + .PHONY: catalog-build 316 + catalog-build: opm ## Build a catalog image. 317 + $(OPM) index add --container-tool docker --mode semver --tag $(CATALOG_IMG) --bundles $(BUNDLE_IMGS) $(FROM_INDEX_OPT) 318 + 319 + # Push the catalog image. 320 + .PHONY: catalog-push 321 + catalog-push: ## Push a catalog image. 322 + $(MAKE) docker-push IMG=$(CATALOG_IMG)
+23
PROJECT
··· 1 + # Code generated by tool. DO NOT EDIT. 2 + # This file is used to track the info used to scaffold your project 3 + # and allow the plugins properly work. 4 + # More info: https://book.kubebuilder.io/reference/project-config.html 5 + domain: j5t.io 6 + layout: 7 + - go.kubebuilder.io/v4 8 + plugins: 9 + manifests.sdk.operatorframework.io/v2: {} 10 + scorecard.sdk.operatorframework.io/v2: {} 11 + projectName: secret-service-operator 12 + repo: github.com/evanjarrett/secret-service-operator 13 + resources: 14 + - api: 15 + crdVersion: v1 16 + namespaced: true 17 + controller: true 18 + domain: j5t.io 19 + group: apps 20 + kind: SecretService 21 + path: github.com/evanjarrett/secret-service-operator/api/v1 22 + version: v1 23 + version: "3"
+114
README.md
··· 1 + # secret-service-operator 2 + // TODO(user): Add simple overview of use/purpose 3 + 4 + ## Description 5 + // TODO(user): An in-depth paragraph about your project and overview of use 6 + 7 + ## Getting Started 8 + 9 + ### Prerequisites 10 + - go version v1.21.0+ 11 + - docker version 17.03+. 12 + - kubectl version v1.11.3+. 13 + - Access to a Kubernetes v1.11.3+ cluster. 14 + 15 + ### To Deploy on the cluster 16 + **Build and push your image to the location specified by `IMG`:** 17 + 18 + ```sh 19 + make docker-build docker-push IMG=<some-registry>/secret-service-operator:tag 20 + ``` 21 + 22 + **NOTE:** This image ought to be published in the personal registry you specified. 23 + And it is required to have access to pull the image from the working environment. 24 + Make sure you have the proper permission to the registry if the above commands don’t work. 25 + 26 + **Install the CRDs into the cluster:** 27 + 28 + ```sh 29 + make install 30 + ``` 31 + 32 + **Deploy the Manager to the cluster with the image specified by `IMG`:** 33 + 34 + ```sh 35 + make deploy IMG=<some-registry>/secret-service-operator:tag 36 + ``` 37 + 38 + > **NOTE**: If you encounter RBAC errors, you may need to grant yourself cluster-admin 39 + privileges or be logged in as admin. 40 + 41 + **Create instances of your solution** 42 + You can apply the samples (examples) from the config/sample: 43 + 44 + ```sh 45 + kubectl apply -k config/samples/ 46 + ``` 47 + 48 + >**NOTE**: Ensure that the samples has default values to test it out. 49 + 50 + ### To Uninstall 51 + **Delete the instances (CRs) from the cluster:** 52 + 53 + ```sh 54 + kubectl delete -k config/samples/ 55 + ``` 56 + 57 + **Delete the APIs(CRDs) from the cluster:** 58 + 59 + ```sh 60 + make uninstall 61 + ``` 62 + 63 + **UnDeploy the controller from the cluster:** 64 + 65 + ```sh 66 + make undeploy 67 + ``` 68 + 69 + ## Project Distribution 70 + 71 + Following are the steps to build the installer and distribute this project to users. 72 + 73 + 1. Build the installer for the image built and published in the registry: 74 + 75 + ```sh 76 + make build-installer IMG=<some-registry>/secret-service-operator:tag 77 + ``` 78 + 79 + NOTE: The makefile target mentioned above generates an 'install.yaml' 80 + file in the dist directory. This file contains all the resources built 81 + with Kustomize, which are necessary to install this project without 82 + its dependencies. 83 + 84 + 2. Using the installer 85 + 86 + Users can just run kubectl apply -f <URL for YAML BUNDLE> to install the project, i.e.: 87 + 88 + ```sh 89 + kubectl apply -f https://raw.githubusercontent.com/<org>/secret-service-operator/<tag or branch>/dist/install.yaml 90 + ``` 91 + 92 + ## Contributing 93 + // TODO(user): Add detailed information on how you would like others to contribute to this project 94 + 95 + **NOTE:** Run `make help` for more information on all potential `make` targets 96 + 97 + More information can be found via the [Kubebuilder Documentation](https://book.kubebuilder.io/introduction.html) 98 + 99 + ## License 100 + 101 + Copyright 2024. 102 + 103 + Licensed under the Apache License, Version 2.0 (the "License"); 104 + you may not use this file except in compliance with the License. 105 + You may obtain a copy of the License at 106 + 107 + http://www.apache.org/licenses/LICENSE-2.0 108 + 109 + Unless required by applicable law or agreed to in writing, software 110 + distributed under the License is distributed on an "AS IS" BASIS, 111 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 112 + See the License for the specific language governing permissions and 113 + limitations under the License. 114 +
+36
api/v1/groupversion_info.go
··· 1 + /* 2 + Copyright 2024. 3 + 4 + Licensed under the Apache License, Version 2.0 (the "License"); 5 + you may not use this file except in compliance with the License. 6 + You may obtain a copy of the License at 7 + 8 + http://www.apache.org/licenses/LICENSE-2.0 9 + 10 + Unless required by applicable law or agreed to in writing, software 11 + distributed under the License is distributed on an "AS IS" BASIS, 12 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 + See the License for the specific language governing permissions and 14 + limitations under the License. 15 + */ 16 + 17 + // Package v1 contains API Schema definitions for the apps v1 API group 18 + // +kubebuilder:object:generate=true 19 + // +groupName=apps.j5t.io 20 + package v1 21 + 22 + import ( 23 + "k8s.io/apimachinery/pkg/runtime/schema" 24 + "sigs.k8s.io/controller-runtime/pkg/scheme" 25 + ) 26 + 27 + var ( 28 + // GroupVersion is group version used to register these objects 29 + GroupVersion = schema.GroupVersion{Group: "apps.j5t.io", Version: "v1"} 30 + 31 + // SchemeBuilder is used to add go types to the GroupVersionKind scheme 32 + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} 33 + 34 + // AddToScheme adds the types in this group-version to the given scheme. 35 + AddToScheme = SchemeBuilder.AddToScheme 36 + )
+63
api/v1/secretservice_types.go
··· 1 + /* 2 + Copyright 2024. 3 + 4 + Licensed under the Apache License, Version 2.0 (the "License"); 5 + you may not use this file except in compliance with the License. 6 + You may obtain a copy of the License at 7 + 8 + http://www.apache.org/licenses/LICENSE-2.0 9 + 10 + Unless required by applicable law or agreed to in writing, software 11 + distributed under the License is distributed on an "AS IS" BASIS, 12 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 + See the License for the specific language governing permissions and 14 + limitations under the License. 15 + */ 16 + 17 + package v1 18 + 19 + import ( 20 + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 + ) 22 + 23 + // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! 24 + // NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. 25 + 26 + // SecretServiceSpec defines the desired state of SecretService 27 + type SecretServiceSpec struct { 28 + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster 29 + // Important: Run "make" to regenerate code after modifying this file 30 + 31 + SecretName string `json:"secretName"` 32 + } 33 + 34 + // SecretServiceStatus defines the observed state of SecretService 35 + type SecretServiceStatus struct { 36 + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster 37 + // Important: Run "make" to regenerate code after modifying this file 38 + } 39 + 40 + //+kubebuilder:object:root=true 41 + //+kubebuilder:subresource:status 42 + 43 + // SecretService is the Schema for the secretservices API 44 + type SecretService struct { 45 + metav1.TypeMeta `json:",inline"` 46 + metav1.ObjectMeta `json:"metadata,omitempty"` 47 + 48 + Spec SecretServiceSpec `json:"spec,omitempty"` 49 + Status SecretServiceStatus `json:"status,omitempty"` 50 + } 51 + 52 + //+kubebuilder:object:root=true 53 + 54 + // SecretServiceList contains a list of SecretService 55 + type SecretServiceList struct { 56 + metav1.TypeMeta `json:",inline"` 57 + metav1.ListMeta `json:"metadata,omitempty"` 58 + Items []SecretService `json:"items"` 59 + } 60 + 61 + func init() { 62 + SchemeBuilder.Register(&SecretService{}, &SecretServiceList{}) 63 + }
+114
api/v1/zz_generated.deepcopy.go
··· 1 + //go:build !ignore_autogenerated 2 + 3 + /* 4 + Copyright 2024. 5 + 6 + Licensed under the Apache License, Version 2.0 (the "License"); 7 + you may not use this file except in compliance with the License. 8 + You may obtain a copy of the License at 9 + 10 + http://www.apache.org/licenses/LICENSE-2.0 11 + 12 + Unless required by applicable law or agreed to in writing, software 13 + distributed under the License is distributed on an "AS IS" BASIS, 14 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + See the License for the specific language governing permissions and 16 + limitations under the License. 17 + */ 18 + 19 + // Code generated by controller-gen. DO NOT EDIT. 20 + 21 + package v1 22 + 23 + import ( 24 + runtime "k8s.io/apimachinery/pkg/runtime" 25 + ) 26 + 27 + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 28 + func (in *SecretService) DeepCopyInto(out *SecretService) { 29 + *out = *in 30 + out.TypeMeta = in.TypeMeta 31 + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) 32 + out.Spec = in.Spec 33 + out.Status = in.Status 34 + } 35 + 36 + // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretService. 37 + func (in *SecretService) DeepCopy() *SecretService { 38 + if in == nil { 39 + return nil 40 + } 41 + out := new(SecretService) 42 + in.DeepCopyInto(out) 43 + return out 44 + } 45 + 46 + // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 47 + func (in *SecretService) DeepCopyObject() runtime.Object { 48 + if c := in.DeepCopy(); c != nil { 49 + return c 50 + } 51 + return nil 52 + } 53 + 54 + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 55 + func (in *SecretServiceList) DeepCopyInto(out *SecretServiceList) { 56 + *out = *in 57 + out.TypeMeta = in.TypeMeta 58 + in.ListMeta.DeepCopyInto(&out.ListMeta) 59 + if in.Items != nil { 60 + in, out := &in.Items, &out.Items 61 + *out = make([]SecretService, len(*in)) 62 + for i := range *in { 63 + (*in)[i].DeepCopyInto(&(*out)[i]) 64 + } 65 + } 66 + } 67 + 68 + // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretServiceList. 69 + func (in *SecretServiceList) DeepCopy() *SecretServiceList { 70 + if in == nil { 71 + return nil 72 + } 73 + out := new(SecretServiceList) 74 + in.DeepCopyInto(out) 75 + return out 76 + } 77 + 78 + // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 79 + func (in *SecretServiceList) DeepCopyObject() runtime.Object { 80 + if c := in.DeepCopy(); c != nil { 81 + return c 82 + } 83 + return nil 84 + } 85 + 86 + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 87 + func (in *SecretServiceSpec) DeepCopyInto(out *SecretServiceSpec) { 88 + *out = *in 89 + } 90 + 91 + // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretServiceSpec. 92 + func (in *SecretServiceSpec) DeepCopy() *SecretServiceSpec { 93 + if in == nil { 94 + return nil 95 + } 96 + out := new(SecretServiceSpec) 97 + in.DeepCopyInto(out) 98 + return out 99 + } 100 + 101 + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 102 + func (in *SecretServiceStatus) DeepCopyInto(out *SecretServiceStatus) { 103 + *out = *in 104 + } 105 + 106 + // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretServiceStatus. 107 + func (in *SecretServiceStatus) DeepCopy() *SecretServiceStatus { 108 + if in == nil { 109 + return nil 110 + } 111 + out := new(SecretServiceStatus) 112 + in.DeepCopyInto(out) 113 + return out 114 + }
+148
cmd/main.go
··· 1 + /* 2 + Copyright 2024. 3 + 4 + Licensed under the Apache License, Version 2.0 (the "License"); 5 + you may not use this file except in compliance with the License. 6 + You may obtain a copy of the License at 7 + 8 + http://www.apache.org/licenses/LICENSE-2.0 9 + 10 + Unless required by applicable law or agreed to in writing, software 11 + distributed under the License is distributed on an "AS IS" BASIS, 12 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 + See the License for the specific language governing permissions and 14 + limitations under the License. 15 + */ 16 + 17 + package main 18 + 19 + import ( 20 + "crypto/tls" 21 + "flag" 22 + "os" 23 + 24 + // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) 25 + // to ensure that exec-entrypoint and run can make use of them. 26 + _ "k8s.io/client-go/plugin/pkg/client/auth" 27 + 28 + "k8s.io/apimachinery/pkg/runtime" 29 + utilruntime "k8s.io/apimachinery/pkg/util/runtime" 30 + clientgoscheme "k8s.io/client-go/kubernetes/scheme" 31 + ctrl "sigs.k8s.io/controller-runtime" 32 + "sigs.k8s.io/controller-runtime/pkg/healthz" 33 + "sigs.k8s.io/controller-runtime/pkg/log/zap" 34 + metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" 35 + "sigs.k8s.io/controller-runtime/pkg/webhook" 36 + 37 + appsv1 "github.com/evanjarrett/secret-service-operator/api/v1" 38 + "github.com/evanjarrett/secret-service-operator/internal/controller" 39 + //+kubebuilder:scaffold:imports 40 + ) 41 + 42 + var ( 43 + scheme = runtime.NewScheme() 44 + setupLog = ctrl.Log.WithName("setup") 45 + ) 46 + 47 + func init() { 48 + utilruntime.Must(clientgoscheme.AddToScheme(scheme)) 49 + 50 + utilruntime.Must(appsv1.AddToScheme(scheme)) 51 + //+kubebuilder:scaffold:scheme 52 + } 53 + 54 + func main() { 55 + var metricsAddr string 56 + var enableLeaderElection bool 57 + var probeAddr string 58 + var secureMetrics bool 59 + var enableHTTP2 bool 60 + flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.") 61 + flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") 62 + flag.BoolVar(&enableLeaderElection, "leader-elect", false, 63 + "Enable leader election for controller manager. "+ 64 + "Enabling this will ensure there is only one active controller manager.") 65 + flag.BoolVar(&secureMetrics, "metrics-secure", false, 66 + "If set the metrics endpoint is served securely") 67 + flag.BoolVar(&enableHTTP2, "enable-http2", false, 68 + "If set, HTTP/2 will be enabled for the metrics and webhook servers") 69 + opts := zap.Options{ 70 + Development: true, 71 + } 72 + opts.BindFlags(flag.CommandLine) 73 + flag.Parse() 74 + 75 + ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) 76 + 77 + // if the enable-http2 flag is false (the default), http/2 should be disabled 78 + // due to its vulnerabilities. More specifically, disabling http/2 will 79 + // prevent from being vulnerable to the HTTP/2 Stream Cancellation and 80 + // Rapid Reset CVEs. For more information see: 81 + // - https://github.com/advisories/GHSA-qppj-fm5r-hxr3 82 + // - https://github.com/advisories/GHSA-4374-p667-p6c8 83 + disableHTTP2 := func(c *tls.Config) { 84 + setupLog.Info("disabling http/2") 85 + c.NextProtos = []string{"http/1.1"} 86 + } 87 + 88 + tlsOpts := []func(*tls.Config){} 89 + if !enableHTTP2 { 90 + tlsOpts = append(tlsOpts, disableHTTP2) 91 + } 92 + 93 + webhookServer := webhook.NewServer(webhook.Options{ 94 + TLSOpts: tlsOpts, 95 + }) 96 + 97 + mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ 98 + Scheme: scheme, 99 + Metrics: metricsserver.Options{ 100 + BindAddress: metricsAddr, 101 + SecureServing: secureMetrics, 102 + TLSOpts: tlsOpts, 103 + }, 104 + WebhookServer: webhookServer, 105 + HealthProbeBindAddress: probeAddr, 106 + LeaderElection: enableLeaderElection, 107 + LeaderElectionID: "edaf68d8.j5t.io", 108 + // LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily 109 + // when the Manager ends. This requires the binary to immediately end when the 110 + // Manager is stopped, otherwise, this setting is unsafe. Setting this significantly 111 + // speeds up voluntary leader transitions as the new leader don't have to wait 112 + // LeaseDuration time first. 113 + // 114 + // In the default scaffold provided, the program ends immediately after 115 + // the manager stops, so would be fine to enable this option. However, 116 + // if you are doing or is intended to do any operation such as perform cleanups 117 + // after the manager stops then its usage might be unsafe. 118 + // LeaderElectionReleaseOnCancel: true, 119 + }) 120 + if err != nil { 121 + setupLog.Error(err, "unable to start manager") 122 + os.Exit(1) 123 + } 124 + 125 + if err = (&controller.SecretServiceReconciler{ 126 + Client: mgr.GetClient(), 127 + Scheme: mgr.GetScheme(), 128 + }).SetupWithManager(mgr); err != nil { 129 + setupLog.Error(err, "unable to create controller", "controller", "SecretService") 130 + os.Exit(1) 131 + } 132 + //+kubebuilder:scaffold:builder 133 + 134 + if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { 135 + setupLog.Error(err, "unable to set up health check") 136 + os.Exit(1) 137 + } 138 + if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil { 139 + setupLog.Error(err, "unable to set up ready check") 140 + os.Exit(1) 141 + } 142 + 143 + setupLog.Info("starting manager") 144 + if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { 145 + setupLog.Error(err, "problem running manager") 146 + os.Exit(1) 147 + } 148 + }
+54
config/crd/bases/apps.j5t.io_secretservices.yaml
··· 1 + --- 2 + apiVersion: apiextensions.k8s.io/v1 3 + kind: CustomResourceDefinition 4 + metadata: 5 + annotations: 6 + controller-gen.kubebuilder.io/version: v0.14.0 7 + name: secretservices.apps.j5t.io 8 + spec: 9 + group: apps.j5t.io 10 + names: 11 + kind: SecretService 12 + listKind: SecretServiceList 13 + plural: secretservices 14 + singular: secretservice 15 + scope: Namespaced 16 + versions: 17 + - name: v1 18 + schema: 19 + openAPIV3Schema: 20 + description: SecretService is the Schema for the secretservices API 21 + properties: 22 + apiVersion: 23 + description: |- 24 + APIVersion defines the versioned schema of this representation of an object. 25 + Servers should convert recognized schemas to the latest internal value, and 26 + may reject unrecognized values. 27 + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources 28 + type: string 29 + kind: 30 + description: |- 31 + Kind is a string value representing the REST resource this object represents. 32 + Servers may infer this from the endpoint the client submits requests to. 33 + Cannot be updated. 34 + In CamelCase. 35 + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds 36 + type: string 37 + metadata: 38 + type: object 39 + spec: 40 + description: SecretServiceSpec defines the desired state of SecretService 41 + properties: 42 + secretName: 43 + type: string 44 + required: 45 + - secretName 46 + type: object 47 + status: 48 + description: SecretServiceStatus defines the observed state of SecretService 49 + type: object 50 + type: object 51 + served: true 52 + storage: true 53 + subresources: 54 + status: {}
+22
config/crd/kustomization.yaml
··· 1 + # This kustomization.yaml is not intended to be run by itself, 2 + # since it depends on service name and namespace that are out of this kustomize package. 3 + # It should be run by config/default 4 + resources: 5 + - bases/apps.j5t.io_secretservices.yaml 6 + #+kubebuilder:scaffold:crdkustomizeresource 7 + 8 + patches: 9 + # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. 10 + # patches here are for enabling the conversion webhook for each CRD 11 + #+kubebuilder:scaffold:crdkustomizewebhookpatch 12 + 13 + # [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. 14 + # patches here are for enabling the CA injection for each CRD 15 + #- path: patches/cainjection_in_secretservices.yaml 16 + #+kubebuilder:scaffold:crdkustomizecainjectionpatch 17 + 18 + # [WEBHOOK] To enable webhook, uncomment the following section 19 + # the following config is for teaching kustomize how to do kustomization for CRDs. 20 + 21 + #configurations: 22 + #- kustomizeconfig.yaml
+19
config/crd/kustomizeconfig.yaml
··· 1 + # This file is for teaching kustomize how to substitute name and namespace reference in CRD 2 + nameReference: 3 + - kind: Service 4 + version: v1 5 + fieldSpecs: 6 + - kind: CustomResourceDefinition 7 + version: v1 8 + group: apiextensions.k8s.io 9 + path: spec/conversion/webhook/clientConfig/service/name 10 + 11 + namespace: 12 + - kind: CustomResourceDefinition 13 + version: v1 14 + group: apiextensions.k8s.io 15 + path: spec/conversion/webhook/clientConfig/service/namespace 16 + create: false 17 + 18 + varReference: 19 + - path: metadata/annotations
+142
config/default/kustomization.yaml
··· 1 + # Adds namespace to all resources. 2 + namespace: secret-service-operator-system 3 + 4 + # Value of this field is prepended to the 5 + # names of all resources, e.g. a deployment named 6 + # "wordpress" becomes "alices-wordpress". 7 + # Note that it should also match with the prefix (text before '-') of the namespace 8 + # field above. 9 + namePrefix: secret-service-operator- 10 + 11 + # Labels to add to all resources and selectors. 12 + #labels: 13 + #- includeSelectors: true 14 + # pairs: 15 + # someName: someValue 16 + 17 + resources: 18 + - ../crd 19 + - ../rbac 20 + - ../manager 21 + # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in 22 + # crd/kustomization.yaml 23 + #- ../webhook 24 + # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required. 25 + #- ../certmanager 26 + # [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. 27 + #- ../prometheus 28 + 29 + patches: 30 + # Protect the /metrics endpoint by putting it behind auth. 31 + # If you want your controller-manager to expose the /metrics 32 + # endpoint w/o any authn/z, please comment the following line. 33 + - path: manager_auth_proxy_patch.yaml 34 + 35 + # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in 36 + # crd/kustomization.yaml 37 + #- path: manager_webhook_patch.yaml 38 + 39 + # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 40 + # Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks. 41 + # 'CERTMANAGER' needs to be enabled to use ca injection 42 + #- path: webhookcainjection_patch.yaml 43 + 44 + # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. 45 + # Uncomment the following replacements to add the cert-manager CA injection annotations 46 + #replacements: 47 + # - source: # Add cert-manager annotation to ValidatingWebhookConfiguration, MutatingWebhookConfiguration and CRDs 48 + # kind: Certificate 49 + # group: cert-manager.io 50 + # version: v1 51 + # name: serving-cert # this name should match the one in certificate.yaml 52 + # fieldPath: .metadata.namespace # namespace of the certificate CR 53 + # targets: 54 + # - select: 55 + # kind: ValidatingWebhookConfiguration 56 + # fieldPaths: 57 + # - .metadata.annotations.[cert-manager.io/inject-ca-from] 58 + # options: 59 + # delimiter: '/' 60 + # index: 0 61 + # create: true 62 + # - select: 63 + # kind: MutatingWebhookConfiguration 64 + # fieldPaths: 65 + # - .metadata.annotations.[cert-manager.io/inject-ca-from] 66 + # options: 67 + # delimiter: '/' 68 + # index: 0 69 + # create: true 70 + # - select: 71 + # kind: CustomResourceDefinition 72 + # fieldPaths: 73 + # - .metadata.annotations.[cert-manager.io/inject-ca-from] 74 + # options: 75 + # delimiter: '/' 76 + # index: 0 77 + # create: true 78 + # - source: 79 + # kind: Certificate 80 + # group: cert-manager.io 81 + # version: v1 82 + # name: serving-cert # this name should match the one in certificate.yaml 83 + # fieldPath: .metadata.name 84 + # targets: 85 + # - select: 86 + # kind: ValidatingWebhookConfiguration 87 + # fieldPaths: 88 + # - .metadata.annotations.[cert-manager.io/inject-ca-from] 89 + # options: 90 + # delimiter: '/' 91 + # index: 1 92 + # create: true 93 + # - select: 94 + # kind: MutatingWebhookConfiguration 95 + # fieldPaths: 96 + # - .metadata.annotations.[cert-manager.io/inject-ca-from] 97 + # options: 98 + # delimiter: '/' 99 + # index: 1 100 + # create: true 101 + # - select: 102 + # kind: CustomResourceDefinition 103 + # fieldPaths: 104 + # - .metadata.annotations.[cert-manager.io/inject-ca-from] 105 + # options: 106 + # delimiter: '/' 107 + # index: 1 108 + # create: true 109 + # - source: # Add cert-manager annotation to the webhook Service 110 + # kind: Service 111 + # version: v1 112 + # name: webhook-service 113 + # fieldPath: .metadata.name # namespace of the service 114 + # targets: 115 + # - select: 116 + # kind: Certificate 117 + # group: cert-manager.io 118 + # version: v1 119 + # fieldPaths: 120 + # - .spec.dnsNames.0 121 + # - .spec.dnsNames.1 122 + # options: 123 + # delimiter: '.' 124 + # index: 0 125 + # create: true 126 + # - source: 127 + # kind: Service 128 + # version: v1 129 + # name: webhook-service 130 + # fieldPath: .metadata.namespace # namespace of the service 131 + # targets: 132 + # - select: 133 + # kind: Certificate 134 + # group: cert-manager.io 135 + # version: v1 136 + # fieldPaths: 137 + # - .spec.dnsNames.0 138 + # - .spec.dnsNames.1 139 + # options: 140 + # delimiter: '.' 141 + # index: 1 142 + # create: true
+39
config/default/manager_auth_proxy_patch.yaml
··· 1 + # This patch inject a sidecar container which is a HTTP proxy for the 2 + # controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews. 3 + apiVersion: apps/v1 4 + kind: Deployment 5 + metadata: 6 + name: controller-manager 7 + namespace: system 8 + spec: 9 + template: 10 + spec: 11 + containers: 12 + - name: kube-rbac-proxy 13 + securityContext: 14 + allowPrivilegeEscalation: false 15 + capabilities: 16 + drop: 17 + - "ALL" 18 + image: gcr.io/kubebuilder/kube-rbac-proxy:v0.16.0 19 + args: 20 + - "--secure-listen-address=0.0.0.0:8443" 21 + - "--upstream=http://127.0.0.1:8080/" 22 + - "--logtostderr=true" 23 + - "--v=0" 24 + ports: 25 + - containerPort: 8443 26 + protocol: TCP 27 + name: https 28 + resources: 29 + limits: 30 + cpu: 500m 31 + memory: 128Mi 32 + requests: 33 + cpu: 5m 34 + memory: 64Mi 35 + - name: manager 36 + args: 37 + - "--health-probe-bind-address=:8081" 38 + - "--metrics-bind-address=127.0.0.1:8080" 39 + - "--leader-elect"
+10
config/default/manager_config_patch.yaml
··· 1 + apiVersion: apps/v1 2 + kind: Deployment 3 + metadata: 4 + name: controller-manager 5 + namespace: system 6 + spec: 7 + template: 8 + spec: 9 + containers: 10 + - name: manager
+2
config/manager/kustomization.yaml
··· 1 + resources: 2 + - manager.yaml
+94
config/manager/manager.yaml
··· 1 + apiVersion: v1 2 + kind: Namespace 3 + metadata: 4 + labels: 5 + control-plane: controller-manager 6 + app.kubernetes.io/name: secret-service-operator 7 + app.kubernetes.io/managed-by: kustomize 8 + name: system 9 + --- 10 + apiVersion: apps/v1 11 + kind: Deployment 12 + metadata: 13 + name: controller-manager 14 + namespace: system 15 + labels: 16 + control-plane: controller-manager 17 + app.kubernetes.io/name: secret-service-operator 18 + app.kubernetes.io/managed-by: kustomize 19 + spec: 20 + selector: 21 + matchLabels: 22 + control-plane: controller-manager 23 + replicas: 1 24 + template: 25 + metadata: 26 + annotations: 27 + kubectl.kubernetes.io/default-container: manager 28 + labels: 29 + control-plane: controller-manager 30 + spec: 31 + # TODO(user): Uncomment the following code to configure the nodeAffinity expression 32 + # according to the platforms which are supported by your solution. 33 + # It is considered best practice to support multiple architectures. You can 34 + # build your manager image using the makefile target docker-buildx. 35 + # affinity: 36 + # nodeAffinity: 37 + # requiredDuringSchedulingIgnoredDuringExecution: 38 + # nodeSelectorTerms: 39 + # - matchExpressions: 40 + # - key: kubernetes.io/arch 41 + # operator: In 42 + # values: 43 + # - amd64 44 + # - arm64 45 + # - ppc64le 46 + # - s390x 47 + # - key: kubernetes.io/os 48 + # operator: In 49 + # values: 50 + # - linux 51 + securityContext: 52 + runAsNonRoot: true 53 + # TODO(user): For common cases that do not require escalating privileges 54 + # it is recommended to ensure that all your Pods/Containers are restrictive. 55 + # More info: https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted 56 + # Please uncomment the following code if your project does NOT have to work on old Kubernetes 57 + # versions < 1.19 or on vendors versions which do NOT support this field by default (i.e. Openshift < 4.11 ). 58 + # seccompProfile: 59 + # type: RuntimeDefault 60 + containers: 61 + - command: 62 + - /manager 63 + args: 64 + - --leader-elect 65 + image: controller:latest 66 + name: manager 67 + securityContext: 68 + allowPrivilegeEscalation: false 69 + capabilities: 70 + drop: 71 + - "ALL" 72 + livenessProbe: 73 + httpGet: 74 + path: /healthz 75 + port: 8081 76 + initialDelaySeconds: 15 77 + periodSeconds: 20 78 + readinessProbe: 79 + httpGet: 80 + path: /readyz 81 + port: 8081 82 + initialDelaySeconds: 5 83 + periodSeconds: 10 84 + # TODO(user): Configure the resources accordingly based on the project requirements. 85 + # More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ 86 + resources: 87 + limits: 88 + cpu: 500m 89 + memory: 128Mi 90 + requests: 91 + cpu: 10m 92 + memory: 64Mi 93 + serviceAccountName: controller-manager 94 + terminationGracePeriodSeconds: 10
+28
config/manifests/kustomization.yaml
··· 1 + # These resources constitute the fully configured set of manifests 2 + # used to generate the 'manifests/' directory in a bundle. 3 + resources: 4 + - bases/secret-service-operator.clusterserviceversion.yaml 5 + - ../default 6 + - ../samples 7 + - ../scorecard 8 + 9 + # [WEBHOOK] To enable webhooks, uncomment all the sections with [WEBHOOK] prefix. 10 + # Do NOT uncomment sections with prefix [CERTMANAGER], as OLM does not support cert-manager. 11 + # These patches remove the unnecessary "cert" volume and its manager container volumeMount. 12 + #patchesJson6902: 13 + #- target: 14 + # group: apps 15 + # version: v1 16 + # kind: Deployment 17 + # name: controller-manager 18 + # namespace: system 19 + # patch: |- 20 + # # Remove the manager container's "cert" volumeMount, since OLM will create and mount a set of certs. 21 + # # Update the indices in this path if adding or removing containers/volumeMounts in the manager's Deployment. 22 + # - op: remove 23 + 24 + # path: /spec/template/spec/containers/0/volumeMounts/0 25 + # # Remove the "cert" volume, since OLM will create and mount a set of certs. 26 + # # Update the indices in this path if adding or removing volumes in the manager's Deployment. 27 + # - op: remove 28 + # path: /spec/template/spec/volumes/0
+2
config/prometheus/kustomization.yaml
··· 1 + resources: 2 + - monitor.yaml
+21
config/prometheus/monitor.yaml
··· 1 + # Prometheus Monitor Service (Metrics) 2 + apiVersion: monitoring.coreos.com/v1 3 + kind: ServiceMonitor 4 + metadata: 5 + labels: 6 + control-plane: controller-manager 7 + app.kubernetes.io/name: secret-service-operator 8 + app.kubernetes.io/managed-by: kustomize 9 + name: controller-manager-metrics-monitor 10 + namespace: system 11 + spec: 12 + endpoints: 13 + - path: /metrics 14 + port: https 15 + scheme: https 16 + bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token 17 + tlsConfig: 18 + insecureSkipVerify: true 19 + selector: 20 + matchLabels: 21 + control-plane: controller-manager
+12
config/rbac/auth_proxy_client_clusterrole.yaml
··· 1 + apiVersion: rbac.authorization.k8s.io/v1 2 + kind: ClusterRole 3 + metadata: 4 + labels: 5 + app.kubernetes.io/name: secret-service-operator 6 + app.kubernetes.io/managed-by: kustomize 7 + name: metrics-reader 8 + rules: 9 + - nonResourceURLs: 10 + - "/metrics" 11 + verbs: 12 + - get
+20
config/rbac/auth_proxy_role.yaml
··· 1 + apiVersion: rbac.authorization.k8s.io/v1 2 + kind: ClusterRole 3 + metadata: 4 + labels: 5 + app.kubernetes.io/name: secret-service-operator 6 + app.kubernetes.io/managed-by: kustomize 7 + name: proxy-role 8 + rules: 9 + - apiGroups: 10 + - authentication.k8s.io 11 + resources: 12 + - tokenreviews 13 + verbs: 14 + - create 15 + - apiGroups: 16 + - authorization.k8s.io 17 + resources: 18 + - subjectaccessreviews 19 + verbs: 20 + - create
+15
config/rbac/auth_proxy_role_binding.yaml
··· 1 + apiVersion: rbac.authorization.k8s.io/v1 2 + kind: ClusterRoleBinding 3 + metadata: 4 + labels: 5 + app.kubernetes.io/name: secret-service-operator 6 + app.kubernetes.io/managed-by: kustomize 7 + name: proxy-rolebinding 8 + roleRef: 9 + apiGroup: rbac.authorization.k8s.io 10 + kind: ClusterRole 11 + name: proxy-role 12 + subjects: 13 + - kind: ServiceAccount 14 + name: controller-manager 15 + namespace: system
+17
config/rbac/auth_proxy_service.yaml
··· 1 + apiVersion: v1 2 + kind: Service 3 + metadata: 4 + labels: 5 + control-plane: controller-manager 6 + app.kubernetes.io/name: secret-service-operator 7 + app.kubernetes.io/managed-by: kustomize 8 + name: controller-manager-metrics-service 9 + namespace: system 10 + spec: 11 + ports: 12 + - name: https 13 + port: 8443 14 + protocol: TCP 15 + targetPort: https 16 + selector: 17 + control-plane: controller-manager
+24
config/rbac/kustomization.yaml
··· 1 + resources: 2 + # All RBAC will be applied under this service account in 3 + # the deployment namespace. You may comment out this resource 4 + # if your manager will use a service account that exists at 5 + # runtime. Be sure to update RoleBinding and ClusterRoleBinding 6 + # subjects if changing service account names. 7 + - service_account.yaml 8 + - role.yaml 9 + - role_binding.yaml 10 + - leader_election_role.yaml 11 + - leader_election_role_binding.yaml 12 + # Comment the following 4 lines if you want to disable 13 + # the auth proxy (https://github.com/brancz/kube-rbac-proxy) 14 + # which protects your /metrics endpoint. 15 + - auth_proxy_service.yaml 16 + - auth_proxy_role.yaml 17 + - auth_proxy_role_binding.yaml 18 + - auth_proxy_client_clusterrole.yaml 19 + # For each CRD, "Editor" and "Viewer" roles are scaffolded by 20 + # default, aiding admins in cluster management. Those roles are 21 + # not used by the Project itself. You can comment the following lines 22 + # if you do not want those helpers be installed with your Project. 23 + - secretservice_editor_role.yaml 24 + - secretservice_viewer_role.yaml
+40
config/rbac/leader_election_role.yaml
··· 1 + # permissions to do leader election. 2 + apiVersion: rbac.authorization.k8s.io/v1 3 + kind: Role 4 + metadata: 5 + labels: 6 + app.kubernetes.io/name: secret-service-operator 7 + app.kubernetes.io/managed-by: kustomize 8 + name: leader-election-role 9 + rules: 10 + - apiGroups: 11 + - "" 12 + resources: 13 + - configmaps 14 + verbs: 15 + - get 16 + - list 17 + - watch 18 + - create 19 + - update 20 + - patch 21 + - delete 22 + - apiGroups: 23 + - coordination.k8s.io 24 + resources: 25 + - leases 26 + verbs: 27 + - get 28 + - list 29 + - watch 30 + - create 31 + - update 32 + - patch 33 + - delete 34 + - apiGroups: 35 + - "" 36 + resources: 37 + - events 38 + verbs: 39 + - create 40 + - patch
+15
config/rbac/leader_election_role_binding.yaml
··· 1 + apiVersion: rbac.authorization.k8s.io/v1 2 + kind: RoleBinding 3 + metadata: 4 + labels: 5 + app.kubernetes.io/name: secret-service-operator 6 + app.kubernetes.io/managed-by: kustomize 7 + name: leader-election-rolebinding 8 + roleRef: 9 + apiGroup: rbac.authorization.k8s.io 10 + kind: Role 11 + name: leader-election-role 12 + subjects: 13 + - kind: ServiceAccount 14 + name: controller-manager 15 + namespace: system
+32
config/rbac/role.yaml
··· 1 + --- 2 + apiVersion: rbac.authorization.k8s.io/v1 3 + kind: ClusterRole 4 + metadata: 5 + name: manager-role 6 + rules: 7 + - apiGroups: 8 + - apps.j5t.io 9 + resources: 10 + - secretservices 11 + verbs: 12 + - create 13 + - delete 14 + - get 15 + - list 16 + - patch 17 + - update 18 + - watch 19 + - apiGroups: 20 + - apps.j5t.io 21 + resources: 22 + - secretservices/finalizers 23 + verbs: 24 + - update 25 + - apiGroups: 26 + - apps.j5t.io 27 + resources: 28 + - secretservices/status 29 + verbs: 30 + - get 31 + - patch 32 + - update
+15
config/rbac/role_binding.yaml
··· 1 + apiVersion: rbac.authorization.k8s.io/v1 2 + kind: ClusterRoleBinding 3 + metadata: 4 + labels: 5 + app.kubernetes.io/name: secret-service-operator 6 + app.kubernetes.io/managed-by: kustomize 7 + name: manager-rolebinding 8 + roleRef: 9 + apiGroup: rbac.authorization.k8s.io 10 + kind: ClusterRole 11 + name: manager-role 12 + subjects: 13 + - kind: ServiceAccount 14 + name: controller-manager 15 + namespace: system
+27
config/rbac/secretservice_editor_role.yaml
··· 1 + # permissions for end users to edit secretservices. 2 + apiVersion: rbac.authorization.k8s.io/v1 3 + kind: ClusterRole 4 + metadata: 5 + labels: 6 + app.kubernetes.io/name: secret-service-operator 7 + app.kubernetes.io/managed-by: kustomize 8 + name: secretservice-editor-role 9 + rules: 10 + - apiGroups: 11 + - apps.j5t.io 12 + resources: 13 + - secretservices 14 + verbs: 15 + - create 16 + - delete 17 + - get 18 + - list 19 + - patch 20 + - update 21 + - watch 22 + - apiGroups: 23 + - apps.j5t.io 24 + resources: 25 + - secretservices/status 26 + verbs: 27 + - get
+23
config/rbac/secretservice_viewer_role.yaml
··· 1 + # permissions for end users to view secretservices. 2 + apiVersion: rbac.authorization.k8s.io/v1 3 + kind: ClusterRole 4 + metadata: 5 + labels: 6 + app.kubernetes.io/name: secret-service-operator 7 + app.kubernetes.io/managed-by: kustomize 8 + name: secretservice-viewer-role 9 + rules: 10 + - apiGroups: 11 + - apps.j5t.io 12 + resources: 13 + - secretservices 14 + verbs: 15 + - get 16 + - list 17 + - watch 18 + - apiGroups: 19 + - apps.j5t.io 20 + resources: 21 + - secretservices/status 22 + verbs: 23 + - get
+8
config/rbac/service_account.yaml
··· 1 + apiVersion: v1 2 + kind: ServiceAccount 3 + metadata: 4 + labels: 5 + app.kubernetes.io/name: secret-service-operator 6 + app.kubernetes.io/managed-by: kustomize 7 + name: controller-manager 8 + namespace: system
+8
config/samples/apps_v1_secretservice.yaml
··· 1 + apiVersion: apps.j5t.io/v1 2 + kind: SecretService 3 + metadata: 4 + labels: 5 + app.kubernetes.io/name: secret-service-operator 6 + name: endpoint-secretservice 7 + spec: 8 + secretName: endpoint-secret
+4
config/samples/kustomization.yaml
··· 1 + ## Append samples of your project ## 2 + resources: 3 + - apps_v1_secretservice.yaml 4 + #+kubebuilder:scaffold:manifestskustomizesamples
+7
config/scorecard/bases/config.yaml
··· 1 + apiVersion: scorecard.operatorframework.io/v1alpha3 2 + kind: Configuration 3 + metadata: 4 + name: config 5 + stages: 6 + - parallel: true 7 + tests: []
+16
config/scorecard/kustomization.yaml
··· 1 + resources: 2 + - bases/config.yaml 3 + patchesJson6902: 4 + - path: patches/basic.config.yaml 5 + target: 6 + group: scorecard.operatorframework.io 7 + version: v1alpha3 8 + kind: Configuration 9 + name: config 10 + - path: patches/olm.config.yaml 11 + target: 12 + group: scorecard.operatorframework.io 13 + version: v1alpha3 14 + kind: Configuration 15 + name: config 16 + #+kubebuilder:scaffold:patchesJson6902
+10
config/scorecard/patches/basic.config.yaml
··· 1 + - op: add 2 + path: /stages/0/tests/- 3 + value: 4 + entrypoint: 5 + - scorecard-test 6 + - basic-check-spec 7 + image: quay.io/operator-framework/scorecard-test:v1.37.0 8 + labels: 9 + suite: basic 10 + test: basic-check-spec-test
+50
config/scorecard/patches/olm.config.yaml
··· 1 + - op: add 2 + path: /stages/0/tests/- 3 + value: 4 + entrypoint: 5 + - scorecard-test 6 + - olm-bundle-validation 7 + image: quay.io/operator-framework/scorecard-test:v1.37.0 8 + labels: 9 + suite: olm 10 + test: olm-bundle-validation-test 11 + - op: add 12 + path: /stages/0/tests/- 13 + value: 14 + entrypoint: 15 + - scorecard-test 16 + - olm-crds-have-validation 17 + image: quay.io/operator-framework/scorecard-test:v1.37.0 18 + labels: 19 + suite: olm 20 + test: olm-crds-have-validation-test 21 + - op: add 22 + path: /stages/0/tests/- 23 + value: 24 + entrypoint: 25 + - scorecard-test 26 + - olm-crds-have-resources 27 + image: quay.io/operator-framework/scorecard-test:v1.37.0 28 + labels: 29 + suite: olm 30 + test: olm-crds-have-resources-test 31 + - op: add 32 + path: /stages/0/tests/- 33 + value: 34 + entrypoint: 35 + - scorecard-test 36 + - olm-spec-descriptors 37 + image: quay.io/operator-framework/scorecard-test:v1.37.0 38 + labels: 39 + suite: olm 40 + test: olm-spec-descriptors-test 41 + - op: add 42 + path: /stages/0/tests/- 43 + value: 44 + entrypoint: 45 + - scorecard-test 46 + - olm-status-descriptors 47 + image: quay.io/operator-framework/scorecard-test:v1.37.0 48 + labels: 49 + suite: olm 50 + test: olm-status-descriptors-test
+73
go.mod
··· 1 + module github.com/evanjarrett/secret-service-operator 2 + 3 + go 1.21.0 4 + 5 + require ( 6 + github.com/onsi/ginkgo/v2 v2.19.0 7 + github.com/onsi/gomega v1.33.1 8 + k8s.io/api v0.29.4 9 + k8s.io/apimachinery v0.29.4 10 + k8s.io/client-go v0.29.4 11 + sigs.k8s.io/controller-runtime v0.17.3 12 + ) 13 + 14 + require ( 15 + github.com/beorn7/perks v1.0.1 // indirect 16 + github.com/cespare/xxhash/v2 v2.2.0 // indirect 17 + github.com/davecgh/go-spew v1.1.1 // indirect 18 + github.com/emicklei/go-restful/v3 v3.11.0 // indirect 19 + github.com/evanphx/json-patch/v5 v5.8.0 // indirect 20 + github.com/fsnotify/fsnotify v1.7.0 // indirect 21 + github.com/go-logr/logr v1.4.1 // indirect 22 + github.com/go-logr/zapr v1.3.0 // indirect 23 + github.com/go-openapi/jsonpointer v0.19.6 // indirect 24 + github.com/go-openapi/jsonreference v0.20.2 // indirect 25 + github.com/go-openapi/swag v0.22.3 // indirect 26 + github.com/go-task/slim-sprig/v3 v3.0.0 // indirect 27 + github.com/gogo/protobuf v1.3.2 // indirect 28 + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 29 + github.com/golang/protobuf v1.5.4 // indirect 30 + github.com/google/gnostic-models v0.6.8 // indirect 31 + github.com/google/go-cmp v0.6.0 // indirect 32 + github.com/google/gofuzz v1.2.0 // indirect 33 + github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 // indirect 34 + github.com/google/uuid v1.3.0 // indirect 35 + github.com/imdario/mergo v0.3.6 // indirect 36 + github.com/josharian/intern v1.0.0 // indirect 37 + github.com/json-iterator/go v1.1.12 // indirect 38 + github.com/mailru/easyjson v0.7.7 // indirect 39 + github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect 40 + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 41 + github.com/modern-go/reflect2 v1.0.2 // indirect 42 + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect 43 + github.com/pkg/errors v0.9.1 // indirect 44 + github.com/prometheus/client_golang v1.18.0 // indirect 45 + github.com/prometheus/client_model v0.5.0 // indirect 46 + github.com/prometheus/common v0.45.0 // indirect 47 + github.com/prometheus/procfs v0.12.0 // indirect 48 + github.com/spf13/pflag v1.0.5 // indirect 49 + go.uber.org/multierr v1.11.0 // indirect 50 + go.uber.org/zap v1.26.0 // indirect 51 + golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect 52 + golang.org/x/net v0.25.0 // indirect 53 + golang.org/x/oauth2 v0.12.0 // indirect 54 + golang.org/x/sys v0.20.0 // indirect 55 + golang.org/x/term v0.20.0 // indirect 56 + golang.org/x/text v0.15.0 // indirect 57 + golang.org/x/time v0.3.0 // indirect 58 + golang.org/x/tools v0.21.0 // indirect 59 + gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect 60 + google.golang.org/appengine v1.6.7 // indirect 61 + google.golang.org/protobuf v1.33.0 // indirect 62 + gopkg.in/inf.v0 v0.9.1 // indirect 63 + gopkg.in/yaml.v2 v2.4.0 // indirect 64 + gopkg.in/yaml.v3 v3.0.1 // indirect 65 + k8s.io/apiextensions-apiserver v0.29.2 // indirect 66 + k8s.io/component-base v0.29.2 // indirect 67 + k8s.io/klog/v2 v2.110.1 // indirect 68 + k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect 69 + k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect 70 + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect 71 + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect 72 + sigs.k8s.io/yaml v1.4.0 // indirect 73 + )
+195
go.sum
··· 1 + github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 2 + github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 3 + github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= 4 + github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 5 + github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 6 + github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 7 + github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 8 + github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 9 + github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= 10 + github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= 11 + github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= 12 + github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= 13 + github.com/evanphx/json-patch/v5 v5.8.0 h1:lRj6N9Nci7MvzrXuX6HFzU8XjmhPiXPlsKEy1u0KQro= 14 + github.com/evanphx/json-patch/v5 v5.8.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= 15 + github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= 16 + github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= 17 + github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 18 + github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= 19 + github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 20 + github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= 21 + github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= 22 + github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= 23 + github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= 24 + github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= 25 + github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= 26 + github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= 27 + github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= 28 + github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= 29 + github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= 30 + github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 31 + github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 32 + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= 33 + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 34 + github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 35 + github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= 36 + github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= 37 + github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= 38 + github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= 39 + github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 40 + github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 41 + github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 42 + github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 43 + github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= 44 + github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 45 + github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg= 46 + github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= 47 + github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 48 + github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 49 + github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= 50 + github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= 51 + github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= 52 + github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= 53 + github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 54 + github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 55 + github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 56 + github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 57 + github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 58 + github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 59 + github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 60 + github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 61 + github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 62 + github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 63 + github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 64 + github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= 65 + github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= 66 + github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= 67 + github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= 68 + github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 69 + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 70 + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 71 + github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 72 + github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 73 + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= 74 + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 75 + github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA= 76 + github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= 77 + github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= 78 + github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= 79 + github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 80 + github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 81 + github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 82 + github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 83 + github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= 84 + github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= 85 + github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= 86 + github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= 87 + github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= 88 + github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= 89 + github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= 90 + github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= 91 + github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= 92 + github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= 93 + github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 94 + github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 95 + github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 96 + github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 97 + github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 98 + github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 99 + github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 100 + github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 101 + github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 102 + github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 103 + github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 104 + github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 105 + github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 106 + go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= 107 + go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= 108 + go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= 109 + go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 110 + go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= 111 + go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= 112 + golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 113 + golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 114 + golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 115 + golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA= 116 + golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA= 117 + golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 118 + golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 119 + golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 120 + golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 121 + golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 122 + golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 123 + golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 124 + golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= 125 + golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= 126 + golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4= 127 + golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= 128 + golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 129 + golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 130 + golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 131 + golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 132 + golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 133 + golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 134 + golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= 135 + golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 136 + golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= 137 + golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= 138 + golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 139 + golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 140 + golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 141 + golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= 142 + golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 143 + golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= 144 + golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 145 + golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 146 + golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 147 + golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 148 + golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 149 + golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= 150 + golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= 151 + golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 152 + golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 153 + golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 154 + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 155 + gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= 156 + gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= 157 + google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= 158 + google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 159 + google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= 160 + google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 161 + gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 162 + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 163 + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 164 + gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= 165 + gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 166 + gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 167 + gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 168 + gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 169 + gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 170 + gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 171 + gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 172 + k8s.io/api v0.29.4 h1:WEnF/XdxuCxdG3ayHNRR8yH3cI1B/llkWBma6bq4R3w= 173 + k8s.io/api v0.29.4/go.mod h1:DetSv0t4FBTcEpfA84NJV3g9a7+rSzlUHk5ADAYHUv0= 174 + k8s.io/apiextensions-apiserver v0.29.2 h1:UK3xB5lOWSnhaCk0RFZ0LUacPZz9RY4wi/yt2Iu+btg= 175 + k8s.io/apiextensions-apiserver v0.29.2/go.mod h1:aLfYjpA5p3OwtqNXQFkhJ56TB+spV8Gc4wfMhUA3/b8= 176 + k8s.io/apimachinery v0.29.4 h1:RaFdJiDmuKs/8cm1M6Dh1Kvyh59YQFDcFuFTSmXes6Q= 177 + k8s.io/apimachinery v0.29.4/go.mod h1:i3FJVwhvSp/6n8Fl4K97PJEP8C+MM+aoDq4+ZJBf70Y= 178 + k8s.io/client-go v0.29.4 h1:79ytIedxVfyXV8rpH3jCBW0u+un0fxHDwX5F9K8dPR8= 179 + k8s.io/client-go v0.29.4/go.mod h1:kC1thZQ4zQWYwldsfI088BbK6RkxK+aF5ebV8y9Q4tk= 180 + k8s.io/component-base v0.29.2 h1:lpiLyuvPA9yV1aQwGLENYyK7n/8t6l3nn3zAtFTJYe8= 181 + k8s.io/component-base v0.29.2/go.mod h1:BfB3SLrefbZXiBfbM+2H1dlat21Uewg/5qtKOl8degM= 182 + k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0= 183 + k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo= 184 + k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780= 185 + k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= 186 + k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= 187 + k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= 188 + sigs.k8s.io/controller-runtime v0.17.3 h1:65QmN7r3FWgTxDMz9fvGnO1kbf2nu+acg9p2R9oYYYk= 189 + sigs.k8s.io/controller-runtime v0.17.3/go.mod h1:N0jpP5Lo7lMTF9aL56Z/B2oWBJjey6StQM0jRbKQXtY= 190 + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= 191 + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= 192 + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= 193 + sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= 194 + sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= 195 + sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
+15
hack/boilerplate.go.txt
··· 1 + /* 2 + Copyright 2024. 3 + 4 + Licensed under the Apache License, Version 2.0 (the "License"); 5 + you may not use this file except in compliance with the License. 6 + You may obtain a copy of the License at 7 + 8 + http://www.apache.org/licenses/LICENSE-2.0 9 + 10 + Unless required by applicable law or agreed to in writing, software 11 + distributed under the License is distributed on an "AS IS" BASIS, 12 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 + See the License for the specific language governing permissions and 14 + limitations under the License. 15 + */
+200
internal/controller/secretservice_controller.go
··· 1 + /* 2 + Copyright 2024. 3 + 4 + Licensed under the Apache License, Version 2.0 (the "License"); 5 + you may not use this file except in compliance with the License. 6 + You may obtain a copy of the License at 7 + 8 + http://www.apache.org/licenses/LICENSE-2.0 9 + 10 + Unless required by applicable law or agreed to in writing, software 11 + distributed under the License is distributed on an "AS IS" BASIS, 12 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 + See the License for the specific language governing permissions and 14 + limitations under the License. 15 + */ 16 + 17 + package controller 18 + 19 + import ( 20 + "context" 21 + "net" 22 + "strconv" 23 + "strings" 24 + "time" 25 + 26 + "k8s.io/apimachinery/pkg/api/errors" 27 + "k8s.io/apimachinery/pkg/runtime" 28 + "k8s.io/apimachinery/pkg/types" 29 + "k8s.io/apimachinery/pkg/util/intstr" 30 + ctrl "sigs.k8s.io/controller-runtime" 31 + "sigs.k8s.io/controller-runtime/pkg/client" 32 + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" 33 + "sigs.k8s.io/controller-runtime/pkg/log" 34 + 35 + appsv1 "github.com/evanjarrett/secret-service-operator/api/v1" 36 + corev1 "k8s.io/api/core/v1" 37 + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 38 + ) 39 + 40 + // SecretServiceReconciler reconciles a SecretService object 41 + type SecretServiceReconciler struct { 42 + client.Client 43 + Scheme *runtime.Scheme 44 + } 45 + 46 + //+kubebuilder:rbac:groups=apps.j5t.io,resources=secretservices,verbs=get;list;watch;create;update;patch;delete 47 + //+kubebuilder:rbac:groups=apps.j5t.io,resources=secretservices/status,verbs=get;update;patch 48 + //+kubebuilder:rbac:groups=apps.j5t.io,resources=secretservices/finalizers,verbs=update 49 + 50 + // Reconcile is part of the main kubernetes reconciliation loop which aims to 51 + // move the current state of the cluster closer to the desired state. 52 + // TODO(user): Modify the Reconcile function to compare the state specified by 53 + // the SecretService object against the actual cluster state, and then 54 + // perform operations to make the cluster state reflect the state specified by 55 + // the user. 56 + // 57 + // For more details, check Reconcile and its Result here: 58 + // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.17.3/pkg/reconcile 59 + func (r *SecretServiceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { 60 + logger := log.FromContext(ctx) 61 + 62 + // Get the SecretService object 63 + instance := &appsv1.SecretService{} 64 + if err := r.Get(ctx, req.NamespacedName, instance); err != nil { 65 + if errors.IsNotFound(err) { 66 + return ctrl.Result{}, nil // Don't requeue if the object is gone 67 + } 68 + return ctrl.Result{}, err 69 + } 70 + 71 + // Get the Secret 72 + secret := &corev1.Secret{} 73 + if err := r.Get(ctx, types.NamespacedName{Name: instance.Spec.SecretName, Namespace: instance.Namespace}, secret); err != nil { 74 + if errors.IsNotFound(err) { 75 + // If the secret doesn't exist, requeue after a delay 76 + logger.Error(err, "Secret not found", "secretName", instance.Spec.SecretName) 77 + return ctrl.Result{RequeueAfter: 30 * time.Second}, nil 78 + } 79 + return ctrl.Result{}, err 80 + } 81 + 82 + for serviceName, value := range secret.Data { 83 + 84 + endpointPort := string(value) 85 + parts := strings.Split(endpointPort, ":") 86 + if len(parts) != 2 { 87 + // Log a warning or skip if it doesn't match the expected format, but don't return an error 88 + logger.Info("Skipping secret key with invalid format:", "key", serviceName, "value", endpointPort) 89 + continue // Skip this key and process the next one 90 + } 91 + 92 + endpointIP := parts[0] 93 + portStr := parts[1] 94 + 95 + // Validate IP address format 96 + if ip := net.ParseIP(endpointIP); ip == nil { 97 + logger.Info("Skipping secret key with invalid IP address:", "key", serviceName, "ip", endpointIP) 98 + continue 99 + } 100 + 101 + port, err := strconv.Atoi(portStr) 102 + if err != nil || port < 1 || port > 65535 { 103 + logger.Info("Skipping secret key with invalid port:", "key", serviceName, "port", portStr) 104 + continue 105 + } 106 + 107 + // Create or update the Service (Example: ClusterIP service) 108 + service := &corev1.Service{ 109 + ObjectMeta: metav1.ObjectMeta{ 110 + Name: serviceName, 111 + Namespace: instance.Namespace, 112 + }, 113 + Spec: corev1.ServiceSpec{ 114 + Type: corev1.ServiceTypeClusterIP, 115 + Ports: []corev1.ServicePort{ 116 + { 117 + Name: "http", 118 + Port: int32(port), 119 + TargetPort: intstr.FromInt(port), // Or string target port if needed 120 + Protocol: corev1.ProtocolTCP, 121 + }, 122 + }, 123 + }, 124 + } 125 + 126 + if err := r.CreateOrUpdate(ctx, service, instance); err != nil { 127 + return ctrl.Result{}, err 128 + } 129 + 130 + // Create or update Endpoints (for ClusterIP service) 131 + endpoints := &corev1.Endpoints{ 132 + ObjectMeta: metav1.ObjectMeta{ 133 + Name: serviceName, 134 + Namespace: instance.Namespace, 135 + }, 136 + Subsets: []corev1.EndpointSubset{ 137 + { 138 + Addresses: []corev1.EndpointAddress{ 139 + { 140 + IP: endpointIP, 141 + }, 142 + }, 143 + Ports: []corev1.EndpointPort{ 144 + { 145 + Name: "http", 146 + Port: int32(port), 147 + Protocol: corev1.ProtocolTCP, 148 + }, 149 + }, 150 + }, 151 + }, 152 + } 153 + 154 + if err := r.CreateOrUpdate(ctx, endpoints, instance); err != nil { 155 + return ctrl.Result{}, err 156 + } 157 + } 158 + 159 + return ctrl.Result{}, nil 160 + } 161 + 162 + // CreateOrUpdate helper function 163 + func (r *SecretServiceReconciler) CreateOrUpdate(ctx context.Context, obj client.Object, owner metav1.Object) error { 164 + logger := log.FromContext(ctx) 165 + key := client.ObjectKeyFromObject(obj) 166 + 167 + // 1. Attempt to get the object. This checks if it already exists. 168 + if err := r.Client.Get(ctx, key, obj); err != nil { 169 + // 2. If the object is not found, create it. 170 + if !errors.IsNotFound(err) { 171 + return err // Return any error other than NotFound 172 + } 173 + 174 + // Use Controllerutil to manage the obj 175 + if err := controllerutil.SetControllerReference(owner, obj, r.Scheme); err != nil { 176 + return err 177 + } 178 + 179 + logger.Info("Creating resource", "object", key) 180 + return r.Client.Create(ctx, obj) 181 + } 182 + 183 + // 3. If the object exists, update it. 184 + existing := obj.DeepCopyObject().(client.Object) // Deep copy to avoid modifying the cache 185 + 186 + // Use Patch to apply only the changes, improving efficiency 187 + if err := r.Client.Patch(ctx, obj, client.MergeFrom(existing)); err != nil { 188 + return err 189 + } 190 + 191 + logger.Info("Updated resource", "object", key) 192 + return nil 193 + } 194 + 195 + // SetupWithManager sets up the controller with the Manager. 196 + func (r *SecretServiceReconciler) SetupWithManager(mgr ctrl.Manager) error { 197 + return ctrl.NewControllerManagedBy(mgr). 198 + For(&appsv1.SecretService{}). 199 + Complete(r) 200 + }
+84
internal/controller/secretservice_controller_test.go
··· 1 + /* 2 + Copyright 2024. 3 + 4 + Licensed under the Apache License, Version 2.0 (the "License"); 5 + you may not use this file except in compliance with the License. 6 + You may obtain a copy of the License at 7 + 8 + http://www.apache.org/licenses/LICENSE-2.0 9 + 10 + Unless required by applicable law or agreed to in writing, software 11 + distributed under the License is distributed on an "AS IS" BASIS, 12 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 + See the License for the specific language governing permissions and 14 + limitations under the License. 15 + */ 16 + 17 + package controller 18 + 19 + import ( 20 + "context" 21 + 22 + . "github.com/onsi/ginkgo/v2" 23 + . "github.com/onsi/gomega" 24 + "k8s.io/apimachinery/pkg/api/errors" 25 + "k8s.io/apimachinery/pkg/types" 26 + "sigs.k8s.io/controller-runtime/pkg/reconcile" 27 + 28 + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 29 + 30 + appsv1 "github.com/evanjarrett/secret-service-operator/api/v1" 31 + ) 32 + 33 + var _ = Describe("SecretService Controller", func() { 34 + Context("When reconciling a resource", func() { 35 + const resourceName = "test-resource" 36 + 37 + ctx := context.Background() 38 + 39 + typeNamespacedName := types.NamespacedName{ 40 + Name: resourceName, 41 + Namespace: "default", // TODO(user):Modify as needed 42 + } 43 + secretservice := &appsv1.SecretService{} 44 + 45 + BeforeEach(func() { 46 + By("creating the custom resource for the Kind SecretService") 47 + err := k8sClient.Get(ctx, typeNamespacedName, secretservice) 48 + if err != nil && errors.IsNotFound(err) { 49 + resource := &appsv1.SecretService{ 50 + ObjectMeta: metav1.ObjectMeta{ 51 + Name: resourceName, 52 + Namespace: "default", 53 + }, 54 + // TODO(user): Specify other spec details if needed. 55 + } 56 + Expect(k8sClient.Create(ctx, resource)).To(Succeed()) 57 + } 58 + }) 59 + 60 + AfterEach(func() { 61 + // TODO(user): Cleanup logic after each test, like removing the resource instance. 62 + resource := &appsv1.SecretService{} 63 + err := k8sClient.Get(ctx, typeNamespacedName, resource) 64 + Expect(err).NotTo(HaveOccurred()) 65 + 66 + By("Cleanup the specific resource instance SecretService") 67 + Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) 68 + }) 69 + It("should successfully reconcile the resource", func() { 70 + By("Reconciling the created resource") 71 + controllerReconciler := &SecretServiceReconciler{ 72 + Client: k8sClient, 73 + Scheme: k8sClient.Scheme(), 74 + } 75 + 76 + _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ 77 + NamespacedName: typeNamespacedName, 78 + }) 79 + Expect(err).NotTo(HaveOccurred()) 80 + // TODO(user): Add more specific assertions depending on your controller's reconciliation logic. 81 + // Example: If you expect a certain status condition after reconciliation, verify it here. 82 + }) 83 + }) 84 + })
+90
internal/controller/suite_test.go
··· 1 + /* 2 + Copyright 2024. 3 + 4 + Licensed under the Apache License, Version 2.0 (the "License"); 5 + you may not use this file except in compliance with the License. 6 + You may obtain a copy of the License at 7 + 8 + http://www.apache.org/licenses/LICENSE-2.0 9 + 10 + Unless required by applicable law or agreed to in writing, software 11 + distributed under the License is distributed on an "AS IS" BASIS, 12 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 + See the License for the specific language governing permissions and 14 + limitations under the License. 15 + */ 16 + 17 + package controller 18 + 19 + import ( 20 + "fmt" 21 + "path/filepath" 22 + "runtime" 23 + "testing" 24 + 25 + . "github.com/onsi/ginkgo/v2" 26 + . "github.com/onsi/gomega" 27 + 28 + "k8s.io/client-go/kubernetes/scheme" 29 + "k8s.io/client-go/rest" 30 + "sigs.k8s.io/controller-runtime/pkg/client" 31 + "sigs.k8s.io/controller-runtime/pkg/envtest" 32 + logf "sigs.k8s.io/controller-runtime/pkg/log" 33 + "sigs.k8s.io/controller-runtime/pkg/log/zap" 34 + 35 + appsv1 "github.com/evanjarrett/secret-service-operator/api/v1" 36 + //+kubebuilder:scaffold:imports 37 + ) 38 + 39 + // These tests use Ginkgo (BDD-style Go testing framework). Refer to 40 + // http://onsi.github.io/ginkgo/ to learn more about Ginkgo. 41 + 42 + var cfg *rest.Config 43 + var k8sClient client.Client 44 + var testEnv *envtest.Environment 45 + 46 + func TestControllers(t *testing.T) { 47 + RegisterFailHandler(Fail) 48 + 49 + RunSpecs(t, "Controller Suite") 50 + } 51 + 52 + var _ = BeforeSuite(func() { 53 + logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) 54 + 55 + By("bootstrapping test environment") 56 + testEnv = &envtest.Environment{ 57 + CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")}, 58 + ErrorIfCRDPathMissing: true, 59 + 60 + // The BinaryAssetsDirectory is only required if you want to run the tests directly 61 + // without call the makefile target test. If not informed it will look for the 62 + // default path defined in controller-runtime which is /usr/local/kubebuilder/. 63 + // Note that you must have the required binaries setup under the bin directory to perform 64 + // the tests directly. When we run make test it will be setup and used automatically. 65 + BinaryAssetsDirectory: filepath.Join("..", "..", "bin", "k8s", 66 + fmt.Sprintf("1.29.0-%s-%s", runtime.GOOS, runtime.GOARCH)), 67 + } 68 + 69 + var err error 70 + // cfg is defined in this file globally. 71 + cfg, err = testEnv.Start() 72 + Expect(err).NotTo(HaveOccurred()) 73 + Expect(cfg).NotTo(BeNil()) 74 + 75 + err = appsv1.AddToScheme(scheme.Scheme) 76 + Expect(err).NotTo(HaveOccurred()) 77 + 78 + //+kubebuilder:scaffold:scheme 79 + 80 + k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) 81 + Expect(err).NotTo(HaveOccurred()) 82 + Expect(k8sClient).NotTo(BeNil()) 83 + 84 + }) 85 + 86 + var _ = AfterSuite(func() { 87 + By("tearing down the test environment") 88 + err := testEnv.Stop() 89 + Expect(err).NotTo(HaveOccurred()) 90 + })
+32
test/e2e/e2e_suite_test.go
··· 1 + /* 2 + Copyright 2024. 3 + 4 + Licensed under the Apache License, Version 2.0 (the "License"); 5 + you may not use this file except in compliance with the License. 6 + You may obtain a copy of the License at 7 + 8 + http://www.apache.org/licenses/LICENSE-2.0 9 + 10 + Unless required by applicable law or agreed to in writing, software 11 + distributed under the License is distributed on an "AS IS" BASIS, 12 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 + See the License for the specific language governing permissions and 14 + limitations under the License. 15 + */ 16 + 17 + package e2e 18 + 19 + import ( 20 + "fmt" 21 + "testing" 22 + 23 + . "github.com/onsi/ginkgo/v2" 24 + . "github.com/onsi/gomega" 25 + ) 26 + 27 + // Run e2e tests using the Ginkgo runner. 28 + func TestE2E(t *testing.T) { 29 + RegisterFailHandler(Fail) 30 + fmt.Fprintf(GinkgoWriter, "Starting secret-service-operator suite\n") 31 + RunSpecs(t, "e2e suite") 32 + }
+122
test/e2e/e2e_test.go
··· 1 + /* 2 + Copyright 2024. 3 + 4 + Licensed under the Apache License, Version 2.0 (the "License"); 5 + you may not use this file except in compliance with the License. 6 + You may obtain a copy of the License at 7 + 8 + http://www.apache.org/licenses/LICENSE-2.0 9 + 10 + Unless required by applicable law or agreed to in writing, software 11 + distributed under the License is distributed on an "AS IS" BASIS, 12 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 + See the License for the specific language governing permissions and 14 + limitations under the License. 15 + */ 16 + 17 + package e2e 18 + 19 + import ( 20 + "fmt" 21 + "os/exec" 22 + "time" 23 + 24 + . "github.com/onsi/ginkgo/v2" 25 + . "github.com/onsi/gomega" 26 + 27 + "github.com/evanjarrett/secret-service-operator/test/utils" 28 + ) 29 + 30 + const namespace = "secret-service-operator-system" 31 + 32 + var _ = Describe("controller", Ordered, func() { 33 + BeforeAll(func() { 34 + By("installing prometheus operator") 35 + Expect(utils.InstallPrometheusOperator()).To(Succeed()) 36 + 37 + By("installing the cert-manager") 38 + Expect(utils.InstallCertManager()).To(Succeed()) 39 + 40 + By("creating manager namespace") 41 + cmd := exec.Command("kubectl", "create", "ns", namespace) 42 + _, _ = utils.Run(cmd) 43 + }) 44 + 45 + AfterAll(func() { 46 + By("uninstalling the Prometheus manager bundle") 47 + utils.UninstallPrometheusOperator() 48 + 49 + By("uninstalling the cert-manager bundle") 50 + utils.UninstallCertManager() 51 + 52 + By("removing manager namespace") 53 + cmd := exec.Command("kubectl", "delete", "ns", namespace) 54 + _, _ = utils.Run(cmd) 55 + }) 56 + 57 + Context("Operator", func() { 58 + It("should run successfully", func() { 59 + var controllerPodName string 60 + var err error 61 + 62 + // projectimage stores the name of the image used in the example 63 + var projectimage = "example.com/secret-service-operator:v0.0.1" 64 + 65 + By("building the manager(Operator) image") 66 + cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", projectimage)) 67 + _, err = utils.Run(cmd) 68 + ExpectWithOffset(1, err).NotTo(HaveOccurred()) 69 + 70 + By("loading the the manager(Operator) image on Kind") 71 + err = utils.LoadImageToKindClusterWithName(projectimage) 72 + ExpectWithOffset(1, err).NotTo(HaveOccurred()) 73 + 74 + By("installing CRDs") 75 + cmd = exec.Command("make", "install") 76 + _, err = utils.Run(cmd) 77 + ExpectWithOffset(1, err).NotTo(HaveOccurred()) 78 + 79 + By("deploying the controller-manager") 80 + cmd = exec.Command("make", "deploy", fmt.Sprintf("IMG=%s", projectimage)) 81 + _, err = utils.Run(cmd) 82 + ExpectWithOffset(1, err).NotTo(HaveOccurred()) 83 + 84 + By("validating that the controller-manager pod is running as expected") 85 + verifyControllerUp := func() error { 86 + // Get pod name 87 + 88 + cmd = exec.Command("kubectl", "get", 89 + "pods", "-l", "control-plane=controller-manager", 90 + "-o", "go-template={{ range .items }}"+ 91 + "{{ if not .metadata.deletionTimestamp }}"+ 92 + "{{ .metadata.name }}"+ 93 + "{{ \"\\n\" }}{{ end }}{{ end }}", 94 + "-n", namespace, 95 + ) 96 + 97 + podOutput, err := utils.Run(cmd) 98 + ExpectWithOffset(2, err).NotTo(HaveOccurred()) 99 + podNames := utils.GetNonEmptyLines(string(podOutput)) 100 + if len(podNames) != 1 { 101 + return fmt.Errorf("expect 1 controller pods running, but got %d", len(podNames)) 102 + } 103 + controllerPodName = podNames[0] 104 + ExpectWithOffset(2, controllerPodName).Should(ContainSubstring("controller-manager")) 105 + 106 + // Validate pod status 107 + cmd = exec.Command("kubectl", "get", 108 + "pods", controllerPodName, "-o", "jsonpath={.status.phase}", 109 + "-n", namespace, 110 + ) 111 + status, err := utils.Run(cmd) 112 + ExpectWithOffset(2, err).NotTo(HaveOccurred()) 113 + if string(status) != "Running" { 114 + return fmt.Errorf("controller pod in %s status", status) 115 + } 116 + return nil 117 + } 118 + EventuallyWithOffset(1, verifyControllerUp, time.Minute, time.Second).Should(Succeed()) 119 + 120 + }) 121 + }) 122 + })
+140
test/utils/utils.go
··· 1 + /* 2 + Copyright 2024. 3 + 4 + Licensed under the Apache License, Version 2.0 (the "License"); 5 + you may not use this file except in compliance with the License. 6 + You may obtain a copy of the License at 7 + 8 + http://www.apache.org/licenses/LICENSE-2.0 9 + 10 + Unless required by applicable law or agreed to in writing, software 11 + distributed under the License is distributed on an "AS IS" BASIS, 12 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 + See the License for the specific language governing permissions and 14 + limitations under the License. 15 + */ 16 + 17 + package utils 18 + 19 + import ( 20 + "fmt" 21 + "os" 22 + "os/exec" 23 + "strings" 24 + 25 + . "github.com/onsi/ginkgo/v2" //nolint:golint,revive 26 + ) 27 + 28 + const ( 29 + prometheusOperatorVersion = "v0.72.0" 30 + prometheusOperatorURL = "https://github.com/prometheus-operator/prometheus-operator/" + 31 + "releases/download/%s/bundle.yaml" 32 + 33 + certmanagerVersion = "v1.14.4" 34 + certmanagerURLTmpl = "https://github.com/jetstack/cert-manager/releases/download/%s/cert-manager.yaml" 35 + ) 36 + 37 + func warnError(err error) { 38 + fmt.Fprintf(GinkgoWriter, "warning: %v\n", err) 39 + } 40 + 41 + // InstallPrometheusOperator installs the prometheus Operator to be used to export the enabled metrics. 42 + func InstallPrometheusOperator() error { 43 + url := fmt.Sprintf(prometheusOperatorURL, prometheusOperatorVersion) 44 + cmd := exec.Command("kubectl", "create", "-f", url) 45 + _, err := Run(cmd) 46 + return err 47 + } 48 + 49 + // Run executes the provided command within this context 50 + func Run(cmd *exec.Cmd) ([]byte, error) { 51 + dir, _ := GetProjectDir() 52 + cmd.Dir = dir 53 + 54 + if err := os.Chdir(cmd.Dir); err != nil { 55 + fmt.Fprintf(GinkgoWriter, "chdir dir: %s\n", err) 56 + } 57 + 58 + cmd.Env = append(os.Environ(), "GO111MODULE=on") 59 + command := strings.Join(cmd.Args, " ") 60 + fmt.Fprintf(GinkgoWriter, "running: %s\n", command) 61 + output, err := cmd.CombinedOutput() 62 + if err != nil { 63 + return output, fmt.Errorf("%s failed with error: (%v) %s", command, err, string(output)) 64 + } 65 + 66 + return output, nil 67 + } 68 + 69 + // UninstallPrometheusOperator uninstalls the prometheus 70 + func UninstallPrometheusOperator() { 71 + url := fmt.Sprintf(prometheusOperatorURL, prometheusOperatorVersion) 72 + cmd := exec.Command("kubectl", "delete", "-f", url) 73 + if _, err := Run(cmd); err != nil { 74 + warnError(err) 75 + } 76 + } 77 + 78 + // UninstallCertManager uninstalls the cert manager 79 + func UninstallCertManager() { 80 + url := fmt.Sprintf(certmanagerURLTmpl, certmanagerVersion) 81 + cmd := exec.Command("kubectl", "delete", "-f", url) 82 + if _, err := Run(cmd); err != nil { 83 + warnError(err) 84 + } 85 + } 86 + 87 + // InstallCertManager installs the cert manager bundle. 88 + func InstallCertManager() error { 89 + url := fmt.Sprintf(certmanagerURLTmpl, certmanagerVersion) 90 + cmd := exec.Command("kubectl", "apply", "-f", url) 91 + if _, err := Run(cmd); err != nil { 92 + return err 93 + } 94 + // Wait for cert-manager-webhook to be ready, which can take time if cert-manager 95 + // was re-installed after uninstalling on a cluster. 96 + cmd = exec.Command("kubectl", "wait", "deployment.apps/cert-manager-webhook", 97 + "--for", "condition=Available", 98 + "--namespace", "cert-manager", 99 + "--timeout", "5m", 100 + ) 101 + 102 + _, err := Run(cmd) 103 + return err 104 + } 105 + 106 + // LoadImageToKindCluster loads a local docker image to the kind cluster 107 + func LoadImageToKindClusterWithName(name string) error { 108 + cluster := "kind" 109 + if v, ok := os.LookupEnv("KIND_CLUSTER"); ok { 110 + cluster = v 111 + } 112 + kindOptions := []string{"load", "docker-image", name, "--name", cluster} 113 + cmd := exec.Command("kind", kindOptions...) 114 + _, err := Run(cmd) 115 + return err 116 + } 117 + 118 + // GetNonEmptyLines converts given command output string into individual objects 119 + // according to line breakers, and ignores the empty elements in it. 120 + func GetNonEmptyLines(output string) []string { 121 + var res []string 122 + elements := strings.Split(output, "\n") 123 + for _, element := range elements { 124 + if element != "" { 125 + res = append(res, element) 126 + } 127 + } 128 + 129 + return res 130 + } 131 + 132 + // GetProjectDir will return the directory where the project is 133 + func GetProjectDir() (string, error) { 134 + wd, err := os.Getwd() 135 + if err != nil { 136 + return wd, err 137 + } 138 + wd = strings.Replace(wd, "/test/e2e", "", -1) 139 + return wd, nil 140 + }