├── .dockerignore ├── .gitignore ├── .vscode ├── configurationCache.log ├── dryrun.log ├── launch.json ├── settings.json └── targets.log ├── Dockerfile ├── Makefile ├── PROJECT ├── README.md ├── api └── v1alpha1 │ ├── groupversion_info.go │ ├── secretsynchronizer_types.go │ └── zz_generated.deepcopy.go ├── checksums.txt ├── checksums.txt.asc ├── config ├── crd │ ├── bases │ │ └── synchronizer.a1tan_secretsynchronizers.yaml │ ├── kustomization.yaml │ ├── kustomizeconfig.yaml │ └── patches │ │ ├── cainjection_in_secretsynchronizers.yaml │ │ └── webhook_in_secretsynchronizers.yaml ├── default │ ├── kustomization.yaml │ ├── manager_auth_proxy_patch.yaml │ └── manager_config_patch.yaml ├── manager │ ├── controller_manager_config.yaml │ ├── kustomization.yaml │ └── manager.yaml ├── manifests │ └── kustomization.yaml ├── prometheus │ ├── kustomization.yaml │ └── monitor.yaml ├── rbac │ ├── auth_proxy_client_clusterrole.yaml │ ├── auth_proxy_role.yaml │ ├── auth_proxy_role_binding.yaml │ ├── auth_proxy_service.yaml │ ├── kustomization.yaml │ ├── leader_election_role.yaml │ ├── leader_election_role_binding.yaml │ ├── role.yaml │ ├── role_binding.yaml │ ├── secretsynchronizer_editor_role.yaml │ ├── secretsynchronizer_viewer_role.yaml │ └── service_account.yaml ├── samples │ ├── kustomization.yaml │ └── synchronizer_v1alpha1_secretsynchronizer.yaml └── scorecard │ ├── bases │ └── config.yaml │ ├── kustomization.yaml │ └── patches │ ├── basic.config.yaml │ └── olm.config.yaml ├── controllers ├── secretsynchronizer_controller.go └── suite_test.go ├── go.mod ├── go.sum ├── hack └── boilerplate.go.txt ├── kubectl-crossplane ├── main.go └── path.sh /.dockerignore: -------------------------------------------------------------------------------- 1 | # More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file 2 | # Ignore build and test binaries. 3 | bin/ 4 | testbin/ 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Binaries for programs and plugins 3 | *.exe 4 | *.exe~ 5 | *.dll 6 | *.so 7 | *.dylib 8 | bin 9 | testbin/* 10 | 11 | # Test binary, build with `go test -c` 12 | *.test 13 | 14 | # Output of the go coverage tool, specifically when used with LiteIDE 15 | *.out 16 | 17 | # Kubernetes Generated files - skip generated files, except for vendored files 18 | 19 | !vendor/**/zz_generated.* 20 | 21 | # editor and IDE paraphernalia 22 | .idea 23 | *.swp 24 | *.swo 25 | *~ 26 | -------------------------------------------------------------------------------- /.vscode/configurationCache.log: -------------------------------------------------------------------------------- 1 | {"buildTargets":["all","build","bundle","bundle-build","bundle-push","catalog-build","catalog-push","controller-gen","deploy","docker-build","docker-push","envtest","fmt","generate","help","install","kustomize","manifests","opm","run","test","undeploy","uninstall","vet"],"launchTargets":[],"customConfigurationProvider":{"workspaceBrowse":{"browsePath":[],"compilerArgs":[]},"fileIndex":[]}} -------------------------------------------------------------------------------- /.vscode/dryrun.log: -------------------------------------------------------------------------------- 1 | make --dry-run --always-make --keep-going --print-directory 2 | make: Entering directory '/mnt/c/Users/alaltund/Desktop/Work/Kubernetes/Operators/ArgoCDSecretSynchronizer' 3 | [ -f /mnt/c/Users/alaltund/Desktop/Work/Kubernetes/Operators/ArgoCDSecretSynchronizer/bin/controller-gen ] || { set -e ; TMP_DIR=$(mktemp -d) ; cd $TMP_DIR ; go mod init tmp ; echo "Downloading sigs.k8s.io/controller-tools/cmd/controller-gen@v0.7.0" ; GOBIN=/mnt/c/Users/alaltund/Desktop/Work/Kubernetes/Operators/ArgoCDSecretSynchronizer/bin go get sigs.k8s.io/controller-tools/cmd/controller-gen@v0.7.0 ; rm -rf $TMP_DIR ; } 4 | /mnt/c/Users/alaltund/Desktop/Work/Kubernetes/Operators/ArgoCDSecretSynchronizer/bin/controller-gen object:headerFile="hack/boilerplate.go.txt" paths="./..." 5 | go fmt ./... 6 | go vet ./... 7 | go build -o bin/manager main.go 8 | make: Leaving directory '/mnt/c/Users/alaltund/Desktop/Work/Kubernetes/Operators/ArgoCDSecretSynchronizer' 9 | 10 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Launch Package", 9 | "type": "go", 10 | "request": "launch", 11 | "mode": "auto", 12 | "program": "${fileDirname}" 13 | }, 14 | { 15 | "name": "Debug Operator", 16 | "type": "go", 17 | "request": "launch", 18 | "mode": "debug", 19 | "program": "${workspaceFolder}/controllers/secretsynchronizer_controller.go", 20 | "env": { 21 | "WATCH_NAMESPACE": "argocdsecretsynchronizer-system", 22 | "POD_NAME": "argocdsecretsynchronizer-controller-manager-67ff56ff46-fx9xv", 23 | "OPERATOR_NAME": "argocdsecretsynchronizer" 24 | } 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "go.gocodeAutoBuild": true, 3 | // "go.useLanguageServer": false, 4 | // "go.goroot": "/mnt/c/applinux/go", 5 | "go.goroot": "/usr/local/go", 6 | "go.inferGopath": false, 7 | "go.toolsEnvVars": { 8 | "GO111MODULE": "on" 9 | }, 10 | // "go.gopath": "/mnt/c/Users/alaltund/Desktop/Work/Kubernetes/Operators/ArgoCDSecretSynchronizer" 11 | } -------------------------------------------------------------------------------- /.vscode/targets.log: -------------------------------------------------------------------------------- 1 | make all --print-data-base --no-builtin-variables --no-builtin-rules --question 2 | # GNU Make 4.2.1 3 | # Built for x86_64-pc-linux-gnu 4 | # Copyright (C) 1988-2016 Free Software Foundation, Inc. 5 | # License GPLv3+: GNU GPL version 3 or later 6 | # This is free software: you are free to change and redistribute it. 7 | # There is NO WARRANTY, to the extent permitted by law. 8 | 9 | # Make data base, printed on Sun Jul 31 15:41:38 2022 10 | 11 | # Variables 12 | 13 | # automatic 14 | \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) 350 | 351 | kustomize: 352 | # Phony target (prerequisite of .PHONY). 353 | # Implicit rule search has not been done. 354 | # File does not exist. 355 | # File has not been updated. 356 | # recipe to execute (from 'Makefile', line 147): 357 | $(call go-get-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v3@v3.8.7) 358 | 359 | generate: controller-gen 360 | # Phony target (prerequisite of .PHONY). 361 | # Implicit rule search has not been done. 362 | # File does not exist. 363 | # File has been updated. 364 | # Needs to be updated (-q is set). 365 | # variable set hash-table stats: 366 | # Load=0/32=0%, Rehash=0, Collisions=0/10=0% 367 | # recipe to execute (from 'Makefile', line 84): 368 | $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." 369 | 370 | undeploy: 371 | # Phony target (prerequisite of .PHONY). 372 | # Implicit rule search has not been done. 373 | # File does not exist. 374 | # File has not been updated. 375 | # recipe to execute (from 'Makefile', line 137): 376 | $(KUSTOMIZE) build config/default | kubectl delete --ignore-not-found=$(ignore-not-found) -f - 377 | 378 | manifests: controller-gen 379 | # Phony target (prerequisite of .PHONY). 380 | # Implicit rule search has not been done. 381 | # File does not exist. 382 | # File has not been updated. 383 | # recipe to execute (from 'Makefile', line 80): 384 | $(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases 385 | 386 | # Not a target: 387 | .DEFAULT: 388 | # Implicit rule search has not been done. 389 | # Modification time never checked. 390 | # File has not been updated. 391 | 392 | run: manifests generate fmt vet 393 | # Phony target (prerequisite of .PHONY). 394 | # Implicit rule search has not been done. 395 | # File does not exist. 396 | # File has not been updated. 397 | # recipe to execute (from 'Makefile', line 106): 398 | go run ./main.go 399 | 400 | bundle-build: 401 | # Phony target (prerequisite of .PHONY). 402 | # Implicit rule search has not been done. 403 | # File does not exist. 404 | # File has not been updated. 405 | # recipe to execute (from 'Makefile', line 177): 406 | docker build -f bundle.Dockerfile -t $(BUNDLE_IMG) . 407 | 408 | fmt: 409 | # Phony target (prerequisite of .PHONY). 410 | # Implicit rule search has not been done. 411 | # File does not exist. 412 | # File has not been updated. 413 | # recipe to execute (from 'Makefile', line 88): 414 | go fmt ./... 415 | 416 | deploy: manifests kustomize 417 | # Phony target (prerequisite of .PHONY). 418 | # Implicit rule search has not been done. 419 | # File does not exist. 420 | # File has not been updated. 421 | # recipe to execute (from 'Makefile', line 132): 422 | cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} 423 | $(KUSTOMIZE) build config/default | kubectl apply -f - 424 | 425 | # 'override' directive 426 | controller-gen: .SHELLSTATUS := 0 427 | controller-gen: 428 | # Phony target (prerequisite of .PHONY). 429 | # Implicit rule search has not been done. 430 | # Implicit/static pattern stem: '' 431 | # File does not exist. 432 | # File has been updated. 433 | # Needs to be updated (-q is set). 434 | # automatic 435 | # @ := controller-gen 436 | # automatic 437 | # % := 438 | # automatic 439 | # * := 440 | # automatic 441 | # + := 442 | # automatic 443 | # | := 444 | # automatic 445 | # < := 446 | # automatic 447 | # ^ := 448 | # automatic 449 | # ? := 450 | # variable set hash-table stats: 451 | # Load=9/32=28%, Rehash=0, Collisions=4/19=21% 452 | # recipe to execute (from 'Makefile', line 142): 453 | $(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.7.0) 454 | 455 | docker-push: 456 | # Phony target (prerequisite of .PHONY). 457 | # Implicit rule search has not been done. 458 | # File does not exist. 459 | # File has not been updated. 460 | # recipe to execute (from 'Makefile', line 114): 461 | docker push ${IMG} 462 | 463 | install: manifests kustomize 464 | # Phony target (prerequisite of .PHONY). 465 | # Implicit rule search has not been done. 466 | # File does not exist. 467 | # File has not been updated. 468 | # recipe to execute (from 'Makefile', line 124): 469 | $(KUSTOMIZE) build config/crd | kubectl apply -f - 470 | 471 | # files hash-table stats: 472 | # Load=28/1024=3%, Rehash=0, Collisions=6/135=4% 473 | # VPATH Search Paths 474 | 475 | # No 'vpath' search paths. 476 | 477 | # No general ('VPATH' variable) search path. 478 | 479 | # strcache buffers: 1 (0) / strings = 51 / storage = 461 B / avg = 9 B 480 | # current buf: size = 8162 B / used = 461 B / count = 51 / avg = 9 B 481 | 482 | # strcache performance: lookups = 177 / hit rate = 71% 483 | # hash-table stats: 484 | # Load=51/8192=1%, Rehash=0, Collisions=4/177=2% 485 | # Finished Make data base on Sun Jul 31 15:41:38 2022 486 | 487 | 488 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Build the manager binary 2 | FROM golang:1.16 as builder 3 | 4 | WORKDIR /workspace 5 | # Copy the Go Modules manifests 6 | COPY go.mod go.mod 7 | COPY go.sum go.sum 8 | # cache deps before building and copying source so that we don't need to re-download as much 9 | # and so that source changes don't invalidate our downloaded layer 10 | RUN go mod download 11 | 12 | # Copy the go source 13 | COPY main.go main.go 14 | COPY api/ api/ 15 | COPY controllers/ controllers/ 16 | 17 | # Build 18 | RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o manager main.go 19 | 20 | # Use distroless as minimal base image to package the manager binary 21 | # Refer to https://github.com/GoogleContainerTools/distroless for more details 22 | FROM gcr.io/distroless/static:nonroot 23 | WORKDIR / 24 | COPY --from=builder /workspace/manager . 25 | USER 65532:65532 26 | 27 | ENTRYPOINT ["/manager"] 28 | -------------------------------------------------------------------------------- /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 | # a1tan/argocdsecretsynchronizer-bundle:$VERSION and a1tan/argocdsecretsynchronizer-catalog:$VERSION. 32 | IMAGE_TAG_BASE ?= a1tan/argocdsecretsynchronizer 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=/:) 36 | BUNDLE_IMG ?= $(IMAGE_TAG_BASE)-bundle:v$(VERSION) 37 | 38 | # Image URL to use all building/pushing image targets 39 | IMG ?= a1tan/argocdsecretsynchronizer:0.0.38 40 | # ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. 41 | ENVTEST_K8S_VERSION = 1.22 42 | 43 | # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) 44 | ifeq (,$(shell go env GOBIN)) 45 | GOBIN=$(shell go env GOPATH)/bin 46 | else 47 | GOBIN=$(shell go env GOBIN) 48 | endif 49 | 50 | # Setting SHELL to bash allows bash commands to be executed by recipes. 51 | # This is a requirement for 'setup-envtest.sh' in the test target. 52 | # Options are set to exit when a recipe line exits non-zero or a piped command fails. 53 | SHELL = /usr/bin/env bash -o pipefail 54 | .SHELLFLAGS = -ec 55 | 56 | .PHONY: all 57 | all: build 58 | 59 | ##@ General 60 | 61 | # The help target prints out all targets with their descriptions organized 62 | # beneath their categories. The categories are represented by '##@' and the 63 | # target descriptions by '##'. The awk commands is responsible for reading the 64 | # entire set of makefiles included in this invocation, looking for lines of the 65 | # file as xyz: ## something, and then pretty-format the target and help. Then, 66 | # if there's a line with ##@ something, that gets pretty-printed as a category. 67 | # More info on the usage of ANSI control characters for terminal formatting: 68 | # https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters 69 | # More info on the awk command: 70 | # http://linuxcommand.org/lc3_adv_awk.php 71 | 72 | .PHONY: help 73 | help: ## Display this help. 74 | @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) 75 | 76 | ##@ Development 77 | 78 | .PHONY: manifests 79 | manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. 80 | $(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases 81 | 82 | .PHONY: generate 83 | generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. 84 | $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." 85 | 86 | .PHONY: fmt 87 | fmt: ## Run go fmt against code. 88 | go fmt ./... 89 | 90 | .PHONY: vet 91 | vet: ## Run go vet against code. 92 | go vet ./... 93 | 94 | .PHONY: test 95 | test: manifests generate fmt vet envtest ## Run tests. 96 | KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test ./... -coverprofile cover.out 97 | 98 | ##@ Build 99 | 100 | .PHONY: build 101 | build: generate fmt vet ## Build manager binary. 102 | go build -o bin/manager main.go 103 | 104 | .PHONY: run 105 | run: manifests generate fmt vet ## Run a controller from your host. 106 | go run ./main.go 107 | 108 | .PHONY: docker-build 109 | docker-build: test ## Build docker image with the manager. 110 | docker build -t ${IMG} . 111 | 112 | .PHONY: docker-push 113 | docker-push: ## Push docker image with the manager. 114 | docker push ${IMG} 115 | 116 | ##@ Deployment 117 | 118 | ifndef ignore-not-found 119 | ignore-not-found = false 120 | endif 121 | 122 | .PHONY: install 123 | install: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config. 124 | $(KUSTOMIZE) build config/crd | kubectl apply -f - 125 | 126 | .PHONY: uninstall 127 | 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. 128 | $(KUSTOMIZE) build config/crd | kubectl delete --ignore-not-found=$(ignore-not-found) -f - 129 | 130 | .PHONY: deploy 131 | deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config. 132 | cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} 133 | $(KUSTOMIZE) build config/default | kubectl apply -f - 134 | 135 | .PHONY: undeploy 136 | undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. 137 | $(KUSTOMIZE) build config/default | kubectl delete --ignore-not-found=$(ignore-not-found) -f - 138 | 139 | CONTROLLER_GEN = $(shell pwd)/bin/controller-gen 140 | .PHONY: controller-gen 141 | controller-gen: ## Download controller-gen locally if necessary. 142 | $(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.7.0) 143 | 144 | KUSTOMIZE = $(shell pwd)/bin/kustomize 145 | .PHONY: kustomize 146 | kustomize: ## Download kustomize locally if necessary. 147 | $(call go-get-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v3@v3.8.7) 148 | 149 | ENVTEST = $(shell pwd)/bin/setup-envtest 150 | .PHONY: envtest 151 | envtest: ## Download envtest-setup locally if necessary. 152 | $(call go-get-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest@latest) 153 | 154 | # go-get-tool will 'go get' any package $2 and install it to $1. 155 | PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST)))) 156 | define go-get-tool 157 | @[ -f $(1) ] || { \ 158 | set -e ;\ 159 | TMP_DIR=$$(mktemp -d) ;\ 160 | cd $$TMP_DIR ;\ 161 | go mod init tmp ;\ 162 | echo "Downloading $(2)" ;\ 163 | GOBIN=$(PROJECT_DIR)/bin go get $(2) ;\ 164 | rm -rf $$TMP_DIR ;\ 165 | } 166 | endef 167 | 168 | .PHONY: bundle 169 | bundle: manifests kustomize ## Generate bundle manifests and metadata, then validate generated files. 170 | operator-sdk generate kustomize manifests -q 171 | cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG) 172 | $(KUSTOMIZE) build config/manifests | operator-sdk generate bundle -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS) 173 | operator-sdk bundle validate ./bundle 174 | 175 | .PHONY: bundle-build 176 | bundle-build: ## Build the bundle image. 177 | docker build -f bundle.Dockerfile -t $(BUNDLE_IMG) . 178 | 179 | .PHONY: bundle-push 180 | bundle-push: ## Push the bundle image. 181 | $(MAKE) docker-push IMG=$(BUNDLE_IMG) 182 | 183 | .PHONY: opm 184 | OPM = ./bin/opm 185 | opm: ## Download opm locally if necessary. 186 | ifeq (,$(wildcard $(OPM))) 187 | ifeq (,$(shell which opm 2>/dev/null)) 188 | @{ \ 189 | set -e ;\ 190 | mkdir -p $(dir $(OPM)) ;\ 191 | OS=$(shell go env GOOS) && ARCH=$(shell go env GOARCH) && \ 192 | curl -sSLo $(OPM) https://github.com/operator-framework/operator-registry/releases/download/v1.19.1/$${OS}-$${ARCH}-opm ;\ 193 | chmod +x $(OPM) ;\ 194 | } 195 | else 196 | OPM = $(shell which opm) 197 | endif 198 | endif 199 | 200 | # 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). 201 | # These images MUST exist in a registry and be pull-able. 202 | BUNDLE_IMGS ?= $(BUNDLE_IMG) 203 | 204 | # The image tag given to the resulting catalog image (e.g. make catalog-build CATALOG_IMG=example.com/operator-catalog:v0.2.0). 205 | CATALOG_IMG ?= $(IMAGE_TAG_BASE)-catalog:v$(VERSION) 206 | 207 | # Set CATALOG_BASE_IMG to an existing catalog image tag to add $BUNDLE_IMGS to that image. 208 | ifneq ($(origin CATALOG_BASE_IMG), undefined) 209 | FROM_INDEX_OPT := --from-index $(CATALOG_BASE_IMG) 210 | endif 211 | 212 | # Build a catalog image by adding bundle images to an empty catalog using the operator package manager tool, 'opm'. 213 | # This recipe invokes 'opm' in 'semver' bundle add mode. For more information on add modes, see: 214 | # https://github.com/operator-framework/community-operators/blob/7f1438c/docs/packaging-operator.md#updating-your-existing-operator 215 | .PHONY: catalog-build 216 | catalog-build: opm ## Build a catalog image. 217 | $(OPM) index add --container-tool docker --mode semver --tag $(CATALOG_IMG) --bundles $(BUNDLE_IMGS) $(FROM_INDEX_OPT) 218 | 219 | # Push the catalog image. 220 | .PHONY: catalog-push 221 | catalog-push: ## Push a catalog image. 222 | $(MAKE) docker-push IMG=$(CATALOG_IMG) 223 | -------------------------------------------------------------------------------- /PROJECT: -------------------------------------------------------------------------------- 1 | domain: a1tan 2 | layout: 3 | - go.kubebuilder.io/v3 4 | plugins: 5 | manifests.sdk.operatorframework.io/v2: {} 6 | scorecard.sdk.operatorframework.io/v2: {} 7 | projectName: argocdsecretsynchronizer 8 | repo: github.com/a1tan/argocdsecretsynchronizer 9 | resources: 10 | - api: 11 | crdVersion: v1 12 | namespaced: true 13 | controller: true 14 | domain: a1tan 15 | group: synchronizer 16 | kind: SecretSynchronizer 17 | path: github.com/a1tan/argocdsecretsynchronizer/api/v1alpha1 18 | version: v1alpha1 19 | version: "3" 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Argo CD Secret Synchronizer 2 | This tool is an experimental operator to synchronize secrets between declarative kubernetes cluster managers and Argo CD. There is a difference between secrets which are produced by these cluster managers and Argo CD uses as cluster declaration. 3 | 4 | This operator does a simple thing: 5 | * Takes the secrets produced when a cluster added 6 | * Uses the kubeconfig on that secret to create service account, role and rolebinding on created cluster 7 | * Takes the token and CA from the service account secret created 8 | * Creates the related Argo CD Cluster definition which is also another secret 9 | 10 | These cluster managers are: 11 | * Crossplane 12 | * VCluster 13 | * Cluster API 14 | * Azure Service Operator(Not tested yet) 15 | 16 | ## Installation 17 | * Clone this repository 18 | * Run below commands in order 19 | 20 | ``` 21 | kubectl apply -k config/default/ 22 | kubectl apply -k config/samples/ 23 | ``` 24 | 25 | ## Tips 26 | ### Vcluster 27 | When using with Vcluster, Vcluster uses localhost as the kubeconfig server which cannot be used to connect created cluster. 28 | To solve this problem below two parameters has to be set. These parameters point to the service created on the Vcluster namespace on parent cluster. 29 | 30 | ``` 31 | syncer: 32 | extraArgs: 33 | - out-kube-config-server: https://{{service}}.{{namespace}}.svc.cluster.local 34 | - tls-san: {{service}}.{{namespace}}.svc.cluster.local 35 | ``` 36 | -------------------------------------------------------------------------------- /api/v1alpha1/groupversion_info.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022. 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 v1alpha1 contains API Schema definitions for the synchronizer v1alpha1 API group 18 | //+kubebuilder:object:generate=true 19 | //+groupName=synchronizer.a1tan 20 | package v1alpha1 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: "synchronizer.a1tan", Version: "v1alpha1"} 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 | ) 37 | -------------------------------------------------------------------------------- /api/v1alpha1/secretsynchronizer_types.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022. 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 v1alpha1 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 | // SecretSynchronizerSpec defines the desired state of SecretSynchronizer 27 | type SecretSynchronizerSpec struct { 28 | // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster 29 | // Important: Run "make" to regenerate code after modifying this file 30 | 31 | // Foo is an example field of SecretSynchronizer. Edit secretsynchronizer_types.go to remove/update 32 | // Foo string `json:"foo,omitempty"` 33 | Size int32 `json:"size"` 34 | Prune bool `json:"prune"` 35 | // +kubebuilder:default="argocd" 36 | ArgoCDNamespace string `json:"argocd-namespace,omitempty"` 37 | } 38 | 39 | // SecretSynchronizerStatus defines the observed state of SecretSynchronizer 40 | type SecretSynchronizerStatus struct { 41 | // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster 42 | // Important: Run "make" to regenerate code after modifying this file 43 | } 44 | 45 | //+kubebuilder:object:root=true 46 | //+kubebuilder:subresource:status 47 | 48 | // SecretSynchronizer is the Schema for the secretsynchronizers API 49 | type SecretSynchronizer struct { 50 | metav1.TypeMeta `json:",inline"` 51 | metav1.ObjectMeta `json:"metadata,omitempty"` 52 | 53 | Spec SecretSynchronizerSpec `json:"spec,omitempty"` 54 | Status SecretSynchronizerStatus `json:"status,omitempty"` 55 | } 56 | 57 | //+kubebuilder:object:root=true 58 | 59 | // SecretSynchronizerList contains a list of SecretSynchronizer 60 | type SecretSynchronizerList struct { 61 | metav1.TypeMeta `json:",inline"` 62 | metav1.ListMeta `json:"metadata,omitempty"` 63 | Items []SecretSynchronizer `json:"items"` 64 | } 65 | 66 | func init() { 67 | SchemeBuilder.Register(&SecretSynchronizer{}, &SecretSynchronizerList{}) 68 | } 69 | -------------------------------------------------------------------------------- /api/v1alpha1/zz_generated.deepcopy.go: -------------------------------------------------------------------------------- 1 | // +build !ignore_autogenerated 2 | 3 | /* 4 | Copyright 2022. 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 v1alpha1 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 *SecretSynchronizer) DeepCopyInto(out *SecretSynchronizer) { 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 SecretSynchronizer. 37 | func (in *SecretSynchronizer) DeepCopy() *SecretSynchronizer { 38 | if in == nil { 39 | return nil 40 | } 41 | out := new(SecretSynchronizer) 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 *SecretSynchronizer) 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 *SecretSynchronizerList) DeepCopyInto(out *SecretSynchronizerList) { 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([]SecretSynchronizer, 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 SecretSynchronizerList. 69 | func (in *SecretSynchronizerList) DeepCopy() *SecretSynchronizerList { 70 | if in == nil { 71 | return nil 72 | } 73 | out := new(SecretSynchronizerList) 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 *SecretSynchronizerList) 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 *SecretSynchronizerSpec) DeepCopyInto(out *SecretSynchronizerSpec) { 88 | *out = *in 89 | } 90 | 91 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretSynchronizerSpec. 92 | func (in *SecretSynchronizerSpec) DeepCopy() *SecretSynchronizerSpec { 93 | if in == nil { 94 | return nil 95 | } 96 | out := new(SecretSynchronizerSpec) 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 *SecretSynchronizerStatus) DeepCopyInto(out *SecretSynchronizerStatus) { 103 | *out = *in 104 | } 105 | 106 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretSynchronizerStatus. 107 | func (in *SecretSynchronizerStatus) DeepCopy() *SecretSynchronizerStatus { 108 | if in == nil { 109 | return nil 110 | } 111 | out := new(SecretSynchronizerStatus) 112 | in.DeepCopyInto(out) 113 | return out 114 | } 115 | -------------------------------------------------------------------------------- /checksums.txt: -------------------------------------------------------------------------------- 1 | 00d856e39d592571b58299878494d545fdcb045da40c067b1303cfa096d2941a operator-sdk_darwin_arm64 2 | 146b78f7fda0585ab4688e52d92b02b31b956f318b309698917222da83661087 ansible-operator_darwin_amd64 3 | 3ae3b4f94dbd06387095ad711f889aedaa939fe8789dd7db20161466f1885109 operator-sdk_linux_arm64 4 | 4cce14c7779492d87d582aaf220f8435af3213a10556c9753d2c2a66c91ca5ed helm-operator_linux_ppc64le 5 | 51ded654744179f333f354f9cbcdb8addbd22baa029456f1fa0474743ed24975 ansible-operator_darwin_arm64 6 | 56ded20e09dd31830052fcee96cdaf2b3098e309b582f917e9dc569d33613d83 helm-operator_linux_arm64 7 | 606531c3ee98be96ea41d7b6f44da1c53bbc229f4bea08da59fbb7ace71bbbbf helm-operator_linux_amd64 8 | 6bbb933e6169c1f36dca1f02ee337802b6216695f8d97b97e767553667ebdebf operator-sdk_linux_amd64 9 | 7419d7fcd2beae6b39187e77349fadd69c724634ec52686e8dadcc7ef9d03098 operator-sdk_linux_ppc64le 10 | 790c2ff879c5db0c9bafa71d1b1fa1d6f18a57ebb5254344fb45088caeb9b9a4 operator-sdk_linux_s390x 11 | 97c0d19c76b08eed5cf3961e5c2fd9e3e39df9763092f8eb6c62e7d2d8f233a0 helm-operator_darwin_amd64 12 | a916082a9bdc4d12377ea278a8fd5156062cfe2c732c8806e7419efd4d339e43 operator-sdk_darwin_amd64 13 | b6cd0f421c6b42417b8a486cde1ab0dcc5ccac45bf0fb5a819f893c38b03abb7 helm-operator_linux_s390x 14 | c3e4c3a2362969619c98b777e3c876ea8dd0909df74884c7f7ffe9c9fdfc2f25 ansible-operator_linux_ppc64le 15 | d701d1fb0b8194ecaec2ad58c5535dd4d5e093de7c7bc920cf7f48143960f64c ansible-operator_linux_arm64 16 | e27a6d8dbb589a3e44007b3a970b0de0bb711da09049efd9131d5ae9936599d1 helm-operator_darwin_arm64 17 | e2d71b80c75cd4b5f4147c853450103a943291659cdc6df47fd280eb1c1327f7 ansible-operator_linux_s390x 18 | f42ef49d3ba90a0d6a9ec86dfdcbef841e7abeb3f8141385b8d49181b249a43b ansible-operator_linux_amd64 19 | -------------------------------------------------------------------------------- /checksums.txt.asc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a1tan/argocdsecretsynchronizer/1d3e103e99b77fa5232c7d91b7601ae8aedea570/checksums.txt.asc -------------------------------------------------------------------------------- /config/crd/bases/synchronizer.a1tan_secretsynchronizers.yaml: -------------------------------------------------------------------------------- 1 | 2 | --- 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | annotations: 7 | controller-gen.kubebuilder.io/version: v0.7.0 8 | creationTimestamp: null 9 | name: secretsynchronizers.synchronizer.a1tan 10 | spec: 11 | group: synchronizer.a1tan 12 | names: 13 | kind: SecretSynchronizer 14 | listKind: SecretSynchronizerList 15 | plural: secretsynchronizers 16 | singular: secretsynchronizer 17 | scope: Namespaced 18 | versions: 19 | - name: v1alpha1 20 | schema: 21 | openAPIV3Schema: 22 | description: SecretSynchronizer is the Schema for the secretsynchronizers 23 | API 24 | properties: 25 | apiVersion: 26 | description: 'APIVersion defines the versioned schema of this representation 27 | of an object. Servers should convert recognized schemas to the latest 28 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 29 | type: string 30 | kind: 31 | description: 'Kind is a string value representing the REST resource this 32 | object represents. Servers may infer this from the endpoint the client 33 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 34 | type: string 35 | metadata: 36 | type: object 37 | spec: 38 | description: SecretSynchronizerSpec defines the desired state of SecretSynchronizer 39 | properties: 40 | argocd-namespace: 41 | default: argocd 42 | type: string 43 | kubeconfig-data-field: 44 | default: kubeconfig 45 | type: string 46 | prune: 47 | type: boolean 48 | size: 49 | description: Foo is an example field of SecretSynchronizer. Edit secretsynchronizer_types.go 50 | to remove/update Foo string `json:"foo,omitempty"` 51 | format: int32 52 | type: integer 53 | required: 54 | - prune 55 | - size 56 | type: object 57 | status: 58 | description: SecretSynchronizerStatus defines the observed state of SecretSynchronizer 59 | type: object 60 | type: object 61 | served: true 62 | storage: true 63 | subresources: 64 | status: {} 65 | status: 66 | acceptedNames: 67 | kind: "" 68 | plural: "" 69 | conditions: [] 70 | storedVersions: [] 71 | -------------------------------------------------------------------------------- /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/synchronizer.a1tan_secretsynchronizers.yaml 6 | #+kubebuilder:scaffold:crdkustomizeresource 7 | 8 | patchesStrategicMerge: 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 | #- patches/webhook_in_secretsynchronizers.yaml 12 | #+kubebuilder:scaffold:crdkustomizewebhookpatch 13 | 14 | # [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. 15 | # patches here are for enabling the CA injection for each CRD 16 | #- patches/cainjection_in_secretsynchronizers.yaml 17 | #+kubebuilder:scaffold:crdkustomizecainjectionpatch 18 | 19 | # the following config is for teaching kustomize how to do kustomization for CRDs. 20 | configurations: 21 | - kustomizeconfig.yaml 22 | -------------------------------------------------------------------------------- /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 20 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_secretsynchronizers.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 7 | name: secretsynchronizers.synchronizer.a1tan 8 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_secretsynchronizers.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables a conversion webhook for the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: secretsynchronizers.synchronizer.a1tan 6 | spec: 7 | conversion: 8 | strategy: Webhook 9 | webhook: 10 | clientConfig: 11 | service: 12 | namespace: system 13 | name: webhook-service 14 | path: /convert 15 | conversionReviewVersions: 16 | - v1 17 | -------------------------------------------------------------------------------- /config/default/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # Adds namespace to all resources. 2 | namespace: argocdsecretsynchronizer-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: argocdsecretsynchronizer- 10 | 11 | # Labels to add to all resources and selectors. 12 | #commonLabels: 13 | # someName: someValue 14 | 15 | bases: 16 | - ../crd 17 | - ../rbac 18 | - ../manager 19 | # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in 20 | # crd/kustomization.yaml 21 | #- ../webhook 22 | # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required. 23 | #- ../certmanager 24 | # [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. 25 | #- ../prometheus 26 | 27 | patchesStrategicMerge: 28 | # Protect the /metrics endpoint by putting it behind auth. 29 | # If you want your controller-manager to expose the /metrics 30 | # endpoint w/o any authn/z, please comment the following line. 31 | - manager_auth_proxy_patch.yaml 32 | 33 | # Mount the controller config file for loading manager configurations 34 | # through a ComponentConfig type 35 | #- manager_config_patch.yaml 36 | 37 | # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in 38 | # crd/kustomization.yaml 39 | #- manager_webhook_patch.yaml 40 | 41 | # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 42 | # Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks. 43 | # 'CERTMANAGER' needs to be enabled to use ca injection 44 | #- webhookcainjection_patch.yaml 45 | 46 | # the following config is for teaching kustomize how to do var substitution 47 | vars: 48 | # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. 49 | #- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR 50 | # objref: 51 | # kind: Certificate 52 | # group: cert-manager.io 53 | # version: v1 54 | # name: serving-cert # this name should match the one in certificate.yaml 55 | # fieldref: 56 | # fieldpath: metadata.namespace 57 | #- name: CERTIFICATE_NAME 58 | # objref: 59 | # kind: Certificate 60 | # group: cert-manager.io 61 | # version: v1 62 | # name: serving-cert # this name should match the one in certificate.yaml 63 | #- name: SERVICE_NAMESPACE # namespace of the service 64 | # objref: 65 | # kind: Service 66 | # version: v1 67 | # name: webhook-service 68 | # fieldref: 69 | # fieldpath: metadata.namespace 70 | #- name: SERVICE_NAME 71 | # objref: 72 | # kind: Service 73 | # version: v1 74 | # name: webhook-service 75 | -------------------------------------------------------------------------------- /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 | image: gcr.io/kubebuilder/kube-rbac-proxy:v0.8.0 14 | args: 15 | - "--secure-listen-address=0.0.0.0:8443" 16 | - "--upstream=http://127.0.0.1:8080/" 17 | - "--logtostderr=true" 18 | - "--v=10" 19 | ports: 20 | - containerPort: 8443 21 | protocol: TCP 22 | name: https 23 | - name: manager 24 | args: 25 | - "--health-probe-bind-address=:8082" 26 | - "--metrics-bind-address=127.0.0.1:8080" 27 | - "--leader-elect" 28 | -------------------------------------------------------------------------------- /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 11 | args: 12 | - "--config=controller_manager_config.yaml" 13 | volumeMounts: 14 | - name: manager-config 15 | mountPath: /controller_manager_config.yaml 16 | subPath: controller_manager_config.yaml 17 | volumes: 18 | - name: manager-config 19 | configMap: 20 | name: manager-config 21 | -------------------------------------------------------------------------------- /config/manager/controller_manager_config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: controller-runtime.sigs.k8s.io/v1alpha1 2 | kind: ControllerManagerConfig 3 | health: 4 | healthProbeBindAddress: :8082 5 | metrics: 6 | bindAddress: 127.0.0.1:8080 7 | webhook: 8 | port: 9443 9 | leaderElection: 10 | leaderElect: true 11 | resourceName: e7ac29f6.a1tan 12 | -------------------------------------------------------------------------------- /config/manager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - manager.yaml 3 | 4 | generatorOptions: 5 | disableNameSuffixHash: true 6 | 7 | configMapGenerator: 8 | - files: 9 | - controller_manager_config.yaml 10 | name: manager-config 11 | apiVersion: kustomize.config.k8s.io/v1beta1 12 | kind: Kustomization 13 | images: 14 | - name: controller 15 | newName: a1tan/argocdsecretsynchronizer 16 | newTag: 0.0.38 17 | -------------------------------------------------------------------------------- /config/manager/manager.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | labels: 5 | control-plane: controller-manager 6 | name: system 7 | --- 8 | apiVersion: apps/v1 9 | kind: Deployment 10 | metadata: 11 | name: controller-manager 12 | namespace: system 13 | labels: 14 | control-plane: controller-manager 15 | spec: 16 | selector: 17 | matchLabels: 18 | control-plane: controller-manager 19 | replicas: 1 20 | template: 21 | metadata: 22 | annotations: 23 | kubectl.kubernetes.io/default-container: manager 24 | labels: 25 | control-plane: controller-manager 26 | spec: 27 | securityContext: 28 | runAsNonRoot: true 29 | containers: 30 | - command: 31 | - /manager 32 | args: 33 | - --leader-elect 34 | image: controller:latest 35 | name: manager 36 | securityContext: 37 | allowPrivilegeEscalation: false 38 | livenessProbe: 39 | httpGet: 40 | path: /healthz 41 | port: 8082 42 | initialDelaySeconds: 15 43 | periodSeconds: 20 44 | readinessProbe: 45 | httpGet: 46 | path: /readyz 47 | port: 8082 48 | initialDelaySeconds: 5 49 | periodSeconds: 10 50 | # TODO(user): Configure the resources accordingly based on the project requirements. 51 | # More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ 52 | resources: 53 | limits: 54 | cpu: 500m 55 | memory: 128Mi 56 | requests: 57 | cpu: 10m 58 | memory: 64Mi 59 | serviceAccountName: controller-manager 60 | terminationGracePeriodSeconds: 10 61 | -------------------------------------------------------------------------------- /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/argocdsecretsynchronizer.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 | # path: /spec/template/spec/containers/1/volumeMounts/0 24 | # # Remove the "cert" volume, since OLM will create and mount a set of certs. 25 | # # Update the indices in this path if adding or removing volumes in the manager's Deployment. 26 | # - op: remove 27 | # path: /spec/template/spec/volumes/0 28 | -------------------------------------------------------------------------------- /config/prometheus/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - monitor.yaml 3 | -------------------------------------------------------------------------------- /config/prometheus/monitor.yaml: -------------------------------------------------------------------------------- 1 | 2 | # Prometheus Monitor Service (Metrics) 3 | apiVersion: monitoring.coreos.com/v1 4 | kind: ServiceMonitor 5 | metadata: 6 | labels: 7 | control-plane: controller-manager 8 | name: controller-manager-metrics-monitor 9 | namespace: system 10 | spec: 11 | endpoints: 12 | - path: /metrics 13 | port: https 14 | scheme: https 15 | bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token 16 | tlsConfig: 17 | insecureSkipVerify: true 18 | selector: 19 | matchLabels: 20 | control-plane: controller-manager 21 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_client_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: metrics-reader 5 | rules: 6 | - nonResourceURLs: 7 | - "/metrics" 8 | verbs: 9 | - get 10 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: proxy-role 5 | rules: 6 | - apiGroups: 7 | - authentication.k8s.io 8 | resources: 9 | - tokenreviews 10 | verbs: 11 | - create 12 | - apiGroups: 13 | - authorization.k8s.io 14 | resources: 15 | - subjectaccessreviews 16 | verbs: 17 | - create 18 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: proxy-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: proxy-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: controller-manager 12 | namespace: system 13 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | control-plane: controller-manager 6 | name: controller-manager-metrics-service 7 | namespace: system 8 | spec: 9 | ports: 10 | - name: https 11 | port: 8443 12 | protocol: TCP 13 | targetPort: https 14 | selector: 15 | control-plane: controller-manager 16 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | name: leader-election-role 6 | rules: 7 | - apiGroups: 8 | - "" 9 | resources: 10 | - configmaps 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - create 16 | - update 17 | - patch 18 | - delete 19 | - apiGroups: 20 | - coordination.k8s.io 21 | resources: 22 | - leases 23 | verbs: 24 | - get 25 | - list 26 | - watch 27 | - create 28 | - update 29 | - patch 30 | - delete 31 | - apiGroups: 32 | - "" 33 | resources: 34 | - events 35 | verbs: 36 | - create 37 | - patch 38 | -------------------------------------------------------------------------------- /config/rbac/leader_election_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | name: leader-election-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: Role 8 | name: leader-election-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: controller-manager 12 | namespace: system 13 | -------------------------------------------------------------------------------- /config/rbac/role.yaml: -------------------------------------------------------------------------------- 1 | 2 | --- 3 | apiVersion: rbac.authorization.k8s.io/v1 4 | kind: ClusterRole 5 | metadata: 6 | creationTimestamp: null 7 | name: manager-role 8 | rules: 9 | - apiGroups: 10 | - "" 11 | resources: 12 | - secrets 13 | verbs: 14 | - create 15 | - delete 16 | - get 17 | - list 18 | - patch 19 | - update 20 | - watch 21 | - apiGroups: 22 | - "" 23 | resources: 24 | - services 25 | verbs: 26 | - get 27 | - list 28 | - watch 29 | - apiGroups: 30 | - synchronizer.a1tan 31 | resources: 32 | - secretsynchronizers 33 | verbs: 34 | - create 35 | - delete 36 | - get 37 | - list 38 | - patch 39 | - update 40 | - watch 41 | - apiGroups: 42 | - synchronizer.a1tan 43 | resources: 44 | - secretsynchronizers/finalizers 45 | verbs: 46 | - update 47 | - apiGroups: 48 | - synchronizer.a1tan 49 | resources: 50 | - secretsynchronizers/status 51 | verbs: 52 | - get 53 | - patch 54 | - update 55 | -------------------------------------------------------------------------------- /config/rbac/role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: manager-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: manager-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: controller-manager 12 | namespace: system 13 | -------------------------------------------------------------------------------- /config/rbac/secretsynchronizer_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit secretsynchronizers. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: secretsynchronizer-editor-role 6 | rules: 7 | - apiGroups: 8 | - synchronizer.a1tan 9 | resources: 10 | - secretsynchronizers 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - synchronizer.a1tan 21 | resources: 22 | - secretsynchronizers/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /config/rbac/secretsynchronizer_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view secretsynchronizers. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: secretsynchronizer-viewer-role 6 | rules: 7 | - apiGroups: 8 | - synchronizer.a1tan 9 | resources: 10 | - secretsynchronizers 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - synchronizer.a1tan 17 | resources: 18 | - secretsynchronizers/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/rbac/service_account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: controller-manager 5 | namespace: system 6 | -------------------------------------------------------------------------------- /config/samples/kustomization.yaml: -------------------------------------------------------------------------------- 1 | ## Append samples you want in your CSV to this file as resources ## 2 | resources: 3 | - synchronizer_v1alpha1_secretsynchronizer.yaml 4 | #+kubebuilder:scaffold:manifestskustomizesamples 5 | -------------------------------------------------------------------------------- /config/samples/synchronizer_v1alpha1_secretsynchronizer.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: synchronizer.a1tan/v1alpha1 2 | kind: SecretSynchronizer 3 | metadata: 4 | labels: 5 | app: secretsynchronizer 6 | name: secretsynchronizer 7 | namespace: argocdsecretsynchronizer-system 8 | spec: 9 | size: 1 10 | prune: true 11 | argocd-namespace: argocd #default: argocd -------------------------------------------------------------------------------- /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: [] 8 | -------------------------------------------------------------------------------- /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 17 | -------------------------------------------------------------------------------- /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.16.0 8 | labels: 9 | suite: basic 10 | test: basic-check-spec-test 11 | -------------------------------------------------------------------------------- /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.16.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.16.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.16.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.16.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.16.0 48 | labels: 49 | suite: olm 50 | test: olm-status-descriptors-test 51 | -------------------------------------------------------------------------------- /controllers/secretsynchronizer_controller.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022. 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 controllers 18 | 19 | import ( 20 | "context" 21 | "encoding/json" 22 | 23 | synchronizerv1alpha1 "github.com/a1tan/argocdsecretsynchronizer/api/v1alpha1" 24 | "github.com/go-logr/logr" 25 | "github.com/prometheus/common/log" 26 | "gopkg.in/yaml.v2" 27 | corev1 "k8s.io/api/core/v1" 28 | rbacv1 "k8s.io/api/rbac/v1" 29 | "k8s.io/apimachinery/pkg/api/errors" 30 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 31 | "k8s.io/apimachinery/pkg/labels" 32 | "k8s.io/apimachinery/pkg/runtime" 33 | "k8s.io/apimachinery/pkg/types" 34 | "k8s.io/client-go/kubernetes" 35 | "k8s.io/client-go/rest" 36 | "k8s.io/client-go/tools/clientcmd" 37 | kops "k8s.io/kops/pkg/kubeconfig" 38 | ctrl "sigs.k8s.io/controller-runtime" 39 | "sigs.k8s.io/controller-runtime/pkg/client" 40 | "sigs.k8s.io/controller-runtime/pkg/event" 41 | "sigs.k8s.io/controller-runtime/pkg/handler" 42 | ctrllog "sigs.k8s.io/controller-runtime/pkg/log" 43 | "sigs.k8s.io/controller-runtime/pkg/predicate" 44 | "sigs.k8s.io/controller-runtime/pkg/reconcile" 45 | "sigs.k8s.io/controller-runtime/pkg/source" 46 | ) 47 | 48 | // SecretSynchronizerReconciler reconciles a SecretSynchronizer object 49 | type SecretSynchronizerReconciler struct { 50 | client.Client 51 | Scheme *runtime.Scheme 52 | Log logr.Logger 53 | } 54 | 55 | type Result struct { 56 | ID string `json:"id"` 57 | Name string `json:"name"` 58 | Test []interface{} `json:"test"` 59 | } 60 | 61 | type ArgoConfig struct { 62 | BearerToken string `json:"bearerToken"` 63 | TlsClientConfig ArgoTlsConfig `json:"tlsClientConfig"` 64 | } 65 | 66 | type ArgoTlsConfig struct { 67 | Insecure bool `json:"insecure"` 68 | CaData []byte `json:"caData"` 69 | } 70 | 71 | var ManagementClusterPolicyRules = []rbacv1.PolicyRule{ 72 | { 73 | APIGroups: []string{"*"}, 74 | Resources: []string{"*"}, 75 | Verbs: []string{"*"}, 76 | }, 77 | { 78 | NonResourceURLs: []string{"*"}, 79 | Verbs: []string{"*"}, 80 | }, 81 | } 82 | var ( 83 | setupLog = ctrl.Log.WithName("setup") 84 | ) 85 | 86 | //+kubebuilder:rbac:groups=synchronizer.a1tan,resources=secretsynchronizers,verbs=get;list;watch;create;update;patch;delete 87 | //+kubebuilder:rbac:groups=core,resources=secrets,verbs=get;list;watch;create;update;patch;delete 88 | //+kubebuilder:rbac:groups=core,resources=services,verbs=get;list;watch 89 | //+kubebuilder:rbac:groups=synchronizer.a1tan,resources=secretsynchronizers/status,verbs=get;update;patch 90 | //+kubebuilder:rbac:groups=synchronizer.a1tan,resources=secretsynchronizers/finalizers,verbs=update 91 | 92 | // Reconcile is part of the main kubernetes reconciliation loop which aims to 93 | // move the current state of the cluster closer to the desired state. 94 | // TODO(user): Modify the Reconcile function to compare the state specified by 95 | // the SecretSynchronizer object against the actual cluster state, and then 96 | // perform operations to make the cluster state reflect the state specified by 97 | // the user. 98 | // 99 | // For more details, check Reconcile and its Result here: 100 | // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.10.0/pkg/reconcile 101 | func (r *SecretSynchronizerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { 102 | // log := r.Log.WithValues("SecretSynchronizer", req.NamespacedName) 103 | log := ctrllog.FromContext(ctx) 104 | log.Info("Reconcile method has started") 105 | 106 | // cfg, err := rest.InClusterConfig() 107 | // if err != nil { 108 | // log.Error(err, "Failed to get Kubernetes config") 109 | // return ctrl.Result{}, nil 110 | // } 111 | 112 | // scheme := runtime.NewScheme() 113 | // _ = synchronizerv1alpha1.AddToScheme(scheme) 114 | // crtClient, err := client.New(cfg, client.Options{Scheme: scheme}) 115 | // if err != nil { 116 | // log.Error(err, "Failed to create controller-runtime client") 117 | // return ctrl.Result{}, nil 118 | // } 119 | 120 | // selector := labels.Set{"app": "secretsynchronizer"}.AsSelector() 121 | 122 | // syncList := &synchronizerv1alpha1.SecretSynchronizerList{} 123 | // if err := crtClient.List(ctx, syncList, client.MatchingLabelsSelector{Selector: selector}); err != nil { 124 | // if !errors.IsNotFound(err) { 125 | // log.Error(err, "Failed to list SecretSynchronizer objects") 126 | // return ctrl.Result{}, nil 127 | // } 128 | // } 129 | // var synchronizer synchronizerv1alpha1.SecretSynchronizer 130 | // if len(syncList.Items) > 0 { 131 | // synchronizer = syncList.Items[0] 132 | // } else { 133 | // return ctrl.Result{}, nil 134 | // } 135 | 136 | var synchronizer synchronizerv1alpha1.SecretSynchronizer = *GetSecretSynchronizer(ctx) 137 | 138 | secret := &corev1.Secret{} 139 | err := r.Get(ctx, req.NamespacedName, secret) 140 | if err != nil { 141 | log.Error(err, "Failed to find secret and started to delete related resources") 142 | return r.DeleteIrrelevantSecrets(ctx) 143 | } 144 | 145 | var kubeconfigByte []byte 146 | var configExists bool 147 | var kubeconf kops.KubectlConfig 148 | 149 | kubeconfigByte, configExists = secret.Data["kubeconfig"] 150 | if !configExists { 151 | kubeconfigByte, configExists = secret.Data["config"] 152 | if !configExists { 153 | kubeconfigByte, configExists = secret.Data["value"] 154 | } 155 | } 156 | if configExists { 157 | if err != nil { 158 | log.Error(err, "Kubeconfig Decoding Error") 159 | return ctrl.Result{}, err 160 | } 161 | 162 | err = yaml.Unmarshal(kubeconfigByte, &kubeconf) 163 | if err != nil { 164 | log.Error(err, "Kubeconfig Json Convert Error") 165 | return ctrl.Result{}, err 166 | } 167 | 168 | config, err := clientcmd.RESTConfigFromKubeConfig(kubeconfigByte) 169 | if err != nil { 170 | log.Error(err, "RestConfig Generation Error") 171 | return ctrl.Result{}, err 172 | } 173 | 174 | clientset, err := kubernetes.NewForConfig(config) 175 | if err != nil { 176 | log.Error(err, "Clientset Generation Error") 177 | return ctrl.Result{}, err 178 | } 179 | 180 | argoSecret, err := CreateServiceAccountWithToken(ctx, clientset, "kube-system", "argocd-manager") 181 | if err != nil { 182 | log.Error(err, "Service account creation error") 183 | return ctrl.Result{}, err 184 | } 185 | err = r.CreateArgoCDSecret(ctx, argoSecret, secret, kubeconf, &synchronizer) 186 | if err != nil { 187 | log.Error(err, "Argo CD secret creation error") 188 | return ctrl.Result{}, err 189 | } 190 | } else { 191 | log.Info("Kubeconfig not found", "data", kubeconfigByte) 192 | } 193 | 194 | return ctrl.Result{}, nil 195 | } 196 | 197 | // SetupWithManager sets up the controller with the Manager. 198 | func (r *SecretSynchronizerReconciler) SetupWithManager(mgr ctrl.Manager) error { 199 | return ctrl.NewControllerManagedBy(mgr). 200 | For(&synchronizerv1alpha1.SecretSynchronizer{}). 201 | Owns(&corev1.Secret{}). 202 | WithEventFilter(ignoreIrrelevantSecrets()). 203 | Watches(&source.Kind{Type: &corev1.Secret{}}, 204 | handler.EnqueueRequestsFromMapFunc( 205 | func(obj client.Object) []reconcile.Request { 206 | secret, ok := obj.(*corev1.Secret) 207 | if !ok { 208 | setupLog.Info("Unable to get secret", "secret", secret) 209 | return nil 210 | } 211 | _, isArgoSecret := secret.Labels["argocd.argoproj.io/secret-type"] 212 | if isArgoSecret { 213 | setupLog.Info("Secret already is an ArgoCD secret") 214 | return nil 215 | } 216 | // _, configExists := secret.Data["config"] 217 | // _, kubeconfigExists := secret.Data["kubeconfig"] 218 | // if configExists || kubeconfigExists { 219 | return []reconcile.Request{ 220 | {NamespacedName: types.NamespacedName{ 221 | Name: secret.GetName(), 222 | Namespace: secret.GetNamespace(), 223 | }}, 224 | } 225 | // } 226 | // return nil 227 | }, 228 | ), 229 | ). 230 | Complete(r) 231 | } 232 | 233 | func ignoreIrrelevantSecrets() predicate.Predicate { 234 | return predicate.Funcs{ 235 | UpdateFunc: func(e event.UpdateEvent) bool { 236 | var ok bool 237 | secret, ok := e.ObjectNew.(*corev1.Secret) 238 | if !ok { 239 | setupLog.Info("Unable to get secret", "secret", secret, "object", e.ObjectNew) 240 | return false 241 | } 242 | setupLog.Info("Validating secret") 243 | return isValidKubeconfig(secret.Data) 244 | }, 245 | CreateFunc: func(e event.CreateEvent) bool { 246 | secret, ok := e.Object.(*corev1.Secret) 247 | if !ok { 248 | setupLog.Info("Unable to get secret", "secret", secret, "object", e.Object) 249 | return false 250 | } 251 | setupLog.Info("Validating secret") 252 | return isValidKubeconfig(secret.Data) 253 | }, 254 | DeleteFunc: func(e event.DeleteEvent) bool { 255 | // Evaluates to false if the object has been confirmed deleted. 256 | return !e.DeleteStateUnknown 257 | }, 258 | } 259 | } 260 | 261 | func isValidKubeconfig(secretData map[string][]byte) bool { 262 | var ok bool 263 | 264 | var kubeconfigData []byte 265 | var kubeconf kops.KubectlConfig 266 | var synchronizer synchronizerv1alpha1.SecretSynchronizer = *GetSecretSynchronizer(ctx) 267 | // setupLog.Info("Validating secret", "secretData", secretData) 268 | if kubeconfigData, ok = secretData["kubeconfig"]; !ok { 269 | if kubeconfigData, ok = secretData["config"]; !ok { 270 | if kubeconfigData, ok = secretData["value"]; !ok { 271 | setupLog.Info("Cannot find kubeconfig in secret") 272 | return false 273 | } 274 | } 275 | } 276 | 277 | err := yaml.Unmarshal(kubeconfigData, &kubeconf) 278 | if err != nil { 279 | return false 280 | } 281 | 282 | _, err = clientcmd.RESTConfigFromKubeConfig(kubeconfigData) 283 | if err != nil { 284 | return false 285 | } 286 | 287 | // kubeconfig, err := base64.StdEncoding.DecodeString(string(kubeconfigData)) 288 | // if err != nil { 289 | // setupLog.Info("Cannot decode kubeconfig", "kubeconfigData", kubeconfigData, "error", err) 290 | // return false 291 | // } 292 | 293 | // config, err := clientcmd.Load(kubeconfig) 294 | // if err != nil { 295 | // setupLog.Info("Cannot load kubeconfig") 296 | // return false 297 | // } 298 | 299 | // if len(config.Contexts) == 0 || len(config.Clusters) == 0 { 300 | // setupLog.Info("Kubeconfig is empty") 301 | // return false 302 | // } 303 | 304 | return true 305 | } 306 | func GetSecretSynchronizer(ctx context.Context) *synchronizerv1alpha1.SecretSynchronizer { 307 | cfg, err := rest.InClusterConfig() 308 | if err != nil { 309 | log.Error(err, "Failed to get Kubernetes config") 310 | return nil 311 | } 312 | 313 | scheme := runtime.NewScheme() 314 | _ = synchronizerv1alpha1.AddToScheme(scheme) 315 | crtClient, err := client.New(cfg, client.Options{Scheme: scheme}) 316 | if err != nil { 317 | log.Error(err, "Failed to create controller-runtime client") 318 | return nil 319 | } 320 | 321 | selector := labels.Set{"app": "secretsynchronizer"}.AsSelector() 322 | 323 | syncList := &synchronizerv1alpha1.SecretSynchronizerList{} 324 | if err := crtClient.List(ctx, syncList, client.MatchingLabelsSelector{Selector: selector}); err != nil { 325 | if !errors.IsNotFound(err) { 326 | log.Error(err, "Failed to list SecretSynchronizer objects") 327 | return nil 328 | } 329 | } 330 | 331 | if len(syncList.Items) > 0 { 332 | return &syncList.Items[0] 333 | } else { 334 | return nil 335 | } 336 | } 337 | 338 | func (r *SecretSynchronizerReconciler) CreateArgoCDSecret(ctx context.Context, argoSecret *corev1.Secret, secret *corev1.Secret, kubeconf kops.KubectlConfig, synchronizer *synchronizerv1alpha1.SecretSynchronizer) error { 339 | var argoTlsConfig ArgoTlsConfig 340 | argoTlsConfig.Insecure = false 341 | argoTlsConfig.CaData = argoSecret.Data["ca.crt"] 342 | 343 | var argoConfig ArgoConfig 344 | argoConfig.BearerToken = string(argoSecret.Data["token"]) 345 | argoConfig.TlsClientConfig = argoTlsConfig 346 | 347 | var secretName string = secret.Name + "-argocd" 348 | 349 | cfg, err := rest.InClusterConfig() 350 | if err != nil { 351 | log.Error(err, "Failed to get Kubernetes config") 352 | return nil 353 | } 354 | 355 | clientset, err := kubernetes.NewForConfig(cfg) 356 | if err != nil { 357 | panic(err.Error()) 358 | } 359 | 360 | argoConfigAsJson, _ := json.Marshal(argoConfig) 361 | existingSecret, err := clientset.CoreV1().Secrets(synchronizer.Spec.ArgoCDNamespace).Get(context.Background(), secretName, metav1.GetOptions{}) 362 | if errors.IsNotFound(err) { 363 | err := r.Create(ctx, &corev1.Secret{ 364 | ObjectMeta: metav1.ObjectMeta{ 365 | Name: secretName, 366 | Namespace: synchronizer.Spec.ArgoCDNamespace, 367 | Labels: map[string]string{ 368 | "argocd.argoproj.io/secret-type": "cluster", 369 | "argocdsecretsynchronizer": secret.Name, 370 | }, 371 | }, 372 | Type: corev1.SecretTypeOpaque, 373 | Data: map[string][]byte{ 374 | "name": []byte(kubeconf.Clusters[0].Name), 375 | "server": []byte(kubeconf.Clusters[0].Cluster.Server), 376 | "config": argoConfigAsJson, 377 | }, 378 | }) 379 | log.Info("Argo CD Secret Created Successfully") 380 | return err 381 | } else { 382 | existingSecret.Data["config"] = argoConfigAsJson 383 | existingSecret.Data["name"] = []byte(kubeconf.Clusters[0].Name) 384 | existingSecret.Data["server"] = []byte(kubeconf.Clusters[0].Cluster.Server) 385 | _, err = clientset.CoreV1().Secrets(synchronizer.Spec.ArgoCDNamespace).Update(context.Background(), existingSecret, metav1.UpdateOptions{}) 386 | log.Info("Argo CD Secret Is Already Created, Updating It") 387 | return err 388 | } 389 | } 390 | 391 | func (r *SecretSynchronizerReconciler) DeleteIrrelevantSecrets(ctx context.Context) (ctrl.Result, error) { 392 | log := ctrllog.FromContext(ctx) 393 | secretList := &corev1.SecretList{} 394 | opts := []client.ListOption{ 395 | client.HasLabels{"argocdsecretsynchronizer"}, 396 | } 397 | 398 | err := r.List(ctx, secretList, opts...) 399 | if err != nil { 400 | log.Error(err, "There is an error while finding secrets and exiting reconcile") 401 | return reconcile.Result{}, nil 402 | } 403 | for _, oRef := range secretList.Items { 404 | mainSecret := &corev1.Secret{} 405 | err := r.Get(ctx, types.NamespacedName{Name: oRef.Labels["argocdsecretsynchronizer"]}, mainSecret) 406 | if err != nil { 407 | //Double check before deleting 408 | _, isSecretSynchronizerSecret := oRef.Labels["argocdsecretsynchronizer"] 409 | if isSecretSynchronizerSecret { 410 | err = r.Delete(ctx, &oRef) 411 | if err != nil { 412 | log.Error(err, "Cannot delete related secrets and exiting reconcile") 413 | return reconcile.Result{}, nil 414 | } 415 | } 416 | } 417 | } 418 | return reconcile.Result{}, nil 419 | } 420 | func CreateServiceAccountWithToken(ctx context.Context, clientset kubernetes.Interface, namespace string, name string) (*corev1.Secret, error) { 421 | log := ctrllog.FromContext(ctx) 422 | var err error 423 | 424 | _, err = clientset.RbacV1().ClusterRoles().Get(context.Background(), name+"role", metav1.GetOptions{}) 425 | if errors.IsNotFound(err) { 426 | clusterRole := rbacv1.ClusterRole{ 427 | TypeMeta: metav1.TypeMeta{ 428 | APIVersion: "rbac.authorization.k8s.io/v1", 429 | Kind: "ClusterRole", 430 | }, 431 | ObjectMeta: metav1.ObjectMeta{ 432 | Name: name + "role", 433 | }, 434 | Rules: ManagementClusterPolicyRules, 435 | } 436 | _, err = clientset.RbacV1().ClusterRoles().Create(context.Background(), &clusterRole, metav1.CreateOptions{}) 437 | if err != nil { 438 | log.Info("An Error Occured While Creating ClusterRole", "error", err) 439 | return nil, err 440 | } 441 | } 442 | 443 | _, err = clientset.RbacV1().ClusterRoleBindings().Get(context.Background(), name+"rolebinding", metav1.GetOptions{}) 444 | if errors.IsNotFound(err) { 445 | roleBinding := rbacv1.ClusterRoleBinding{ 446 | TypeMeta: metav1.TypeMeta{ 447 | APIVersion: "rbac.authorization.k8s.io/v1", 448 | Kind: "ClusterRoleBinding", 449 | }, 450 | ObjectMeta: metav1.ObjectMeta{ 451 | Name: name + "rolebinding", 452 | }, 453 | RoleRef: rbacv1.RoleRef{ 454 | APIGroup: "rbac.authorization.k8s.io", 455 | Kind: "ClusterRole", 456 | Name: name + "role", 457 | }, 458 | Subjects: []rbacv1.Subject{rbacv1.Subject{ 459 | Kind: rbacv1.ServiceAccountKind, 460 | Name: name, 461 | Namespace: namespace, 462 | }}, 463 | } 464 | _, err = clientset.RbacV1().ClusterRoleBindings().Create(context.Background(), &roleBinding, metav1.CreateOptions{}) 465 | if err != nil { 466 | log.Info("An Error Occured While Creating ClusterRoleBinding", "error", err) 467 | return nil, err 468 | } 469 | } 470 | 471 | serviceAccountCreated, err := clientset.CoreV1().ServiceAccounts(namespace).Get(context.Background(), name, metav1.GetOptions{}) 472 | if errors.IsNotFound(err) { 473 | serviceAccount := corev1.ServiceAccount{ 474 | TypeMeta: metav1.TypeMeta{ 475 | APIVersion: "v1", 476 | Kind: "ServiceAccount", 477 | }, 478 | ObjectMeta: metav1.ObjectMeta{ 479 | Name: name, 480 | Namespace: namespace, 481 | }, 482 | } 483 | serviceAccountCreated, err = clientset.CoreV1().ServiceAccounts(namespace).Create(context.Background(), &serviceAccount, metav1.CreateOptions{}) 484 | if err != nil { 485 | log.Info("An Error Occured While Creating Service Account", "error", err) 486 | return nil, err 487 | } 488 | log.Info("Service Account Created", "Service Account", serviceAccountCreated) 489 | } 490 | 491 | if serviceAccountCreated.Secrets == nil { 492 | secret, err := clientset.CoreV1().Secrets(namespace).Get(context.Background(), name, metav1.GetOptions{}) 493 | if errors.IsNotFound(err) { 494 | secretToCreate := corev1.Secret{ 495 | TypeMeta: metav1.TypeMeta{ 496 | APIVersion: "v1", 497 | Kind: "Secret", 498 | }, 499 | ObjectMeta: metav1.ObjectMeta{ 500 | Name: name, 501 | Namespace: namespace, 502 | Annotations: map[string]string{"kubernetes.io/service-account.name": serviceAccountCreated.Name}, 503 | }, 504 | Type: corev1.SecretTypeServiceAccountToken, 505 | } 506 | secret, err = clientset.CoreV1().Secrets(namespace).Create(context.Background(), &secretToCreate, metav1.CreateOptions{}) 507 | 508 | if err != nil { 509 | log.Error(err, "An error occurred while creating secret", "secret", secretToCreate) 510 | return nil, err 511 | } 512 | } 513 | return secret, nil 514 | } 515 | 516 | for _, oRef := range serviceAccountCreated.Secrets { 517 | secret, err := clientset.CoreV1().Secrets(namespace).Get(context.Background(), oRef.Name, metav1.GetOptions{}) 518 | if err != nil { 519 | log.Error(err, "An error occurred while getting secret") 520 | return nil, err 521 | } 522 | if secret.Type == corev1.SecretTypeServiceAccountToken { 523 | return secret, nil 524 | } 525 | } 526 | 527 | return nil, nil 528 | } 529 | -------------------------------------------------------------------------------- /controllers/suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022. 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 controllers 18 | 19 | import ( 20 | "path/filepath" 21 | "testing" 22 | 23 | . "github.com/onsi/ginkgo" 24 | . "github.com/onsi/gomega" 25 | "k8s.io/client-go/kubernetes/scheme" 26 | "k8s.io/client-go/rest" 27 | "sigs.k8s.io/controller-runtime/pkg/client" 28 | "sigs.k8s.io/controller-runtime/pkg/envtest" 29 | "sigs.k8s.io/controller-runtime/pkg/envtest/printer" 30 | logf "sigs.k8s.io/controller-runtime/pkg/log" 31 | "sigs.k8s.io/controller-runtime/pkg/log/zap" 32 | 33 | synchronizerv1alpha1 "github.com/a1tan/argocdsecretsynchronizer/api/v1alpha1" 34 | //+kubebuilder:scaffold:imports 35 | ) 36 | 37 | // These tests use Ginkgo (BDD-style Go testing framework). Refer to 38 | // http://onsi.github.io/ginkgo/ to learn more about Ginkgo. 39 | 40 | var cfg *rest.Config 41 | var k8sClient client.Client 42 | var testEnv *envtest.Environment 43 | 44 | func TestAPIs(t *testing.T) { 45 | RegisterFailHandler(Fail) 46 | 47 | RunSpecsWithDefaultAndCustomReporters(t, 48 | "Controller Suite", 49 | []Reporter{printer.NewlineReporter{}}) 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 | 61 | cfg, err := testEnv.Start() 62 | Expect(err).NotTo(HaveOccurred()) 63 | Expect(cfg).NotTo(BeNil()) 64 | 65 | err = synchronizerv1alpha1.AddToScheme(scheme.Scheme) 66 | Expect(err).NotTo(HaveOccurred()) 67 | 68 | //+kubebuilder:scaffold:scheme 69 | 70 | k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) 71 | Expect(err).NotTo(HaveOccurred()) 72 | Expect(k8sClient).NotTo(BeNil()) 73 | 74 | }, 60) 75 | 76 | var _ = AfterSuite(func() { 77 | By("tearing down the test environment") 78 | err := testEnv.Stop() 79 | Expect(err).NotTo(HaveOccurred()) 80 | }) 81 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/a1tan/argocdsecretsynchronizer 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/go-logr/logr v0.4.0 // indirect 7 | github.com/onsi/ginkgo v1.16.4 8 | github.com/onsi/gomega v1.15.0 9 | github.com/prometheus/common v0.26.0 // indirect 10 | gopkg.in/yaml.v2 v2.4.0 // indirect 11 | k8s.io/api v0.22.2 // indirect 12 | k8s.io/apimachinery v0.22.2 13 | k8s.io/client-go v0.22.2 14 | k8s.io/kops v1.21.3 // indirect 15 | sigs.k8s.io/controller-runtime v0.10.0 16 | ) 17 | -------------------------------------------------------------------------------- /hack/boilerplate.go.txt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022. 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 | */ -------------------------------------------------------------------------------- /kubectl-crossplane: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a1tan/argocdsecretsynchronizer/1d3e103e99b77fa5232c7d91b7601ae8aedea570/kubectl-crossplane -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022. 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 | "flag" 21 | "os" 22 | 23 | // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) 24 | // to ensure that exec-entrypoint and run can make use of them. 25 | _ "k8s.io/client-go/plugin/pkg/client/auth" 26 | 27 | "k8s.io/apimachinery/pkg/runtime" 28 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 29 | clientgoscheme "k8s.io/client-go/kubernetes/scheme" 30 | ctrl "sigs.k8s.io/controller-runtime" 31 | "sigs.k8s.io/controller-runtime/pkg/healthz" 32 | "sigs.k8s.io/controller-runtime/pkg/log/zap" 33 | 34 | synchronizerv1alpha1 "github.com/a1tan/argocdsecretsynchronizer/api/v1alpha1" 35 | "github.com/a1tan/argocdsecretsynchronizer/controllers" 36 | //+kubebuilder:scaffold:imports 37 | ) 38 | 39 | var ( 40 | scheme = runtime.NewScheme() 41 | setupLog = ctrl.Log.WithName("setup") 42 | ) 43 | 44 | func init() { 45 | utilruntime.Must(clientgoscheme.AddToScheme(scheme)) 46 | 47 | utilruntime.Must(synchronizerv1alpha1.AddToScheme(scheme)) 48 | //+kubebuilder:scaffold:scheme 49 | } 50 | 51 | func main() { 52 | var metricsAddr string 53 | var enableLeaderElection bool 54 | var probeAddr string 55 | flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.") 56 | flag.StringVar(&probeAddr, "health-probe-bind-address", ":8082", "The address the probe endpoint binds to.") 57 | flag.BoolVar(&enableLeaderElection, "leader-elect", false, 58 | "Enable leader election for controller manager. "+ 59 | "Enabling this will ensure there is only one active controller manager.") 60 | opts := zap.Options{ 61 | Development: true, 62 | } 63 | opts.BindFlags(flag.CommandLine) 64 | flag.Parse() 65 | 66 | ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) 67 | 68 | mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ 69 | Scheme: scheme, 70 | MetricsBindAddress: metricsAddr, 71 | Port: 9443, 72 | HealthProbeBindAddress: probeAddr, 73 | LeaderElection: enableLeaderElection, 74 | LeaderElectionID: "e7ac29f6.a1tan", 75 | }) 76 | if err != nil { 77 | setupLog.Error(err, "unable to start manager") 78 | os.Exit(1) 79 | } 80 | 81 | if err = (&controllers.SecretSynchronizerReconciler{ 82 | Client: mgr.GetClient(), 83 | Scheme: mgr.GetScheme(), 84 | }).SetupWithManager(mgr); err != nil { 85 | setupLog.Error(err, "unable to create controller", "controller", "SecretSynchronizer") 86 | os.Exit(1) 87 | } 88 | //+kubebuilder:scaffold:builder 89 | 90 | if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { 91 | setupLog.Error(err, "unable to set up health check") 92 | os.Exit(1) 93 | } 94 | if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil { 95 | setupLog.Error(err, "unable to set up ready check") 96 | os.Exit(1) 97 | } 98 | 99 | setupLog.Info("starting manager") 100 | if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { 101 | setupLog.Error(err, "problem running manager") 102 | os.Exit(1) 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /path.sh: -------------------------------------------------------------------------------- 1 | 2 | sudo cp -r /mnt/c/applinux/go /usr/local/go 3 | export KUBECONFIG="/mnt/c/Users/alaltund/.kube/config" 4 | export PATH="$PATH:/mnt/c/applinux" 5 | export GOROOT="/usr/local/go" 6 | # export GOPATH="/mnt/c/Users/alaltund/Desktop/Work/Kubernetes/Operators/ArgoCDSecretSynchronizer" 7 | export PATH="$PATH:$GOROOT/bin" 8 | export GO111MODULE=on --------------------------------------------------------------------------------