mirror of
https://github.com/Infisical/infisical.git
synced 2026-01-08 23:18:05 -05:00
feat: removed k8s operator
This commit is contained in:
59
.github/workflows/release-k8-operator-helm.yml
vendored
59
.github/workflows/release-k8-operator-helm.yml
vendored
@@ -1,59 +0,0 @@
|
||||
name: Release K8 Operator Helm Chart
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
test-helm:
|
||||
name: Test Helm Chart
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Helm
|
||||
uses: azure/setup-helm@v4.2.0
|
||||
with:
|
||||
version: v3.17.0
|
||||
|
||||
- uses: actions/setup-python@v5.3.0
|
||||
with:
|
||||
python-version: "3.x"
|
||||
check-latest: true
|
||||
|
||||
- name: Set up chart-testing
|
||||
uses: helm/chart-testing-action@v2.7.0
|
||||
|
||||
- name: Run chart-testing (lint)
|
||||
run: ct lint --config ct.yaml --charts helm-charts/secrets-operator
|
||||
|
||||
- name: Create kind cluster
|
||||
uses: helm/kind-action@v1.12.0
|
||||
|
||||
- name: Run chart-testing (install)
|
||||
run: ct install --config ct.yaml --charts helm-charts/secrets-operator
|
||||
|
||||
release-helm:
|
||||
name: Release Helm Chart
|
||||
needs: test-helm
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install Helm
|
||||
uses: azure/setup-helm@v3
|
||||
with:
|
||||
version: v3.10.0
|
||||
|
||||
- name: Install python
|
||||
uses: actions/setup-python@v4
|
||||
|
||||
- name: Install Cloudsmith CLI
|
||||
run: pip install --upgrade cloudsmith-cli
|
||||
|
||||
- name: Build and push helm package to CloudSmith
|
||||
run: cd helm-charts && sh upload-k8s-operator-cloudsmith.sh
|
||||
env:
|
||||
CLOUDSMITH_API_KEY: ${{ secrets.CLOUDSMITH_API_KEY }}
|
||||
104
.github/workflows/release_docker_k8_operator.yaml
vendored
104
.github/workflows/release_docker_k8_operator.yaml
vendored
@@ -1,104 +0,0 @@
|
||||
name: Release K8 Operator Docker Image
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "infisical-k8-operator/v*.*.*"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
release-image:
|
||||
name: Generate Helm Chart PR
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
pr_number: ${{ steps.create-pr.outputs.pull-request-number }}
|
||||
steps:
|
||||
- name: Extract version from tag
|
||||
id: extract_version
|
||||
run: echo "::set-output name=version::${GITHUB_REF_NAME#infisical-k8-operator/}"
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Dependency for helm generation
|
||||
- name: Install Helm
|
||||
uses: azure/setup-helm@v3
|
||||
with:
|
||||
version: v3.10.0
|
||||
|
||||
# Dependency for helm generation
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.21
|
||||
|
||||
# Install binaries for helm generation
|
||||
- name: Install dependencies
|
||||
working-directory: k8-operator
|
||||
run: |
|
||||
make helmify
|
||||
make kustomize
|
||||
make controller-gen
|
||||
|
||||
- name: Generate Helm Chart
|
||||
working-directory: k8-operator
|
||||
run: make helm VERSION=${{ steps.extract_version.outputs.version }}
|
||||
|
||||
- name: Debug - Check file changes
|
||||
run: |
|
||||
echo "Current git status:"
|
||||
git status
|
||||
echo ""
|
||||
echo "Modified files:"
|
||||
git diff --name-only
|
||||
|
||||
# If there is no diff, exit with error. Version should always be changed, so if there is no diff, something is wrong and we should exit.
|
||||
if [ -z "$(git diff --name-only)" ]; then
|
||||
echo "No helm changes or version changes. Invalid release detected, Exiting."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Create Helm Chart PR
|
||||
id: create-pr
|
||||
uses: peter-evans/create-pull-request@v5
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
commit-message: "Update Helm chart to version ${{ steps.extract_version.outputs.version }}"
|
||||
committer: GitHub <noreply@github.com>
|
||||
author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com>
|
||||
branch: helm-update-${{ steps.extract_version.outputs.version }}
|
||||
delete-branch: true
|
||||
title: "Update Helm chart to version ${{ steps.extract_version.outputs.version }}"
|
||||
body: |
|
||||
This PR updates the Helm chart to version `${{ steps.extract_version.outputs.version }}`.
|
||||
Additionally the helm chart has been updated to match the latest operator code changes.
|
||||
|
||||
Associated Release Workflow: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
|
||||
Once you have approved this PR, you can trigger the helm release workflow manually.
|
||||
base: main
|
||||
|
||||
- name: 🔧 Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
|
||||
- name: 🔧 Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: 🐋 Login to Docker Hub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Build and push
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: k8-operator
|
||||
push: true
|
||||
platforms: linux/amd64,linux/arm64
|
||||
tags: |
|
||||
infisical/kubernetes-operator:latest
|
||||
infisical/kubernetes-operator:${{ steps.extract_version.outputs.version }}
|
||||
@@ -1,38 +0,0 @@
|
||||
name: Run Helm Chart Tests for Secret Operator
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "helm-charts/secrets-operator/**"
|
||||
- ".github/workflows/run-helm-chart-tests-secret-operator.yml"
|
||||
|
||||
jobs:
|
||||
test-helm:
|
||||
name: Test Helm Chart
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Helm
|
||||
uses: azure/setup-helm@v4.2.0
|
||||
with:
|
||||
version: v3.17.0
|
||||
|
||||
- uses: actions/setup-python@v5.3.0
|
||||
with:
|
||||
python-version: "3.x"
|
||||
check-latest: true
|
||||
|
||||
- name: Set up chart-testing
|
||||
uses: helm/chart-testing-action@v2.7.0
|
||||
|
||||
- name: Run chart-testing (lint)
|
||||
run: ct lint --config ct.yaml --charts helm-charts/secrets-operator
|
||||
|
||||
- name: Create kind cluster
|
||||
uses: helm/kind-action@v1.12.0
|
||||
|
||||
- name: Run chart-testing (install)
|
||||
run: ct install --config ct.yaml --charts helm-charts/secrets-operator
|
||||
@@ -1,4 +0,0 @@
|
||||
# More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file
|
||||
# Ignore build and test binaries.
|
||||
bin/
|
||||
testbin/
|
||||
29
k8-operator/.gitignore
vendored
29
k8-operator/.gitignore
vendored
@@ -1,29 +0,0 @@
|
||||
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
bin
|
||||
testbin/*
|
||||
Dockerfile.cross
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Kubernetes Generated files - skip generated files, except for vendored files
|
||||
|
||||
!vendor/**/zz_generated.*
|
||||
|
||||
# editor and IDE paraphernalia
|
||||
.idea
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# Testing directories
|
||||
auto-token
|
||||
@@ -1,33 +0,0 @@
|
||||
# Build the manager binary
|
||||
FROM golang:1.24 AS builder
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
|
||||
WORKDIR /workspace
|
||||
# Copy the Go Modules manifests
|
||||
COPY go.mod go.mod
|
||||
COPY go.sum go.sum
|
||||
# cache deps before building and copying source so that we don't need to re-download as much
|
||||
# and so that source changes don't invalidate our downloaded layer
|
||||
RUN go mod download
|
||||
|
||||
# Copy the go source
|
||||
COPY cmd/main.go cmd/main.go
|
||||
COPY api/ api/
|
||||
COPY internal/ internal/
|
||||
|
||||
# Build
|
||||
# the GOARCH has not a default value to allow the binary be built according to the host where the command
|
||||
# was called. For example, if we call make docker-build in a local env which has the Apple Silicon M1 SO
|
||||
# the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore,
|
||||
# by leaving it empty we can ensure that the container and binary shipped on it will have the same platform.
|
||||
RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager cmd/main.go
|
||||
|
||||
# Use distroless as minimal base image to package the manager binary
|
||||
# Refer to https://github.com/GoogleContainerTools/distroless for more details
|
||||
FROM gcr.io/distroless/static:nonroot
|
||||
WORKDIR /
|
||||
COPY --from=builder /workspace/manager .
|
||||
USER 65532:65532
|
||||
|
||||
ENTRYPOINT ["/manager"]
|
||||
@@ -1,261 +0,0 @@
|
||||
# Image URL to use all building/pushing image targets
|
||||
VERSION ?= latest
|
||||
IMG ?= infisical/kubernetes-operator:${VERSION} # ${VERSION} will be replaced by the version in the CI step
|
||||
|
||||
# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
|
||||
ifeq (,$(shell go env GOBIN))
|
||||
GOBIN=$(shell go env GOPATH)/bin
|
||||
else
|
||||
GOBIN=$(shell go env GOBIN)
|
||||
endif
|
||||
|
||||
# CONTAINER_TOOL defines the container tool to be used for building images.
|
||||
# Be aware that the target commands are only tested with Docker which is
|
||||
# scaffolded by default. However, you might want to replace it to use other
|
||||
# tools. (i.e. podman)
|
||||
CONTAINER_TOOL ?= docker
|
||||
|
||||
# Setting SHELL to bash allows bash commands to be executed by recipes.
|
||||
# Options are set to exit when a recipe line exits non-zero or a piped command fails.
|
||||
SHELL = /usr/bin/env bash -o pipefail
|
||||
.SHELLFLAGS = -ec
|
||||
|
||||
.PHONY: all
|
||||
all: build
|
||||
|
||||
##@ General
|
||||
|
||||
HELMIFY ?= $(LOCALBIN)/helmify
|
||||
|
||||
.PHONY: helmify
|
||||
helmify: $(HELMIFY) ## Download helmify locally if necessary.
|
||||
$(HELMIFY): $(LOCALBIN)
|
||||
test -s $(LOCALBIN)/helmify || GOBIN=$(LOCALBIN) go install github.com/arttor/helmify/cmd/helmify@latest
|
||||
|
||||
legacy-helm: manifests kustomize helmify
|
||||
$(KUSTOMIZE) build config/default | $(HELMIFY) ../helm-charts/secrets-operator
|
||||
|
||||
helm: manifests kustomize helmify
|
||||
cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG}
|
||||
./scripts/generate-helm.sh ${VERSION}
|
||||
cd config/manager && $(KUSTOMIZE) edit set image controller=controller:latest # reset back
|
||||
|
||||
## Yaml for Kubectl
|
||||
kubectl-install: manifests kustomize
|
||||
mkdir -p kubectl-install
|
||||
cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG}
|
||||
$(KUSTOMIZE) build config/default > kubectl-install/install-secrets-operator.yaml
|
||||
|
||||
|
||||
# The help target prints out all targets with their descriptions organized
|
||||
# beneath their categories. The categories are represented by '##@' and the
|
||||
# target descriptions by '##'. The awk command is responsible for reading the
|
||||
# entire set of makefiles included in this invocation, looking for lines of the
|
||||
# file as xyz: ## something, and then pretty-format the target and help. Then,
|
||||
# if there's a line with ##@ something, that gets pretty-printed as a category.
|
||||
# More info on the usage of ANSI control characters for terminal formatting:
|
||||
# https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters
|
||||
# More info on the awk command:
|
||||
# http://linuxcommand.org/lc3_adv_awk.php
|
||||
|
||||
.PHONY: help
|
||||
help: ## Display this help.
|
||||
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<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)
|
||||
|
||||
##@ Development
|
||||
|
||||
.PHONY: manifests
|
||||
manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects.
|
||||
$(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases
|
||||
|
||||
.PHONY: generate
|
||||
generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations.
|
||||
$(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..."
|
||||
|
||||
.PHONY: fmt
|
||||
fmt: ## Run go fmt against code.
|
||||
go fmt ./...
|
||||
|
||||
.PHONY: vet
|
||||
vet: ## Run go vet against code.
|
||||
go vet ./...
|
||||
|
||||
.PHONY: test
|
||||
test: manifests generate fmt vet setup-envtest ## Run tests.
|
||||
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $$(go list ./... | grep -v /e2e) -coverprofile cover.out
|
||||
|
||||
# TODO(user): To use a different vendor for e2e tests, modify the setup under 'tests/e2e'.
|
||||
# The default setup assumes Kind is pre-installed and builds/loads the Manager Docker image locally.
|
||||
# CertManager is installed by default; skip with:
|
||||
# - CERT_MANAGER_INSTALL_SKIP=true
|
||||
KIND_CLUSTER ?= infisical-operator-test-e2e
|
||||
|
||||
.PHONY: setup-test-e2e
|
||||
setup-test-e2e: ## Set up a Kind cluster for e2e tests if it does not exist
|
||||
@command -v $(KIND) >/dev/null 2>&1 || { \
|
||||
echo "Kind is not installed. Please install Kind manually."; \
|
||||
exit 1; \
|
||||
}
|
||||
@case "$$($(KIND) get clusters)" in \
|
||||
*"$(KIND_CLUSTER)"*) \
|
||||
echo "Kind cluster '$(KIND_CLUSTER)' already exists. Skipping creation." ;; \
|
||||
*) \
|
||||
echo "Creating Kind cluster '$(KIND_CLUSTER)'..."; \
|
||||
$(KIND) create cluster --name $(KIND_CLUSTER) ;; \
|
||||
esac
|
||||
|
||||
.PHONY: test-e2e
|
||||
test-e2e: setup-test-e2e manifests generate fmt vet ## Run the e2e tests. Expected an isolated environment using Kind.
|
||||
KIND_CLUSTER=$(KIND_CLUSTER) go test ./test/e2e/ -v -ginkgo.v
|
||||
$(MAKE) cleanup-test-e2e
|
||||
|
||||
.PHONY: cleanup-test-e2e
|
||||
cleanup-test-e2e: ## Tear down the Kind cluster used for e2e tests
|
||||
@$(KIND) delete cluster --name $(KIND_CLUSTER)
|
||||
|
||||
.PHONY: lint
|
||||
lint: golangci-lint ## Run golangci-lint linter
|
||||
$(GOLANGCI_LINT) run
|
||||
|
||||
.PHONY: lint-fix
|
||||
lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes
|
||||
$(GOLANGCI_LINT) run --fix
|
||||
|
||||
.PHONY: lint-config
|
||||
lint-config: golangci-lint ## Verify golangci-lint linter configuration
|
||||
$(GOLANGCI_LINT) config verify
|
||||
|
||||
##@ Build
|
||||
|
||||
.PHONY: build
|
||||
build: manifests generate fmt vet ## Build manager binary.
|
||||
go build -o bin/manager cmd/main.go
|
||||
|
||||
.PHONY: run
|
||||
run: manifests generate fmt vet ## Run a controller from your host.
|
||||
go run ./cmd/main.go
|
||||
|
||||
# If you wish to build the manager image targeting other platforms you can use the --platform flag.
|
||||
# (i.e. docker build --platform linux/arm64). However, you must enable docker buildKit for it.
|
||||
# More info: https://docs.docker.com/develop/develop-images/build_enhancements/
|
||||
.PHONY: docker-build
|
||||
docker-build: ## Build docker image with the manager.
|
||||
$(CONTAINER_TOOL) build -t ${IMG} .
|
||||
|
||||
.PHONY: docker-push
|
||||
docker-push: ## Push docker image with the manager.
|
||||
$(CONTAINER_TOOL) push ${IMG}
|
||||
|
||||
# PLATFORMS defines the target platforms for the manager image be built to provide support to multiple
|
||||
# architectures. (i.e. make docker-buildx IMG=myregistry/mypoperator:0.0.1). To use this option you need to:
|
||||
# - be able to use docker buildx. More info: https://docs.docker.com/build/buildx/
|
||||
# - have enabled BuildKit. More info: https://docs.docker.com/develop/develop-images/build_enhancements/
|
||||
# - be able to push the image to your registry (i.e. if you do not set a valid value via IMG=<myregistry/image:<tag>> then the export will fail)
|
||||
# To adequately provide solutions that are compatible with multiple platforms, you should consider using this option.
|
||||
PLATFORMS ?= linux/arm64,linux/amd64,linux/s390x,linux/ppc64le
|
||||
.PHONY: docker-buildx
|
||||
docker-buildx: ## Build and push docker image for the manager for cross-platform support
|
||||
# copy existing Dockerfile and insert --platform=${BUILDPLATFORM} into Dockerfile.cross, and preserve the original Dockerfile
|
||||
sed -e '1 s/\(^FROM\)/FROM --platform=\$$\{BUILDPLATFORM\}/; t' -e ' 1,// s//FROM --platform=\$$\{BUILDPLATFORM\}/' Dockerfile > Dockerfile.cross
|
||||
- $(CONTAINER_TOOL) buildx create --name infisical-operator-builder
|
||||
$(CONTAINER_TOOL) buildx use infisical-operator-builder
|
||||
- $(CONTAINER_TOOL) buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile.cross .
|
||||
- $(CONTAINER_TOOL) buildx rm infisical-operator-builder
|
||||
rm Dockerfile.cross
|
||||
|
||||
.PHONY: build-installer
|
||||
build-installer: manifests generate kustomize ## Generate a consolidated YAML with CRDs and deployment.
|
||||
mkdir -p dist
|
||||
cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG}
|
||||
$(KUSTOMIZE) build config/default > dist/install.yaml
|
||||
|
||||
##@ Deployment
|
||||
|
||||
ifndef ignore-not-found
|
||||
ignore-not-found = false
|
||||
endif
|
||||
|
||||
.PHONY: install
|
||||
install: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config.
|
||||
$(KUSTOMIZE) build config/crd | $(KUBECTL) apply -f -
|
||||
|
||||
.PHONY: uninstall
|
||||
uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion.
|
||||
$(KUSTOMIZE) build config/crd | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f -
|
||||
|
||||
.PHONY: deploy
|
||||
deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config.
|
||||
cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG}
|
||||
$(KUSTOMIZE) build config/default | $(KUBECTL) apply -f -
|
||||
|
||||
.PHONY: undeploy
|
||||
undeploy: kustomize ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion.
|
||||
$(KUSTOMIZE) build config/default | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f -
|
||||
|
||||
##@ Dependencies
|
||||
|
||||
## Location to install dependencies to
|
||||
LOCALBIN ?= $(shell pwd)/bin
|
||||
$(LOCALBIN):
|
||||
mkdir -p $(LOCALBIN)
|
||||
|
||||
## Tool Binaries
|
||||
KUBECTL ?= kubectl
|
||||
KIND ?= kind
|
||||
KUSTOMIZE ?= $(LOCALBIN)/kustomize
|
||||
CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen
|
||||
ENVTEST ?= $(LOCALBIN)/setup-envtest
|
||||
GOLANGCI_LINT = $(LOCALBIN)/golangci-lint
|
||||
|
||||
## Tool Versions
|
||||
KUSTOMIZE_VERSION ?= v5.6.0
|
||||
CONTROLLER_TOOLS_VERSION ?= v0.18.0
|
||||
#ENVTEST_VERSION is the version of controller-runtime release branch to fetch the envtest setup script (i.e. release-0.20)
|
||||
ENVTEST_VERSION ?= $(shell go list -m -f "{{ .Version }}" sigs.k8s.io/controller-runtime | awk -F'[v.]' '{printf "release-%d.%d", $$2, $$3}')
|
||||
#ENVTEST_K8S_VERSION is the version of Kubernetes to use for setting up ENVTEST binaries (i.e. 1.31)
|
||||
ENVTEST_K8S_VERSION ?= $(shell go list -m -f "{{ .Version }}" k8s.io/api | awk -F'[v.]' '{printf "1.%d", $$3}')
|
||||
GOLANGCI_LINT_VERSION ?= v2.1.6
|
||||
|
||||
.PHONY: kustomize
|
||||
kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary.
|
||||
$(KUSTOMIZE): $(LOCALBIN)
|
||||
$(call go-install-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v5,$(KUSTOMIZE_VERSION))
|
||||
|
||||
.PHONY: controller-gen
|
||||
controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary.
|
||||
$(CONTROLLER_GEN): $(LOCALBIN)
|
||||
$(call go-install-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen,$(CONTROLLER_TOOLS_VERSION))
|
||||
|
||||
.PHONY: setup-envtest
|
||||
setup-envtest: envtest ## Download the binaries required for ENVTEST in the local bin directory.
|
||||
@echo "Setting up envtest binaries for Kubernetes version $(ENVTEST_K8S_VERSION)..."
|
||||
@$(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path || { \
|
||||
echo "Error: Failed to set up envtest binaries for version $(ENVTEST_K8S_VERSION)."; \
|
||||
exit 1; \
|
||||
}
|
||||
|
||||
.PHONY: envtest
|
||||
envtest: $(ENVTEST) ## Download setup-envtest locally if necessary.
|
||||
$(ENVTEST): $(LOCALBIN)
|
||||
$(call go-install-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest,$(ENVTEST_VERSION))
|
||||
|
||||
.PHONY: golangci-lint
|
||||
golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary.
|
||||
$(GOLANGCI_LINT): $(LOCALBIN)
|
||||
$(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/v2/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION))
|
||||
|
||||
# go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist
|
||||
# $1 - target path with name of binary
|
||||
# $2 - package url which can be installed
|
||||
# $3 - specific version of package
|
||||
define go-install-tool
|
||||
@[ -f "$(1)-$(3)" ] || { \
|
||||
set -e; \
|
||||
package=$(2)@$(3) ;\
|
||||
echo "Downloading $${package}" ;\
|
||||
rm -f $(1) || true ;\
|
||||
GOBIN=$(LOCALBIN) go install $${package} ;\
|
||||
mv $(1) $(1)-$(3) ;\
|
||||
} ;\
|
||||
ln -sf $(1)-$(3) $(1)
|
||||
endef
|
||||
@@ -1,39 +0,0 @@
|
||||
# Code generated by tool. DO NOT EDIT.
|
||||
# This file is used to track the info used to scaffold your project
|
||||
# and allow the plugins properly work.
|
||||
# More info: https://book.kubebuilder.io/reference/project-config.html
|
||||
cliVersion: 4.7.0
|
||||
domain: infisical.com
|
||||
layout:
|
||||
- go.kubebuilder.io/v4
|
||||
projectName: k8-operator
|
||||
repo: github.com/Infisical/infisical/k8-operator
|
||||
resources:
|
||||
- api:
|
||||
crdVersion: v1
|
||||
namespaced: true
|
||||
controller: true
|
||||
domain: infisical.com
|
||||
group: secrets
|
||||
kind: InfisicalSecret
|
||||
path: github.com/Infisical/infisical/k8-operator/api/v1alpha1
|
||||
version: v1alpha1
|
||||
- api:
|
||||
crdVersion: v1
|
||||
namespaced: true
|
||||
controller: true
|
||||
domain: infisical.com
|
||||
group: secrets
|
||||
kind: InfisicalPushSecret
|
||||
path: github.com/Infisical/infisical/k8-operator/api/v1alpha1
|
||||
version: v1alpha1
|
||||
- api:
|
||||
crdVersion: v1
|
||||
namespaced: true
|
||||
controller: true
|
||||
domain: infisical.com
|
||||
group: secrets
|
||||
kind: InfisicalDynamicSecret
|
||||
path: github.com/Infisical/infisical/k8-operator/api/v1alpha1
|
||||
version: v1alpha1
|
||||
version: "3"
|
||||
@@ -1,84 +0,0 @@
|
||||
# k8-operator
|
||||
// TODO
|
||||
|
||||
## Description
|
||||
// TODO
|
||||
|
||||
## Getting Started
|
||||
You’ll need a Kubernetes cluster to run against. You can use [KIND](https://sigs.k8s.io/kind) to get a local cluster for testing, or run against a remote cluster.
|
||||
**Note:** Your controller will automatically use the current context in your kubeconfig file (i.e. whatever cluster `kubectl cluster-info` shows).
|
||||
|
||||
### Running on the cluster
|
||||
1. Install Instances of Custom Resources:
|
||||
|
||||
```sh
|
||||
kubectl apply -f config/samples/
|
||||
```
|
||||
|
||||
2. Build and push your image to the location specified by `IMG`:
|
||||
|
||||
```sh
|
||||
make docker-build docker-push IMG=<some-registry>/k8-operator:tag
|
||||
```
|
||||
|
||||
3. Deploy the controller to the cluster with the image specified by `IMG`:
|
||||
|
||||
```sh
|
||||
make deploy IMG=<some-registry>/k8-operator:tag
|
||||
```
|
||||
|
||||
### Uninstall CRDs
|
||||
To delete the CRDs from the cluster:
|
||||
|
||||
```sh
|
||||
make uninstall
|
||||
```
|
||||
|
||||
### Undeploy controller
|
||||
UnDeploy the controller to the cluster:
|
||||
|
||||
```sh
|
||||
make undeploy
|
||||
```
|
||||
|
||||
## Contributing
|
||||
// TODO
|
||||
|
||||
### How it works
|
||||
This project aims to follow the Kubernetes [Operator pattern](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/)
|
||||
|
||||
It uses [Controllers](https://kubernetes.io/docs/concepts/architecture/controller/)
|
||||
which provides a reconcile function responsible for synchronizing resources untile the desired state is reached on the cluster
|
||||
|
||||
### Test It Out
|
||||
1. Install the CRDs into the cluster:
|
||||
|
||||
```sh
|
||||
make install
|
||||
```
|
||||
|
||||
2. Run your controller (this will run in the foreground, so switch to a new terminal if you want to leave it running):
|
||||
|
||||
```sh
|
||||
make run
|
||||
```
|
||||
|
||||
**NOTE:** You can also run this in one step by running: `make install run`
|
||||
|
||||
### Modifying the API definitions
|
||||
If you are editing the API definitions, generate the manifests such as CRs or CRDs using:
|
||||
|
||||
```sh
|
||||
make manifests
|
||||
```
|
||||
|
||||
Also, after editing the API definitions, update the kubectl-install folder:
|
||||
|
||||
```sh
|
||||
make kubectl-install
|
||||
```
|
||||
|
||||
**NOTE:** Run `make --help` for more information on all potential `make` targets
|
||||
|
||||
More information can be found via the [Kubebuilder Documentation](https://book.kubebuilder.io/introduction.html)
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
package v1alpha1
|
||||
|
||||
type GenericInfisicalAuthentication struct {
|
||||
// +kubebuilder:validation:Optional
|
||||
UniversalAuth GenericUniversalAuth `json:"universalAuth,omitempty"`
|
||||
// +kubebuilder:validation:Optional
|
||||
KubernetesAuth GenericKubernetesAuth `json:"kubernetesAuth,omitempty"`
|
||||
// +kubebuilder:validation:Optional
|
||||
AwsIamAuth GenericAwsIamAuth `json:"awsIamAuth,omitempty"`
|
||||
// +kubebuilder:validation:Optional
|
||||
AzureAuth GenericAzureAuth `json:"azureAuth,omitempty"`
|
||||
// +kubebuilder:validation:Optional
|
||||
GcpIdTokenAuth GenericGcpIdTokenAuth `json:"gcpIdTokenAuth,omitempty"`
|
||||
// +kubebuilder:validation:Optional
|
||||
GcpIamAuth GenericGcpIamAuth `json:"gcpIamAuth,omitempty"`
|
||||
// +kubebuilder:validation:Optional
|
||||
LdapAuth GenericLdapAuth `json:"ldapAuth,omitempty"`
|
||||
}
|
||||
|
||||
type GenericUniversalAuth struct {
|
||||
// +kubebuilder:validation:Required
|
||||
CredentialsRef KubeSecretReference `json:"credentialsRef"`
|
||||
}
|
||||
|
||||
type GenericLdapAuth struct {
|
||||
// +kubebuilder:validation:Required
|
||||
IdentityID string `json:"identityId"`
|
||||
// +kubebuilder:validation:Required
|
||||
CredentialsRef KubeSecretReference `json:"credentialsRef"`
|
||||
}
|
||||
|
||||
type GenericAwsIamAuth struct {
|
||||
// +kubebuilder:validation:Required
|
||||
IdentityID string `json:"identityId"`
|
||||
}
|
||||
|
||||
type GenericAzureAuth struct {
|
||||
// +kubebuilder:validation:Required
|
||||
IdentityID string `json:"identityId"`
|
||||
// +kubebuilder:validation:Optional
|
||||
Resource string `json:"resource,omitempty"`
|
||||
}
|
||||
|
||||
type GenericGcpIdTokenAuth struct {
|
||||
// +kubebuilder:validation:Required
|
||||
IdentityID string `json:"identityId"`
|
||||
}
|
||||
|
||||
type GenericGcpIamAuth struct {
|
||||
// +kubebuilder:validation:Required
|
||||
IdentityID string `json:"identityId"`
|
||||
// +kubebuilder:validation:Required
|
||||
ServiceAccountKeyFilePath string `json:"serviceAccountKeyFilePath"`
|
||||
}
|
||||
|
||||
type GenericKubernetesAuth struct {
|
||||
// +kubebuilder:validation:Required
|
||||
IdentityID string `json:"identityId"`
|
||||
// +kubebuilder:validation:Required
|
||||
ServiceAccountRef KubernetesServiceAccountRef `json:"serviceAccountRef"`
|
||||
|
||||
// Optionally automatically create a service account token for the configured service account.
|
||||
// If this is set to `true`, the operator will automatically create a service account token for the configured service account. This field is recommended in most cases.
|
||||
// +kubebuilder:validation:Optional
|
||||
AutoCreateServiceAccountToken bool `json:"autoCreateServiceAccountToken"`
|
||||
// The audiences to use for the service account token. This is only relevant if `autoCreateServiceAccountToken` is true.
|
||||
// +kubebuilder:validation:Optional
|
||||
ServiceAccountTokenAudiences []string `json:"serviceAccountTokenAudiences"`
|
||||
}
|
||||
|
||||
type TLSConfig struct {
|
||||
// Reference to secret containing CA cert
|
||||
// +kubebuilder:validation:Optional
|
||||
CaRef CaReference `json:"caRef,omitempty"`
|
||||
}
|
||||
|
||||
type CaReference struct {
|
||||
// The name of the Kubernetes Secret
|
||||
// +kubebuilder:validation:Required
|
||||
SecretName string `json:"secretName"`
|
||||
|
||||
// The namespace where the Kubernetes Secret is located
|
||||
// +kubebuilder:validation:Required
|
||||
SecretNamespace string `json:"secretNamespace"`
|
||||
|
||||
// +kubebuilder:validation:Required
|
||||
// The name of the secret property with the CA certificate value
|
||||
SecretKey string `json:"key"`
|
||||
}
|
||||
|
||||
type KubeSecretReference struct {
|
||||
// The name of the Kubernetes Secret
|
||||
// +kubebuilder:validation:Required
|
||||
SecretName string `json:"secretName"`
|
||||
|
||||
// The name space where the Kubernetes Secret is located
|
||||
// +kubebuilder:validation:Required
|
||||
SecretNamespace string `json:"secretNamespace"`
|
||||
}
|
||||
|
||||
type ManagedKubeSecretConfig struct {
|
||||
// The name of the Kubernetes Secret
|
||||
// +kubebuilder:validation:Required
|
||||
SecretName string `json:"secretName"`
|
||||
|
||||
// The name space where the Kubernetes Secret is located
|
||||
// +kubebuilder:validation:Required
|
||||
SecretNamespace string `json:"secretNamespace"`
|
||||
|
||||
// The Kubernetes Secret type (experimental feature). More info: https://kubernetes.io/docs/concepts/configuration/secret/#secret-types
|
||||
// +kubebuilder:validation:Optional
|
||||
// +kubebuilder:default:=Opaque
|
||||
SecretType string `json:"secretType"`
|
||||
|
||||
// The Kubernetes Secret creation policy.
|
||||
// Enum with values: 'Owner', 'Orphan'.
|
||||
// Owner creates the secret and sets .metadata.ownerReferences of the InfisicalSecret CRD that created it.
|
||||
// Orphan will not set the secret owner. This will result in the secret being orphaned and not deleted when the resource is deleted.
|
||||
// +kubebuilder:validation:Optional
|
||||
// +kubebuilder:default:=Orphan
|
||||
CreationPolicy string `json:"creationPolicy"`
|
||||
|
||||
// The template to transform the secret data
|
||||
// +kubebuilder:validation:Optional
|
||||
Template *SecretTemplate `json:"template,omitempty"`
|
||||
}
|
||||
|
||||
type ManagedKubeConfigMapConfig struct {
|
||||
// The name of the Kubernetes ConfigMap
|
||||
// +kubebuilder:validation:Required
|
||||
ConfigMapName string `json:"configMapName"`
|
||||
|
||||
// The Kubernetes ConfigMap creation policy.
|
||||
// Enum with values: 'Owner', 'Orphan'.
|
||||
// Owner creates the config map and sets .metadata.ownerReferences of the InfisicalSecret CRD that created it.
|
||||
// Orphan will not set the config map owner. This will result in the config map being orphaned and not deleted when the resource is deleted.
|
||||
// +kubebuilder:validation:Optional
|
||||
// +kubebuilder:default:=Orphan
|
||||
CreationPolicy string `json:"creationPolicy"`
|
||||
|
||||
// The namespace where the Kubernetes ConfigMap is located
|
||||
// +kubebuilder:validation:Required
|
||||
ConfigMapNamespace string `json:"configMapNamespace"`
|
||||
|
||||
// The template to transform the secret data
|
||||
// +kubebuilder:validation:Optional
|
||||
Template *SecretTemplate `json:"template,omitempty"`
|
||||
}
|
||||
|
||||
type SecretTemplate struct {
|
||||
// This injects all retrieved secrets into the top level of your template.
|
||||
// Secrets defined in the template will take precedence over the injected ones.
|
||||
// +kubebuilder:validation:Optional
|
||||
IncludeAllSecrets bool `json:"includeAllSecrets"`
|
||||
// The template key values
|
||||
// +kubebuilder:validation:Optional
|
||||
Data map[string]string `json:"data,omitempty"`
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
/*
|
||||
Copyright 2022.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// GeneratorKind represents a kind of generator.
|
||||
// +kubebuilder:validation:Enum=Password;UUID
|
||||
type GeneratorKind string
|
||||
|
||||
const (
|
||||
GeneratorKindPassword GeneratorKind = "Password"
|
||||
GeneratorKindUUID GeneratorKind = "UUID"
|
||||
)
|
||||
|
||||
type ClusterGeneratorSpec struct {
|
||||
// Kind the kind of this generator.
|
||||
Kind GeneratorKind `json:"kind"`
|
||||
|
||||
// Generator the spec for this generator, must match the kind.
|
||||
Generator GeneratorSpec `json:"generator,omitempty"`
|
||||
}
|
||||
|
||||
type GeneratorSpec struct {
|
||||
// +kubebuilder:validation:Optional
|
||||
PasswordSpec *PasswordSpec `json:"passwordSpec,omitempty"`
|
||||
// +kubebuilder:validation:Optional
|
||||
UUIDSpec *UUIDSpec `json:"uuidSpec,omitempty"`
|
||||
}
|
||||
|
||||
// ClusterGenerator represents a cluster-wide generator
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:storageversion
|
||||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:resource:scope=Cluster
|
||||
type ClusterGenerator struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec ClusterGeneratorSpec `json:"spec,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// ClusterGeneratorList contains a list of ClusterGenerator resources.
|
||||
type ClusterGeneratorList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []ClusterGenerator `json:"items"`
|
||||
}
|
||||
|
||||
// ! UUID Generator
|
||||
|
||||
// UUIDSpec controls the behavior of the uuid generator.
|
||||
type UUIDSpec struct{}
|
||||
|
||||
// UUID generates a version 4 UUID (e56657e3-764f-11ef-a397-65231a88c216).
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:subresource:status
|
||||
type UUID struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec UUIDSpec `json:"spec,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// UUIDList contains a list of UUID resources.
|
||||
type UUIDList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []UUID `json:"items"`
|
||||
}
|
||||
|
||||
// ! Password Generator
|
||||
|
||||
// PasswordSpec controls the behavior of the password generator.
|
||||
type PasswordSpec struct {
|
||||
// Length of the password to be generated.
|
||||
// Defaults to 24
|
||||
// +kubebuilder:validation:Optional
|
||||
// +kubebuilder:default=24
|
||||
Length int `json:"length"`
|
||||
|
||||
// digits specifies the number of digits in the generated
|
||||
// password. If omitted it defaults to 25% of the length of the password
|
||||
Digits *int `json:"digits,omitempty"`
|
||||
|
||||
// symbols specifies the number of symbol characters in the generated
|
||||
// password. If omitted it defaults to 25% of the length of the password
|
||||
Symbols *int `json:"symbols,omitempty"`
|
||||
|
||||
// symbolCharacters specifies the special characters that should be used
|
||||
// in the generated password.
|
||||
SymbolCharacters *string `json:"symbolCharacters,omitempty"`
|
||||
|
||||
// Set noUpper to disable uppercase characters
|
||||
// +kubebuilder:validation:Optional
|
||||
// +kubebuilder:default=false
|
||||
NoUpper bool `json:"noUpper"`
|
||||
|
||||
// set allowRepeat to true to allow repeating characters.
|
||||
// +kubebuilder:validation:Optional
|
||||
// +kubebuilder:default=false
|
||||
AllowRepeat bool `json:"allowRepeat"`
|
||||
}
|
||||
|
||||
// Password generates a random password based on the
|
||||
// configuration parameters in spec.
|
||||
// You can specify the length, characterset and other attributes.
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:storageversion
|
||||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:resource:scope=Namespaced
|
||||
type Password struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec PasswordSpec `json:"spec,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// PasswordList contains a list of Password resources.
|
||||
type PasswordList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []Password `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&Password{}, &PasswordList{})
|
||||
SchemeBuilder.Register(&UUID{}, &UUIDList{})
|
||||
SchemeBuilder.Register(&ClusterGenerator{}, &ClusterGeneratorList{})
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
// Package v1alpha1 contains API Schema definitions for the secrets v1alpha1 API group
|
||||
// +kubebuilder:object:generate=true
|
||||
// +groupName=secrets.infisical.com
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"sigs.k8s.io/controller-runtime/pkg/scheme"
|
||||
)
|
||||
|
||||
var (
|
||||
// GroupVersion is group version used to register these objects
|
||||
GroupVersion = schema.GroupVersion{Group: "secrets.infisical.com", Version: "v1alpha1"}
|
||||
|
||||
// SchemeBuilder is used to add go types to the GroupVersionKind scheme
|
||||
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
|
||||
|
||||
// AddToScheme adds the types in this group-version to the given scheme.
|
||||
AddToScheme = SchemeBuilder.AddToScheme
|
||||
)
|
||||
@@ -1,99 +0,0 @@
|
||||
/*
|
||||
Copyright 2022.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type InfisicalDynamicSecretLease struct {
|
||||
ID string `json:"id"`
|
||||
Version int64 `json:"version"`
|
||||
CreationTimestamp metav1.Time `json:"creationTimestamp"`
|
||||
ExpiresAt metav1.Time `json:"expiresAt"`
|
||||
}
|
||||
|
||||
type DynamicSecretDetails struct {
|
||||
// +kubebuilder:validation:Required
|
||||
// +kubebuilder:validation:Immutable
|
||||
SecretName string `json:"secretName"`
|
||||
// +kubebuilder:validation:Required
|
||||
// +kubebuilder:validation:Immutable
|
||||
SecretPath string `json:"secretsPath"`
|
||||
// +kubebuilder:validation:Required
|
||||
// +kubebuilder:validation:Immutable
|
||||
EnvironmentSlug string `json:"environmentSlug"`
|
||||
// +kubebuilder:validation:Required
|
||||
// +kubebuilder:validation:Immutable
|
||||
ProjectID string `json:"projectId"`
|
||||
}
|
||||
|
||||
// InfisicalDynamicSecretSpec defines the desired state of InfisicalDynamicSecret.
|
||||
type InfisicalDynamicSecretSpec struct {
|
||||
// +kubebuilder:validation:Required
|
||||
ManagedSecretReference ManagedKubeSecretConfig `json:"managedSecretReference"` // The destination to store the lease in.
|
||||
|
||||
// +kubebuilder:validation:Required
|
||||
Authentication GenericInfisicalAuthentication `json:"authentication"` // The authentication to use for authenticating with Infisical.
|
||||
|
||||
// +kubebuilder:validation:Required
|
||||
DynamicSecret DynamicSecretDetails `json:"dynamicSecret"` // The dynamic secret to create the lease for. Required.
|
||||
|
||||
LeaseRevocationPolicy string `json:"leaseRevocationPolicy"` // Revoke will revoke the lease when the resource is deleted. Optional, will default to no revocation.
|
||||
LeaseTTL string `json:"leaseTTL"` // The TTL of the lease in seconds. Optional, will default to the dynamic secret default TTL.
|
||||
|
||||
// +kubebuilder:validation:Optional
|
||||
HostAPI string `json:"hostAPI"`
|
||||
|
||||
// +kubebuilder:validation:Optional
|
||||
TLS TLSConfig `json:"tls"`
|
||||
}
|
||||
|
||||
// InfisicalDynamicSecretStatus defines the observed state of InfisicalDynamicSecret.
|
||||
type InfisicalDynamicSecretStatus struct {
|
||||
Conditions []metav1.Condition `json:"conditions"`
|
||||
|
||||
Lease *InfisicalDynamicSecretLease `json:"lease,omitempty"`
|
||||
DynamicSecretID string `json:"dynamicSecretId,omitempty"`
|
||||
// The MaxTTL can be null, if it's null, there's no max TTL and we should never have to renew.
|
||||
MaxTTL string `json:"maxTTL,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:subresource:status
|
||||
|
||||
// InfisicalDynamicSecret is the Schema for the infisicaldynamicsecrets API.
|
||||
type InfisicalDynamicSecret struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec InfisicalDynamicSecretSpec `json:"spec,omitempty"`
|
||||
Status InfisicalDynamicSecretStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// InfisicalDynamicSecretList contains a list of InfisicalDynamicSecret.
|
||||
type InfisicalDynamicSecretList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []InfisicalDynamicSecret `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&InfisicalDynamicSecret{}, &InfisicalDynamicSecretList{})
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type InfisicalPushSecretDestination struct {
|
||||
// +kubebuilder:validation:Required
|
||||
// +kubebuilder:validation:Immutable
|
||||
SecretsPath string `json:"secretsPath"`
|
||||
// +kubebuilder:validation:Required
|
||||
// +kubebuilder:validation:Immutable
|
||||
EnvironmentSlug string `json:"environmentSlug"`
|
||||
// +kubebuilder:validation:Required
|
||||
// +kubebuilder:validation:Immutable
|
||||
ProjectID string `json:"projectId"`
|
||||
}
|
||||
|
||||
type InfisicalPushSecretSource struct {
|
||||
// The name of the Kubernetes Secret
|
||||
// +kubebuilder:validation:Required
|
||||
SecretName string `json:"secretName"`
|
||||
|
||||
// The name space where the Kubernetes Secret is located
|
||||
// +kubebuilder:validation:Required
|
||||
SecretNamespace string `json:"secretNamespace"`
|
||||
|
||||
// +kubebuilder:validation:Optional
|
||||
Template *SecretTemplate `json:"template,omitempty"`
|
||||
}
|
||||
|
||||
type GeneratorRef struct {
|
||||
// Specify the Kind of the generator resource
|
||||
// +kubebuilder:validation:Enum=Password;UUID
|
||||
// +kubebuilder:validation:Required
|
||||
Kind GeneratorKind `json:"kind"`
|
||||
|
||||
// +kubebuilder:validation:Required
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type SecretPushGenerator struct {
|
||||
// +kubebuilder:validation:Required
|
||||
DestinationSecretName string `json:"destinationSecretName"`
|
||||
// +kubebuilder:validation:Required
|
||||
GeneratorRef GeneratorRef `json:"generatorRef"`
|
||||
}
|
||||
|
||||
type SecretPush struct {
|
||||
// +kubebuilder:validation:Optional
|
||||
Secret *InfisicalPushSecretSource `json:"secret,omitempty"`
|
||||
// +kubebuilder:validation:Optional
|
||||
Generators []SecretPushGenerator `json:"generators,omitempty"`
|
||||
}
|
||||
|
||||
// InfisicalPushSecretSpec defines the desired state of InfisicalPushSecret
|
||||
type InfisicalPushSecretSpec struct {
|
||||
// +kubebuilder:validation:Optional
|
||||
UpdatePolicy string `json:"updatePolicy"`
|
||||
|
||||
// +kubebuilder:validation:Optional
|
||||
DeletionPolicy string `json:"deletionPolicy"`
|
||||
|
||||
// +kubebuilder:validation:Required
|
||||
// +kubebuilder:validation:Immutable
|
||||
Destination InfisicalPushSecretDestination `json:"destination"`
|
||||
|
||||
// +kubebuilder:validation:Optional
|
||||
Authentication GenericInfisicalAuthentication `json:"authentication"`
|
||||
|
||||
// +kubebuilder:validation:Required
|
||||
Push SecretPush `json:"push"`
|
||||
|
||||
// +kubebuilder:validation:Optional
|
||||
ResyncInterval *string `json:"resyncInterval,omitempty"`
|
||||
|
||||
// Infisical host to pull secrets from
|
||||
// +kubebuilder:validation:Optional
|
||||
HostAPI string `json:"hostAPI"`
|
||||
|
||||
// +kubebuilder:validation:Optional
|
||||
TLS TLSConfig `json:"tls"`
|
||||
}
|
||||
|
||||
// InfisicalPushSecretStatus defines the observed state of InfisicalPushSecret
|
||||
type InfisicalPushSecretStatus struct {
|
||||
Conditions []metav1.Condition `json:"conditions"`
|
||||
|
||||
// managed secrets is a map where the key is the ID, and the value is the secret key (string[id], string[key] )
|
||||
ManagedSecrets map[string]string `json:"managedSecrets"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:subresource:status
|
||||
// InfisicalPushSecret is the Schema for the infisicalpushsecrets API
|
||||
type InfisicalPushSecret struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec InfisicalPushSecretSpec `json:"spec,omitempty"`
|
||||
Status InfisicalPushSecretStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
//+kubebuilder:object:root=true
|
||||
|
||||
// InfisicalPushSecretList contains a list of InfisicalPushSecret
|
||||
type InfisicalPushSecretList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []InfisicalPushSecret `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&InfisicalPushSecret{}, &InfisicalPushSecretList{})
|
||||
}
|
||||
@@ -1,196 +0,0 @@
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type Authentication struct {
|
||||
// +kubebuilder:validation:Optional
|
||||
ServiceAccount ServiceAccountDetails `json:"serviceAccount"`
|
||||
// +kubebuilder:validation:Optional
|
||||
ServiceToken ServiceTokenDetails `json:"serviceToken"`
|
||||
// +kubebuilder:validation:Optional
|
||||
UniversalAuth UniversalAuthDetails `json:"universalAuth"`
|
||||
// +kubebuilder:validation:Optional
|
||||
KubernetesAuth KubernetesAuthDetails `json:"kubernetesAuth"`
|
||||
// +kubebuilder:validation:Optional
|
||||
AwsIamAuth AWSIamAuthDetails `json:"awsIamAuth"`
|
||||
// +kubebuilder:validation:Optional
|
||||
AzureAuth AzureAuthDetails `json:"azureAuth"`
|
||||
// +kubebuilder:validation:Optional
|
||||
GcpIdTokenAuth GCPIdTokenAuthDetails `json:"gcpIdTokenAuth"`
|
||||
// +kubebuilder:validation:Optional
|
||||
GcpIamAuth GcpIamAuthDetails `json:"gcpIamAuth"`
|
||||
// +kubebuilder:validation:Optional
|
||||
LdapAuth LdapAuthDetails `json:"ldapAuth"`
|
||||
}
|
||||
|
||||
type UniversalAuthDetails struct {
|
||||
// +kubebuilder:validation:Required
|
||||
CredentialsRef KubeSecretReference `json:"credentialsRef"`
|
||||
// +kubebuilder:validation:Required
|
||||
SecretsScope MachineIdentityScopeInWorkspace `json:"secretsScope"`
|
||||
}
|
||||
|
||||
type LdapAuthDetails struct {
|
||||
// +kubebuilder:validation:Required
|
||||
IdentityID string `json:"identityId"`
|
||||
// +kubebuilder:validation:Required
|
||||
CredentialsRef KubeSecretReference `json:"credentialsRef"`
|
||||
// +kubebuilder:validation:Required
|
||||
SecretsScope MachineIdentityScopeInWorkspace `json:"secretsScope"`
|
||||
}
|
||||
|
||||
type KubernetesAuthDetails struct {
|
||||
// +kubebuilder:validation:Required
|
||||
IdentityID string `json:"identityId"`
|
||||
// +kubebuilder:validation:Required
|
||||
ServiceAccountRef KubernetesServiceAccountRef `json:"serviceAccountRef"`
|
||||
|
||||
// +kubebuilder:validation:Required
|
||||
SecretsScope MachineIdentityScopeInWorkspace `json:"secretsScope"`
|
||||
|
||||
// Optionally automatically create a service account token for the configured service account.
|
||||
// If this is set to `true`, the operator will automatically create a service account token for the configured service account.
|
||||
// +kubebuilder:validation:Optional
|
||||
AutoCreateServiceAccountToken bool `json:"autoCreateServiceAccountToken"`
|
||||
// The audiences to use for the service account token. This is only relevant if `autoCreateServiceAccountToken` is true.
|
||||
// +kubebuilder:validation:Optional
|
||||
ServiceAccountTokenAudiences []string `json:"serviceAccountTokenAudiences"`
|
||||
}
|
||||
|
||||
type KubernetesServiceAccountRef struct {
|
||||
// +kubebuilder:validation:Required
|
||||
Name string `json:"name"`
|
||||
// +kubebuilder:validation:Required
|
||||
Namespace string `json:"namespace"`
|
||||
}
|
||||
|
||||
type AWSIamAuthDetails struct {
|
||||
// +kubebuilder:validation:Required
|
||||
IdentityID string `json:"identityId"`
|
||||
|
||||
// +kubebuilder:validation:Required
|
||||
SecretsScope MachineIdentityScopeInWorkspace `json:"secretsScope"`
|
||||
}
|
||||
|
||||
type AzureAuthDetails struct {
|
||||
// +kubebuilder:validation:Required
|
||||
IdentityID string `json:"identityId"`
|
||||
// +kubebuilder:validation:Optional
|
||||
Resource string `json:"resource"`
|
||||
|
||||
// +kubebuilder:validation:Required
|
||||
SecretsScope MachineIdentityScopeInWorkspace `json:"secretsScope"`
|
||||
}
|
||||
|
||||
type GCPIdTokenAuthDetails struct {
|
||||
// +kubebuilder:validation:Required
|
||||
IdentityID string `json:"identityId"`
|
||||
|
||||
// +kubebuilder:validation:Required
|
||||
SecretsScope MachineIdentityScopeInWorkspace `json:"secretsScope"`
|
||||
}
|
||||
|
||||
type GcpIamAuthDetails struct {
|
||||
// +kubebuilder:validation:Required
|
||||
IdentityID string `json:"identityId"`
|
||||
// +kubebuilder:validation:Required
|
||||
ServiceAccountKeyFilePath string `json:"serviceAccountKeyFilePath"`
|
||||
|
||||
// +kubebuilder:validation:Required
|
||||
SecretsScope MachineIdentityScopeInWorkspace `json:"secretsScope"`
|
||||
}
|
||||
|
||||
type ServiceTokenDetails struct {
|
||||
// +kubebuilder:validation:Required
|
||||
ServiceTokenSecretReference KubeSecretReference `json:"serviceTokenSecretReference"`
|
||||
// +kubebuilder:validation:Required
|
||||
SecretsScope SecretScopeInWorkspace `json:"secretsScope"`
|
||||
}
|
||||
|
||||
type ServiceAccountDetails struct {
|
||||
ServiceAccountSecretReference KubeSecretReference `json:"serviceAccountSecretReference"`
|
||||
ProjectId string `json:"projectId"`
|
||||
EnvironmentName string `json:"environmentName"`
|
||||
}
|
||||
|
||||
type SecretScopeInWorkspace struct {
|
||||
// +kubebuilder:validation:Required
|
||||
SecretsPath string `json:"secretsPath"`
|
||||
// +kubebuilder:validation:Required
|
||||
EnvSlug string `json:"envSlug"`
|
||||
// +kubebuilder:validation:Optional
|
||||
Recursive bool `json:"recursive"`
|
||||
}
|
||||
|
||||
type MachineIdentityScopeInWorkspace struct {
|
||||
// +kubebuilder:validation:Required
|
||||
SecretsPath string `json:"secretsPath"`
|
||||
// +kubebuilder:validation:Required
|
||||
EnvSlug string `json:"envSlug"`
|
||||
// +kubebuilder:validation:Required
|
||||
ProjectSlug string `json:"projectSlug"`
|
||||
// +kubebuilder:validation:Optional
|
||||
Recursive bool `json:"recursive"`
|
||||
}
|
||||
|
||||
// InfisicalSecretSpec defines the desired state of InfisicalSecret
|
||||
type InfisicalSecretSpec struct {
|
||||
// +kubebuilder:validation:Optional
|
||||
TokenSecretReference KubeSecretReference `json:"tokenSecretReference"`
|
||||
|
||||
// +kubebuilder:validation:Optional
|
||||
Authentication Authentication `json:"authentication"`
|
||||
|
||||
// +kubebuilder:validation:Optional
|
||||
ManagedSecretReference ManagedKubeSecretConfig `json:"managedSecretReference"`
|
||||
|
||||
// +kubebuilder:validation:Optional
|
||||
ManagedKubeSecretReferences []ManagedKubeSecretConfig `json:"managedKubeSecretReferences"`
|
||||
// +kubebuilder:validation:Optional
|
||||
ManagedKubeConfigMapReferences []ManagedKubeConfigMapConfig `json:"managedKubeConfigMapReferences"`
|
||||
|
||||
// +kubebuilder:default:=60
|
||||
ResyncInterval int `json:"resyncInterval"`
|
||||
|
||||
// Infisical host to pull secrets from
|
||||
// +kubebuilder:validation:Optional
|
||||
HostAPI string `json:"hostAPI"`
|
||||
|
||||
// +kubebuilder:validation:Optional
|
||||
TLS TLSConfig `json:"tls"`
|
||||
|
||||
// +kubebuilder:validation:Optional
|
||||
InstantUpdates bool `json:"instantUpdates"`
|
||||
}
|
||||
|
||||
// InfisicalSecretStatus defines the observed state of InfisicalSecret
|
||||
type InfisicalSecretStatus struct {
|
||||
Conditions []metav1.Condition `json:"conditions"`
|
||||
}
|
||||
|
||||
//+kubebuilder:object:root=true
|
||||
//+kubebuilder:subresource:status
|
||||
|
||||
// InfisicalSecret is the Schema for the infisicalsecrets API
|
||||
type InfisicalSecret struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec InfisicalSecretSpec `json:"spec,omitempty"`
|
||||
Status InfisicalSecretStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
//+kubebuilder:object:root=true
|
||||
|
||||
// InfisicalSecretList contains a list of InfisicalSecret
|
||||
type InfisicalSecretList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []InfisicalSecret `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&InfisicalSecret{}, &InfisicalSecretList{})
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,285 +0,0 @@
|
||||
/*
|
||||
Copyright 2025.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
|
||||
// to ensure that exec-entrypoint and run can make use of them.
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/cache"
|
||||
"sigs.k8s.io/controller-runtime/pkg/certwatcher"
|
||||
"sigs.k8s.io/controller-runtime/pkg/healthz"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log/zap"
|
||||
"sigs.k8s.io/controller-runtime/pkg/metrics/filters"
|
||||
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook"
|
||||
|
||||
secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
||||
"github.com/Infisical/infisical/k8-operator/internal/controller"
|
||||
// +kubebuilder:scaffold:imports
|
||||
)
|
||||
|
||||
var (
|
||||
scheme = runtime.NewScheme()
|
||||
setupLog = ctrl.Log.WithName("setup")
|
||||
)
|
||||
|
||||
func init() {
|
||||
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
|
||||
|
||||
utilruntime.Must(secretsv1alpha1.AddToScheme(scheme))
|
||||
// +kubebuilder:scaffold:scheme
|
||||
}
|
||||
|
||||
// nolint:gocyclo
|
||||
func main() {
|
||||
var metricsAddr string
|
||||
var metricsCertPath, metricsCertName, metricsCertKey string
|
||||
var webhookCertPath, webhookCertName, webhookCertKey string
|
||||
var enableLeaderElection bool
|
||||
var probeAddr string
|
||||
var secureMetrics bool
|
||||
var enableHTTP2 bool
|
||||
var namespace string
|
||||
|
||||
var tlsOpts []func(*tls.Config)
|
||||
flag.StringVar(&namespace, "namespace", "", "Watch InfisicalSecrets scoped in the provided namespace only")
|
||||
flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+
|
||||
"Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.")
|
||||
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
|
||||
flag.BoolVar(&enableLeaderElection, "leader-elect", false,
|
||||
"Enable leader election for controller manager. "+
|
||||
"Enabling this will ensure there is only one active controller manager.")
|
||||
flag.BoolVar(&secureMetrics, "metrics-secure", true,
|
||||
"If set, the metrics endpoint is served securely via HTTPS. Use --metrics-secure=false to use HTTP instead.")
|
||||
flag.StringVar(&webhookCertPath, "webhook-cert-path", "", "The directory that contains the webhook certificate.")
|
||||
flag.StringVar(&webhookCertName, "webhook-cert-name", "tls.crt", "The name of the webhook certificate file.")
|
||||
flag.StringVar(&webhookCertKey, "webhook-cert-key", "tls.key", "The name of the webhook key file.")
|
||||
flag.StringVar(&metricsCertPath, "metrics-cert-path", "",
|
||||
"The directory that contains the metrics server certificate.")
|
||||
flag.StringVar(&metricsCertName, "metrics-cert-name", "tls.crt", "The name of the metrics server certificate file.")
|
||||
flag.StringVar(&metricsCertKey, "metrics-cert-key", "tls.key", "The name of the metrics server key file.")
|
||||
flag.BoolVar(&enableHTTP2, "enable-http2", false,
|
||||
"If set, HTTP/2 will be enabled for the metrics and webhook servers")
|
||||
opts := zap.Options{
|
||||
Development: true,
|
||||
}
|
||||
opts.BindFlags(flag.CommandLine)
|
||||
flag.Parse()
|
||||
|
||||
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
|
||||
|
||||
// if the enable-http2 flag is false (the default), http/2 should be disabled
|
||||
// due to its vulnerabilities. More specifically, disabling http/2 will
|
||||
// prevent from being vulnerable to the HTTP/2 Stream Cancellation and
|
||||
// Rapid Reset CVEs. For more information see:
|
||||
// - https://github.com/advisories/GHSA-qppj-fm5r-hxr3
|
||||
// - https://github.com/advisories/GHSA-4374-p667-p6c8
|
||||
disableHTTP2 := func(c *tls.Config) {
|
||||
setupLog.Info("disabling http/2")
|
||||
c.NextProtos = []string{"http/1.1"}
|
||||
}
|
||||
|
||||
if !enableHTTP2 {
|
||||
tlsOpts = append(tlsOpts, disableHTTP2)
|
||||
}
|
||||
|
||||
// Create watchers for metrics and webhooks certificates
|
||||
var metricsCertWatcher, webhookCertWatcher *certwatcher.CertWatcher
|
||||
|
||||
// Initial webhook TLS options
|
||||
webhookTLSOpts := tlsOpts
|
||||
|
||||
if len(webhookCertPath) > 0 {
|
||||
setupLog.Info("Initializing webhook certificate watcher using provided certificates",
|
||||
"webhook-cert-path", webhookCertPath, "webhook-cert-name", webhookCertName, "webhook-cert-key", webhookCertKey)
|
||||
|
||||
var err error
|
||||
webhookCertWatcher, err = certwatcher.New(
|
||||
filepath.Join(webhookCertPath, webhookCertName),
|
||||
filepath.Join(webhookCertPath, webhookCertKey),
|
||||
)
|
||||
if err != nil {
|
||||
setupLog.Error(err, "Failed to initialize webhook certificate watcher")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
webhookTLSOpts = append(webhookTLSOpts, func(config *tls.Config) {
|
||||
config.GetCertificate = webhookCertWatcher.GetCertificate
|
||||
})
|
||||
}
|
||||
|
||||
webhookServer := webhook.NewServer(webhook.Options{
|
||||
TLSOpts: webhookTLSOpts,
|
||||
})
|
||||
|
||||
// Metrics endpoint is enabled in 'config/default/kustomization.yaml'. The Metrics options configure the server.
|
||||
// More info:
|
||||
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.21.0/pkg/metrics/server
|
||||
// - https://book.kubebuilder.io/reference/metrics.html
|
||||
metricsServerOptions := metricsserver.Options{
|
||||
BindAddress: metricsAddr,
|
||||
SecureServing: secureMetrics,
|
||||
TLSOpts: tlsOpts,
|
||||
}
|
||||
|
||||
if secureMetrics {
|
||||
// FilterProvider is used to protect the metrics endpoint with authn/authz.
|
||||
// These configurations ensure that only authorized users and service accounts
|
||||
// can access the metrics endpoint. The RBAC are configured in 'config/rbac/kustomization.yaml'. More info:
|
||||
// https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.21.0/pkg/metrics/filters#WithAuthenticationAndAuthorization
|
||||
metricsServerOptions.FilterProvider = filters.WithAuthenticationAndAuthorization
|
||||
}
|
||||
|
||||
// If the certificate is not specified, controller-runtime will automatically
|
||||
// generate self-signed certificates for the metrics server. While convenient for development and testing,
|
||||
// this setup is not recommended for production.
|
||||
//
|
||||
// TODO(user): If you enable certManager, uncomment the following lines:
|
||||
// - [METRICS-WITH-CERTS] at config/default/kustomization.yaml to generate and use certificates
|
||||
// managed by cert-manager for the metrics server.
|
||||
// - [PROMETHEUS-WITH-CERTS] at config/prometheus/kustomization.yaml for TLS certification.
|
||||
if len(metricsCertPath) > 0 {
|
||||
setupLog.Info("Initializing metrics certificate watcher using provided certificates",
|
||||
"metrics-cert-path", metricsCertPath, "metrics-cert-name", metricsCertName, "metrics-cert-key", metricsCertKey)
|
||||
|
||||
var err error
|
||||
metricsCertWatcher, err = certwatcher.New(
|
||||
filepath.Join(metricsCertPath, metricsCertName),
|
||||
filepath.Join(metricsCertPath, metricsCertKey),
|
||||
)
|
||||
if err != nil {
|
||||
setupLog.Error(err, "to initialize metrics certificate watcher", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
metricsServerOptions.TLSOpts = append(metricsServerOptions.TLSOpts, func(config *tls.Config) {
|
||||
config.GetCertificate = metricsCertWatcher.GetCertificate
|
||||
})
|
||||
}
|
||||
|
||||
managerOptions := ctrl.Options{
|
||||
Scheme: scheme,
|
||||
Metrics: metricsServerOptions,
|
||||
WebhookServer: webhookServer,
|
||||
HealthProbeBindAddress: probeAddr,
|
||||
LeaderElection: enableLeaderElection,
|
||||
LeaderElectionID: "cf2b8c44.infisical.com",
|
||||
// LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily
|
||||
// when the Manager ends. This requires the binary to immediately end when the
|
||||
// Manager is stopped, otherwise, this setting is unsafe. Setting this significantly
|
||||
// speeds up voluntary leader transitions as the new leader don't have to wait
|
||||
// LeaseDuration time first.
|
||||
//
|
||||
// In the default scaffold provided, the program ends immediately after
|
||||
// the manager stops, so would be fine to enable this option. However,
|
||||
// if you are doing or is intended to do any operation such as perform cleanups
|
||||
// after the manager stops then its usage might be unsafe.
|
||||
// LeaderElectionReleaseOnCancel: true,
|
||||
}
|
||||
|
||||
// Only set cache options if we're namespace-scoped
|
||||
if namespace != "" {
|
||||
managerOptions.Cache = cache.Options{
|
||||
Scheme: scheme,
|
||||
DefaultNamespaces: map[string]cache.Config{
|
||||
namespace: {}, // whichever namespace the operator is running in
|
||||
},
|
||||
}
|
||||
ctrl.Log.Info(fmt.Sprintf("Watching CRDs in [namespace=%s]", namespace))
|
||||
}
|
||||
|
||||
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), managerOptions)
|
||||
if err != nil {
|
||||
setupLog.Error(err, "unable to start manager")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err := (&controller.InfisicalSecretReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
BaseLogger: ctrl.Log,
|
||||
Namespace: namespace,
|
||||
IsNamespaceScoped: namespace != "",
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "InfisicalSecret")
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := (&controller.InfisicalPushSecretReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
IsNamespaceScoped: namespace != "",
|
||||
Namespace: namespace,
|
||||
BaseLogger: ctrl.Log,
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "InfisicalPushSecret")
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := (&controller.InfisicalDynamicSecretReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
BaseLogger: ctrl.Log,
|
||||
IsNamespaceScoped: namespace != "",
|
||||
Namespace: namespace,
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "InfisicalDynamicSecret")
|
||||
os.Exit(1)
|
||||
}
|
||||
// +kubebuilder:scaffold:builder
|
||||
|
||||
if metricsCertWatcher != nil {
|
||||
setupLog.Info("Adding metrics certificate watcher to manager")
|
||||
if err := mgr.Add(metricsCertWatcher); err != nil {
|
||||
setupLog.Error(err, "unable to add metrics certificate watcher to manager")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if webhookCertWatcher != nil {
|
||||
setupLog.Info("Adding webhook certificate watcher to manager")
|
||||
if err := mgr.Add(webhookCertWatcher); err != nil {
|
||||
setupLog.Error(err, "unable to add webhook certificate watcher to manager")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
|
||||
setupLog.Error(err, "unable to set up health check")
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
|
||||
setupLog.Error(err, "unable to set up ready check")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
setupLog.Info("starting manager")
|
||||
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
|
||||
setupLog.Error(err, "problem running manager")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.18.0
|
||||
name: clustergenerators.secrets.infisical.com
|
||||
spec:
|
||||
group: secrets.infisical.com
|
||||
names:
|
||||
kind: ClusterGenerator
|
||||
listKind: ClusterGeneratorList
|
||||
plural: clustergenerators
|
||||
singular: clustergenerator
|
||||
scope: Cluster
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: ClusterGenerator represents a cluster-wide generator
|
||||
properties:
|
||||
apiVersion:
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
type: string
|
||||
kind:
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
properties:
|
||||
generator:
|
||||
description: Generator the spec for this generator, must match the
|
||||
kind.
|
||||
properties:
|
||||
passwordSpec:
|
||||
description: PasswordSpec controls the behavior of the password
|
||||
generator.
|
||||
properties:
|
||||
allowRepeat:
|
||||
default: false
|
||||
description: set allowRepeat to true to allow repeating characters.
|
||||
type: boolean
|
||||
digits:
|
||||
description: |-
|
||||
digits specifies the number of digits in the generated
|
||||
password. If omitted it defaults to 25% of the length of the password
|
||||
type: integer
|
||||
length:
|
||||
default: 24
|
||||
description: |-
|
||||
Length of the password to be generated.
|
||||
Defaults to 24
|
||||
type: integer
|
||||
noUpper:
|
||||
default: false
|
||||
description: Set noUpper to disable uppercase characters
|
||||
type: boolean
|
||||
symbolCharacters:
|
||||
description: |-
|
||||
symbolCharacters specifies the special characters that should be used
|
||||
in the generated password.
|
||||
type: string
|
||||
symbols:
|
||||
description: |-
|
||||
symbols specifies the number of symbol characters in the generated
|
||||
password. If omitted it defaults to 25% of the length of the password
|
||||
type: integer
|
||||
type: object
|
||||
uuidSpec:
|
||||
description: UUIDSpec controls the behavior of the uuid generator.
|
||||
type: object
|
||||
type: object
|
||||
kind:
|
||||
description: Kind the kind of this generator.
|
||||
enum:
|
||||
- Password
|
||||
- UUID
|
||||
type: string
|
||||
required:
|
||||
- kind
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
@@ -1,330 +0,0 @@
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.18.0
|
||||
name: infisicaldynamicsecrets.secrets.infisical.com
|
||||
spec:
|
||||
group: secrets.infisical.com
|
||||
names:
|
||||
kind: InfisicalDynamicSecret
|
||||
listKind: InfisicalDynamicSecretList
|
||||
plural: infisicaldynamicsecrets
|
||||
singular: infisicaldynamicsecret
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: InfisicalDynamicSecret is the Schema for the infisicaldynamicsecrets
|
||||
API.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
type: string
|
||||
kind:
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: InfisicalDynamicSecretSpec defines the desired state of InfisicalDynamicSecret.
|
||||
properties:
|
||||
authentication:
|
||||
properties:
|
||||
awsIamAuth:
|
||||
properties:
|
||||
identityId:
|
||||
type: string
|
||||
required:
|
||||
- identityId
|
||||
type: object
|
||||
azureAuth:
|
||||
properties:
|
||||
identityId:
|
||||
type: string
|
||||
resource:
|
||||
type: string
|
||||
required:
|
||||
- identityId
|
||||
type: object
|
||||
gcpIamAuth:
|
||||
properties:
|
||||
identityId:
|
||||
type: string
|
||||
serviceAccountKeyFilePath:
|
||||
type: string
|
||||
required:
|
||||
- identityId
|
||||
- serviceAccountKeyFilePath
|
||||
type: object
|
||||
gcpIdTokenAuth:
|
||||
properties:
|
||||
identityId:
|
||||
type: string
|
||||
required:
|
||||
- identityId
|
||||
type: object
|
||||
kubernetesAuth:
|
||||
properties:
|
||||
autoCreateServiceAccountToken:
|
||||
description: |-
|
||||
Optionally automatically create a service account token for the configured service account.
|
||||
If this is set to `true`, the operator will automatically create a service account token for the configured service account. This field is recommended in most cases.
|
||||
type: boolean
|
||||
identityId:
|
||||
type: string
|
||||
serviceAccountRef:
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- namespace
|
||||
type: object
|
||||
serviceAccountTokenAudiences:
|
||||
description: The audiences to use for the service account
|
||||
token. This is only relevant if `autoCreateServiceAccountToken`
|
||||
is true.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- identityId
|
||||
- serviceAccountRef
|
||||
type: object
|
||||
ldapAuth:
|
||||
properties:
|
||||
credentialsRef:
|
||||
properties:
|
||||
secretName:
|
||||
description: The name of the Kubernetes Secret
|
||||
type: string
|
||||
secretNamespace:
|
||||
description: The name space where the Kubernetes Secret
|
||||
is located
|
||||
type: string
|
||||
required:
|
||||
- secretName
|
||||
- secretNamespace
|
||||
type: object
|
||||
identityId:
|
||||
type: string
|
||||
required:
|
||||
- credentialsRef
|
||||
- identityId
|
||||
type: object
|
||||
universalAuth:
|
||||
properties:
|
||||
credentialsRef:
|
||||
properties:
|
||||
secretName:
|
||||
description: The name of the Kubernetes Secret
|
||||
type: string
|
||||
secretNamespace:
|
||||
description: The name space where the Kubernetes Secret
|
||||
is located
|
||||
type: string
|
||||
required:
|
||||
- secretName
|
||||
- secretNamespace
|
||||
type: object
|
||||
required:
|
||||
- credentialsRef
|
||||
type: object
|
||||
type: object
|
||||
dynamicSecret:
|
||||
properties:
|
||||
environmentSlug:
|
||||
type: string
|
||||
projectId:
|
||||
type: string
|
||||
secretName:
|
||||
type: string
|
||||
secretsPath:
|
||||
type: string
|
||||
required:
|
||||
- environmentSlug
|
||||
- projectId
|
||||
- secretName
|
||||
- secretsPath
|
||||
type: object
|
||||
hostAPI:
|
||||
type: string
|
||||
leaseRevocationPolicy:
|
||||
type: string
|
||||
leaseTTL:
|
||||
type: string
|
||||
managedSecretReference:
|
||||
properties:
|
||||
creationPolicy:
|
||||
default: Orphan
|
||||
description: |-
|
||||
The Kubernetes Secret creation policy.
|
||||
Enum with values: 'Owner', 'Orphan'.
|
||||
Owner creates the secret and sets .metadata.ownerReferences of the InfisicalSecret CRD that created it.
|
||||
Orphan will not set the secret owner. This will result in the secret being orphaned and not deleted when the resource is deleted.
|
||||
type: string
|
||||
secretName:
|
||||
description: The name of the Kubernetes Secret
|
||||
type: string
|
||||
secretNamespace:
|
||||
description: The name space where the Kubernetes Secret is located
|
||||
type: string
|
||||
secretType:
|
||||
default: Opaque
|
||||
description: 'The Kubernetes Secret type (experimental feature).
|
||||
More info: https://kubernetes.io/docs/concepts/configuration/secret/#secret-types'
|
||||
type: string
|
||||
template:
|
||||
description: The template to transform the secret data
|
||||
properties:
|
||||
data:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: The template key values
|
||||
type: object
|
||||
includeAllSecrets:
|
||||
description: |-
|
||||
This injects all retrieved secrets into the top level of your template.
|
||||
Secrets defined in the template will take precedence over the injected ones.
|
||||
type: boolean
|
||||
type: object
|
||||
required:
|
||||
- secretName
|
||||
- secretNamespace
|
||||
type: object
|
||||
tls:
|
||||
properties:
|
||||
caRef:
|
||||
description: Reference to secret containing CA cert
|
||||
properties:
|
||||
key:
|
||||
description: The name of the secret property with the CA certificate
|
||||
value
|
||||
type: string
|
||||
secretName:
|
||||
description: The name of the Kubernetes Secret
|
||||
type: string
|
||||
secretNamespace:
|
||||
description: The namespace where the Kubernetes Secret is
|
||||
located
|
||||
type: string
|
||||
required:
|
||||
- key
|
||||
- secretName
|
||||
- secretNamespace
|
||||
type: object
|
||||
type: object
|
||||
required:
|
||||
- authentication
|
||||
- dynamicSecret
|
||||
- leaseRevocationPolicy
|
||||
- leaseTTL
|
||||
- managedSecretReference
|
||||
type: object
|
||||
status:
|
||||
description: InfisicalDynamicSecretStatus defines the observed state of
|
||||
InfisicalDynamicSecret.
|
||||
properties:
|
||||
conditions:
|
||||
items:
|
||||
description: Condition contains details for one aspect of the current
|
||||
state of this API Resource.
|
||||
properties:
|
||||
lastTransitionTime:
|
||||
description: |-
|
||||
lastTransitionTime is the last time the condition transitioned from one status to another.
|
||||
This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
|
||||
format: date-time
|
||||
type: string
|
||||
message:
|
||||
description: |-
|
||||
message is a human readable message indicating details about the transition.
|
||||
This may be an empty string.
|
||||
maxLength: 32768
|
||||
type: string
|
||||
observedGeneration:
|
||||
description: |-
|
||||
observedGeneration represents the .metadata.generation that the condition was set based upon.
|
||||
For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
|
||||
with respect to the current state of the instance.
|
||||
format: int64
|
||||
minimum: 0
|
||||
type: integer
|
||||
reason:
|
||||
description: |-
|
||||
reason contains a programmatic identifier indicating the reason for the condition's last transition.
|
||||
Producers of specific condition types may define expected values and meanings for this field,
|
||||
and whether the values are considered a guaranteed API.
|
||||
The value should be a CamelCase string.
|
||||
This field may not be empty.
|
||||
maxLength: 1024
|
||||
minLength: 1
|
||||
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
|
||||
type: string
|
||||
status:
|
||||
description: status of the condition, one of True, False, Unknown.
|
||||
enum:
|
||||
- "True"
|
||||
- "False"
|
||||
- Unknown
|
||||
type: string
|
||||
type:
|
||||
description: type of condition in CamelCase or in foo.example.com/CamelCase.
|
||||
maxLength: 316
|
||||
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
|
||||
type: string
|
||||
required:
|
||||
- lastTransitionTime
|
||||
- message
|
||||
- reason
|
||||
- status
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
dynamicSecretId:
|
||||
type: string
|
||||
lease:
|
||||
properties:
|
||||
creationTimestamp:
|
||||
format: date-time
|
||||
type: string
|
||||
expiresAt:
|
||||
format: date-time
|
||||
type: string
|
||||
id:
|
||||
type: string
|
||||
version:
|
||||
format: int64
|
||||
type: integer
|
||||
required:
|
||||
- creationTimestamp
|
||||
- expiresAt
|
||||
- id
|
||||
- version
|
||||
type: object
|
||||
maxTTL:
|
||||
description: The MaxTTL can be null, if it's null, there's no max
|
||||
TTL and we should never have to renew.
|
||||
type: string
|
||||
required:
|
||||
- conditions
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
@@ -1,326 +0,0 @@
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.18.0
|
||||
name: infisicalpushsecrets.secrets.infisical.com
|
||||
spec:
|
||||
group: secrets.infisical.com
|
||||
names:
|
||||
kind: InfisicalPushSecret
|
||||
listKind: InfisicalPushSecretList
|
||||
plural: infisicalpushsecrets
|
||||
singular: infisicalpushsecret
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: InfisicalPushSecret is the Schema for the infisicalpushsecrets
|
||||
API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
type: string
|
||||
kind:
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: InfisicalPushSecretSpec defines the desired state of InfisicalPushSecret
|
||||
properties:
|
||||
authentication:
|
||||
properties:
|
||||
awsIamAuth:
|
||||
properties:
|
||||
identityId:
|
||||
type: string
|
||||
required:
|
||||
- identityId
|
||||
type: object
|
||||
azureAuth:
|
||||
properties:
|
||||
identityId:
|
||||
type: string
|
||||
resource:
|
||||
type: string
|
||||
required:
|
||||
- identityId
|
||||
type: object
|
||||
gcpIamAuth:
|
||||
properties:
|
||||
identityId:
|
||||
type: string
|
||||
serviceAccountKeyFilePath:
|
||||
type: string
|
||||
required:
|
||||
- identityId
|
||||
- serviceAccountKeyFilePath
|
||||
type: object
|
||||
gcpIdTokenAuth:
|
||||
properties:
|
||||
identityId:
|
||||
type: string
|
||||
required:
|
||||
- identityId
|
||||
type: object
|
||||
kubernetesAuth:
|
||||
properties:
|
||||
autoCreateServiceAccountToken:
|
||||
description: |-
|
||||
Optionally automatically create a service account token for the configured service account.
|
||||
If this is set to `true`, the operator will automatically create a service account token for the configured service account. This field is recommended in most cases.
|
||||
type: boolean
|
||||
identityId:
|
||||
type: string
|
||||
serviceAccountRef:
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- namespace
|
||||
type: object
|
||||
serviceAccountTokenAudiences:
|
||||
description: The audiences to use for the service account
|
||||
token. This is only relevant if `autoCreateServiceAccountToken`
|
||||
is true.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- identityId
|
||||
- serviceAccountRef
|
||||
type: object
|
||||
ldapAuth:
|
||||
properties:
|
||||
credentialsRef:
|
||||
properties:
|
||||
secretName:
|
||||
description: The name of the Kubernetes Secret
|
||||
type: string
|
||||
secretNamespace:
|
||||
description: The name space where the Kubernetes Secret
|
||||
is located
|
||||
type: string
|
||||
required:
|
||||
- secretName
|
||||
- secretNamespace
|
||||
type: object
|
||||
identityId:
|
||||
type: string
|
||||
required:
|
||||
- credentialsRef
|
||||
- identityId
|
||||
type: object
|
||||
universalAuth:
|
||||
properties:
|
||||
credentialsRef:
|
||||
properties:
|
||||
secretName:
|
||||
description: The name of the Kubernetes Secret
|
||||
type: string
|
||||
secretNamespace:
|
||||
description: The name space where the Kubernetes Secret
|
||||
is located
|
||||
type: string
|
||||
required:
|
||||
- secretName
|
||||
- secretNamespace
|
||||
type: object
|
||||
required:
|
||||
- credentialsRef
|
||||
type: object
|
||||
type: object
|
||||
deletionPolicy:
|
||||
type: string
|
||||
destination:
|
||||
properties:
|
||||
environmentSlug:
|
||||
type: string
|
||||
projectId:
|
||||
type: string
|
||||
secretsPath:
|
||||
type: string
|
||||
required:
|
||||
- environmentSlug
|
||||
- projectId
|
||||
- secretsPath
|
||||
type: object
|
||||
hostAPI:
|
||||
description: Infisical host to pull secrets from
|
||||
type: string
|
||||
push:
|
||||
properties:
|
||||
generators:
|
||||
items:
|
||||
properties:
|
||||
destinationSecretName:
|
||||
type: string
|
||||
generatorRef:
|
||||
properties:
|
||||
kind:
|
||||
allOf:
|
||||
- enum:
|
||||
- Password
|
||||
- UUID
|
||||
- enum:
|
||||
- Password
|
||||
- UUID
|
||||
description: Specify the Kind of the generator resource
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
required:
|
||||
- kind
|
||||
- name
|
||||
type: object
|
||||
required:
|
||||
- destinationSecretName
|
||||
- generatorRef
|
||||
type: object
|
||||
type: array
|
||||
secret:
|
||||
properties:
|
||||
secretName:
|
||||
description: The name of the Kubernetes Secret
|
||||
type: string
|
||||
secretNamespace:
|
||||
description: The name space where the Kubernetes Secret is
|
||||
located
|
||||
type: string
|
||||
template:
|
||||
properties:
|
||||
data:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: The template key values
|
||||
type: object
|
||||
includeAllSecrets:
|
||||
description: |-
|
||||
This injects all retrieved secrets into the top level of your template.
|
||||
Secrets defined in the template will take precedence over the injected ones.
|
||||
type: boolean
|
||||
type: object
|
||||
required:
|
||||
- secretName
|
||||
- secretNamespace
|
||||
type: object
|
||||
type: object
|
||||
resyncInterval:
|
||||
type: string
|
||||
tls:
|
||||
properties:
|
||||
caRef:
|
||||
description: Reference to secret containing CA cert
|
||||
properties:
|
||||
key:
|
||||
description: The name of the secret property with the CA certificate
|
||||
value
|
||||
type: string
|
||||
secretName:
|
||||
description: The name of the Kubernetes Secret
|
||||
type: string
|
||||
secretNamespace:
|
||||
description: The namespace where the Kubernetes Secret is
|
||||
located
|
||||
type: string
|
||||
required:
|
||||
- key
|
||||
- secretName
|
||||
- secretNamespace
|
||||
type: object
|
||||
type: object
|
||||
updatePolicy:
|
||||
type: string
|
||||
required:
|
||||
- destination
|
||||
- push
|
||||
type: object
|
||||
status:
|
||||
description: InfisicalPushSecretStatus defines the observed state of InfisicalPushSecret
|
||||
properties:
|
||||
conditions:
|
||||
items:
|
||||
description: Condition contains details for one aspect of the current
|
||||
state of this API Resource.
|
||||
properties:
|
||||
lastTransitionTime:
|
||||
description: |-
|
||||
lastTransitionTime is the last time the condition transitioned from one status to another.
|
||||
This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
|
||||
format: date-time
|
||||
type: string
|
||||
message:
|
||||
description: |-
|
||||
message is a human readable message indicating details about the transition.
|
||||
This may be an empty string.
|
||||
maxLength: 32768
|
||||
type: string
|
||||
observedGeneration:
|
||||
description: |-
|
||||
observedGeneration represents the .metadata.generation that the condition was set based upon.
|
||||
For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
|
||||
with respect to the current state of the instance.
|
||||
format: int64
|
||||
minimum: 0
|
||||
type: integer
|
||||
reason:
|
||||
description: |-
|
||||
reason contains a programmatic identifier indicating the reason for the condition's last transition.
|
||||
Producers of specific condition types may define expected values and meanings for this field,
|
||||
and whether the values are considered a guaranteed API.
|
||||
The value should be a CamelCase string.
|
||||
This field may not be empty.
|
||||
maxLength: 1024
|
||||
minLength: 1
|
||||
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
|
||||
type: string
|
||||
status:
|
||||
description: status of the condition, one of True, False, Unknown.
|
||||
enum:
|
||||
- "True"
|
||||
- "False"
|
||||
- Unknown
|
||||
type: string
|
||||
type:
|
||||
description: type of condition in CamelCase or in foo.example.com/CamelCase.
|
||||
maxLength: 316
|
||||
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
|
||||
type: string
|
||||
required:
|
||||
- lastTransitionTime
|
||||
- message
|
||||
- reason
|
||||
- status
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
managedSecrets:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: managed secrets is a map where the key is the ID, and
|
||||
the value is the secret key (string[id], string[key] )
|
||||
type: object
|
||||
required:
|
||||
- conditions
|
||||
- managedSecrets
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
@@ -1,542 +0,0 @@
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.18.0
|
||||
name: infisicalsecrets.secrets.infisical.com
|
||||
spec:
|
||||
group: secrets.infisical.com
|
||||
names:
|
||||
kind: InfisicalSecret
|
||||
listKind: InfisicalSecretList
|
||||
plural: infisicalsecrets
|
||||
singular: infisicalsecret
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: InfisicalSecret is the Schema for the infisicalsecrets API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
type: string
|
||||
kind:
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: InfisicalSecretSpec defines the desired state of InfisicalSecret
|
||||
properties:
|
||||
authentication:
|
||||
properties:
|
||||
awsIamAuth:
|
||||
properties:
|
||||
identityId:
|
||||
type: string
|
||||
secretsScope:
|
||||
properties:
|
||||
envSlug:
|
||||
type: string
|
||||
projectSlug:
|
||||
type: string
|
||||
recursive:
|
||||
type: boolean
|
||||
secretsPath:
|
||||
type: string
|
||||
required:
|
||||
- envSlug
|
||||
- projectSlug
|
||||
- secretsPath
|
||||
type: object
|
||||
required:
|
||||
- identityId
|
||||
- secretsScope
|
||||
type: object
|
||||
azureAuth:
|
||||
properties:
|
||||
identityId:
|
||||
type: string
|
||||
resource:
|
||||
type: string
|
||||
secretsScope:
|
||||
properties:
|
||||
envSlug:
|
||||
type: string
|
||||
projectSlug:
|
||||
type: string
|
||||
recursive:
|
||||
type: boolean
|
||||
secretsPath:
|
||||
type: string
|
||||
required:
|
||||
- envSlug
|
||||
- projectSlug
|
||||
- secretsPath
|
||||
type: object
|
||||
required:
|
||||
- identityId
|
||||
- secretsScope
|
||||
type: object
|
||||
gcpIamAuth:
|
||||
properties:
|
||||
identityId:
|
||||
type: string
|
||||
secretsScope:
|
||||
properties:
|
||||
envSlug:
|
||||
type: string
|
||||
projectSlug:
|
||||
type: string
|
||||
recursive:
|
||||
type: boolean
|
||||
secretsPath:
|
||||
type: string
|
||||
required:
|
||||
- envSlug
|
||||
- projectSlug
|
||||
- secretsPath
|
||||
type: object
|
||||
serviceAccountKeyFilePath:
|
||||
type: string
|
||||
required:
|
||||
- identityId
|
||||
- secretsScope
|
||||
- serviceAccountKeyFilePath
|
||||
type: object
|
||||
gcpIdTokenAuth:
|
||||
properties:
|
||||
identityId:
|
||||
type: string
|
||||
secretsScope:
|
||||
properties:
|
||||
envSlug:
|
||||
type: string
|
||||
projectSlug:
|
||||
type: string
|
||||
recursive:
|
||||
type: boolean
|
||||
secretsPath:
|
||||
type: string
|
||||
required:
|
||||
- envSlug
|
||||
- projectSlug
|
||||
- secretsPath
|
||||
type: object
|
||||
required:
|
||||
- identityId
|
||||
- secretsScope
|
||||
type: object
|
||||
kubernetesAuth:
|
||||
properties:
|
||||
autoCreateServiceAccountToken:
|
||||
description: |-
|
||||
Optionally automatically create a service account token for the configured service account.
|
||||
If this is set to `true`, the operator will automatically create a service account token for the configured service account.
|
||||
type: boolean
|
||||
identityId:
|
||||
type: string
|
||||
secretsScope:
|
||||
properties:
|
||||
envSlug:
|
||||
type: string
|
||||
projectSlug:
|
||||
type: string
|
||||
recursive:
|
||||
type: boolean
|
||||
secretsPath:
|
||||
type: string
|
||||
required:
|
||||
- envSlug
|
||||
- projectSlug
|
||||
- secretsPath
|
||||
type: object
|
||||
serviceAccountRef:
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- namespace
|
||||
type: object
|
||||
serviceAccountTokenAudiences:
|
||||
description: The audiences to use for the service account
|
||||
token. This is only relevant if `autoCreateServiceAccountToken`
|
||||
is true.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- identityId
|
||||
- secretsScope
|
||||
- serviceAccountRef
|
||||
type: object
|
||||
ldapAuth:
|
||||
properties:
|
||||
credentialsRef:
|
||||
properties:
|
||||
secretName:
|
||||
description: The name of the Kubernetes Secret
|
||||
type: string
|
||||
secretNamespace:
|
||||
description: The name space where the Kubernetes Secret
|
||||
is located
|
||||
type: string
|
||||
required:
|
||||
- secretName
|
||||
- secretNamespace
|
||||
type: object
|
||||
identityId:
|
||||
type: string
|
||||
secretsScope:
|
||||
properties:
|
||||
envSlug:
|
||||
type: string
|
||||
projectSlug:
|
||||
type: string
|
||||
recursive:
|
||||
type: boolean
|
||||
secretsPath:
|
||||
type: string
|
||||
required:
|
||||
- envSlug
|
||||
- projectSlug
|
||||
- secretsPath
|
||||
type: object
|
||||
required:
|
||||
- credentialsRef
|
||||
- identityId
|
||||
- secretsScope
|
||||
type: object
|
||||
serviceAccount:
|
||||
properties:
|
||||
environmentName:
|
||||
type: string
|
||||
projectId:
|
||||
type: string
|
||||
serviceAccountSecretReference:
|
||||
properties:
|
||||
secretName:
|
||||
description: The name of the Kubernetes Secret
|
||||
type: string
|
||||
secretNamespace:
|
||||
description: The name space where the Kubernetes Secret
|
||||
is located
|
||||
type: string
|
||||
required:
|
||||
- secretName
|
||||
- secretNamespace
|
||||
type: object
|
||||
required:
|
||||
- environmentName
|
||||
- projectId
|
||||
- serviceAccountSecretReference
|
||||
type: object
|
||||
serviceToken:
|
||||
properties:
|
||||
secretsScope:
|
||||
properties:
|
||||
envSlug:
|
||||
type: string
|
||||
recursive:
|
||||
type: boolean
|
||||
secretsPath:
|
||||
type: string
|
||||
required:
|
||||
- envSlug
|
||||
- secretsPath
|
||||
type: object
|
||||
serviceTokenSecretReference:
|
||||
properties:
|
||||
secretName:
|
||||
description: The name of the Kubernetes Secret
|
||||
type: string
|
||||
secretNamespace:
|
||||
description: The name space where the Kubernetes Secret
|
||||
is located
|
||||
type: string
|
||||
required:
|
||||
- secretName
|
||||
- secretNamespace
|
||||
type: object
|
||||
required:
|
||||
- secretsScope
|
||||
- serviceTokenSecretReference
|
||||
type: object
|
||||
universalAuth:
|
||||
properties:
|
||||
credentialsRef:
|
||||
properties:
|
||||
secretName:
|
||||
description: The name of the Kubernetes Secret
|
||||
type: string
|
||||
secretNamespace:
|
||||
description: The name space where the Kubernetes Secret
|
||||
is located
|
||||
type: string
|
||||
required:
|
||||
- secretName
|
||||
- secretNamespace
|
||||
type: object
|
||||
secretsScope:
|
||||
properties:
|
||||
envSlug:
|
||||
type: string
|
||||
projectSlug:
|
||||
type: string
|
||||
recursive:
|
||||
type: boolean
|
||||
secretsPath:
|
||||
type: string
|
||||
required:
|
||||
- envSlug
|
||||
- projectSlug
|
||||
- secretsPath
|
||||
type: object
|
||||
required:
|
||||
- credentialsRef
|
||||
- secretsScope
|
||||
type: object
|
||||
type: object
|
||||
hostAPI:
|
||||
description: Infisical host to pull secrets from
|
||||
type: string
|
||||
instantUpdates:
|
||||
type: boolean
|
||||
managedKubeConfigMapReferences:
|
||||
items:
|
||||
properties:
|
||||
configMapName:
|
||||
description: The name of the Kubernetes ConfigMap
|
||||
type: string
|
||||
configMapNamespace:
|
||||
description: The namespace where the Kubernetes ConfigMap is
|
||||
located
|
||||
type: string
|
||||
creationPolicy:
|
||||
default: Orphan
|
||||
description: |-
|
||||
The Kubernetes ConfigMap creation policy.
|
||||
Enum with values: 'Owner', 'Orphan'.
|
||||
Owner creates the config map and sets .metadata.ownerReferences of the InfisicalSecret CRD that created it.
|
||||
Orphan will not set the config map owner. This will result in the config map being orphaned and not deleted when the resource is deleted.
|
||||
type: string
|
||||
template:
|
||||
description: The template to transform the secret data
|
||||
properties:
|
||||
data:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: The template key values
|
||||
type: object
|
||||
includeAllSecrets:
|
||||
description: |-
|
||||
This injects all retrieved secrets into the top level of your template.
|
||||
Secrets defined in the template will take precedence over the injected ones.
|
||||
type: boolean
|
||||
type: object
|
||||
required:
|
||||
- configMapName
|
||||
- configMapNamespace
|
||||
type: object
|
||||
type: array
|
||||
managedKubeSecretReferences:
|
||||
items:
|
||||
properties:
|
||||
creationPolicy:
|
||||
default: Orphan
|
||||
description: |-
|
||||
The Kubernetes Secret creation policy.
|
||||
Enum with values: 'Owner', 'Orphan'.
|
||||
Owner creates the secret and sets .metadata.ownerReferences of the InfisicalSecret CRD that created it.
|
||||
Orphan will not set the secret owner. This will result in the secret being orphaned and not deleted when the resource is deleted.
|
||||
type: string
|
||||
secretName:
|
||||
description: The name of the Kubernetes Secret
|
||||
type: string
|
||||
secretNamespace:
|
||||
description: The name space where the Kubernetes Secret is located
|
||||
type: string
|
||||
secretType:
|
||||
default: Opaque
|
||||
description: 'The Kubernetes Secret type (experimental feature).
|
||||
More info: https://kubernetes.io/docs/concepts/configuration/secret/#secret-types'
|
||||
type: string
|
||||
template:
|
||||
description: The template to transform the secret data
|
||||
properties:
|
||||
data:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: The template key values
|
||||
type: object
|
||||
includeAllSecrets:
|
||||
description: |-
|
||||
This injects all retrieved secrets into the top level of your template.
|
||||
Secrets defined in the template will take precedence over the injected ones.
|
||||
type: boolean
|
||||
type: object
|
||||
required:
|
||||
- secretName
|
||||
- secretNamespace
|
||||
type: object
|
||||
type: array
|
||||
managedSecretReference:
|
||||
properties:
|
||||
creationPolicy:
|
||||
default: Orphan
|
||||
description: |-
|
||||
The Kubernetes Secret creation policy.
|
||||
Enum with values: 'Owner', 'Orphan'.
|
||||
Owner creates the secret and sets .metadata.ownerReferences of the InfisicalSecret CRD that created it.
|
||||
Orphan will not set the secret owner. This will result in the secret being orphaned and not deleted when the resource is deleted.
|
||||
type: string
|
||||
secretName:
|
||||
description: The name of the Kubernetes Secret
|
||||
type: string
|
||||
secretNamespace:
|
||||
description: The name space where the Kubernetes Secret is located
|
||||
type: string
|
||||
secretType:
|
||||
default: Opaque
|
||||
description: 'The Kubernetes Secret type (experimental feature).
|
||||
More info: https://kubernetes.io/docs/concepts/configuration/secret/#secret-types'
|
||||
type: string
|
||||
template:
|
||||
description: The template to transform the secret data
|
||||
properties:
|
||||
data:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: The template key values
|
||||
type: object
|
||||
includeAllSecrets:
|
||||
description: |-
|
||||
This injects all retrieved secrets into the top level of your template.
|
||||
Secrets defined in the template will take precedence over the injected ones.
|
||||
type: boolean
|
||||
type: object
|
||||
required:
|
||||
- secretName
|
||||
- secretNamespace
|
||||
type: object
|
||||
resyncInterval:
|
||||
default: 60
|
||||
type: integer
|
||||
tls:
|
||||
properties:
|
||||
caRef:
|
||||
description: Reference to secret containing CA cert
|
||||
properties:
|
||||
key:
|
||||
description: The name of the secret property with the CA certificate
|
||||
value
|
||||
type: string
|
||||
secretName:
|
||||
description: The name of the Kubernetes Secret
|
||||
type: string
|
||||
secretNamespace:
|
||||
description: The namespace where the Kubernetes Secret is
|
||||
located
|
||||
type: string
|
||||
required:
|
||||
- key
|
||||
- secretName
|
||||
- secretNamespace
|
||||
type: object
|
||||
type: object
|
||||
tokenSecretReference:
|
||||
properties:
|
||||
secretName:
|
||||
description: The name of the Kubernetes Secret
|
||||
type: string
|
||||
secretNamespace:
|
||||
description: The name space where the Kubernetes Secret is located
|
||||
type: string
|
||||
required:
|
||||
- secretName
|
||||
- secretNamespace
|
||||
type: object
|
||||
required:
|
||||
- resyncInterval
|
||||
type: object
|
||||
status:
|
||||
description: InfisicalSecretStatus defines the observed state of InfisicalSecret
|
||||
properties:
|
||||
conditions:
|
||||
items:
|
||||
description: Condition contains details for one aspect of the current
|
||||
state of this API Resource.
|
||||
properties:
|
||||
lastTransitionTime:
|
||||
description: |-
|
||||
lastTransitionTime is the last time the condition transitioned from one status to another.
|
||||
This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
|
||||
format: date-time
|
||||
type: string
|
||||
message:
|
||||
description: |-
|
||||
message is a human readable message indicating details about the transition.
|
||||
This may be an empty string.
|
||||
maxLength: 32768
|
||||
type: string
|
||||
observedGeneration:
|
||||
description: |-
|
||||
observedGeneration represents the .metadata.generation that the condition was set based upon.
|
||||
For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
|
||||
with respect to the current state of the instance.
|
||||
format: int64
|
||||
minimum: 0
|
||||
type: integer
|
||||
reason:
|
||||
description: |-
|
||||
reason contains a programmatic identifier indicating the reason for the condition's last transition.
|
||||
Producers of specific condition types may define expected values and meanings for this field,
|
||||
and whether the values are considered a guaranteed API.
|
||||
The value should be a CamelCase string.
|
||||
This field may not be empty.
|
||||
maxLength: 1024
|
||||
minLength: 1
|
||||
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
|
||||
type: string
|
||||
status:
|
||||
description: status of the condition, one of True, False, Unknown.
|
||||
enum:
|
||||
- "True"
|
||||
- "False"
|
||||
- Unknown
|
||||
type: string
|
||||
type:
|
||||
description: type of condition in CamelCase or in foo.example.com/CamelCase.
|
||||
maxLength: 316
|
||||
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
|
||||
type: string
|
||||
required:
|
||||
- lastTransitionTime
|
||||
- message
|
||||
- reason
|
||||
- status
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- conditions
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
@@ -1,79 +0,0 @@
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.18.0
|
||||
name: passwords.secrets.infisical.com
|
||||
spec:
|
||||
group: secrets.infisical.com
|
||||
names:
|
||||
kind: Password
|
||||
listKind: PasswordList
|
||||
plural: passwords
|
||||
singular: password
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: |-
|
||||
Password generates a random password based on the
|
||||
configuration parameters in spec.
|
||||
You can specify the length, characterset and other attributes.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
type: string
|
||||
kind:
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: PasswordSpec controls the behavior of the password generator.
|
||||
properties:
|
||||
allowRepeat:
|
||||
default: false
|
||||
description: set allowRepeat to true to allow repeating characters.
|
||||
type: boolean
|
||||
digits:
|
||||
description: |-
|
||||
digits specifies the number of digits in the generated
|
||||
password. If omitted it defaults to 25% of the length of the password
|
||||
type: integer
|
||||
length:
|
||||
default: 24
|
||||
description: |-
|
||||
Length of the password to be generated.
|
||||
Defaults to 24
|
||||
type: integer
|
||||
noUpper:
|
||||
default: false
|
||||
description: Set noUpper to disable uppercase characters
|
||||
type: boolean
|
||||
symbolCharacters:
|
||||
description: |-
|
||||
symbolCharacters specifies the special characters that should be used
|
||||
in the generated password.
|
||||
type: string
|
||||
symbols:
|
||||
description: |-
|
||||
symbols specifies the number of symbol characters in the generated
|
||||
password. If omitted it defaults to 25% of the length of the password
|
||||
type: integer
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
@@ -1,46 +0,0 @@
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.18.0
|
||||
name: uuids.secrets.infisical.com
|
||||
spec:
|
||||
group: secrets.infisical.com
|
||||
names:
|
||||
kind: UUID
|
||||
listKind: UUIDList
|
||||
plural: uuids
|
||||
singular: uuid
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: UUID generates a version 4 UUID (e56657e3-764f-11ef-a397-65231a88c216).
|
||||
properties:
|
||||
apiVersion:
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
type: string
|
||||
kind:
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: UUIDSpec controls the behavior of the uuid generator.
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
@@ -1,24 +0,0 @@
|
||||
# This kustomization.yaml is not intended to be run by itself,
|
||||
# since it depends on service name and namespace that are out of this kustomize package.
|
||||
# It should be run by config/default
|
||||
resources:
|
||||
- bases/secrets.infisical.com_infisicalsecrets.yaml
|
||||
- bases/secrets.infisical.com_infisicalpushsecrets.yaml
|
||||
- bases/secrets.infisical.com_infisicaldynamicsecrets.yaml
|
||||
- bases/secrets.infisical.com_clustergenerators.yaml
|
||||
#+kubebuilder:scaffold:crdkustomizeresource
|
||||
|
||||
patchesStrategicMerge:
|
||||
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix.
|
||||
# patches here are for enabling the conversion webhook for each CRD
|
||||
#- patches/webhook_in_infisicalsecrets.yaml
|
||||
#+kubebuilder:scaffold:crdkustomizewebhookpatch
|
||||
|
||||
# [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix.
|
||||
# patches here are for enabling the CA injection for each CRD
|
||||
#- patches/cainjection_in_infisicalsecrets.yaml
|
||||
#+kubebuilder:scaffold:crdkustomizecainjectionpatch
|
||||
|
||||
# the following config is for teaching kustomize how to do kustomization for CRDs.
|
||||
configurations:
|
||||
- kustomizeconfig.yaml
|
||||
@@ -1,19 +0,0 @@
|
||||
# This file is for teaching kustomize how to substitute name and namespace reference in CRD
|
||||
nameReference:
|
||||
- kind: Service
|
||||
version: v1
|
||||
fieldSpecs:
|
||||
- kind: CustomResourceDefinition
|
||||
version: v1
|
||||
group: apiextensions.k8s.io
|
||||
path: spec/conversion/webhook/clientConfig/service/name
|
||||
|
||||
namespace:
|
||||
- kind: CustomResourceDefinition
|
||||
version: v1
|
||||
group: apiextensions.k8s.io
|
||||
path: spec/conversion/webhook/clientConfig/service/namespace
|
||||
create: false
|
||||
|
||||
varReference:
|
||||
- path: metadata/annotations
|
||||
@@ -1,30 +0,0 @@
|
||||
# This patch adds the args, volumes, and ports to allow the manager to use the metrics-server certs.
|
||||
|
||||
# Add the volumeMount for the metrics-server certs
|
||||
- op: add
|
||||
path: /spec/template/spec/containers/0/volumeMounts/-
|
||||
value:
|
||||
mountPath: /tmp/k8s-metrics-server/metrics-certs
|
||||
name: metrics-certs
|
||||
readOnly: true
|
||||
|
||||
# Add the --metrics-cert-path argument for the metrics server
|
||||
- op: add
|
||||
path: /spec/template/spec/containers/0/args/-
|
||||
value: --metrics-cert-path=/tmp/k8s-metrics-server/metrics-certs
|
||||
|
||||
# Add the metrics-server certs volume configuration
|
||||
- op: add
|
||||
path: /spec/template/spec/volumes/-
|
||||
value:
|
||||
name: metrics-certs
|
||||
secret:
|
||||
secretName: metrics-server-cert
|
||||
optional: false
|
||||
items:
|
||||
- key: ca.crt
|
||||
path: ca.crt
|
||||
- key: tls.crt
|
||||
path: tls.crt
|
||||
- key: tls.key
|
||||
path: tls.key
|
||||
@@ -1,26 +0,0 @@
|
||||
# Adds namespace to all resources.
|
||||
namespace: infisical-operator-system
|
||||
|
||||
# Value of this field is prepended to the
|
||||
# names of all resources, e.g. a deployment named
|
||||
# "wordpress" becomes "alices-wordpress".
|
||||
# Note that it should also match with the prefix (text before '-') of the namespace
|
||||
# field above.
|
||||
namePrefix: infisical-operator-
|
||||
|
||||
# Labels to add to all resources and selectors.
|
||||
#labels:
|
||||
#- includeSelectors: true
|
||||
# pairs:
|
||||
# someName: someValue
|
||||
|
||||
resources:
|
||||
- ../crd
|
||||
- ../rbac
|
||||
- ../manager
|
||||
- metrics_service.yaml
|
||||
|
||||
patches:
|
||||
- path: manager_metrics_patch.yaml
|
||||
target:
|
||||
kind: Deployment
|
||||
@@ -1,4 +0,0 @@
|
||||
# This patch adds the args to allow exposing the metrics endpoint using HTTPS
|
||||
- op: add
|
||||
path: /spec/template/spec/containers/0/args/0
|
||||
value: --metrics-bind-address=:8443
|
||||
@@ -1,17 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
labels:
|
||||
control-plane: controller-manager
|
||||
app.kubernetes.io/name: k8-operator
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: controller-manager-metrics-service
|
||||
namespace: system
|
||||
spec:
|
||||
ports:
|
||||
- name: https
|
||||
port: 8443
|
||||
protocol: TCP
|
||||
targetPort: 8443
|
||||
selector:
|
||||
control-plane: controller-manager
|
||||
@@ -1,8 +0,0 @@
|
||||
resources:
|
||||
- manager.yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
images:
|
||||
- name: controller
|
||||
newName: controller
|
||||
newTag: latest
|
||||
@@ -1,97 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
labels:
|
||||
control-plane: controller-manager
|
||||
app.kubernetes.io/name: k8-operator
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: system
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: controller-manager
|
||||
namespace: system
|
||||
labels:
|
||||
control-plane: controller-manager
|
||||
app.kubernetes.io/name: k8-operator
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
control-plane: controller-manager
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
kubectl.kubernetes.io/default-container: manager
|
||||
labels:
|
||||
control-plane: controller-manager
|
||||
spec:
|
||||
# TODO(user): Uncomment the following code to configure the nodeAffinity expression
|
||||
# according to the platforms which are supported by your solution.
|
||||
# It is considered best practice to support multiple architectures. You can
|
||||
# build your manager image using the makefile target docker-buildx.
|
||||
# affinity:
|
||||
# nodeAffinity:
|
||||
# requiredDuringSchedulingIgnoredDuringExecution:
|
||||
# nodeSelectorTerms:
|
||||
# - matchExpressions:
|
||||
# - key: kubernetes.io/arch
|
||||
# operator: In
|
||||
# values:
|
||||
# - amd64
|
||||
# - arm64
|
||||
# - ppc64le
|
||||
# - s390x
|
||||
# - key: kubernetes.io/os
|
||||
# operator: In
|
||||
# values:
|
||||
# - linux
|
||||
securityContext:
|
||||
# Projects are configured by default to adhere to the "restricted" Pod Security Standards.
|
||||
# This ensures that deployments meet the highest security requirements for Kubernetes.
|
||||
# For more details, see: https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted
|
||||
runAsNonRoot: true
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
containers:
|
||||
- command:
|
||||
- /manager
|
||||
args:
|
||||
- --leader-elect
|
||||
- --health-probe-bind-address=:8081
|
||||
image: controller:latest
|
||||
name: manager
|
||||
ports: []
|
||||
securityContext:
|
||||
readOnlyRootFilesystem: true
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop:
|
||||
- "ALL"
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 8081
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 20
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /readyz
|
||||
port: 8081
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
# TODO(user): Configure the resources accordingly based on the project requirements.
|
||||
# More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
|
||||
resources:
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 128Mi
|
||||
requests:
|
||||
cpu: 10m
|
||||
memory: 64Mi
|
||||
volumeMounts: []
|
||||
volumes: []
|
||||
serviceAccountName: controller-manager
|
||||
terminationGracePeriodSeconds: 10
|
||||
@@ -1,27 +0,0 @@
|
||||
# This NetworkPolicy allows ingress traffic
|
||||
# with Pods running on namespaces labeled with 'metrics: enabled'. Only Pods on those
|
||||
# namespaces are able to gather data from the metrics endpoint.
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: k8-operator
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: allow-metrics-traffic
|
||||
namespace: system
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
control-plane: controller-manager
|
||||
app.kubernetes.io/name: k8-operator
|
||||
policyTypes:
|
||||
- Ingress
|
||||
ingress:
|
||||
# This allows ingress traffic from any namespace with the label metrics: enabled
|
||||
- from:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
metrics: enabled # Only from namespaces with this label
|
||||
ports:
|
||||
- port: 8443
|
||||
protocol: TCP
|
||||
@@ -1,2 +0,0 @@
|
||||
resources:
|
||||
- allow-metrics-traffic.yaml
|
||||
@@ -1,11 +0,0 @@
|
||||
resources:
|
||||
- monitor.yaml
|
||||
|
||||
# [PROMETHEUS-WITH-CERTS] The following patch configures the ServiceMonitor in ../prometheus
|
||||
# to securely reference certificates created and managed by cert-manager.
|
||||
# Additionally, ensure that you uncomment the [METRICS WITH CERTMANAGER] patch under config/default/kustomization.yaml
|
||||
# to mount the "metrics-server-cert" secret in the Manager Deployment.
|
||||
#patches:
|
||||
# - path: monitor_tls_patch.yaml
|
||||
# target:
|
||||
# kind: ServiceMonitor
|
||||
@@ -1,27 +0,0 @@
|
||||
# Prometheus Monitor Service (Metrics)
|
||||
apiVersion: monitoring.coreos.com/v1
|
||||
kind: ServiceMonitor
|
||||
metadata:
|
||||
labels:
|
||||
control-plane: controller-manager
|
||||
app.kubernetes.io/name: k8-operator
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: controller-manager-metrics-monitor
|
||||
namespace: system
|
||||
spec:
|
||||
endpoints:
|
||||
- path: /metrics
|
||||
port: https # Ensure this is the name of the port that exposes HTTPS metrics
|
||||
scheme: https
|
||||
bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
|
||||
tlsConfig:
|
||||
# TODO(user): The option insecureSkipVerify: true is not recommended for production since it disables
|
||||
# certificate verification, exposing the system to potential man-in-the-middle attacks.
|
||||
# For production environments, it is recommended to use cert-manager for automatic TLS certificate management.
|
||||
# To apply this configuration, enable cert-manager and use the patch located at config/prometheus/servicemonitor_tls_patch.yaml,
|
||||
# which securely references the certificate from the 'metrics-server-cert' secret.
|
||||
insecureSkipVerify: true
|
||||
selector:
|
||||
matchLabels:
|
||||
control-plane: controller-manager
|
||||
app.kubernetes.io/name: k8-operator
|
||||
@@ -1,19 +0,0 @@
|
||||
# Patch for Prometheus ServiceMonitor to enable secure TLS configuration
|
||||
# using certificates managed by cert-manager
|
||||
- op: replace
|
||||
path: /spec/endpoints/0/tlsConfig
|
||||
value:
|
||||
# SERVICE_NAME and SERVICE_NAMESPACE will be substituted by kustomize
|
||||
serverName: SERVICE_NAME.SERVICE_NAMESPACE.svc
|
||||
insecureSkipVerify: false
|
||||
ca:
|
||||
secret:
|
||||
name: metrics-server-cert
|
||||
key: ca.crt
|
||||
cert:
|
||||
secret:
|
||||
name: metrics-server-cert
|
||||
key: tls.crt
|
||||
keySecret:
|
||||
name: metrics-server-cert
|
||||
key: tls.key
|
||||
@@ -1,27 +0,0 @@
|
||||
# This rule is not used by the project k8-operator itself.
|
||||
# It is provided to allow the cluster admin to help manage permissions for users.
|
||||
#
|
||||
# Grants full permissions ('*') over secrets.infisical.com.
|
||||
# This role is intended for users authorized to modify roles and bindings within the cluster,
|
||||
# enabling them to delegate specific permissions to other users or groups as needed.
|
||||
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: k8-operator
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: infisicaldynamicsecret-admin-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- secrets.infisical.com
|
||||
resources:
|
||||
- infisicaldynamicsecrets
|
||||
verbs:
|
||||
- '*'
|
||||
- apiGroups:
|
||||
- secrets.infisical.com
|
||||
resources:
|
||||
- infisicaldynamicsecrets/status
|
||||
verbs:
|
||||
- get
|
||||
@@ -1,33 +0,0 @@
|
||||
# This rule is not used by the project k8-operator itself.
|
||||
# It is provided to allow the cluster admin to help manage permissions for users.
|
||||
#
|
||||
# Grants permissions to create, update, and delete resources within the secrets.infisical.com.
|
||||
# This role is intended for users who need to manage these resources
|
||||
# but should not control RBAC or manage permissions for others.
|
||||
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: k8-operator
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: infisicaldynamicsecret-editor-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- secrets.infisical.com
|
||||
resources:
|
||||
- infisicaldynamicsecrets
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- secrets.infisical.com
|
||||
resources:
|
||||
- infisicaldynamicsecrets/status
|
||||
verbs:
|
||||
- get
|
||||
@@ -1,29 +0,0 @@
|
||||
# This rule is not used by the project k8-operator itself.
|
||||
# It is provided to allow the cluster admin to help manage permissions for users.
|
||||
#
|
||||
# Grants read-only access to secrets.infisical.com resources.
|
||||
# This role is intended for users who need visibility into these resources
|
||||
# without permissions to modify them. It is ideal for monitoring purposes and limited-access viewing.
|
||||
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: k8-operator
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: infisicaldynamicsecret-viewer-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- secrets.infisical.com
|
||||
resources:
|
||||
- infisicaldynamicsecrets
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- secrets.infisical.com
|
||||
resources:
|
||||
- infisicaldynamicsecrets/status
|
||||
verbs:
|
||||
- get
|
||||
@@ -1,27 +0,0 @@
|
||||
# This rule is not used by the project k8-operator itself.
|
||||
# It is provided to allow the cluster admin to help manage permissions for users.
|
||||
#
|
||||
# Grants full permissions ('*') over secrets.infisical.com.
|
||||
# This role is intended for users authorized to modify roles and bindings within the cluster,
|
||||
# enabling them to delegate specific permissions to other users or groups as needed.
|
||||
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: k8-operator
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: infisicalpushsecretsecret-admin-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- secrets.infisical.com
|
||||
resources:
|
||||
- infisicalpushsecretsecrets
|
||||
verbs:
|
||||
- '*'
|
||||
- apiGroups:
|
||||
- secrets.infisical.com
|
||||
resources:
|
||||
- infisicalpushsecretsecrets/status
|
||||
verbs:
|
||||
- get
|
||||
@@ -1,33 +0,0 @@
|
||||
# This rule is not used by the project k8-operator itself.
|
||||
# It is provided to allow the cluster admin to help manage permissions for users.
|
||||
#
|
||||
# Grants permissions to create, update, and delete resources within the secrets.infisical.com.
|
||||
# This role is intended for users who need to manage these resources
|
||||
# but should not control RBAC or manage permissions for others.
|
||||
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: k8-operator
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: infisicalpushsecretsecret-editor-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- secrets.infisical.com
|
||||
resources:
|
||||
- infisicalpushsecretsecrets
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- secrets.infisical.com
|
||||
resources:
|
||||
- infisicalpushsecretsecrets/status
|
||||
verbs:
|
||||
- get
|
||||
@@ -1,29 +0,0 @@
|
||||
# This rule is not used by the project k8-operator itself.
|
||||
# It is provided to allow the cluster admin to help manage permissions for users.
|
||||
#
|
||||
# Grants read-only access to secrets.infisical.com resources.
|
||||
# This role is intended for users who need visibility into these resources
|
||||
# without permissions to modify them. It is ideal for monitoring purposes and limited-access viewing.
|
||||
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: k8-operator
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: infisicalpushsecretsecret-viewer-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- secrets.infisical.com
|
||||
resources:
|
||||
- infisicalpushsecretsecrets
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- secrets.infisical.com
|
||||
resources:
|
||||
- infisicalpushsecretsecrets/status
|
||||
verbs:
|
||||
- get
|
||||
@@ -1,27 +0,0 @@
|
||||
# This rule is not used by the project k8-operator itself.
|
||||
# It is provided to allow the cluster admin to help manage permissions for users.
|
||||
#
|
||||
# Grants full permissions ('*') over secrets.infisical.com.
|
||||
# This role is intended for users authorized to modify roles and bindings within the cluster,
|
||||
# enabling them to delegate specific permissions to other users or groups as needed.
|
||||
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: k8-operator
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: infisicalsecret-admin-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- secrets.infisical.com
|
||||
resources:
|
||||
- infisicalsecrets
|
||||
verbs:
|
||||
- '*'
|
||||
- apiGroups:
|
||||
- secrets.infisical.com
|
||||
resources:
|
||||
- infisicalsecrets/status
|
||||
verbs:
|
||||
- get
|
||||
@@ -1,33 +0,0 @@
|
||||
# This rule is not used by the project k8-operator itself.
|
||||
# It is provided to allow the cluster admin to help manage permissions for users.
|
||||
#
|
||||
# Grants permissions to create, update, and delete resources within the secrets.infisical.com.
|
||||
# This role is intended for users who need to manage these resources
|
||||
# but should not control RBAC or manage permissions for others.
|
||||
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: k8-operator
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: infisicalsecret-editor-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- secrets.infisical.com
|
||||
resources:
|
||||
- infisicalsecrets
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- secrets.infisical.com
|
||||
resources:
|
||||
- infisicalsecrets/status
|
||||
verbs:
|
||||
- get
|
||||
@@ -1,29 +0,0 @@
|
||||
# This rule is not used by the project k8-operator itself.
|
||||
# It is provided to allow the cluster admin to help manage permissions for users.
|
||||
#
|
||||
# Grants read-only access to secrets.infisical.com resources.
|
||||
# This role is intended for users who need visibility into these resources
|
||||
# without permissions to modify them. It is ideal for monitoring purposes and limited-access viewing.
|
||||
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: k8-operator
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: infisicalsecret-viewer-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- secrets.infisical.com
|
||||
resources:
|
||||
- infisicalsecrets
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- secrets.infisical.com
|
||||
resources:
|
||||
- infisicalsecrets/status
|
||||
verbs:
|
||||
- get
|
||||
@@ -1,34 +0,0 @@
|
||||
resources:
|
||||
# All RBAC will be applied under this service account in
|
||||
# the deployment namespace. You may comment out this resource
|
||||
# if your manager will use a service account that exists at
|
||||
# runtime. Be sure to update RoleBinding and ClusterRoleBinding
|
||||
# subjects if changing service account names.
|
||||
- service_account.yaml
|
||||
- role.yaml
|
||||
- role_binding.yaml
|
||||
- leader_election_role.yaml
|
||||
- leader_election_role_binding.yaml
|
||||
# The following RBAC configurations are used to protect
|
||||
# the metrics endpoint with authn/authz. These configurations
|
||||
# ensure that only authorized users and service accounts
|
||||
# can access the metrics endpoint. Comment the following
|
||||
# permissions if you want to disable this protection.
|
||||
# More info: https://book.kubebuilder.io/reference/metrics.html
|
||||
- metrics_auth_role.yaml
|
||||
- metrics_auth_role_binding.yaml
|
||||
- metrics_reader_role.yaml
|
||||
# For each CRD, "Admin", "Editor" and "Viewer" roles are scaffolded by
|
||||
# default, aiding admins in cluster management. Those roles are
|
||||
# not used by the k8-operator itself. You can comment the following lines
|
||||
# if you do not want those helpers be installed with your Project.
|
||||
# - infisicaldynamicsecret_admin_role.yaml
|
||||
# - infisicaldynamicsecret_editor_role.yaml
|
||||
# - infisicaldynamicsecret_viewer_role.yaml
|
||||
# - infisicalpushsecret_admin_role.yaml
|
||||
# - infisicalpushsecret_editor_role.yaml
|
||||
# - infisicalpushsecret_viewer_role.yaml
|
||||
# - infisicalsecret_admin_role.yaml
|
||||
# - infisicalsecret_editor_role.yaml
|
||||
# - infisicalsecret_viewer_role.yaml
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
# permissions to do leader election.
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: k8-operator
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: leader-election-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- configmaps
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- create
|
||||
- update
|
||||
- patch
|
||||
- delete
|
||||
- apiGroups:
|
||||
- coordination.k8s.io
|
||||
resources:
|
||||
- leases
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- create
|
||||
- update
|
||||
- patch
|
||||
- delete
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- events
|
||||
verbs:
|
||||
- create
|
||||
- patch
|
||||
@@ -1,15 +0,0 @@
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: k8-operator
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: leader-election-rolebinding
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: leader-election-role
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: controller-manager
|
||||
namespace: system
|
||||
@@ -1,17 +0,0 @@
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: metrics-auth-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- authentication.k8s.io
|
||||
resources:
|
||||
- tokenreviews
|
||||
verbs:
|
||||
- create
|
||||
- apiGroups:
|
||||
- authorization.k8s.io
|
||||
resources:
|
||||
- subjectaccessreviews
|
||||
verbs:
|
||||
- create
|
||||
@@ -1,12 +0,0 @@
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: metrics-auth-rolebinding
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: metrics-auth-role
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: controller-manager
|
||||
namespace: system
|
||||
@@ -1,9 +0,0 @@
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: metrics-reader
|
||||
rules:
|
||||
- nonResourceURLs:
|
||||
- "/metrics"
|
||||
verbs:
|
||||
- get
|
||||
@@ -1,89 +0,0 @@
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: manager-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- configmaps
|
||||
- secrets
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- pods
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- serviceaccounts
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- serviceaccounts/token
|
||||
verbs:
|
||||
- create
|
||||
- apiGroups:
|
||||
- apps
|
||||
resources:
|
||||
- daemonsets
|
||||
- deployments
|
||||
- statefulsets
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- authentication.k8s.io
|
||||
resources:
|
||||
- tokenreviews
|
||||
verbs:
|
||||
- create
|
||||
- apiGroups:
|
||||
- secrets.infisical.com
|
||||
resources:
|
||||
- clustergenerators
|
||||
- infisicaldynamicsecrets
|
||||
- infisicalpushsecrets
|
||||
- infisicalsecrets
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- secrets.infisical.com
|
||||
resources:
|
||||
- infisicaldynamicsecrets/finalizers
|
||||
- infisicalpushsecrets/finalizers
|
||||
- infisicalsecrets/finalizers
|
||||
verbs:
|
||||
- update
|
||||
- apiGroups:
|
||||
- secrets.infisical.com
|
||||
resources:
|
||||
- infisicaldynamicsecrets/status
|
||||
- infisicalpushsecrets/status
|
||||
- infisicalsecrets/status
|
||||
verbs:
|
||||
- get
|
||||
- patch
|
||||
- update
|
||||
@@ -1,15 +0,0 @@
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: k8-operator
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: manager-rolebinding
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: manager-role
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: controller-manager
|
||||
namespace: system
|
||||
@@ -1,8 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: k8-operator
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: controller-manager
|
||||
namespace: system
|
||||
@@ -1,27 +0,0 @@
|
||||
apiVersion: secrets.infisical.com/v1alpha1
|
||||
kind: InfisicalDynamicSecret
|
||||
metadata:
|
||||
name: infisicaldynamicsecret-demo
|
||||
spec:
|
||||
hostAPI: http://localhost:8080/api
|
||||
|
||||
dynamicSecret:
|
||||
secretName: dynamic-secret
|
||||
projectId: 5cdc4fec-f541-413c-b0bc-4c15572e421e
|
||||
secretsPath: /
|
||||
environmentSlug: dev
|
||||
|
||||
leaseRevocationPolicy: Revoke # Revoke or None. Revoke will revoke leases created by the operator if the CRD is deleted.
|
||||
leaseTTL: 1m # TTL for the leases created. Must be below 24 hours.
|
||||
|
||||
# Reference to the secret that you want to store the lease credentials in. If a secret with the name specified name does not exist, it will automatically be created.
|
||||
managedSecretReference:
|
||||
secretName: lease
|
||||
secretNamespace: default
|
||||
creationPolicy: Orphan # Orphan or Owner
|
||||
|
||||
authentication:
|
||||
universalAuth:
|
||||
credentialsRef:
|
||||
secretName: universal-auth-credentials # universal-auth-credentials
|
||||
secretNamespace: default # default
|
||||
@@ -1,112 +0,0 @@
|
||||
apiVersion: secrets.infisical.com/v1alpha1
|
||||
kind: InfisicalSecret
|
||||
metadata:
|
||||
name: infisicalsecret-sample
|
||||
labels:
|
||||
label-to-be-passed-to-managed-secret: sample-value
|
||||
annotations:
|
||||
example.com/annotation-to-be-passed-to-managed-secret: "sample-value"
|
||||
spec:
|
||||
hostAPI: https://app.infisical.com/api
|
||||
resyncInterval: 10
|
||||
# tls:
|
||||
# caRef:
|
||||
# secretName: custom-ca-certificate
|
||||
# secretNamespace: default
|
||||
# key: ca.crt
|
||||
authentication:
|
||||
# Make sure to only have 1 authentication method defined, serviceToken/universalAuth.
|
||||
# If you have multiple authentication methods defined, it may cause issues.
|
||||
|
||||
# (Deprecated) Service Token Auth
|
||||
serviceToken:
|
||||
serviceTokenSecretReference:
|
||||
secretName: service-token
|
||||
secretNamespace: default
|
||||
secretsScope:
|
||||
envSlug: <env-slug>
|
||||
secretsPath: <secrets-path>
|
||||
recursive: true
|
||||
|
||||
# Universal Auth
|
||||
universalAuth:
|
||||
secretsScope:
|
||||
projectSlug: new-ob-em
|
||||
envSlug: dev # "dev", "staging", "prod", etc..
|
||||
secretsPath: "/" # Root is "/"
|
||||
recursive: true # Wether or not to use recursive mode (Fetches all secrets in an environment from a given secret path, and all folders inside the path) / defaults to false
|
||||
credentialsRef:
|
||||
secretName: universal-auth-credentials
|
||||
secretNamespace: default
|
||||
|
||||
# Native Kubernetes Auth
|
||||
kubernetesAuth:
|
||||
serviceAccountRef:
|
||||
name: <secret-name>
|
||||
namespace: <secret-namespace>
|
||||
identityId: <machine-identity-id>
|
||||
serviceAccountTokenPath: "/path/to/your/service-account/token" # Optional, defaults to /var/run/secrets/kubernetes.io/serviceaccount/token
|
||||
|
||||
# secretsScope is identical to the secrets scope in the universalAuth field in this sample.
|
||||
secretsScope:
|
||||
projectSlug: your-project-slug
|
||||
envSlug: prod
|
||||
secretsPath: "/path"
|
||||
recursive: true
|
||||
|
||||
# AWS IAM Auth
|
||||
awsIamAuth:
|
||||
identityId: <your-machine-identity-id>
|
||||
|
||||
# secretsScope is identical to the secrets scope in the universalAuth field in this sample.
|
||||
secretsScope:
|
||||
projectSlug: your-project-slug
|
||||
envSlug: prod
|
||||
secretsPath: "/path"
|
||||
recursive: true
|
||||
|
||||
# Azure Auth
|
||||
azureAuth:
|
||||
identityId: <your-machine-identity-id>
|
||||
resource: https://management.azure.com/&client_id=your_client_id # This field is optional, and will default to "https://management.azure.com/" if nothing is provided.
|
||||
|
||||
# secretsScope is identical to the secrets scope in the universalAuth field in this sample.
|
||||
secretsScope:
|
||||
projectSlug: your-project-slug
|
||||
envSlug: prod
|
||||
secretsPath: "/path"
|
||||
recursive: true
|
||||
|
||||
# GCP ID Token Auth
|
||||
gcpIdTokenAuth:
|
||||
identityId: <your-machine-identity-id>
|
||||
|
||||
# secretsScope is identical to the secrets scope in the universalAuth field in this sample.
|
||||
secretsScope:
|
||||
projectSlug: your-project-slug
|
||||
envSlug: prod
|
||||
secretsPath: "/path"
|
||||
recursive: true
|
||||
|
||||
# GCP IAM Auth
|
||||
gcpIamAuth:
|
||||
identityId: <your-machine-identity-id>
|
||||
serviceAccountKeyFilePath: "/path/to-service-account-key-file-path.json"
|
||||
|
||||
# secretsScope is identical to the secrets scope in the universalAuth field in this sample.
|
||||
secretsScope:
|
||||
projectSlug: your-project-slug
|
||||
envSlug: prod
|
||||
secretsPath: "/path"
|
||||
recursive: true
|
||||
|
||||
managedKubeSecretReferences:
|
||||
- secretName: managed-secret
|
||||
secretNamespace: default
|
||||
creationPolicy: "Orphan" ## Owner | Orphan
|
||||
# secretType: kubernetes.io/dockerconfigjson
|
||||
template:
|
||||
includeAllSecrets: true
|
||||
data:
|
||||
SSH_KEY: "{{ .KEY.SecretPath }} {{ .KEY.Value }}"
|
||||
BINARY_KEY: "{{ decodeBase64ToBytes .BINARY_KEY_BASE64.Value }}"
|
||||
@@ -1,107 +0,0 @@
|
||||
apiVersion: secrets.infisical.com/v1alpha1
|
||||
kind: InfisicalSecret
|
||||
metadata:
|
||||
name: infisicalsecret-sample
|
||||
labels:
|
||||
label-to-be-passed-to-managed-secret: sample-value
|
||||
annotations:
|
||||
example.com/annotation-to-be-passed-to-managed-secret: "sample-value"
|
||||
spec:
|
||||
hostAPI: http://localhost:8080/api
|
||||
resyncInterval: 10
|
||||
instantUpdates: false
|
||||
# tls:
|
||||
# caRef:
|
||||
# secretName: custom-ca-certificate
|
||||
# secretNamespace: default
|
||||
# key: ca.crt
|
||||
authentication:
|
||||
# Universal Auth
|
||||
universalAuth:
|
||||
secretsScope:
|
||||
projectSlug: hello-9zkr
|
||||
envSlug: dev # "dev", "staging", "prod", etc..
|
||||
secretsPath: "/" # Root is "/"
|
||||
recursive: true # Wether or not to use recursive mode (Fetches all secrets in an environment from a given secret path, and all folders inside the path) / defaults to false
|
||||
credentialsRef:
|
||||
secretName: universal-auth-credentials
|
||||
secretNamespace: default
|
||||
|
||||
# Native Kubernetes Auth
|
||||
kubernetesAuth:
|
||||
serviceAccountRef:
|
||||
name: <secret-name>
|
||||
namespace: <secret-namespace>
|
||||
identityId: <machine-identity-id>
|
||||
serviceAccountTokenPath: "/path/to/your/service-account/token" # Optional, defaults to /var/run/secrets/kubernetes.io/serviceaccount/token
|
||||
|
||||
# secretsScope is identical to the secrets scope in the universalAuth field in this sample.
|
||||
secretsScope:
|
||||
projectSlug: your-project-slug
|
||||
envSlug: prod
|
||||
secretsPath: "/path"
|
||||
recursive: true
|
||||
|
||||
# AWS IAM Auth
|
||||
awsIamAuth:
|
||||
identityId: <your-machine-identity-id>
|
||||
|
||||
# secretsScope is identical to the secrets scope in the universalAuth field in this sample.
|
||||
secretsScope:
|
||||
projectSlug: your-project-slug
|
||||
envSlug: prod
|
||||
secretsPath: "/path"
|
||||
recursive: true
|
||||
|
||||
ldapAuth:
|
||||
identityId: <machine-identity-id>
|
||||
credentialsRef:
|
||||
secretName: <secret-name> # ldap-auth-credentials
|
||||
secretNamespace: <secret-namespace> # default
|
||||
|
||||
# secretsScope is identical to the secrets scope in the universalAuth field in this sample.
|
||||
secretsScope:
|
||||
projectSlug: your-project-slug
|
||||
envSlug: prod
|
||||
secretsPath: "/path"
|
||||
recursive: true
|
||||
|
||||
# Azure Auth
|
||||
azureAuth:
|
||||
identityId: <your-machine-identity-id>
|
||||
resource: https://management.azure.com/&client_id=your_client_id # This field is optional, and will default to "https://management.azure.com/" if nothing is provided.
|
||||
|
||||
# secretsScope is identical to the secrets scope in the universalAuth field in this sample.
|
||||
secretsScope:
|
||||
projectSlug: your-project-slug
|
||||
envSlug: prod
|
||||
secretsPath: "/path"
|
||||
recursive: true
|
||||
|
||||
# GCP ID Token Auth
|
||||
gcpIdTokenAuth:
|
||||
identityId: <your-machine-identity-id>
|
||||
|
||||
# secretsScope is identical to the secrets scope in the universalAuth field in this sample.
|
||||
secretsScope:
|
||||
projectSlug: your-project-slug
|
||||
envSlug: prod
|
||||
secretsPath: "/path"
|
||||
recursive: true
|
||||
|
||||
# GCP IAM Auth
|
||||
gcpIamAuth:
|
||||
identityId: <your-machine-identity-id>
|
||||
serviceAccountKeyFilePath: "/path/to-service-account-key-file-path.json"
|
||||
|
||||
# secretsScope is identical to the secrets scope in the universalAuth field in this sample.
|
||||
secretsScope:
|
||||
projectSlug: your-project-slug
|
||||
envSlug: prod
|
||||
secretsPath: "/path"
|
||||
recursive: true
|
||||
|
||||
managedKubeSecretReferences:
|
||||
- secretName: managed-secret
|
||||
secretNamespace: default
|
||||
creationPolicy: "Orphan" ## Owner | Orphan
|
||||
@@ -1,14 +0,0 @@
|
||||
apiVersion: secrets.infisical.com/v1alpha1
|
||||
kind: ClusterGenerator
|
||||
metadata:
|
||||
name: password-generator
|
||||
spec:
|
||||
kind: Password
|
||||
generator:
|
||||
passwordSpec:
|
||||
length: 10
|
||||
digits: 5
|
||||
symbols: 5
|
||||
symbolCharacters: "-_$@"
|
||||
noUpper: false
|
||||
allowRepeat: true
|
||||
@@ -1,91 +0,0 @@
|
||||
apiVersion: secrets.infisical.com/v1alpha1
|
||||
kind: InfisicalPushSecret
|
||||
metadata:
|
||||
name: infisical-api-secret-sample-push
|
||||
spec:
|
||||
resyncInterval: 1m
|
||||
hostAPI: https://app.infisical.com/api # This is the default hostAPI for the Infisical API
|
||||
|
||||
# Optional, defaults to replacement.
|
||||
updatePolicy: Replace # If set to replace, existing secrets inside Infisical will be replaced by the value of the PushSecret on sync.
|
||||
|
||||
# Optional, defaults to no deletion.
|
||||
deletionPolicy: Delete # If set to delete, the secret(s) inside Infisical managed by the operator, will be deleted if the InfisicalPushSecret CRD is deleted.
|
||||
|
||||
destination:
|
||||
projectId: <project-id>
|
||||
environmentSlug: <env-slug>
|
||||
secretsPath: <secret-path>
|
||||
|
||||
push:
|
||||
secret:
|
||||
secretName: push-secret-demo-with-templating
|
||||
secretNamespace: default
|
||||
template:
|
||||
includeAllSecrets: false
|
||||
data:
|
||||
PKCS12_CERT_NO_PASSWORD: "{{ .PKCS12_CONTENT_NO_PASSWORD.Value | decodeBase64ToBytes | pkcs12cert }}"
|
||||
PKCS12_KEY_NO_PASSWORD: "{{ .PKCS12_CONTENT_NO_PASSWORD.Value | decodeBase64ToBytes | pkcs12key }}"
|
||||
|
||||
PKCS12_CERT_WITH_PASSWORD: '{{ .PKCS12_CONTENT_WITH_PASSWORD.Value | decodeBase64ToBytes | pkcs12certPass "123456" }}'
|
||||
PKCS12_KEY_WITH_PASSWORD: '{{ .PKCS12_CONTENT_WITH_PASSWORD.Value | decodeBase64ToBytes | pkcs12keyPass "123456" }}'
|
||||
|
||||
PEM_TO_PKCS12_PASS: '{{ pemToPkcs12Pass
|
||||
(.PKCS12_CONTENT_WITH_PASSWORD.Value | decodeBase64ToBytes | pkcs12certPass "123456")
|
||||
(.PKCS12_CONTENT_WITH_PASSWORD.Value | decodeBase64ToBytes | pkcs12keyPass "123456")
|
||||
"123456" }}'
|
||||
PEM_TO_PKCS12_NO_PASSWORD: "{{ pemToPkcs12
|
||||
(.PKCS12_CONTENT_NO_PASSWORD.Value | decodeBase64ToBytes | pkcs12cert)
|
||||
(.PKCS12_CONTENT_NO_PASSWORD.Value | decodeBase64ToBytes | pkcs12key)
|
||||
}}"
|
||||
|
||||
FULL_PEM_TO_PKCS12_PASS: '{{ fullPemToPkcs12Pass
|
||||
(.PKCS12_CONTENT_WITH_PASSWORD.Value | decodeBase64ToBytes | pkcs12certPass "123456")
|
||||
(.PKCS12_CONTENT_WITH_PASSWORD.Value | decodeBase64ToBytes | pkcs12keyPass "123456")
|
||||
"123456" }}'
|
||||
FULL_PEM_TO_PKCS12_NO_PASSWORD: "{{ fullPemToPkcs12
|
||||
(.PKCS12_CONTENT_NO_PASSWORD.Value | decodeBase64ToBytes | pkcs12cert)
|
||||
(.PKCS12_CONTENT_NO_PASSWORD.Value | decodeBase64ToBytes | pkcs12key)
|
||||
}}"
|
||||
|
||||
FILTERED_PEM_CERT: '{{ filterPEM "CERTIFICATE" (printf "-----BEGIN CERTIFICATE-----\n%s\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\n%s\n-----END CERTIFICATE-----" .JWK_PRIVATE_RSA_PKCS8.Value .PKCS12_CONTENT_NO_PASSWORD.Value) }}'
|
||||
FILTERED_PEM_KEY: '{{ filterPEM "PRIVATE KEY" (printf "-----BEGIN PRIVATE KEY-----\n%s\n-----END PRIVATE KEY-----\n-----BEGIN PRIVATE KEY-----\n%s\n-----END PRIVATE KEY-----" .JWK_PRIVATE_RSA_PKCS8.Value .PKCS12_CONTENT_NO_PASSWORD.Value) }}'
|
||||
|
||||
# Will be empty with our current test data as there is no chain
|
||||
CERT_CHAIN: '{{ filterCertChain "CERTIFICATE" (.PKCS12_CONTENT_NO_PASSWORD.Value | decodeBase64ToBytes | pkcs12cert) }}'
|
||||
|
||||
JWK_RSA_PUBLIC_PEM: "{{ jwkPublicKeyPem .JWK_PUB_RSA.Value }}"
|
||||
JWK_ECDSA_PUBLIC_PEM: "{{ jwkPublicKeyPem .JWK_PUB_ECDSA.Value }}"
|
||||
|
||||
JWK_ECDSA_PRIVATE_PEM: "{{ jwkPrivateKeyPem .JWK_PRIV_ECDSA.Value }}"
|
||||
JWK_RSA_PRIVATE_PEM: "{{ jwkPrivateKeyPem .JWK_PRIV_RSA.Value }}"
|
||||
|
||||
JSON_STR_TO_YAML: "{{ .TEST_JSON_DATA.Value | fromJsonStringToJson | toYaml }}"
|
||||
|
||||
FROM_YAML_TO_JSON: "{{ .TEST_YAML_STRING.Value | fromYaml | toJson }}"
|
||||
YAML_ROUNDTRIP: "{{ .TEST_YAML_STRING.Value | fromYaml | toYaml }}"
|
||||
|
||||
TEST_LOWERCASE_STRING: "{{ .TEST_LOWERCASE_STRING.Value | upper }}"
|
||||
|
||||
# Only have one authentication method defined or you are likely to run into authentication issues.
|
||||
# Remove all except one authentication method.
|
||||
authentication:
|
||||
awsIamAuth:
|
||||
identityId: <machine-identity-id>
|
||||
azureAuth:
|
||||
identityId: <machine-identity-id>
|
||||
gcpIamAuth:
|
||||
identityId: <machine-identity-id>
|
||||
serviceAccountKeyFilePath: </path-to-service-account-key-file.json>
|
||||
gcpIdTokenAuth:
|
||||
identityId: <machine-identity-id>
|
||||
kubernetesAuth:
|
||||
identityId: <machine-identity-id>
|
||||
serviceAccountRef:
|
||||
name: <secret-name>
|
||||
namespace: <secret-namespace>
|
||||
universalAuth:
|
||||
credentialsRef:
|
||||
secretName: <secret-name> # universal-auth-credentials
|
||||
secretNamespace: <secret-namespace> # default
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
apiVersion: secrets.infisical.com/v1alpha1
|
||||
kind: InfisicalPushSecret
|
||||
metadata:
|
||||
name: infisical-api-secret-sample-push
|
||||
spec:
|
||||
resyncInterval: 1m
|
||||
hostAPI: http://localhost:8080/api
|
||||
|
||||
# Optional, defaults to replacement.
|
||||
updatePolicy: Replace # If set to replace, existing secrets inside Infisical will be replaced by the value of the PushSecret on sync.
|
||||
|
||||
# Optional, defaults to no deletion.
|
||||
deletionPolicy: Delete # If set to delete, the secret(s) inside Infisical managed by the operator, will be deleted if the InfisicalPushSecret CRD is deleted.
|
||||
|
||||
destination:
|
||||
projectId: 5cdc4fec-f541-413c-b0bc-4c15572e421e
|
||||
environmentSlug: dev
|
||||
secretsPath: /
|
||||
|
||||
push:
|
||||
secret:
|
||||
secretName: push-secret-source-secret # Secret CRD
|
||||
secretNamespace: default
|
||||
|
||||
# Only have one authentication method defined or you are likely to run into authentication issues.
|
||||
# Remove all except one authentication method.
|
||||
authentication:
|
||||
universalAuth:
|
||||
credentialsRef:
|
||||
secretName: universal-auth-credentials # universal-auth-credentials
|
||||
secretNamespace: default # default
|
||||
@@ -1,91 +0,0 @@
|
||||
# This is the source secret that you can use to demo the advanced templating functionality seen in `push-secret-with-template.yaml`
|
||||
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: push-secret-demo-with-templating
|
||||
namespace: default
|
||||
stringData:
|
||||
PKCS12_CONTENT_NO_PASSWORD: MIIJYQIBAzCCCScGCSqGSIb3DQEHAaCCCRgEggkUMIIJEDCCA8cGCSqGSIb3DQEHBqCCA7gwggO0AgEAMIIDrQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQInZmyWpNTPS4CAggAgIIDgPzZTmogBRiLP0NJZEUghZ3Oh1aqHJJ32HKgXUpD5BJ/5AvpUL9FC7m6a3GD++P1On/35J9N50bDjfBJjJrl2zpA143bzltPQBOK30cBJjNsCeN2Dq1dcsvJZfEy20z75NduXjMF6/qs4BbE+1E6nYFYVNHUybFnaQwSx7+2/2OMbXbcFpt4bv3HTw0YLw2pZeW/4/4A9d+tC9UdVQTTyNbI8l9nf1aeaaPsw1keVLmHurmTihfwh469FvjgwiHUP/P3ZCn1tOpWDR8ck0j+ru6imVP2hn+Kvk6svllmYqo3A5DnDRoF/Cl9R0DAPyS0lw7BeGskgTm7B79mzVitTbzRnIUP+sGJjc1AVghnitfcX4ffv8gq5xWaKGucO/IZXbPBoe7tMhKZmsirKzD4RBhC3nMyrwaHJB6PqUwxMQGMLbuHe7GlWhJAyFlcOTt5dgNl+axIkWdisoKNinYYeOuxudqyX6yPfsyaRCV5MEez3Wu+59MENGlGDRWbw61QuwsZkr1bAT2SJrQ/zHn5aGAluQZ1csJhKQ34iy1Ml9K9F4Zh3/2OWPs0u6+JCb1PC1vChBkguqcqQtEcikRwR9dNF9cdMB1T1Xk5GqlmOPaigkYzGWLgtl8cV5/Zl0m2j77mX9x4HVCTercAABGf9JcCLzSCo04c5OwIYtWUXBkux5n2VI2ZIuS1KF+r6JNyL3lg/D8LColzDUP/6tQCBVVgMar3iLblM17wPMTDMR5Bn+NvenwJj6FWaGGMtdjygtN+oSHpNDbVygfGQy+jEgUtK7yw0uh/WKBMWVw1E6iNuhb8HIyCFtQon8sDkuZ81czOpR3Ta1SWUWrZD+pjpL2Z4y8Nc2wt9pVPvLFOTn+GDFVqGpde3kovh3GfJjYCG/HI5rXZyziflDOoSy0SyG6aVCG4ZqW2LTymoVN/kxf+skqAweX1vxvvJniiv8HgYfEASFUWear4uT641d1YwcEIawNv4n+GKBilK/7ODl2QL86svwqIcbyiJrneyU2tHymKzGcU2VxmSgf8EnjqGuIEo7WXOpk0oUMcvYrM73cgzZ3BchUDIN0KWSDI+vDcVY82dbI39KM6dtOJFAx3kEdms/gdSqZtmHUIeArGp+8caCCAK/W+4wTOvtisK+6MtzdMz6P93N78N4Vo6cs3dkj6t/6tgNog5SCfwlOEyUpmMIIFQQYJKoZIhvcNAQcBoIIFMgSCBS4wggUqMIIFJgYLKoZIhvcNAQwKAQKgggTuMIIE6jAcBgoqhkiG9w0BDAEDMA4ECHVnarQ94cqlAgIIAASCBMgUvEVKsUcqEvYJEJ9JixgB0W3uhSi/Espt931a/mwx5Ja2K7vjlttaOct3Zc8umVrP5C322tmHz9QDVPj3Bln8CGfofC/8Nb6+SDeofmYaQYReOZpZGksEBs4P3yURl8wQpIkG31Oyf3urDTJdplfDrzu6XpEpIf7RicIR+Zh4Q1+F75XwPo52/yNs8q/kVV8H97gSRqQ2GixIdyNu+JLtNjdwAERHy4DeQjwgiMCdL+xMfN+WJyIvkLZDoy9bacXeG4IcQM+n84272C6j1a0BPaOm0K5A7I0H1zpXOJiWfn3MrT4LHDudrQoIWUOvcJjWaIM/KyghotDN50THKN9qCEE9SmtfWXGGFaJmyxbUDFizBIAsFshNtMs/47PoInTSNwzxNvUUQ3ap93iquGZ9EaZAMY2HQHW/QJIQ70IbtcHU28Bus/hrMcV0X9D1p4UeHuk37W7aCrL6hS+ac9pmzwmcDBwZUliyInxRmqCCerjg2ojAM9SVg8FrpQUErP+BOaoCBwQqLLiz9BM+3tUQc/8MyaBHq+c2dUoPfvipDIQXYiq66CkjmPHxPFEL1l9d9oBFoIGkt6SIHDjWnTPc5q5SvJ9tz8Dp1k/1HQSA8OUS6j+XySYuGe8xTvN/oUpVRswef2Qd/kxZlc1FJ4lVAXvbW7C7772l14BJv/WULcFH4Sn83rlL3YwHr4vJMf6wLahn7oQPI0VFSQiiOOb/+gkiTrwO3Gz+HXOkUwaKnW85PeoIt3/q1u0CRl64mUjqCegi7RMY9Q9tRMlD5yx0RsH7mc4b6Eg/3IwGu8VQmZCO5W2unCpfzzyrOx7OaGGaW4RJ2Mx7bJ8uV9HU8MbbNntmc9oxebPdDnBmbt8p8t4ZZxC+zcqcXi3TxACXmwnasogQEi0d0ttXkB5cnDCG00Y8WPdNIWfJdIQh8Hj16LAMYWUacz/J0kLP99ENQntZibVw/Q3zZtHSF5tmsYp7o1HglBpRwLTcd026YTrxB+VCEiUYy4hH6a38oEEpY7wTIiRmEBQPIRM0HUOqVh4z6TNzRx6iIhrQEvg06B8U6iVPqy8FGDkhf3P55Ed95/Rw6uSdlMTHng+Q4aG00k4qKdKOyv55IXPcvEzAeVNBuesknaS8x7Eb/I5mHSoZU3RYAEFGbehUkvkhNr3Xq7/W/400AKiliravJq8j/qKIZ9hAVUWOps09F/4peYfLXM1AhxWWGa5QqvwFkClM+uRyqIRGJwl2Z7asl4sWVXbwtb+Axio+mYGdzxIki5iwJvRCwKapoZplndXKTrn2nYBuhxW2+fRHa8WYdsm/wn0K+jYMlZhquVjNXyL70/Sym6DkzCtJvveQs2CfcEWQuedjRSGFVFT2jV/s5F8L2TV7nQNVj6dEJSNM5JCdZ//OpiMHMCbPNeSxY9koGplUqFhP54F1WU9x+8xiFjEp8WKxQYKHUtj+ace0lLF4CDGXhFR/0k7Icarpax3hYnvagd2OpZyRJdavKBSs5U7/NPuO6sNhZ2NpzsOiul9Iu8bu3UHCECNKkwN4wF4alTlG9sAAbS4ns4wb9XTajG+OPYoDQZmuJfc71McN6m8KBHEnXU8r4epdR7xREe/w+h2MwtPhLvbxwO592tUxJTAjBgkqhkiG9w0BCRUxFgQUOEXV6IFYGpCSHi0MPHz4b3W0KOQwMTAhMAkGBSsOAwIaBQAEFAjyBCA+mr+5UkKuQ1jGw90ASfbVBAjbvqJJZikDPgICCAA=
|
||||
PKCS12_CONTENT_WITH_PASSWORD: MIIJYQIBAzCCCScGCSqGSIb3DQEHAaCCCRgEggkUMIIJEDCCA8cGCSqGSIb3DQEHBqCCA7gwggO0AgEAMIIDrQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQI2eZRJ7Ar+JQCAggAgIIDgFTbOtkFPjqxAoYRHoq1SbyXKf/NRbBA5AqxQlv9aFVT4VcxUSrMGaSWifX2UjsVWQzn134yoLLbdJ0jTorVD+EuBUmtb3xXbBwLqtFZxwcWodYA5WhPQdDcQo0cD3o1vrsXPQARQR6ISSFnhFjPYdH9cO2LqUKV5pjFhIs2/1VPDS2eY7SWZN52DK3QknSj23S3ZW2s4TFEj/5C4ssbO7cWNWBjjaORnd17FMNgVtcRw8ITmLdGBOpFUwP8wIdiLGrXiyjfMLns74nztRelV30/v0DPlz0pZtOPygi/dy0qpbil3wtOFrtQBLEdvLNmt9ikQgGs3pJBS68eMJLu3jAU6rCIKycq0+E0eMXeHcseyMwgguTj2h4t+E4S7nU11lViBFqkSBKxE28+9fNlPvCsZ4WhQZ6TAW3E/jDy/ZSqmak5V7/khMlRPvtrxz71ivksH0iipPdJJkGi7SDEvETySBETiqIslUmsF0ZYeHR5wIBkB5V8zmi8RRZtpvDGbzuQ22V6sNk2mTDh+BRus7gNCoSGWYXWqNNp1PnznuYCJp9T+0mObcAijE7IQuhpYMeQPF+MUIlG5lmpNouzuygTf++xrKIjzP36DcthnMPeD/8LYWfzkuAeRodjl7Z1G6XLvBD5h+Xlq23WPjMcXUiiWYXxTREAQ1EWUf4A9twGcxHJ5AatbvQY3QUoS4a7LNuy17lF7G+O1SFDtGeHZXHHecaVpuAtqZEYeUpqy6ZzMJXtXE1JNl/UR9TtTordb1V5Pf45JTYKLI+SwxVQbRiTgfhulNc+E3tV1AEELZt4CKmh1OFJoJRtyREMfdVuP4rx7ywIoMYuWw8CRqJ3qSmdwz2kmL2pfJNn6vDfN6rNa+sXWErOJ7naSAKQH2CJfnkCOFxkKfwjbOcNRbnGKah8NyWu6xqlv7Y4xEJYqmPahGHmrSfdAt3mpc5zD74DvetLIczcadKaLH7mp6h98xHayvXh0UE7ERHeskfSPrLxL9A3V1RZXDOtcZFWSKHzokuXMDF9OnrcMYDzYgtzof4ReY2t1ldGF7phYINhDlUNyhzyjwyWQbdkxr/+FtWq8Sbm7o2zMTby48c25gnqD9U8RTAO+bY3oV3dQ4bpAOzWdzVmFjESUFx0xVwbTSJGXdkH4YmD5He+xwxTa0Je0HE5+ui5dbP1gxUY+pCGLOhGMIIFQQYJKoZIhvcNAQcBoIIFMgSCBS4wggUqMIIFJgYLKoZIhvcNAQwKAQKgggTuMIIE6jAcBgoqhkiG9w0BDAEDMA4ECGYAccNFWW6kAgIIAASCBMgbXw69iyx73RGWS3FYeuvF5L1VWXQGrDLdUGwa3SdovXxmP1pKBAb82UuiydXDpKSVCmefeEQTs6ucEFRmXrDIldNanqUwbydy3GSJ+4iNsVQHbFgNppH4e90L7BlLcZ3MzSrVEwxWVMHq+XLqTKf3X3QyrmA3mmF96+Nd+icpDIktd+/h2BHnSXHNP0QfVH27H4DwbMDijttXY0JB+8qP9m25Wn4zkmOPEUhrY4Ptv2I08eHFAuNI0jWUwfRhC4FDbUdwFb0aZjA3Te6uYTsu2zAlmg9HuqsD/Ef/wkBEKZLBkjiXa/niFVrwELXhWZDPBAuo+/1UbzXglsW4QDU4LbUutcs6DLag1vLe40a2LO1ODQm7Zw0bxLkb3f/ry6ZFYvO78XmHo4c/oQf4KPUtM2bLz5q7uOxAx07vHYaU2BVt3NjgiIO5VVKjw0075GdgFxwPvYncv1fsC5jSIkX43GuzEtoBTpJKDYb2nhKbN9XWixwGOhUBTK3WYBhn+uaMJs4l3EgkDtK9tsUs5VQQHawj0WrGS1mQhaBfcyZzv4wSn0d3JUO2CN0e9EReJcQvsEnwUvohilOvjDHHhTq8Kp4XU4jbq7TAKqxs3TOmdoskRykn9oKUPExJVhJQonFT3ietV5BHrnN/QoDCSeOR80ZxvWHrQDz3Hm1ygiHd8LYmN4IjiD8b28ZrCALifWxh0WmIYtLZrUjMZavPh+caWH9IG32fTxV9b1bgJD8vWqscj9jCjeMJvkKQo8PFg1kMAxt1u+bIyktTq42O9qxwGrdqEMeBzXxDJMMaRIH3m9LNZ/P5Nk4/hMURhCZJtRtNfOVTK+Q6kKgsdK2EHcuEnp/qBefZjve+xmitbF1W7C4+B7b2JNBacdIm1nE56DwglT/IUk65JrNFP3rf4c5ic76LCQrvyfLiKCGaqcihM9siLVFPYdrnr8TlGbCFnGbpBqMQA5MtZQaDUug50PJtdxlgfwWH4qliimgchCaZbSTcgN5YTguSe16uUSusHD+r6XdtI0939uDILXJjQMczhIKNw8w0Tn4Z3/g2KlB6cwbtaglnnO4a/USh0cPC1a581byNqeFoMi+mAhqfKkwdDuti4GX7OrhkUOkiRjEUXdcckpmmIsyamH/g1dq3CNFXFNIgRRrzIDo4Opr3Ip2VE/4BDQoo/+Rybzxh8bsHgCEujQf8urGxjGyd2ulHoXzHWhz7pPPuY5UN6dC9WZmOQDVous/1nhYThoLVVc61Rk6d83+Ac7iRg4bY5q/73J4HvPMmrTOOOqqn3wc9Pe5ibEy4tFaYnim4p1ZRm8YcwosZmuFPdsP6G5l5qt6uOyr2+qNpXIBkDpG7I6Ls10O7L3PQAX9zRGfcz6Ds0KtuDrLpaVvhuXpewsBwpo1lmhv9bAa4ppBuWznmKigX+vYojSxd/eCRAtMs+Lx6ppZsYNVhbdEIGKXSGwG98sSTZkoLHBMkUW7S8jpeSCHZWEFBUOPJQzAr5cW1w+RAs33cGUygZ5XEEx4DeW8MnO4lCuP+VDOwu3TAKhzAD+qCyXbLEzWiyL5fq3XL+YJtoAc8Mra9lK6jDqzq4u+PLNoYY+kWTBhCyRZ+PfzcXLry8pxuP5E6VtRgfYcxJTAjBgkqhkiG9w0BCRUxFgQUOEXV6IFYGpCSHi0MPHz4b3W0KOQwMTAhMAkGBSsOAwIaBQAEFBa+SV9FU2UObo+nYKdyt/kZVw6FBAgey4GonFtJ2gICCAA=
|
||||
JWK_PRIVATE_RSA_PKCS8: "-----BEGIN PRIVATE KEY-----
|
||||
MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQCmN2yzxloN8Qfo
|
||||
rpTsZ5bafEOpHgg/Tj1+TV8rSWd2KZswxUF0+/+FKmbxPwS0EPGtR2LU4dl8yFSL
|
||||
EZq637edDgYb2czbj2jGEK3Gqo28ReuZBEapzPIvG6H58qf0WD76FL1SlrMel9UA
|
||||
WcHloJ9eg2E+4jygHLIUowpo5WAc2o/k0ESppuIt+1kPdb+WwUI8a7OvhWnRhLvN
|
||||
LaENhJwLag4y7isZTUtwxl/f2nfXncKrttLZeHpj6/DmnDMVhl2NDEOfzHwEbd8n
|
||||
qPxMYtdCxsofXbXz8dxQlG8zB2ltRAbme8DYZdWoup3CnTngvOT38H9/WVWuY4q4
|
||||
eNM0erjzAgMBAAECggEBAJLA5rnHTCV5BRmcYqJjR566DmcXvAJgywxjtb4bPjzm
|
||||
uT2TO5rVD6J8cI1ZrYZqW2c5WvpIOeThXzu2HF4YPh5tjlkysJu9/6y4dyWr2h47
|
||||
warFSrqK191d0WJEq6Oh8mCMxSdRJO7C8W4w0XAzo+Inr0l9KDfZfiWYWg2JT5XI
|
||||
ubibKKq6P2KxND0UVlYbRsp3fv2loEL9WM5H2bjA/oSbQ4tSJtobpjlsQOHmaxbP
|
||||
XhvsIV3Dr2ksDuLEhm0vfXnEGRzNk3HV3gLNT741YEP3Sp2ZRjd5U1qFn0D+eWe0
|
||||
4LfDX9auGQCnfjZTHvu4qghX7JxcF40omjmtgkRmZ/kCgYEA4A5nU4ahEww7B65y
|
||||
uzmGeCUUi8ikWzv1C81pSyUKvKzu8CX41hp9J6oRaLGesKImYiuVQK47FhZ++wwf
|
||||
pRwHvSxtNU9qXb8ewo+BvadyO1eVrIk4tNV543QlSe7pQAoJGkxCia5rfznAE3In
|
||||
KF4JvIlchyqs0RQ8wx7lULqwnn0CgYEAven83GM6SfrmO+TBHbjTk6JhP/3CMsIv
|
||||
mSdo4KrbQNvp4vHO3w1/0zJ3URkmkYGhz2tgPlfd7v1l2I6QkIh4Bumdj6FyFZEB
|
||||
pxjE4MpfdNVcNINvVj87cLyTRmIcaGxmfylY7QErP8GFA+k4UoH/eQmGKGK44TRz
|
||||
Yj5hZYGWIC8CgYEAlmmU/AG5SGxBhJqb8wxfNXDPJjf//i92BgJT2Vp4pskBbr5P
|
||||
GoyV0HbfUQVMnw977RONEurkR6O6gxZUeCclGt4kQlGZ+m0/XSWx13v9t9DIbheA
|
||||
tgVJ2mQyVDvK4m7aRYlEceFh0PsX8vYDS5o1txgPwb3oXkPTtrmbAGMUBpECgYEA
|
||||
mxRTU3QDyR2EnCv0Nl0TCF90oliJGAHR9HJmBe//EjuCBbwHfcT8OG3hWOv8vpzo
|
||||
kQPRl5cQt3NckzX3fs6xlJN4Ai2Hh2zduKFVQ2p+AF2p6Yfahscjtq+GY9cB85Nx
|
||||
Ly2IXCC0PF++Sq9LOrTE9QV988SJy/yUrAjcZ5MmECkCgYEAldHXIrEmMZVaNwGz
|
||||
DF9WG8sHj2mOZmQpw9yrjLK9hAsmsNr5LTyqWAqJIYZSwPTYWhY4nu2O0EY9G9uY
|
||||
iqewXfCKw/UngrJt8Xwfq1Zruz0YY869zPN4GiE9+9rzdZB33RBw8kIOquY3MK74
|
||||
FMwCihYx/LiU2YTHkaoJ3ncvtvg=
|
||||
-----END PRIVATE KEY-----"
|
||||
JWK_PUB_RSA: '{"kid":"ex","kty":"RSA","key_ops":["sign","verify","wrapKey","unwrapKey","encrypt","decrypt"],"n":"p2VQo8qCfWAZmdWBVaYuYb-a-tWWm78K6Sr9poCvNcmv8rUPSLACxitQWR8gZaSH1DklVkqz-Ed8Cdlf8lkDg4Ex5tkB64jRdC1Uvn4CDpOH6cp-N2s8hTFLqy9_YaDmyQS7HiqthOi9oVjil1VMeWfaAbClGtFt6UnKD0Vb_DvLoWYQSqlhgBArFJi966b4E1pOq5Ad02K8pHBDThlIIx7unibLehhDU6q3DCwNH_OOLx6bgNtmvGYJDd1cywpkLQ3YzNCUPWnfMBJRP3iQP_WI21uP6cvo0DqBPBM4wvVzHbCT0vnIflwkbgEWkq1FprqAitZlop9KjLqzjp9vyQ","e":"AQAB"}'
|
||||
JWK_PUB_ECDSA: '{"kid":"https://kv-test-mj.vault.azure.net/keys/ec-p-521/e3d0e9c179b54988860c69c6ae172c65","kty":"EC","key_ops":["sign","verify"],"crv":"P-521","x":"AedOAtb7H7Oz1C_cPKI_R4CN_eai5nteY6KFW07FOoaqgQfVCSkQDK22fCOiMT_28c8LZYJRsiIFz_IIbQUW7bXj","y":"AOnchHnmBphIWXvanmMAmcCDkaED6ycW8GsAl9fQ43BMVZTqcTkJYn6vGnhn7MObizmkNSmgZYTwG-vZkIg03HHs"}'
|
||||
|
||||
JWK_PRIV_RSA: '{"kty" : "RSA","kid" : "cc34c0a0-bd5a-4a3c-a50d-a2a7db7643df","use" : "sig","n" : "pjdss8ZaDfEH6K6U7GeW2nxDqR4IP049fk1fK0lndimbMMVBdPv_hSpm8T8EtBDxrUdi1OHZfMhUixGaut-3nQ4GG9nM249oxhCtxqqNvEXrmQRGqczyLxuh-fKn9Fg--hS9UpazHpfVAFnB5aCfXoNhPuI8oByyFKMKaOVgHNqP5NBEqabiLftZD3W_lsFCPGuzr4Vp0YS7zS2hDYScC2oOMu4rGU1LcMZf39p3153Cq7bS2Xh6Y-vw5pwzFYZdjQxDn8x8BG3fJ6j8TGLXQsbKH1218_HcUJRvMwdpbUQG5nvA2GXVqLqdwp054Lzk9_B_f1lVrmOKuHjTNHq48w","e" : "AQAB","d" : "ksDmucdMJXkFGZxiomNHnroOZxe8AmDLDGO1vhs-POa5PZM7mtUPonxwjVmthmpbZzla-kg55OFfO7YcXhg-Hm2OWTKwm73_rLh3JavaHjvBqsVKuorX3V3RYkSro6HyYIzFJ1Ek7sLxbjDRcDOj4ievSX0oN9l-JZhaDYlPlci5uJsoqro_YrE0PRRWVhtGynd-_aWgQv1YzkfZuMD-hJtDi1Im2humOWxA4eZrFs9eG-whXcOvaSwO4sSGbS99ecQZHM2TcdXeAs1PvjVgQ_dKnZlGN3lTWoWfQP55Z7Tgt8Nf1q4ZAKd-NlMe-7iqCFfsnFwXjSiaOa2CRGZn-Q","p" : "4A5nU4ahEww7B65yuzmGeCUUi8ikWzv1C81pSyUKvKzu8CX41hp9J6oRaLGesKImYiuVQK47FhZ--wwfpRwHvSxtNU9qXb8ewo-BvadyO1eVrIk4tNV543QlSe7pQAoJGkxCia5rfznAE3InKF4JvIlchyqs0RQ8wx7lULqwnn0","q" : "ven83GM6SfrmO-TBHbjTk6JhP_3CMsIvmSdo4KrbQNvp4vHO3w1_0zJ3URkmkYGhz2tgPlfd7v1l2I6QkIh4Bumdj6FyFZEBpxjE4MpfdNVcNINvVj87cLyTRmIcaGxmfylY7QErP8GFA-k4UoH_eQmGKGK44TRzYj5hZYGWIC8","dp" : "lmmU_AG5SGxBhJqb8wxfNXDPJjf__i92BgJT2Vp4pskBbr5PGoyV0HbfUQVMnw977RONEurkR6O6gxZUeCclGt4kQlGZ-m0_XSWx13v9t9DIbheAtgVJ2mQyVDvK4m7aRYlEceFh0PsX8vYDS5o1txgPwb3oXkPTtrmbAGMUBpE","dq" : "mxRTU3QDyR2EnCv0Nl0TCF90oliJGAHR9HJmBe__EjuCBbwHfcT8OG3hWOv8vpzokQPRl5cQt3NckzX3fs6xlJN4Ai2Hh2zduKFVQ2p-AF2p6Yfahscjtq-GY9cB85NxLy2IXCC0PF--Sq9LOrTE9QV988SJy_yUrAjcZ5MmECk","qi" : "ldHXIrEmMZVaNwGzDF9WG8sHj2mOZmQpw9yrjLK9hAsmsNr5LTyqWAqJIYZSwPTYWhY4nu2O0EY9G9uYiqewXfCKw_UngrJt8Xwfq1Zruz0YY869zPN4GiE9-9rzdZB33RBw8kIOquY3MK74FMwCihYx_LiU2YTHkaoJ3ncvtvg"}'
|
||||
JWK_PRIV_ECDSA: '{"kty": "EC","kid": "rie3pHe8u8gjSa0IaJfqk7_iEfHeYfDYx-Bqi7vQc0s","crv": "P-256","x": "fDjg3Nq4jPf8IOZ0277aPVal_8iXySnzLUJAZghUzZM","y": "d863PeyBOK_Q4duiSmWwgIRzi1RPlFZTR-vACMlPg-Q","d": "jJs5xsoHUetdMabtt8H2KyX5T92nGul1chFeMT5hlr0"}'
|
||||
|
||||
TEST_LOWERCASE_STRING: i am a lowercase string
|
||||
|
||||
TEST_YAML_STRING: |
|
||||
name: test-object
|
||||
type: example
|
||||
properties:
|
||||
key1: value1
|
||||
key2: value2
|
||||
numbers:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
|
||||
TEST_JSON_DATA: '{
|
||||
"id": "f1bc87fc-99cf-48d2-b289-7cf578152f9a",
|
||||
"name": "test",
|
||||
"description": "",
|
||||
"type": "secret-manager",
|
||||
"slug": "test-o-bto",
|
||||
"autoCapitalization": false,
|
||||
"orgId": "601815be-6884-4ee4-86c7-bfc6415f2123",
|
||||
"createdAt": "2025-03-26T01:32:39.890Z",
|
||||
"updatedAt": "2025-03-26T01:32:41.688Z",
|
||||
"version": 3,
|
||||
"upgradeStatus": null,
|
||||
"pitVersionLimit": 10,
|
||||
"kmsCertificateKeyId": null,
|
||||
"auditLogsRetentionDays": null,
|
||||
"_id": "f1bc87fc-99cf-48d2-b289-7cf578152f9a",
|
||||
"environments": [
|
||||
{
|
||||
"name": "Development",
|
||||
"slug": "dev",
|
||||
"id": "cbb62f88-44cb-4c29-975a-871f8d7d303b"
|
||||
},
|
||||
{
|
||||
"name": "Staging",
|
||||
"slug": "staging",
|
||||
"id": "c933a63d-418a-4d5c-a7d1-91b74d3ee2eb"
|
||||
},
|
||||
{
|
||||
"name": "Production",
|
||||
"slug": "prod",
|
||||
"id": "0b70125e-47d5-46e8-a03e-a3105df05d37"
|
||||
}
|
||||
]
|
||||
}'
|
||||
@@ -1,9 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: push-secret-source-secret
|
||||
namespace: default
|
||||
stringData:
|
||||
ENCRYPTION_KEY: secret-encryption-key
|
||||
API_URL: https://example.com/api
|
||||
REGION: us-east-1
|
||||
@@ -1,33 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: custom-ca-certificate
|
||||
type: Opaque
|
||||
stringData:
|
||||
ca.crt: |
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEZzCCA0+gAwIBAgIUDk9+HZcMHppiNy0TvoBg8/aMEqIwDQYJKoZIhvcNAQEL
|
||||
BQAwDTELMAkGA1UEChMCUEgwHhcNMjQxMDI1MTU0MjAzWhcNMjUxMDI1MjE0MjAz
|
||||
WjAfMR0wGwYDVQQDExRob3N0LmRvY2tlci5pbnRlcm5hbDCCASIwDQYJKoZIhvcN
|
||||
AQEBBQADggEPADCCAQoCggEBALPBCPhZHCizZWbyGI0LzTLYprsvTMoeZBeR84lj
|
||||
hv/VDUkH3K6jw5g2o2eXg4Aisb/GcQkTxHjmGlUKymhrLBH9zUHjh1yFKPUJdSy1
|
||||
X4YCG+ABNQ8obrTZM/ry5WRHF/KcFIELt/4JpY8OWkxEIisYfe98vObsGH39spcN
|
||||
c3x3Oo4vsBd6ETQOjrXL81kXLoNZoHdsVIU0ZwNpXR1geI477ce3eHOuEhBvKfUR
|
||||
ugRdmX6xUhFNZcKRYiv3RRkm/vnuxWx2CxsecJ0BRoB7nT00gJkkxbt1b5MrPFF4
|
||||
XIdhWIdxSMdMUwtnEo9hT2mzUCkJohLEeqwivZfewghLo88CAwEAAaOCAaswggGn
|
||||
MAkGA1UdEwQCMAAwXgYDVR0fBFcwVTBToFGgT4ZNaHR0cDovL2xvY2FsaG9zdDo4
|
||||
MDgwL2FwaS92MS9wa2kvY3JsLzY2ZDk3OTNkLWMzMTYtNDNhZS05N2RiLTkzNDBj
|
||||
ZmJkNTYxNy9kZXIwHwYDVR0jBBgwFoAU3+CiMP0BF+BnjXBYawENOrnQ+q8wHQYD
|
||||
VR0OBBYEFKUIOV5qAwf0Bd1dMnxIYYglcZT1MIGdBggrBgEFBQcBAQSBkDCBjTCB
|
||||
igYIKwYBBQUHMAKGfmh0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9hcGkvdjEvcGtpL2Nh
|
||||
L2EyNDIyZTdlLTAwZWYtNDlhZC1iY2ZhLTUxMzZhODQxNjEyZC9jZXJ0aWZpY2F0
|
||||
ZXMvYWJhNTRjNGEtNjYxOS00MDFlLTk2YTYtN2UwN2MxNzdjOTI4L2RlcjARBgNV
|
||||
HSAECjAIMAYGBFUdIAAwDgYDVR0PAQH/BAQDAgWgMBYGA1UdJQEB/wQMMAoGCCsG
|
||||
AQUFBwMBMB8GA1UdEQQYMBaCFGhvc3QuZG9ja2VyLmludGVybmFsMA0GCSqGSIb3
|
||||
DQEBCwUAA4IBAQAtUUloE1xU+BNF2Fjc/PSOesHz6dFCzGWvCc0QZceK/6v4EWuZ
|
||||
vEU07brGrufhwJ3UnOXO4zxIl3UplQ1S14Xrba4R69Fp3dggFV39ON8R5lpL9hZe
|
||||
cSRywBycKil2C7SytPsjJtvCXY6RXb6YxFse6rDk0qoMwD/g/ou3JIEpgtB2cPuX
|
||||
Blg9ZWAsaOtKhtmi1IyLjwgHDd86XhMzd9osOna1iuARZMZs80ek5b5H4cdFIBTl
|
||||
rwIQc6b9ZbHAD56NttCIE18YmLWbYBCdvga0Qmqwr2fRPg2DE9qoyF1ZJVbwisOc
|
||||
cJ23MFdpsXKiIoQyDmpZl5jg8aKD/jh0wdUx
|
||||
-----END CERTIFICATE-----
|
||||
@@ -1,26 +0,0 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
labels:
|
||||
app: nginx
|
||||
annotations:
|
||||
secrets.infisical.com/auto-reload: "true"
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.14.2
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: managed-secret
|
||||
ports:
|
||||
- containerPort: 80
|
||||
@@ -1,12 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: infisical-operator-system
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: infisical-config
|
||||
namespace: infisical-operator-system
|
||||
data:
|
||||
hostAPI: "https://example.com/api"
|
||||
@@ -1,13 +0,0 @@
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: role-tokenreview-binding
|
||||
namespace: default
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: system:auth-delegator
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: infisical-auth
|
||||
namespace: default
|
||||
@@ -1,5 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: infisical-auth
|
||||
namespace: default
|
||||
@@ -1,32 +0,0 @@
|
||||
apiVersion: secrets.infisical.com/v1alpha1
|
||||
kind: InfisicalSecret
|
||||
metadata:
|
||||
name: infisicalsecret-sample
|
||||
labels:
|
||||
label-to-be-passed-to-managed-secret: sample-value
|
||||
annotations:
|
||||
example.com/annotation-to-be-passed-to-managed-secret: "sample-value"
|
||||
spec:
|
||||
hostAPI: https://app.infisical.com/api
|
||||
resyncInterval: 10
|
||||
authentication:
|
||||
# Native Kubernetes Auth
|
||||
kubernetesAuth:
|
||||
identityId: <>
|
||||
serviceAccountRef:
|
||||
name: infisical-auth
|
||||
namespace: default
|
||||
|
||||
# secretsScope is identical to the secrets scope in the universalAuth field in this sample.
|
||||
secretsScope:
|
||||
projectSlug: dsf-gpb-t
|
||||
envSlug: dev
|
||||
secretsPath: "/"
|
||||
recursive: true
|
||||
|
||||
|
||||
managedSecretReference:
|
||||
secretName: managed-secret-k8s
|
||||
secretNamespace: default
|
||||
creationPolicy: "Orphan" ## Owner | Orphan
|
||||
# secretType: kubernetes.io/dockerconfigjson
|
||||
@@ -1,7 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
type: kubernetes.io/service-account-token
|
||||
metadata:
|
||||
name: infisical-auth-token
|
||||
annotations:
|
||||
kubernetes.io/service-account.name: "infisical-auth"
|
||||
@@ -1,8 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: ldap-auth-credentials
|
||||
type: Opaque
|
||||
stringData:
|
||||
username: <ldap-username>
|
||||
password: <ldap-password>
|
||||
@@ -1,7 +0,0 @@
|
||||
# apiVersion: v1
|
||||
# kind: Secret
|
||||
# metadata:
|
||||
# name: service-token
|
||||
# type: Opaque
|
||||
# data:
|
||||
# infisicalToken: <base64 infisical token here>
|
||||
@@ -1,8 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: universal-auth-credentials
|
||||
type: Opaque
|
||||
stringData:
|
||||
clientId: your-client-id-here
|
||||
clientSecret: your-client-secret-here
|
||||
@@ -1,152 +0,0 @@
|
||||
module github.com/Infisical/infisical/k8-operator
|
||||
|
||||
go 1.24.0
|
||||
|
||||
require (
|
||||
github.com/Masterminds/sprig/v3 v3.3.0
|
||||
github.com/aws/smithy-go v1.22.4
|
||||
github.com/go-logr/logr v1.4.2
|
||||
github.com/go-resty/resty/v2 v2.16.5
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/infisical/go-sdk v0.5.99
|
||||
github.com/lestrrat-go/jwx/v2 v2.1.6
|
||||
github.com/onsi/ginkgo/v2 v2.22.0
|
||||
github.com/onsi/gomega v1.36.1
|
||||
github.com/sethvargo/go-password v0.3.1
|
||||
golang.org/x/crypto v0.36.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
k8s.io/api v0.33.3
|
||||
k8s.io/apimachinery v0.33.3
|
||||
k8s.io/client-go v0.33.3
|
||||
sigs.k8s.io/controller-runtime v0.21.0
|
||||
software.sslmate.com/src/go-pkcs12 v0.6.0
|
||||
)
|
||||
|
||||
replace github.com/google/go-cmp v0.7.0 => github.com/google/go-cmp v0.6.0
|
||||
|
||||
require (
|
||||
cel.dev/expr v0.19.1 // indirect
|
||||
cloud.google.com/go/auth v0.7.0 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.5.0 // indirect
|
||||
cloud.google.com/go/iam v1.1.11 // indirect
|
||||
dario.cat/mergo v1.0.1 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.3.0 // indirect
|
||||
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.27.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.18 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.18 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.9 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.9 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.20.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.28.12 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||
github.com/evanphx/json-patch/v5 v5.9.11 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-logr/zapr v1.3.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||
github.com/goccy/go-json v0.10.3 // indirect
|
||||
github.com/gofrs/flock v0.8.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/btree v1.1.3 // indirect
|
||||
github.com/google/cel-go v0.23.2 // indirect
|
||||
github.com/google/gnostic-models v0.6.9 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect
|
||||
github.com/google/s2a-go v0.1.7 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.12.5 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/huandu/xstrings v1.5.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/lestrrat-go/blackmagic v1.0.3 // indirect
|
||||
github.com/lestrrat-go/httpcc v1.0.1 // indirect
|
||||
github.com/lestrrat-go/httprc v1.0.6 // indirect
|
||||
github.com/lestrrat-go/iter v1.0.2 // indirect
|
||||
github.com/lestrrat-go/option v1.0.1 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/oracle/oci-go-sdk/v65 v65.95.2 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/prometheus/client_golang v1.22.0 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.62.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/segmentio/asm v1.2.0 // indirect
|
||||
github.com/shopspring/decimal v1.4.0 // indirect
|
||||
github.com/sony/gobreaker v0.5.0 // indirect
|
||||
github.com/spf13/cast v1.7.0 // indirect
|
||||
github.com/spf13/cobra v1.8.1 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/stoewer/go-strcase v1.3.0 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect
|
||||
go.opentelemetry.io/otel v1.33.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.33.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.33.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.33.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.4.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
|
||||
golang.org/x/net v0.38.0 // indirect
|
||||
golang.org/x/oauth2 v0.27.0 // indirect
|
||||
golang.org/x/sync v0.12.0 // indirect
|
||||
golang.org/x/sys v0.31.0 // indirect
|
||||
golang.org/x/term v0.30.0 // indirect
|
||||
golang.org/x/text v0.23.0 // indirect
|
||||
golang.org/x/time v0.9.0 // indirect
|
||||
golang.org/x/tools v0.26.0 // indirect
|
||||
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
|
||||
google.golang.org/api v0.188.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect
|
||||
google.golang.org/grpc v1.68.1 // indirect
|
||||
google.golang.org/protobuf v1.36.5 // indirect
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
k8s.io/apiextensions-apiserver v0.33.0 // indirect
|
||||
k8s.io/apiserver v0.33.0 // indirect
|
||||
k8s.io/component-base v0.33.0 // indirect
|
||||
k8s.io/klog/v2 v2.130.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
|
||||
sigs.k8s.io/randfill v1.0.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
)
|
||||
@@ -1,462 +0,0 @@
|
||||
cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4=
|
||||
cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go/auth v0.7.0 h1:kf/x9B3WTbBUHkC+1VS8wwwli9TzhSt0vSTVBmMR8Ts=
|
||||
cloud.google.com/go/auth v0.7.0/go.mod h1:D+WqdrpcjmiCgWrXmLLxOVq1GACoE36chW6KXoEvuIw=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.2 h1:+TTV8aXpjeChS9M+aTtN/TjdQnzJvmzKFt//oWu7HX4=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q=
|
||||
cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY=
|
||||
cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY=
|
||||
cloud.google.com/go/iam v1.1.11 h1:0mQ8UKSfdHLut6pH9FM3bI55KWR46ketn0PuXleDyxw=
|
||||
cloud.google.com/go/iam v1.1.11/go.mod h1:biXoiLWYIKntto2joP+62sd9uW5EpkZmKIvfNcTWlnQ=
|
||||
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
|
||||
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
|
||||
github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
|
||||
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
|
||||
github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
|
||||
github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
|
||||
github.com/aws/aws-sdk-go-v2 v1.27.2 h1:pLsTXqX93rimAOZG2FIYraDQstZaaGVVN4tNw65v0h8=
|
||||
github.com/aws/aws-sdk-go-v2 v1.27.2/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.18 h1:wFvAnwOKKe7QAyIxziwSKjmer9JBMH1vzIL6W+fYuKk=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.18/go.mod h1:0xz6cgdX55+kmppvPm2IaKzIXOheGJhAufacPJaXZ7c=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.18 h1:D/ALDWqK4JdY3OFgA2thcPO1c9aYTT5STS/CvnkqY1c=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.18/go.mod h1:JuitCWq+F5QGUrmMPsk945rop6bB57jdscu+Glozdnc=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.5 h1:dDgptDO9dxeFkXy+tEgVkzSClHZje/6JkPW5aZyEvrQ=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.5/go.mod h1:gjvE2KBUgUQhcv89jqxrIxH9GaKs1JbZzWejj/DaHGA=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.9 h1:cy8ahBJuhtM8GTTSyOkfy6WVPV1IE+SS5/wfXUYuulw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.9/go.mod h1:CZBXGLaJnEZI6EVNcPd7a6B5IC5cA/GkRWtu9fp3S6Y=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.9 h1:A4SYk07ef04+vxZToz9LWvAXl9LW0NClpPpMsi31cz0=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.9/go.mod h1:5jJcHuwDagxN+ErjQ3PU3ocf6Ylc/p9x+BLO/+X4iXw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 h1:Ji0DY1xUsUr3I8cHps0G+XM3WWU16lP6yG8qu1GAZAs=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2/go.mod h1:5CsjAbs3NlGQyZNFACh+zztPDI7fU6eW9QsxjfnuBKg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.11 h1:o4T+fKxA3gTMcluBNZZXE9DNaMkJuUL1O3mffCUjoJo=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.11/go.mod h1:84oZdJ+VjuJKs9v1UTC9NaodRZRseOXCTgku+vQJWR8=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.20.11 h1:gEYM2GSpr4YNWc6hCd5nod4+d4kd9vWIAWrmGuLdlMw=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.20.11/go.mod h1:gVvwPdPNYehHSP9Rs7q27U1EU+3Or2ZpXvzAYJNh63w=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.5 h1:iXjh3uaH3vsVcnyZX7MqCoCfcyxIrVE9iOQruRaWPrQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.5/go.mod h1:5ZXesEuy/QcO0WUnt+4sDkxhdXRHTu2yG0uCSH8B6os=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.28.12 h1:M/1u4HBpwLuMtjlxuI2y6HoVLzF5e2mfxHCg7ZVMYmk=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.28.12/go.mod h1:kcfd+eTdEi/40FIbLq4Hif3XMXnl5b/+t/KTfLt9xIk=
|
||||
github.com/aws/smithy-go v1.22.4 h1:uqXzVZNuNexwc/xrh6Tb56u89WDlJY6HS+KC0S4QSjw=
|
||||
github.com/aws/smithy-go v1.22.4/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
|
||||
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k=
|
||||
github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ=
|
||||
github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU=
|
||||
github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
|
||||
github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg=
|
||||
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
||||
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
||||
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
||||
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
|
||||
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
|
||||
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
|
||||
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||
github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM=
|
||||
github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
|
||||
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
|
||||
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
||||
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/cel-go v0.23.2 h1:UdEe3CvQh3Nv+E/j9r1Y//WO0K0cSyD7/y0bzyLIMI4=
|
||||
github.com/google/cel-go v0.23.2/go.mod h1:52Pb6QsDbC5kvgxvZhiL9QX1oZEkcUF/ZqaPx1J5Wwo=
|
||||
github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
|
||||
github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
|
||||
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
|
||||
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
|
||||
github.com/googleapis/gax-go/v2 v2.12.5 h1:8gw9KZK8TiVKB6q3zHY3SBzLnrGp6HQjyfYBYGmXdxA=
|
||||
github.com/googleapis/gax-go/v2 v2.12.5/go.mod h1:BUDKcWo+RaKq5SC9vVYL0wLADa3VcfswbOMMRmB9H3E=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 h1:TmHmbvxPmaegwhDubVz0lICL0J5Ka2vwTzhoePEXsGE=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0/go.mod h1:qztMSjm835F2bXf+5HKAPIS5qsmQDqZna/PgVt4rWtI=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
|
||||
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/infisical/go-sdk v0.5.99 h1:trvn7JhKYuSzDkc44h+yqToVjclkrRyP42t315k5kEE=
|
||||
github.com/infisical/go-sdk v0.5.99/go.mod h1:j2D2a5WPNdKXDfHO+3y/TNyLWh5Aq9QYS7EcGI96LZI=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/lestrrat-go/blackmagic v1.0.3 h1:94HXkVLxkZO9vJI/w2u1T0DAoprShFd13xtnSINtDWs=
|
||||
github.com/lestrrat-go/blackmagic v1.0.3/go.mod h1:6AWFyKNNj0zEXQYfTMPfZrAXUWUfTIZ5ECEUEJaijtw=
|
||||
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
|
||||
github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=
|
||||
github.com/lestrrat-go/httprc v1.0.6 h1:qgmgIRhpvBqexMJjA/PmwSvhNk679oqD1RbovdCGW8k=
|
||||
github.com/lestrrat-go/httprc v1.0.6/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo=
|
||||
github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI=
|
||||
github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4=
|
||||
github.com/lestrrat-go/jwx/v2 v2.1.6 h1:hxM1gfDILk/l5ylers6BX/Eq1m/pnxe9NBwW6lVfecA=
|
||||
github.com/lestrrat-go/jwx/v2 v2.1.6/go.mod h1:Y722kU5r/8mV7fYDifjug0r8FK8mZdw0K0GpJw/l8pU=
|
||||
github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
|
||||
github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg=
|
||||
github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
|
||||
github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw=
|
||||
github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
|
||||
github.com/oracle/oci-go-sdk/v65 v65.95.2 h1:0HJ0AgpLydp/DtvYrF2d4str2BjXOVAeNbuW7E07g94=
|
||||
github.com/oracle/oci-go-sdk/v65 v65.95.2/go.mod h1:u6XRPsw9tPziBh76K7GrrRXPa8P8W3BQeqJ6ZZt9VLA=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
|
||||
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
|
||||
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
|
||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
|
||||
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
|
||||
github.com/sethvargo/go-password v0.3.1 h1:WqrLTjo7X6AcVYfC6R7GtSyuUQR9hGyAj/f1PYQZCJU=
|
||||
github.com/sethvargo/go-password v0.3.1/go.mod h1:rXofC1zT54N7R8K/h1WDUdkf9BOx5OptoxrMBcrXzvs=
|
||||
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
||||
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
||||
github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg=
|
||||
github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
|
||||
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
|
||||
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
|
||||
github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 h1:PS8wXpbyaDJQ2VDHHncMe9Vct0Zn1fEjpsjrLxGJoSc=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0/go.mod h1:HDBUsEjOuRC0EzKZ1bSaRGZWUBAzo+MhAcUUORSr4D0=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q=
|
||||
go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw=
|
||||
go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 h1:5pojmb1U1AogINhN3SurB+zm/nIcusopeBNp42f45QM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA=
|
||||
go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ=
|
||||
go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M=
|
||||
go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM=
|
||||
go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM=
|
||||
go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s=
|
||||
go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck=
|
||||
go.opentelemetry.io/proto/otlp v1.4.0 h1:TA9WRvW6zMwP+Ssb6fLoUIuirti1gGbP28GcKG1jgeg=
|
||||
go.opentelemetry.io/proto/otlp v1.4.0/go.mod h1:PPBWZIP98o2ElSqI35IHfu7hIhSwvc5N38Jw8pXuGFY=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
||||
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
|
||||
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
|
||||
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
|
||||
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
|
||||
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
|
||||
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw=
|
||||
gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
|
||||
google.golang.org/api v0.188.0 h1:51y8fJ/b1AaaBRJr4yWm96fPcuxSo0JcegXE3DaHQHw=
|
||||
google.golang.org/api v0.188.0/go.mod h1:VR0d+2SIiWOYG3r/jdm7adPW9hI2aRv9ETOSCQ9Beag=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 h1:8ZmaLZE4XWrtU3MyClkYqqtl6Oegr3235h7jxsDyqCY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0=
|
||||
google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
||||
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
k8s.io/api v0.33.3 h1:SRd5t//hhkI1buzxb288fy2xvjubstenEKL9K51KBI8=
|
||||
k8s.io/api v0.33.3/go.mod h1:01Y/iLUjNBM3TAvypct7DIj0M0NIZc+PzAHCIo0CYGE=
|
||||
k8s.io/apiextensions-apiserver v0.33.0 h1:d2qpYL7Mngbsc1taA4IjJPRJ9ilnsXIrndH+r9IimOs=
|
||||
k8s.io/apiextensions-apiserver v0.33.0/go.mod h1:VeJ8u9dEEN+tbETo+lFkwaaZPg6uFKLGj5vyNEwwSzc=
|
||||
k8s.io/apimachinery v0.33.3 h1:4ZSrmNa0c/ZpZJhAgRdcsFcZOw1PQU1bALVQ0B3I5LA=
|
||||
k8s.io/apimachinery v0.33.3/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
|
||||
k8s.io/apiserver v0.33.0 h1:QqcM6c+qEEjkOODHppFXRiw/cE2zP85704YrQ9YaBbc=
|
||||
k8s.io/apiserver v0.33.0/go.mod h1:EixYOit0YTxt8zrO2kBU7ixAtxFce9gKGq367nFmqI8=
|
||||
k8s.io/client-go v0.33.3 h1:M5AfDnKfYmVJif92ngN532gFqakcGi6RvaOF16efrpA=
|
||||
k8s.io/client-go v0.33.3/go.mod h1:luqKBQggEf3shbxHY4uVENAxrDISLOarxpTKMiUuujg=
|
||||
k8s.io/component-base v0.33.0 h1:Ot4PyJI+0JAD9covDhwLp9UNkUja209OzsJ4FzScBNk=
|
||||
k8s.io/component-base v0.33.0/go.mod h1:aXYZLbw3kihdkOPMDhWbjGCO6sg+luw554KP51t8qCU=
|
||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4=
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 h1:jpcvIRr3GLoUoEKRkHKSmGjxb6lWwrBlJsXc+eUYQHM=
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw=
|
||||
sigs.k8s.io/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8=
|
||||
sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM=
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
|
||||
sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
|
||||
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps=
|
||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||
software.sslmate.com/src/go-pkcs12 v0.6.0 h1:f3sQittAeF+pao32Vb+mkli+ZyT+VwKaD014qFGq6oU=
|
||||
software.sslmate.com/src/go-pkcs12 v0.6.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI=
|
||||
@@ -1,15 +0,0 @@
|
||||
/*
|
||||
Copyright 2025.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
@@ -1,233 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/Infisical/infisical/k8-operator/internal/model"
|
||||
"github.com/go-resty/resty/v2"
|
||||
)
|
||||
|
||||
const USER_AGENT_NAME = "k8-operator"
|
||||
|
||||
func CallGetServiceTokenDetailsV2(httpClient *resty.Client) (GetServiceTokenDetailsResponse, error) {
|
||||
var tokenDetailsResponse GetServiceTokenDetailsResponse
|
||||
response, err := httpClient.
|
||||
R().
|
||||
SetResult(&tokenDetailsResponse).
|
||||
SetHeader("User-Agent", USER_AGENT_NAME).
|
||||
Get(fmt.Sprintf("%v/v2/service-token", API_HOST_URL))
|
||||
|
||||
if err != nil {
|
||||
return GetServiceTokenDetailsResponse{}, fmt.Errorf("CallGetServiceTokenDetails: Unable to complete api request [err=%s]", err)
|
||||
}
|
||||
|
||||
if response.IsError() {
|
||||
return GetServiceTokenDetailsResponse{}, fmt.Errorf("CallGetServiceTokenDetails: Unsuccessful response: [response=%s]", response)
|
||||
}
|
||||
|
||||
return tokenDetailsResponse, nil
|
||||
}
|
||||
|
||||
func CallGetServiceTokenAccountDetailsV2(httpClient *resty.Client) (ServiceAccountDetailsResponse, error) {
|
||||
var serviceAccountDetailsResponse ServiceAccountDetailsResponse
|
||||
response, err := httpClient.
|
||||
R().
|
||||
SetResult(&serviceAccountDetailsResponse).
|
||||
SetHeader("User-Agent", USER_AGENT_NAME).
|
||||
Get(fmt.Sprintf("%v/v2/service-accounts/me", API_HOST_URL))
|
||||
|
||||
if err != nil {
|
||||
return ServiceAccountDetailsResponse{}, fmt.Errorf("CallGetServiceTokenAccountDetailsV2: Unable to complete api request [err=%s]", err)
|
||||
}
|
||||
|
||||
if response.IsError() {
|
||||
return ServiceAccountDetailsResponse{}, fmt.Errorf("CallGetServiceTokenAccountDetailsV2: Unsuccessful response: [response=%s]", response)
|
||||
}
|
||||
|
||||
return serviceAccountDetailsResponse, nil
|
||||
}
|
||||
|
||||
func CallUniversalMachineIdentityLogin(request MachineIdentityUniversalAuthLoginRequest) (MachineIdentityDetailsResponse, error) {
|
||||
var machineIdentityDetailsResponse MachineIdentityDetailsResponse
|
||||
|
||||
response, err := resty.New().
|
||||
R().
|
||||
SetResult(&machineIdentityDetailsResponse).
|
||||
SetBody(request).
|
||||
SetHeader("User-Agent", USER_AGENT_NAME).
|
||||
Post(fmt.Sprintf("%v/v1/auth/universal-auth/login", API_HOST_URL))
|
||||
|
||||
if err != nil {
|
||||
return MachineIdentityDetailsResponse{}, fmt.Errorf("CallUniversalMachineIdentityLogin: Unable to complete api request [err=%s]", err)
|
||||
}
|
||||
|
||||
if response.IsError() {
|
||||
return MachineIdentityDetailsResponse{}, fmt.Errorf("CallUniversalMachineIdentityLogin: Unsuccessful response: [response=%s]", response)
|
||||
}
|
||||
|
||||
return machineIdentityDetailsResponse, nil
|
||||
}
|
||||
|
||||
func CallUniversalMachineIdentityRefreshAccessToken(request MachineIdentityUniversalAuthRefreshRequest) (MachineIdentityDetailsResponse, error) {
|
||||
var universalAuthRefreshResponse MachineIdentityDetailsResponse
|
||||
|
||||
response, err := resty.New().
|
||||
R().
|
||||
SetResult(&universalAuthRefreshResponse).
|
||||
SetHeader("User-Agent", USER_AGENT_NAME).
|
||||
SetBody(request).
|
||||
Post(fmt.Sprintf("%v/v1/auth/token/renew", API_HOST_URL))
|
||||
|
||||
if err != nil {
|
||||
return MachineIdentityDetailsResponse{}, fmt.Errorf("CallUniversalAuthRefreshAccessToken: Unable to complete api request [err=%s]", err)
|
||||
}
|
||||
|
||||
if response.IsError() {
|
||||
return MachineIdentityDetailsResponse{}, fmt.Errorf("CallUniversalAuthRefreshAccessToken: Unsuccessful response [%v %v] [status-code=%v] [response=%v]", response.Request.Method, response.Request.URL, response.StatusCode(), response.String())
|
||||
}
|
||||
|
||||
return universalAuthRefreshResponse, nil
|
||||
}
|
||||
|
||||
func CallGetServiceAccountWorkspacePermissionsV2(httpClient *resty.Client) (ServiceAccountWorkspacePermissions, error) {
|
||||
var serviceAccountWorkspacePermissionsResponse ServiceAccountWorkspacePermissions
|
||||
response, err := httpClient.
|
||||
R().
|
||||
SetResult(&serviceAccountWorkspacePermissionsResponse).
|
||||
SetHeader("User-Agent", USER_AGENT_NAME).
|
||||
Get(fmt.Sprintf("%v/v2/service-accounts/<service-account-id>/permissions/workspace", API_HOST_URL))
|
||||
|
||||
if err != nil {
|
||||
return ServiceAccountWorkspacePermissions{}, fmt.Errorf("CallGetServiceAccountWorkspacePermissionsV2: Unable to complete api request [err=%s]", err)
|
||||
}
|
||||
|
||||
if response.IsError() {
|
||||
return ServiceAccountWorkspacePermissions{}, fmt.Errorf("CallGetServiceAccountWorkspacePermissionsV2: Unsuccessful response: [response=%s]", response)
|
||||
}
|
||||
|
||||
return serviceAccountWorkspacePermissionsResponse, nil
|
||||
}
|
||||
|
||||
func CallGetServiceAccountKeysV2(httpClient *resty.Client, request GetServiceAccountKeysRequest) (GetServiceAccountKeysResponse, error) {
|
||||
var serviceAccountKeysResponse GetServiceAccountKeysResponse
|
||||
response, err := httpClient.
|
||||
R().
|
||||
SetResult(&serviceAccountKeysResponse).
|
||||
SetHeader("User-Agent", USER_AGENT_NAME).
|
||||
Get(fmt.Sprintf("%v/v2/service-accounts/%v/keys", API_HOST_URL, request.ServiceAccountId))
|
||||
|
||||
if err != nil {
|
||||
return GetServiceAccountKeysResponse{}, fmt.Errorf("CallGetServiceAccountKeysV2: Unable to complete api request [err=%s]", err)
|
||||
}
|
||||
|
||||
if response.IsError() {
|
||||
return GetServiceAccountKeysResponse{}, fmt.Errorf("CallGetServiceAccountKeysV2: Unsuccessful response: [response=%s]", response)
|
||||
}
|
||||
|
||||
return serviceAccountKeysResponse, nil
|
||||
}
|
||||
|
||||
func CallGetProjectByID(httpClient *resty.Client, request GetProjectByIDRequest) (GetProjectByIDResponse, error) {
|
||||
|
||||
var projectResponse GetProjectByIDResponse
|
||||
|
||||
response, err := httpClient.
|
||||
R().SetResult(&projectResponse).
|
||||
SetHeader("User-Agent", USER_AGENT_NAME).
|
||||
Get(fmt.Sprintf("%s/v1/workspace/%s", API_HOST_URL, request.ProjectID))
|
||||
|
||||
if err != nil {
|
||||
return GetProjectByIDResponse{}, fmt.Errorf("CallGetProject: Unable to complete api request [err=%s]", err)
|
||||
}
|
||||
|
||||
if response.IsError() {
|
||||
return GetProjectByIDResponse{}, fmt.Errorf("CallGetProject: Unsuccessful response: [response=%s]", response)
|
||||
}
|
||||
|
||||
return projectResponse, nil
|
||||
|
||||
}
|
||||
|
||||
func CallGetProjectByIDv2(httpClient *resty.Client, request GetProjectByIDRequest) (model.Project, error) {
|
||||
var projectResponse model.Project
|
||||
|
||||
response, err := httpClient.
|
||||
R().SetResult(&projectResponse).
|
||||
SetHeader("User-Agent", USER_AGENT_NAME).
|
||||
Get(fmt.Sprintf("%s/v2/workspace/%s", API_HOST_URL, request.ProjectID))
|
||||
|
||||
if err != nil {
|
||||
return model.Project{}, fmt.Errorf("CallGetProject: Unable to complete api request [err=%s]", err)
|
||||
}
|
||||
|
||||
if response.IsError() {
|
||||
return model.Project{}, fmt.Errorf("CallGetProject: Unsuccessful response: [response=%s]", response)
|
||||
}
|
||||
|
||||
return projectResponse, nil
|
||||
|
||||
}
|
||||
|
||||
func CallSubscribeProjectEvents(httpClient *resty.Client, projectId, secretsPath, envSlug, token string) (*http.Response, error) {
|
||||
conditions := &SubscribeProjectEventsRequestCondition{
|
||||
SecretPath: secretsPath,
|
||||
EnvironmentSlug: envSlug,
|
||||
}
|
||||
|
||||
body, err := json.Marshal(&SubscribeProjectEventsRequest{
|
||||
ProjectID: projectId,
|
||||
Register: []SubscribeProjectEventsRequestRegister{
|
||||
{
|
||||
Event: "secret:create",
|
||||
Conditions: conditions,
|
||||
},
|
||||
{
|
||||
Event: "secret:update",
|
||||
Conditions: conditions,
|
||||
},
|
||||
{
|
||||
Event: "secret:delete",
|
||||
Conditions: conditions,
|
||||
},
|
||||
{
|
||||
Event: "secret:import-mutation",
|
||||
Conditions: conditions,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("CallSubscribeProjectEvents: Unable to marshal body [err=%s]", err)
|
||||
}
|
||||
|
||||
response, err := httpClient.
|
||||
R().
|
||||
SetDoNotParseResponse(true).
|
||||
SetHeader("User-Agent", USER_AGENT_NAME).
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("Accept", "text/event-stream").
|
||||
SetHeader("Connection", "keep-alive").
|
||||
SetHeader("Authorization", fmt.Sprint("Bearer ", token)).
|
||||
SetBody(body).
|
||||
Post(fmt.Sprintf("%s/v1/events/subscribe/project-events", API_HOST_URL))
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("CallSubscribeProjectEvents: Unable to complete api request [err=%s]", err)
|
||||
}
|
||||
|
||||
if response.IsError() {
|
||||
data := struct {
|
||||
Message string `json:"message"`
|
||||
}{}
|
||||
|
||||
if err := json.NewDecoder(response.RawBody()).Decode(&data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("CallSubscribeProjectEvents: Unsuccessful response: [message=%s]", data.Message)
|
||||
}
|
||||
|
||||
return response.RawResponse, nil
|
||||
}
|
||||
@@ -1,225 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/Infisical/infisical/k8-operator/internal/model"
|
||||
)
|
||||
|
||||
type GetEncryptedWorkspaceKeyRequest struct {
|
||||
WorkspaceId string `json:"workspaceId"`
|
||||
}
|
||||
|
||||
type GetEncryptedWorkspaceKeyResponse struct {
|
||||
ID string `json:"_id"`
|
||||
EncryptedKey string `json:"encryptedKey"`
|
||||
Nonce string `json:"nonce"`
|
||||
Sender struct {
|
||||
ID string `json:"_id"`
|
||||
Email string `json:"email"`
|
||||
RefreshVersion int `json:"refreshVersion"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
V int `json:"__v"`
|
||||
FirstName string `json:"firstName"`
|
||||
LastName string `json:"lastName"`
|
||||
PublicKey string `json:"publicKey"`
|
||||
} `json:"sender"`
|
||||
Receiver string `json:"receiver"`
|
||||
Workspace string `json:"workspace"`
|
||||
V int `json:"__v"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
type GetEncryptedSecretsV3Request struct {
|
||||
Environment string `json:"environment"`
|
||||
WorkspaceId string `json:"workspaceId"`
|
||||
Recursive bool `json:"recursive"`
|
||||
SecretPath string `json:"secretPath"`
|
||||
IncludeImport bool `json:"include_imports"`
|
||||
ETag string `json:"etag,omitempty"`
|
||||
}
|
||||
|
||||
type EncryptedSecretV3 struct {
|
||||
ID string `json:"_id"`
|
||||
Version int `json:"version"`
|
||||
Workspace string `json:"workspace"`
|
||||
Type string `json:"type"`
|
||||
Tags []struct {
|
||||
ID string `json:"_id"`
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"slug"`
|
||||
Workspace string `json:"workspace"`
|
||||
} `json:"tags"`
|
||||
Environment string `json:"environment"`
|
||||
SecretKeyCiphertext string `json:"secretKeyCiphertext"`
|
||||
SecretKeyIV string `json:"secretKeyIV"`
|
||||
SecretKeyTag string `json:"secretKeyTag"`
|
||||
SecretValueCiphertext string `json:"secretValueCiphertext"`
|
||||
SecretValueIV string `json:"secretValueIV"`
|
||||
SecretValueTag string `json:"secretValueTag"`
|
||||
SecretCommentCiphertext string `json:"secretCommentCiphertext"`
|
||||
SecretCommentIV string `json:"secretCommentIV"`
|
||||
SecretCommentTag string `json:"secretCommentTag"`
|
||||
Algorithm string `json:"algorithm"`
|
||||
KeyEncoding string `json:"keyEncoding"`
|
||||
Folder string `json:"folder"`
|
||||
V int `json:"__v"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
type DecryptedSecretV3 struct {
|
||||
ID string `json:"id"`
|
||||
Workspace string `json:"workspace"`
|
||||
Environment string `json:"environment"`
|
||||
Version int `json:"version"`
|
||||
Type string `json:"string"`
|
||||
SecretKey string `json:"secretKey"`
|
||||
SecretValue string `json:"secretValue"`
|
||||
SecretComment string `json:"secretComment"`
|
||||
}
|
||||
|
||||
type ImportedSecretV3 struct {
|
||||
Environment string `json:"environment"`
|
||||
FolderId string `json:"folderId"`
|
||||
SecretPath string `json:"secretPath"`
|
||||
Secrets []EncryptedSecretV3 `json:"secrets"`
|
||||
}
|
||||
|
||||
type ImportedRawSecretV3 struct {
|
||||
Environment string `json:"environment"`
|
||||
FolderId string `json:"folderId"`
|
||||
SecretPath string `json:"secretPath"`
|
||||
Secrets []DecryptedSecretV3 `json:"secrets"`
|
||||
}
|
||||
|
||||
type GetEncryptedSecretsV3Response struct {
|
||||
Secrets []EncryptedSecretV3 `json:"secrets"`
|
||||
ImportedSecrets []ImportedSecretV3 `json:"imports,omitempty"`
|
||||
Modified bool `json:"modified,omitempty"`
|
||||
ETag string `json:"ETag,omitempty"`
|
||||
}
|
||||
|
||||
type GetDecryptedSecretsV3Response struct {
|
||||
Secrets []DecryptedSecretV3 `json:"secrets"`
|
||||
ETag string `json:"ETag,omitempty"`
|
||||
Modified bool `json:"modified,omitempty"`
|
||||
Imports []ImportedRawSecretV3 `json:"imports,omitempty"`
|
||||
}
|
||||
|
||||
type GetDecryptedSecretsV3Request struct {
|
||||
ProjectID string `json:"workspaceId"`
|
||||
ProjectSlug string `json:"workspaceSlug"`
|
||||
Environment string `json:"environment"`
|
||||
SecretPath string `json:"secretPath"`
|
||||
Recursive bool `json:"recursive"`
|
||||
ExpandSecretReferences bool `json:"expandSecretReferences"`
|
||||
ETag string `json:"etag,omitempty"`
|
||||
}
|
||||
|
||||
type GetServiceTokenDetailsResponse struct {
|
||||
ID string `json:"_id"`
|
||||
Name string `json:"name"`
|
||||
Workspace string `json:"workspace"`
|
||||
Environment string `json:"environment"`
|
||||
EncryptedKey string `json:"encryptedKey"`
|
||||
Iv string `json:"iv"`
|
||||
Tag string `json:"tag"`
|
||||
SecretPath string `json:"secretPath"`
|
||||
}
|
||||
|
||||
type ServiceAccountDetailsResponse struct {
|
||||
ServiceAccount struct {
|
||||
ID string `json:"_id"`
|
||||
Name string `json:"name"`
|
||||
Organization string `json:"organization"`
|
||||
PublicKey string `json:"publicKey"`
|
||||
LastUsed time.Time `json:"lastUsed"`
|
||||
ExpiresAt time.Time `json:"expiresAt"`
|
||||
} `json:"serviceAccount"`
|
||||
}
|
||||
|
||||
type MachineIdentityDetailsResponse struct {
|
||||
AccessToken string `json:"accessToken"`
|
||||
ExpiresIn int `json:"expiresIn"`
|
||||
AccessTokenMaxTTL int `json:"accessTokenMaxTTL"`
|
||||
TokenType string `json:"tokenType"`
|
||||
}
|
||||
|
||||
type ServiceAccountWorkspacePermission struct {
|
||||
ID string `json:"_id"`
|
||||
ServiceAccount string `json:"serviceAccount"`
|
||||
Workspace struct {
|
||||
ID string `json:"_id"`
|
||||
Name string `json:"name"`
|
||||
AutoCapitalization bool `json:"autoCapitalization"`
|
||||
Organization string `json:"organization"`
|
||||
Environments []struct {
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"slug"`
|
||||
ID string `json:"_id"`
|
||||
} `json:"environments"`
|
||||
} `json:"workspace"`
|
||||
Environment string `json:"environment"`
|
||||
Read bool `json:"read"`
|
||||
Write bool `json:"write"`
|
||||
}
|
||||
|
||||
type ServiceAccountWorkspacePermissions struct {
|
||||
ServiceAccountWorkspacePermission []ServiceAccountWorkspacePermissions `json:"serviceAccountWorkspacePermissions"`
|
||||
}
|
||||
|
||||
type GetServiceAccountKeysRequest struct {
|
||||
ServiceAccountId string `json:"id"`
|
||||
}
|
||||
|
||||
type MachineIdentityUniversalAuthLoginRequest struct {
|
||||
ClientId string `json:"clientId"`
|
||||
ClientSecret string `json:"clientSecret"`
|
||||
}
|
||||
|
||||
type MachineIdentityUniversalAuthRefreshRequest struct {
|
||||
AccessToken string `json:"accessToken"`
|
||||
}
|
||||
|
||||
type ServiceAccountKey struct {
|
||||
ID string `json:"_id"`
|
||||
EncryptedKey string `json:"encryptedKey"`
|
||||
Nonce string `json:"nonce"`
|
||||
Sender string `json:"sender"`
|
||||
ServiceAccount string `json:"serviceAccount"`
|
||||
Workspace string `json:"workspace"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
type GetServiceAccountKeysResponse struct {
|
||||
ServiceAccountKeys []ServiceAccountKey `json:"serviceAccountKeys"`
|
||||
}
|
||||
|
||||
type GetProjectByIDRequest struct {
|
||||
ProjectID string
|
||||
}
|
||||
|
||||
type GetProjectByIDResponse struct {
|
||||
Project model.Project `json:"workspace"`
|
||||
}
|
||||
|
||||
type SubscribeProjectEventsRequestRegister struct {
|
||||
Event string `json:"event"`
|
||||
Conditions *SubscribeProjectEventsRequestCondition `json:"conditions"`
|
||||
}
|
||||
|
||||
type SubscribeProjectEventsRequestCondition struct {
|
||||
EnvironmentSlug string `json:"environmentSlug"`
|
||||
SecretPath string `json:"secretPath"`
|
||||
}
|
||||
|
||||
type SubscribeProjectEventsRequest struct {
|
||||
ProjectID string `json:"projectId"`
|
||||
Register []SubscribeProjectEventsRequestRegister `json:"register"`
|
||||
}
|
||||
|
||||
type SubscribeProjectEventsResponse struct{}
|
||||
@@ -1,4 +0,0 @@
|
||||
package api
|
||||
|
||||
var API_HOST_URL string = "https://app.infisical.com/api"
|
||||
var API_CA_CERTIFICATE string = ""
|
||||
@@ -1,42 +0,0 @@
|
||||
package constants
|
||||
|
||||
import "errors"
|
||||
|
||||
const SERVICE_ACCOUNT_ACCESS_KEY = "serviceAccountAccessKey"
|
||||
const SERVICE_ACCOUNT_PUBLIC_KEY = "serviceAccountPublicKey"
|
||||
const SERVICE_ACCOUNT_PRIVATE_KEY = "serviceAccountPrivateKey"
|
||||
|
||||
const INFISICAL_MACHINE_IDENTITY_CLIENT_ID = "clientId"
|
||||
const INFISICAL_MACHINE_IDENTITY_CLIENT_SECRET = "clientSecret"
|
||||
|
||||
const INFISICAL_TOKEN_SECRET_KEY_NAME = "infisicalToken"
|
||||
const SECRET_VERSION_ANNOTATION = "secrets.infisical.com/version" // used to set the version of secrets via Etag
|
||||
const OPERATOR_SETTINGS_CONFIGMAP_NAME = "infisical-config"
|
||||
const OPERATOR_SETTINGS_CONFIGMAP_NAMESPACE = "infisical-operator-system"
|
||||
const INFISICAL_DOMAIN = "https://app.infisical.com/api"
|
||||
|
||||
const INFISICAL_PUSH_SECRET_FINALIZER_NAME = "pushsecret.secrets.infisical.com/finalizer"
|
||||
const INFISICAL_DYNAMIC_SECRET_FINALIZER_NAME = "dynamicsecret.secrets.infisical.com/finalizer"
|
||||
|
||||
type PushSecretReplacePolicy string
|
||||
type PushSecretDeletionPolicy string
|
||||
|
||||
const (
|
||||
PUSH_SECRET_REPLACE_POLICY_ENABLED PushSecretReplacePolicy = "Replace"
|
||||
PUSH_SECRET_DELETE_POLICY_ENABLED PushSecretDeletionPolicy = "Delete"
|
||||
)
|
||||
|
||||
type ManagedKubeResourceType string
|
||||
|
||||
const (
|
||||
MANAGED_KUBE_RESOURCE_TYPE_SECRET ManagedKubeResourceType = "Secret"
|
||||
MANAGED_KUBE_RESOURCE_TYPE_CONFIG_MAP ManagedKubeResourceType = "ConfigMap"
|
||||
)
|
||||
|
||||
type DynamicSecretLeaseRevocationPolicy string
|
||||
|
||||
const (
|
||||
DYNAMIC_SECRET_LEASE_REVOCATION_POLICY_ENABLED DynamicSecretLeaseRevocationPolicy = "Revoke"
|
||||
)
|
||||
|
||||
var ErrInvalidLease = errors.New("invalid dynamic secret lease")
|
||||
@@ -1,238 +0,0 @@
|
||||
/*
|
||||
Copyright 2025.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
infisicaldynamicsecret "github.com/Infisical/infisical/k8-operator/internal/services/infisicaldynamicsecret"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/builder"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
"sigs.k8s.io/controller-runtime/pkg/event"
|
||||
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
||||
|
||||
secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
||||
"github.com/Infisical/infisical/k8-operator/internal/constants"
|
||||
"github.com/Infisical/infisical/k8-operator/internal/controllerhelpers"
|
||||
"github.com/Infisical/infisical/k8-operator/internal/util"
|
||||
"github.com/go-logr/logr"
|
||||
)
|
||||
|
||||
// InfisicalDynamicSecretReconciler reconciles a InfisicalDynamicSecret object
|
||||
type InfisicalDynamicSecretReconciler struct {
|
||||
client.Client
|
||||
BaseLogger logr.Logger
|
||||
Scheme *runtime.Scheme
|
||||
Random *rand.Rand
|
||||
Namespace string
|
||||
IsNamespaceScoped bool
|
||||
}
|
||||
|
||||
var infisicalDynamicSecretsResourceVariablesMap map[string]util.ResourceVariables = make(map[string]util.ResourceVariables)
|
||||
|
||||
func (r *InfisicalDynamicSecretReconciler) GetLogger(req ctrl.Request) logr.Logger {
|
||||
return r.BaseLogger.WithValues("infisicaldynamicsecret", req.NamespacedName)
|
||||
}
|
||||
|
||||
// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicaldynamicsecrets,verbs=get;list;watch;create;update;patch;delete
|
||||
// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicaldynamicsecrets/status,verbs=get;update;patch
|
||||
// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicaldynamicsecrets/finalizers,verbs=update
|
||||
// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;delete
|
||||
// +kubebuilder:rbac:groups="",resources=configmaps,verbs=get;list;watch;create;update;delete
|
||||
// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=list;watch;get;update
|
||||
// +kubebuilder:rbac:groups="",resources=serviceaccounts,verbs=get;list;watch
|
||||
//+kubebuilder:rbac:groups="",resources=pods,verbs=get;list
|
||||
//+kubebuilder:rbac:groups="authentication.k8s.io",resources=tokenreviews,verbs=create
|
||||
//+kubebuilder:rbac:groups="",resources=serviceaccounts/token,verbs=create
|
||||
|
||||
func (r *InfisicalDynamicSecretReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
|
||||
logger := r.GetLogger(req)
|
||||
|
||||
var infisicalDynamicSecretCRD secretsv1alpha1.InfisicalDynamicSecret
|
||||
requeueTime := time.Second * 5
|
||||
|
||||
err := r.Get(ctx, req.NamespacedName, &infisicalDynamicSecretCRD)
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
logger.Info("Infisical Dynamic Secret CRD not found")
|
||||
return ctrl.Result{
|
||||
Requeue: false,
|
||||
}, nil
|
||||
} else {
|
||||
logger.Error(err, "Unable to fetch Infisical Dynamic Secret CRD from cluster")
|
||||
return ctrl.Result{
|
||||
RequeueAfter: requeueTime,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Add finalizer if it doesn't exist
|
||||
if !controllerutil.ContainsFinalizer(&infisicalDynamicSecretCRD, constants.INFISICAL_DYNAMIC_SECRET_FINALIZER_NAME) {
|
||||
controllerutil.AddFinalizer(&infisicalDynamicSecretCRD, constants.INFISICAL_DYNAMIC_SECRET_FINALIZER_NAME)
|
||||
if err := r.Update(ctx, &infisicalDynamicSecretCRD); err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
}
|
||||
|
||||
// Check if it's being deleted
|
||||
if !infisicalDynamicSecretCRD.DeletionTimestamp.IsZero() {
|
||||
logger.Info("Handling deletion of InfisicalDynamicSecret")
|
||||
if controllerutil.ContainsFinalizer(&infisicalDynamicSecretCRD, constants.INFISICAL_DYNAMIC_SECRET_FINALIZER_NAME) {
|
||||
// We remove finalizers before running deletion logic to be completely safe from stuck resources
|
||||
infisicalDynamicSecretCRD.ObjectMeta.Finalizers = []string{}
|
||||
if err := r.Update(ctx, &infisicalDynamicSecretCRD); err != nil {
|
||||
logger.Error(err, fmt.Sprintf("Error removing finalizers from InfisicalDynamicSecret %s", infisicalDynamicSecretCRD.Name))
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
// Initialize the business logic handler
|
||||
handler := infisicaldynamicsecret.NewInfisicalDynamicSecretHandler(r.Client, r.Scheme, r.IsNamespaceScoped)
|
||||
|
||||
err := handler.HandleLeaseRevocation(ctx, logger, &infisicalDynamicSecretCRD, infisicalDynamicSecretsResourceVariablesMap)
|
||||
|
||||
if infisicalDynamicSecretsResourceVariablesMap != nil {
|
||||
if rv, ok := infisicalDynamicSecretsResourceVariablesMap[string(infisicalDynamicSecretCRD.GetUID())]; ok {
|
||||
rv.CancelCtx()
|
||||
delete(infisicalDynamicSecretsResourceVariablesMap, string(infisicalDynamicSecretCRD.GetUID()))
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err // Even if this fails, we still want to delete the CRD
|
||||
}
|
||||
|
||||
}
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
// Get modified/default config
|
||||
infisicalConfig, err := controllerhelpers.GetInfisicalConfigMap(ctx, r.Client, r.IsNamespaceScoped)
|
||||
if err != nil {
|
||||
logger.Error(err, fmt.Sprintf("unable to fetch infisical-config. Will requeue after [requeueTime=%v]", requeueTime))
|
||||
return ctrl.Result{
|
||||
RequeueAfter: requeueTime,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Initialize the business logic handler
|
||||
handler := infisicaldynamicsecret.NewInfisicalDynamicSecretHandler(r.Client, r.Scheme, r.IsNamespaceScoped)
|
||||
|
||||
// Setup API configuration through business logic
|
||||
err = handler.SetupAPIConfig(infisicalDynamicSecretCRD, infisicalConfig)
|
||||
if err != nil {
|
||||
logger.Error(err, fmt.Sprintf("unable to setup API configuration. Will requeue after [requeueTime=%v]", requeueTime))
|
||||
return ctrl.Result{
|
||||
RequeueAfter: requeueTime,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Handle CA certificate through business logic
|
||||
err = handler.HandleCACertificate(ctx, infisicalDynamicSecretCRD)
|
||||
if err != nil {
|
||||
logger.Error(err, fmt.Sprintf("unable to handle CA certificate. Will requeue after [requeueTime=%v]", requeueTime))
|
||||
return ctrl.Result{
|
||||
RequeueAfter: requeueTime,
|
||||
}, nil
|
||||
}
|
||||
|
||||
nextReconcile, err := handler.ReconcileInfisicalDynamicSecret(ctx, logger, &infisicalDynamicSecretCRD, infisicalDynamicSecretsResourceVariablesMap)
|
||||
handler.SetReconcileConditionStatus(ctx, logger, &infisicalDynamicSecretCRD, err)
|
||||
|
||||
if err == nil && nextReconcile.Seconds() >= 5 {
|
||||
requeueTime = nextReconcile
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logger.Error(err, fmt.Sprintf("unable to reconcile Infisical Dynamic Secret. Will requeue after [requeueTime=%v]", requeueTime))
|
||||
return ctrl.Result{
|
||||
RequeueAfter: requeueTime,
|
||||
}, nil
|
||||
}
|
||||
|
||||
numDeployments, err := controllerhelpers.ReconcileDeploymentsWithManagedSecrets(ctx, r.Client, logger, infisicalDynamicSecretCRD.Spec.ManagedSecretReference, r.IsNamespaceScoped)
|
||||
handler.SetReconcileAutoRedeploymentConditionStatus(ctx, logger, &infisicalDynamicSecretCRD, numDeployments, err)
|
||||
|
||||
if err != nil {
|
||||
logger.Error(err, fmt.Sprintf("unable to reconcile auto redeployment. Will requeue after [requeueTime=%v]", requeueTime))
|
||||
return ctrl.Result{
|
||||
RequeueAfter: requeueTime,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Sync again after the specified time
|
||||
logger.Info(fmt.Sprintf("Next reconciliation in [requeueTime=%v]", requeueTime))
|
||||
return ctrl.Result{
|
||||
RequeueAfter: requeueTime,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *InfisicalDynamicSecretReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
|
||||
// Custom predicate that allows both spec changes and deletions
|
||||
specChangeOrDelete := predicate.Funcs{
|
||||
UpdateFunc: func(e event.UpdateEvent) bool {
|
||||
// Only reconcile if spec/generation changed
|
||||
|
||||
isSpecOrGenerationChange := e.ObjectOld.GetGeneration() != e.ObjectNew.GetGeneration()
|
||||
|
||||
if isSpecOrGenerationChange {
|
||||
if infisicalDynamicSecretsResourceVariablesMap != nil {
|
||||
if rv, ok := infisicalDynamicSecretsResourceVariablesMap[string(e.ObjectNew.GetUID())]; ok {
|
||||
rv.CancelCtx()
|
||||
delete(infisicalDynamicSecretsResourceVariablesMap, string(e.ObjectNew.GetUID()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return isSpecOrGenerationChange
|
||||
},
|
||||
DeleteFunc: func(e event.DeleteEvent) bool {
|
||||
// Always reconcile on deletion
|
||||
|
||||
if infisicalDynamicSecretsResourceVariablesMap != nil {
|
||||
if rv, ok := infisicalDynamicSecretsResourceVariablesMap[string(e.Object.GetUID())]; ok {
|
||||
rv.CancelCtx()
|
||||
delete(infisicalDynamicSecretsResourceVariablesMap, string(e.Object.GetUID()))
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
CreateFunc: func(e event.CreateEvent) bool {
|
||||
// Reconcile on creation
|
||||
return true
|
||||
},
|
||||
GenericFunc: func(e event.GenericEvent) bool {
|
||||
// Ignore generic events
|
||||
return false
|
||||
},
|
||||
}
|
||||
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
For(&secretsv1alpha1.InfisicalDynamicSecret{}, builder.WithPredicates(
|
||||
specChangeOrDelete,
|
||||
)).
|
||||
Complete(r)
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
Copyright 2025.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
||||
)
|
||||
|
||||
var _ = Describe("InfisicalDynamicSecret Controller", func() {
|
||||
Context("When reconciling a resource", func() {
|
||||
const resourceName = "test-resource"
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
typeNamespacedName := types.NamespacedName{
|
||||
Name: resourceName,
|
||||
Namespace: "default", // TODO(user):Modify as needed
|
||||
}
|
||||
infisicaldynamicsecret := &secretsv1alpha1.InfisicalDynamicSecret{}
|
||||
|
||||
BeforeEach(func() {
|
||||
By("creating the custom resource for the Kind InfisicalDynamicSecret")
|
||||
err := k8sClient.Get(ctx, typeNamespacedName, infisicaldynamicsecret)
|
||||
if err != nil && errors.IsNotFound(err) {
|
||||
resource := &secretsv1alpha1.InfisicalDynamicSecret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: resourceName,
|
||||
Namespace: "default",
|
||||
},
|
||||
// TODO(user): Specify other spec details if needed.
|
||||
}
|
||||
Expect(k8sClient.Create(ctx, resource)).To(Succeed())
|
||||
}
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
// TODO(user): Cleanup logic after each test, like removing the resource instance.
|
||||
resource := &secretsv1alpha1.InfisicalDynamicSecret{}
|
||||
err := k8sClient.Get(ctx, typeNamespacedName, resource)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Cleanup the specific resource instance InfisicalDynamicSecret")
|
||||
Expect(k8sClient.Delete(ctx, resource)).To(Succeed())
|
||||
})
|
||||
It("should successfully reconcile the resource", func() {
|
||||
By("Reconciling the created resource")
|
||||
controllerReconciler := &InfisicalDynamicSecretReconciler{
|
||||
Client: k8sClient,
|
||||
Scheme: k8sClient.Scheme(),
|
||||
}
|
||||
|
||||
_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
|
||||
NamespacedName: typeNamespacedName,
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
// TODO(user): Add more specific assertions depending on your controller's reconciliation logic.
|
||||
// Example: If you expect a certain status condition after reconciliation, verify it here.
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,349 +0,0 @@
|
||||
/*
|
||||
Copyright 2025.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
infisicalpushsecret "github.com/Infisical/infisical/k8-operator/internal/services/infisicalpushsecret"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/builder"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
"sigs.k8s.io/controller-runtime/pkg/event"
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
|
||||
secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
||||
"github.com/Infisical/infisical/k8-operator/internal/constants"
|
||||
"github.com/Infisical/infisical/k8-operator/internal/controllerhelpers"
|
||||
"github.com/Infisical/infisical/k8-operator/internal/util"
|
||||
"github.com/go-logr/logr"
|
||||
)
|
||||
|
||||
// InfisicalPushSecretReconciler reconciles a InfisicalPushSecret object
|
||||
type InfisicalPushSecretReconciler struct {
|
||||
client.Client
|
||||
BaseLogger logr.Logger
|
||||
Scheme *runtime.Scheme
|
||||
IsNamespaceScoped bool
|
||||
Namespace string
|
||||
}
|
||||
|
||||
var infisicalPushSecretResourceVariablesMap map[string]util.ResourceVariables = make(map[string]util.ResourceVariables)
|
||||
|
||||
func (r *InfisicalPushSecretReconciler) GetLogger(req ctrl.Request) logr.Logger {
|
||||
return r.BaseLogger.WithValues("infisicalpushsecret", req.NamespacedName)
|
||||
}
|
||||
|
||||
//+kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicalpushsecrets,verbs=get;list;watch;create;update;patch;delete
|
||||
//+kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicalpushsecrets/status,verbs=get;update;patch
|
||||
//+kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicalpushsecrets/finalizers,verbs=update
|
||||
//+kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;delete
|
||||
//+kubebuilder:rbac:groups="",resources=configmaps,verbs=get;list;watch;create;update;delete
|
||||
//+kubebuilder:rbac:groups=apps,resources=deployments,verbs=list;watch;get;update
|
||||
//+kubebuilder:rbac:groups="",resources=serviceaccounts,verbs=get;list;watch
|
||||
//+kubebuilder:rbac:groups="",resources=pods,verbs=get;list
|
||||
//+kubebuilder:rbac:groups="authentication.k8s.io",resources=tokenreviews,verbs=create
|
||||
//+kubebuilder:rbac:groups="",resources=serviceaccounts/token,verbs=create
|
||||
//+kubebuilder:rbac:groups=secrets.infisical.com,resources=clustergenerators,verbs=get;list;watch;create;update;patch;delete
|
||||
|
||||
func (r *InfisicalPushSecretReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
|
||||
logger := r.GetLogger(req)
|
||||
|
||||
var infisicalPushSecretCRD secretsv1alpha1.InfisicalPushSecret
|
||||
requeueTime := time.Minute // seconds
|
||||
|
||||
err := r.Get(ctx, req.NamespacedName, &infisicalPushSecretCRD)
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
logger.Info("Infisical Push Secret CRD not found")
|
||||
// Initialize the business logic handler
|
||||
handler := infisicalpushsecret.NewInfisicalPushSecretHandler(r.Client, r.Scheme, r.IsNamespaceScoped)
|
||||
handler.DeleteManagedSecrets(ctx, logger, &infisicalPushSecretCRD, infisicalPushSecretResourceVariablesMap)
|
||||
|
||||
return ctrl.Result{
|
||||
Requeue: false,
|
||||
}, nil
|
||||
} else {
|
||||
logger.Error(err, "Unable to fetch Infisical Secret CRD from cluster")
|
||||
return ctrl.Result{
|
||||
RequeueAfter: requeueTime,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Add finalizer if it doesn't exist
|
||||
if !controllerutil.ContainsFinalizer(&infisicalPushSecretCRD, constants.INFISICAL_PUSH_SECRET_FINALIZER_NAME) {
|
||||
controllerutil.AddFinalizer(&infisicalPushSecretCRD, constants.INFISICAL_PUSH_SECRET_FINALIZER_NAME)
|
||||
if err := r.Update(ctx, &infisicalPushSecretCRD); err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
}
|
||||
|
||||
// Check if it's being deleted
|
||||
if !infisicalPushSecretCRD.DeletionTimestamp.IsZero() {
|
||||
logger.Info("Handling deletion of InfisicalPushSecret")
|
||||
if controllerutil.ContainsFinalizer(&infisicalPushSecretCRD, constants.INFISICAL_PUSH_SECRET_FINALIZER_NAME) {
|
||||
// We remove finalizers before running deletion logic to be completely safe from stuck resources
|
||||
infisicalPushSecretCRD.ObjectMeta.Finalizers = []string{}
|
||||
if err := r.Update(ctx, &infisicalPushSecretCRD); err != nil {
|
||||
logger.Error(err, fmt.Sprintf("Error removing finalizers from InfisicalPushSecret %s", infisicalPushSecretCRD.Name))
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
// Initialize the business logic handler
|
||||
handler := infisicalpushsecret.NewInfisicalPushSecretHandler(r.Client, r.Scheme, r.IsNamespaceScoped)
|
||||
|
||||
if err := handler.DeleteManagedSecrets(ctx, logger, &infisicalPushSecretCRD, infisicalPushSecretResourceVariablesMap); err != nil {
|
||||
return ctrl.Result{}, err // Even if this fails, we still want to delete the CRD
|
||||
}
|
||||
|
||||
}
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
if infisicalPushSecretCRD.Spec.Push.Secret == nil && infisicalPushSecretCRD.Spec.Push.Generators == nil {
|
||||
logger.Info("No secret or generators found, skipping reconciliation. Please define ")
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
duration, err := util.ConvertIntervalToDuration(infisicalPushSecretCRD.Spec.ResyncInterval)
|
||||
|
||||
if err != nil {
|
||||
// if resyncInterval is nil, we don't want to reconcile automatically
|
||||
if infisicalPushSecretCRD.Spec.ResyncInterval != nil {
|
||||
logger.Error(err, fmt.Sprintf("unable to convert resync interval to duration. Will requeue after [requeueTime=%v]", requeueTime))
|
||||
return ctrl.Result{
|
||||
RequeueAfter: requeueTime,
|
||||
}, nil
|
||||
} else {
|
||||
logger.Error(err, "unable to convert resync interval to duration")
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
}
|
||||
|
||||
requeueTime = duration
|
||||
|
||||
if requeueTime != 0 {
|
||||
logger.Info(fmt.Sprintf("Manual re-sync interval set. Interval: %v", requeueTime))
|
||||
}
|
||||
|
||||
// Check if the resource is already marked for deletion
|
||||
if infisicalPushSecretCRD.GetDeletionTimestamp() != nil {
|
||||
return ctrl.Result{
|
||||
Requeue: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Get modified/default config
|
||||
infisicalConfig, err := controllerhelpers.GetInfisicalConfigMap(ctx, r.Client, r.IsNamespaceScoped)
|
||||
if err != nil {
|
||||
if requeueTime != 0 {
|
||||
logger.Error(err, fmt.Sprintf("unable to fetch infisical-config. Will requeue after [requeueTime=%v]", requeueTime))
|
||||
return ctrl.Result{
|
||||
RequeueAfter: requeueTime,
|
||||
}, nil
|
||||
} else {
|
||||
logger.Error(err, "unable to fetch infisical-config")
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the business logic handler
|
||||
handler := infisicalpushsecret.NewInfisicalPushSecretHandler(r.Client, r.Scheme, r.IsNamespaceScoped)
|
||||
|
||||
// Setup API configuration through business logic
|
||||
err = handler.SetupAPIConfig(infisicalPushSecretCRD, infisicalConfig)
|
||||
if err != nil {
|
||||
if requeueTime != 0 {
|
||||
logger.Error(err, fmt.Sprintf("unable to setup API configuration. Will requeue after [requeueTime=%v]", requeueTime))
|
||||
return ctrl.Result{
|
||||
RequeueAfter: requeueTime,
|
||||
}, nil
|
||||
} else {
|
||||
logger.Error(err, "unable to setup API configuration")
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
}
|
||||
|
||||
// Handle CA certificate through business logic
|
||||
err = handler.HandleCACertificate(ctx, infisicalPushSecretCRD)
|
||||
if err != nil {
|
||||
if requeueTime != 0 {
|
||||
logger.Error(err, fmt.Sprintf("unable to fetch CA certificate. Will requeue after [requeueTime=%v]", requeueTime))
|
||||
return ctrl.Result{
|
||||
RequeueAfter: requeueTime,
|
||||
}, nil
|
||||
} else {
|
||||
logger.Error(err, "unable to fetch CA certificate")
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
}
|
||||
|
||||
err = handler.ReconcileInfisicalPushSecret(ctx, logger, &infisicalPushSecretCRD, infisicalPushSecretResourceVariablesMap)
|
||||
handler.SetReconcileStatusCondition(ctx, &infisicalPushSecretCRD, err)
|
||||
|
||||
if err != nil {
|
||||
if requeueTime != 0 {
|
||||
logger.Error(err, fmt.Sprintf("unable to reconcile Infisical Push Secret. Will requeue after [requeueTime=%v]", requeueTime))
|
||||
return ctrl.Result{
|
||||
RequeueAfter: requeueTime,
|
||||
}, nil
|
||||
} else {
|
||||
logger.Error(err, "unable to reconcile Infisical Push Secret")
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
}
|
||||
|
||||
// Sync again after the specified time
|
||||
if requeueTime != 0 {
|
||||
logger.Info(fmt.Sprintf("Operator will requeue after [%v]", requeueTime))
|
||||
return ctrl.Result{
|
||||
RequeueAfter: requeueTime,
|
||||
}, nil
|
||||
} else {
|
||||
logger.Info("Operator will reconcile on next spec change")
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (r *InfisicalPushSecretReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
// Custom predicate that allows both spec changes and deletions
|
||||
specChangeOrDelete := predicate.Funcs{
|
||||
UpdateFunc: func(e event.UpdateEvent) bool {
|
||||
// Only reconcile if spec/generation changed
|
||||
|
||||
isSpecOrGenerationChange := e.ObjectOld.GetGeneration() != e.ObjectNew.GetGeneration()
|
||||
|
||||
if isSpecOrGenerationChange {
|
||||
if infisicalPushSecretResourceVariablesMap != nil {
|
||||
if rv, ok := infisicalPushSecretResourceVariablesMap[string(e.ObjectNew.GetUID())]; ok {
|
||||
rv.CancelCtx()
|
||||
delete(infisicalPushSecretResourceVariablesMap, string(e.ObjectNew.GetUID()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return isSpecOrGenerationChange
|
||||
},
|
||||
DeleteFunc: func(e event.DeleteEvent) bool {
|
||||
// Always reconcile on deletion
|
||||
|
||||
if infisicalPushSecretResourceVariablesMap != nil {
|
||||
if rv, ok := infisicalPushSecretResourceVariablesMap[string(e.Object.GetUID())]; ok {
|
||||
rv.CancelCtx()
|
||||
delete(infisicalPushSecretResourceVariablesMap, string(e.Object.GetUID()))
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
CreateFunc: func(e event.CreateEvent) bool {
|
||||
// Reconcile on creation
|
||||
return true
|
||||
},
|
||||
GenericFunc: func(e event.GenericEvent) bool {
|
||||
// Ignore generic events
|
||||
return false
|
||||
},
|
||||
}
|
||||
|
||||
controllerManager := ctrl.NewControllerManagedBy(mgr).
|
||||
For(&secretsv1alpha1.InfisicalPushSecret{}, builder.WithPredicates(
|
||||
specChangeOrDelete,
|
||||
)).
|
||||
Watches(
|
||||
&corev1.Secret{},
|
||||
handler.EnqueueRequestsFromMapFunc(r.findPushSecretsForSecret),
|
||||
)
|
||||
|
||||
if !r.IsNamespaceScoped {
|
||||
r.BaseLogger.Info("Watching ClusterGenerators for non-namespace scoped operator")
|
||||
controllerManager.Watches(
|
||||
&secretsv1alpha1.ClusterGenerator{},
|
||||
handler.EnqueueRequestsFromMapFunc(r.findPushSecretsForClusterGenerator),
|
||||
)
|
||||
} else {
|
||||
r.BaseLogger.Info("Not watching ClusterGenerators for namespace scoped operator")
|
||||
}
|
||||
|
||||
return controllerManager.Complete(r)
|
||||
}
|
||||
|
||||
func (r *InfisicalPushSecretReconciler) findPushSecretsForClusterGenerator(ctx context.Context, o client.Object) []reconcile.Request {
|
||||
pushSecrets := &secretsv1alpha1.InfisicalPushSecretList{}
|
||||
if err := r.List(ctx, pushSecrets); err != nil {
|
||||
return []reconcile.Request{}
|
||||
}
|
||||
|
||||
clusterGenerator, ok := o.(*secretsv1alpha1.ClusterGenerator)
|
||||
if !ok {
|
||||
return []reconcile.Request{}
|
||||
}
|
||||
|
||||
requests := []reconcile.Request{}
|
||||
|
||||
for _, pushSecret := range pushSecrets.Items {
|
||||
if pushSecret.Spec.Push.Generators != nil {
|
||||
for _, generator := range pushSecret.Spec.Push.Generators {
|
||||
if generator.GeneratorRef.Name == clusterGenerator.GetName() {
|
||||
requests = append(requests, reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{
|
||||
Name: pushSecret.GetName(),
|
||||
Namespace: pushSecret.GetNamespace(),
|
||||
},
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return requests
|
||||
}
|
||||
|
||||
func (r *InfisicalPushSecretReconciler) findPushSecretsForSecret(ctx context.Context, o client.Object) []reconcile.Request {
|
||||
pushSecrets := &secretsv1alpha1.InfisicalPushSecretList{}
|
||||
if err := r.List(ctx, pushSecrets); err != nil {
|
||||
return []reconcile.Request{}
|
||||
}
|
||||
|
||||
requests := []reconcile.Request{}
|
||||
for _, pushSecret := range pushSecrets.Items {
|
||||
if pushSecret.Spec.Push.Secret != nil &&
|
||||
pushSecret.Spec.Push.Secret.SecretName == o.GetName() &&
|
||||
pushSecret.Spec.Push.Secret.SecretNamespace == o.GetNamespace() {
|
||||
requests = append(requests, reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{
|
||||
Name: pushSecret.GetName(),
|
||||
Namespace: pushSecret.GetNamespace(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return requests
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
Copyright 2025.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
||||
)
|
||||
|
||||
var _ = Describe("InfisicalPushSecret Controller", func() {
|
||||
Context("When reconciling a resource", func() {
|
||||
const resourceName = "test-resource"
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
typeNamespacedName := types.NamespacedName{
|
||||
Name: resourceName,
|
||||
Namespace: "default", // TODO(user):Modify as needed
|
||||
}
|
||||
infisicalpushsecret := &secretsv1alpha1.InfisicalPushSecret{}
|
||||
|
||||
BeforeEach(func() {
|
||||
By("creating the custom resource for the Kind InfisicalPushSecret")
|
||||
err := k8sClient.Get(ctx, typeNamespacedName, infisicalpushsecret)
|
||||
if err != nil && errors.IsNotFound(err) {
|
||||
resource := &secretsv1alpha1.InfisicalPushSecret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: resourceName,
|
||||
Namespace: "default",
|
||||
},
|
||||
// TODO(user): Specify other spec details if needed.
|
||||
}
|
||||
Expect(k8sClient.Create(ctx, resource)).To(Succeed())
|
||||
}
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
// TODO(user): Cleanup logic after each test, like removing the resource instance.
|
||||
resource := &secretsv1alpha1.InfisicalPushSecret{}
|
||||
err := k8sClient.Get(ctx, typeNamespacedName, resource)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Cleanup the specific resource instance InfisicalPushSecret")
|
||||
Expect(k8sClient.Delete(ctx, resource)).To(Succeed())
|
||||
})
|
||||
It("should successfully reconcile the resource", func() {
|
||||
By("Reconciling the created resource")
|
||||
controllerReconciler := &InfisicalPushSecretReconciler{
|
||||
Client: k8sClient,
|
||||
Scheme: k8sClient.Scheme(),
|
||||
}
|
||||
|
||||
_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
|
||||
NamespacedName: typeNamespacedName,
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
// TODO(user): Add more specific assertions depending on your controller's reconciliation logic.
|
||||
// Example: If you expect a certain status condition after reconciliation, verify it here.
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,255 +0,0 @@
|
||||
/*
|
||||
Copyright 2025.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
defaultErrors "errors"
|
||||
|
||||
infisicalsecret "github.com/Infisical/infisical/k8-operator/internal/services/infisicalsecret"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/builder"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/event"
|
||||
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
||||
"sigs.k8s.io/controller-runtime/pkg/source"
|
||||
|
||||
secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
||||
"github.com/Infisical/infisical/k8-operator/internal/controllerhelpers"
|
||||
"github.com/Infisical/infisical/k8-operator/internal/util"
|
||||
"github.com/go-logr/logr"
|
||||
)
|
||||
|
||||
// InfisicalSecretReconciler reconciles a InfisicalSecret object
|
||||
type InfisicalSecretReconciler struct {
|
||||
client.Client
|
||||
BaseLogger logr.Logger
|
||||
Scheme *runtime.Scheme
|
||||
|
||||
SourceCh chan event.TypedGenericEvent[client.Object]
|
||||
Namespace string
|
||||
IsNamespaceScoped bool
|
||||
}
|
||||
|
||||
var infisicalSecretResourceVariablesMap map[string]util.ResourceVariables = make(map[string]util.ResourceVariables)
|
||||
|
||||
func (r *InfisicalSecretReconciler) GetLogger(req ctrl.Request) logr.Logger {
|
||||
return r.BaseLogger.WithValues("infisicalsecret", req.NamespacedName)
|
||||
}
|
||||
|
||||
//+kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicalsecrets,verbs=get;list;watch;create;update;patch;delete
|
||||
//+kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicalsecrets/status,verbs=get;update;patch
|
||||
//+kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicalsecrets/finalizers,verbs=update
|
||||
//+kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;delete
|
||||
//+kubebuilder:rbac:groups="",resources=configmaps,verbs=get;list;watch;create;update;delete
|
||||
//+kubebuilder:rbac:groups=apps,resources=deployments;daemonsets;statefulsets,verbs=list;watch;get;update
|
||||
//+kubebuilder:rbac:groups="",resources=serviceaccounts,verbs=get;list;watch
|
||||
//+kubebuilder:rbac:groups="",resources=pods,verbs=get;list
|
||||
//+kubebuilder:rbac:groups="authentication.k8s.io",resources=tokenreviews,verbs=create
|
||||
//+kubebuilder:rbac:groups="",resources=serviceaccounts/token,verbs=create
|
||||
|
||||
// Reconcile is part of the main kubernetes reconciliation loop which aims to
|
||||
// move the current state of the cluster closer to the desired state.
|
||||
// TODO(user): Modify the Reconcile function to compare the state specified by
|
||||
// the InfisicalSecret object against the actual cluster state, and then
|
||||
// perform operations to make the cluster state reflect the state specified by
|
||||
// the user.
|
||||
//
|
||||
// For more details, check Reconcile and its Result here:
|
||||
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.21.0/pkg/reconcile
|
||||
func (r *InfisicalSecretReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
logger := r.GetLogger(req)
|
||||
|
||||
var infisicalSecretCRD secretsv1alpha1.InfisicalSecret
|
||||
requeueTime := time.Minute // seconds
|
||||
|
||||
err := r.Get(ctx, req.NamespacedName, &infisicalSecretCRD)
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
return ctrl.Result{
|
||||
Requeue: false,
|
||||
}, nil
|
||||
} else {
|
||||
logger.Error(err, "unable to fetch Infisical Secret CRD from cluster")
|
||||
return ctrl.Result{
|
||||
RequeueAfter: requeueTime,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// It's important we don't directly modify the CRD object, so we create a copy of it and move existing data into it.
|
||||
managedKubeSecretReferences := infisicalSecretCRD.Spec.ManagedKubeSecretReferences
|
||||
managedKubeConfigMapReferences := infisicalSecretCRD.Spec.ManagedKubeConfigMapReferences
|
||||
|
||||
if infisicalSecretCRD.Spec.ManagedSecretReference.SecretName != "" && managedKubeSecretReferences != nil && len(managedKubeSecretReferences) > 0 {
|
||||
errMessage := "InfisicalSecret CRD cannot have both managedSecretReference and managedKubeSecretReferences"
|
||||
logger.Error(defaultErrors.New(errMessage), errMessage)
|
||||
return ctrl.Result{}, defaultErrors.New(errMessage)
|
||||
}
|
||||
|
||||
if infisicalSecretCRD.Spec.ManagedSecretReference.SecretName != "" {
|
||||
logger.Info("\n\n\nThe field `managedSecretReference` will be deprecated in the near future, please use `managedKubeSecretReferences` instead.\n\nRefer to the documentation for more information: https://infisical.com/docs/integrations/platforms/kubernetes/infisical-secret-crd\n\n\n")
|
||||
|
||||
if managedKubeSecretReferences == nil {
|
||||
managedKubeSecretReferences = []secretsv1alpha1.ManagedKubeSecretConfig{}
|
||||
}
|
||||
managedKubeSecretReferences = append(managedKubeSecretReferences, infisicalSecretCRD.Spec.ManagedSecretReference)
|
||||
}
|
||||
|
||||
if len(managedKubeSecretReferences) == 0 && len(managedKubeConfigMapReferences) == 0 {
|
||||
errMessage := "InfisicalSecret CRD must have at least one managed secret reference set in the `managedKubeSecretReferences` or `managedKubeConfigMapReferences` field"
|
||||
logger.Error(defaultErrors.New(errMessage), errMessage)
|
||||
return ctrl.Result{}, defaultErrors.New(errMessage)
|
||||
}
|
||||
|
||||
// Remove finalizers if they exist. This is to support previous InfisicalSecret CRD's that have finalizers on them.
|
||||
// In order to delete secrets with finalizers, we first remove the finalizers so we can use the simplified and improved deletion process
|
||||
if !infisicalSecretCRD.ObjectMeta.DeletionTimestamp.IsZero() && len(infisicalSecretCRD.ObjectMeta.Finalizers) > 0 {
|
||||
infisicalSecretCRD.ObjectMeta.Finalizers = []string{}
|
||||
if err := r.Update(ctx, &infisicalSecretCRD); err != nil {
|
||||
logger.Error(err, fmt.Sprintf("Error removing finalizers from Infisical Secret %s", infisicalSecretCRD.Name))
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
// Our finalizers have been removed, so the reconciler can do nothing.
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
if infisicalSecretCRD.Spec.ResyncInterval != 0 {
|
||||
requeueTime = time.Second * time.Duration(infisicalSecretCRD.Spec.ResyncInterval)
|
||||
logger.Info(fmt.Sprintf("Manual re-sync interval set. Interval: %v", requeueTime))
|
||||
|
||||
} else {
|
||||
logger.Info(fmt.Sprintf("Re-sync interval set. Interval: %v", requeueTime))
|
||||
}
|
||||
|
||||
// Check if the resource is already marked for deletion
|
||||
if infisicalSecretCRD.GetDeletionTimestamp() != nil {
|
||||
return ctrl.Result{
|
||||
Requeue: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Get modified/default config
|
||||
infisicalConfig, err := controllerhelpers.GetInfisicalConfigMap(ctx, r.Client, r.IsNamespaceScoped)
|
||||
if err != nil {
|
||||
logger.Error(err, fmt.Sprintf("unable to fetch infisical-config. Will requeue after [requeueTime=%v]", requeueTime))
|
||||
return ctrl.Result{
|
||||
RequeueAfter: requeueTime,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Initialize the business logic handler
|
||||
handler := infisicalsecret.NewInfisicalSecretHandler(r.Client, r.Scheme, r.IsNamespaceScoped)
|
||||
|
||||
// Setup API configuration through business logic
|
||||
err = handler.SetupAPIConfig(infisicalSecretCRD, infisicalConfig)
|
||||
if err != nil {
|
||||
logger.Error(err, fmt.Sprintf("unable to setup API configuration. Will requeue after [requeueTime=%v]", requeueTime))
|
||||
return ctrl.Result{
|
||||
RequeueAfter: requeueTime,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Handle CA certificate through business logic
|
||||
err = handler.HandleCACertificate(ctx, infisicalSecretCRD)
|
||||
if err != nil {
|
||||
logger.Error(err, fmt.Sprintf("unable to handle CA certificate. Will requeue after [requeueTime=%v]", requeueTime))
|
||||
return ctrl.Result{
|
||||
RequeueAfter: requeueTime,
|
||||
}, nil
|
||||
}
|
||||
|
||||
secretsCount, err := handler.ReconcileInfisicalSecret(ctx, logger, &infisicalSecretCRD, managedKubeSecretReferences, managedKubeConfigMapReferences, infisicalSecretResourceVariablesMap)
|
||||
handler.SetReadyToSyncSecretsConditions(ctx, logger, &infisicalSecretCRD, secretsCount, err)
|
||||
|
||||
if err != nil {
|
||||
logger.Error(err, fmt.Sprintf("unable to reconcile InfisicalSecret. Will requeue after [requeueTime=%v]", requeueTime))
|
||||
return ctrl.Result{
|
||||
RequeueAfter: requeueTime,
|
||||
}, nil
|
||||
}
|
||||
|
||||
numDeployments, err := controllerhelpers.ReconcileDeploymentsWithMultipleManagedSecrets(ctx, r.Client, logger, managedKubeSecretReferences, r.IsNamespaceScoped)
|
||||
handler.SetInfisicalAutoRedeploymentReady(ctx, logger, &infisicalSecretCRD, numDeployments, err)
|
||||
|
||||
if err != nil {
|
||||
logger.Error(err, fmt.Sprintf("unable to reconcile auto redeployment. Will requeue after [requeueTime=%v]", requeueTime))
|
||||
return ctrl.Result{
|
||||
RequeueAfter: requeueTime,
|
||||
}, nil
|
||||
}
|
||||
|
||||
if infisicalSecretCRD.Spec.InstantUpdates {
|
||||
if err := handler.OpenInstantUpdatesStream(ctx, logger, &infisicalSecretCRD, infisicalSecretResourceVariablesMap, r.SourceCh); err != nil {
|
||||
requeueTime = time.Second * 10
|
||||
logger.Info(fmt.Sprintf("event stream failed. Will requeue after [requeueTime=%v] [error=%s]", requeueTime, err.Error()))
|
||||
return ctrl.Result{
|
||||
RequeueAfter: requeueTime,
|
||||
}, nil
|
||||
}
|
||||
|
||||
logger.Info("Instant updates are enabled")
|
||||
} else {
|
||||
handler.CloseInstantUpdatesStream(ctx, logger, &infisicalSecretCRD, infisicalSecretResourceVariablesMap)
|
||||
}
|
||||
|
||||
// Sync again after the specified time
|
||||
logger.Info(fmt.Sprintf("Successfully synced %d secrets. Operator will requeue after [%v]", secretsCount, requeueTime))
|
||||
return ctrl.Result{
|
||||
RequeueAfter: requeueTime,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *InfisicalSecretReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
r.SourceCh = make(chan event.TypedGenericEvent[client.Object])
|
||||
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
WatchesRawSource(
|
||||
source.Channel[client.Object](r.SourceCh, &util.EnqueueDelayedEventHandler{Delay: time.Second * 10}),
|
||||
).
|
||||
For(&secretsv1alpha1.InfisicalSecret{}, builder.WithPredicates(predicate.Funcs{
|
||||
UpdateFunc: func(e event.UpdateEvent) bool {
|
||||
if e.ObjectOld.GetGeneration() == e.ObjectNew.GetGeneration() {
|
||||
return false // Skip reconciliation for status-only changes
|
||||
}
|
||||
|
||||
if infisicalSecretResourceVariablesMap != nil {
|
||||
if rv, ok := infisicalSecretResourceVariablesMap[string(e.ObjectNew.GetUID())]; ok {
|
||||
rv.CancelCtx()
|
||||
delete(infisicalSecretResourceVariablesMap, string(e.ObjectNew.GetUID()))
|
||||
}
|
||||
}
|
||||
return true
|
||||
},
|
||||
DeleteFunc: func(e event.DeleteEvent) bool {
|
||||
if infisicalSecretResourceVariablesMap != nil {
|
||||
if rv, ok := infisicalSecretResourceVariablesMap[string(e.Object.GetUID())]; ok {
|
||||
rv.CancelCtx()
|
||||
delete(infisicalSecretResourceVariablesMap, string(e.Object.GetUID()))
|
||||
}
|
||||
}
|
||||
return true
|
||||
},
|
||||
})).
|
||||
Complete(r)
|
||||
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
Copyright 2025.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
||||
)
|
||||
|
||||
var _ = Describe("InfisicalSecret Controller", func() {
|
||||
Context("When reconciling a resource", func() {
|
||||
const resourceName = "test-resource"
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
typeNamespacedName := types.NamespacedName{
|
||||
Name: resourceName,
|
||||
Namespace: "default", // TODO(user):Modify as needed
|
||||
}
|
||||
infisicalsecret := &secretsv1alpha1.InfisicalSecret{}
|
||||
|
||||
BeforeEach(func() {
|
||||
By("creating the custom resource for the Kind InfisicalSecret")
|
||||
err := k8sClient.Get(ctx, typeNamespacedName, infisicalsecret)
|
||||
if err != nil && errors.IsNotFound(err) {
|
||||
resource := &secretsv1alpha1.InfisicalSecret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: resourceName,
|
||||
Namespace: "default",
|
||||
},
|
||||
// TODO(user): Specify other spec details if needed.
|
||||
}
|
||||
Expect(k8sClient.Create(ctx, resource)).To(Succeed())
|
||||
}
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
// TODO(user): Cleanup logic after each test, like removing the resource instance.
|
||||
resource := &secretsv1alpha1.InfisicalSecret{}
|
||||
err := k8sClient.Get(ctx, typeNamespacedName, resource)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Cleanup the specific resource instance InfisicalSecret")
|
||||
Expect(k8sClient.Delete(ctx, resource)).To(Succeed())
|
||||
})
|
||||
It("should successfully reconcile the resource", func() {
|
||||
By("Reconciling the created resource")
|
||||
controllerReconciler := &InfisicalSecretReconciler{
|
||||
Client: k8sClient,
|
||||
Scheme: k8sClient.Scheme(),
|
||||
}
|
||||
|
||||
_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
|
||||
NamespacedName: typeNamespacedName,
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
// TODO(user): Add more specific assertions depending on your controller's reconciliation logic.
|
||||
// Example: If you expect a certain status condition after reconciliation, verify it here.
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,116 +0,0 @@
|
||||
/*
|
||||
Copyright 2025.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/envtest"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/log"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log/zap"
|
||||
|
||||
secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
||||
// +kubebuilder:scaffold:imports
|
||||
)
|
||||
|
||||
// These tests use Ginkgo (BDD-style Go testing framework). Refer to
|
||||
// http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
|
||||
|
||||
var (
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
testEnv *envtest.Environment
|
||||
cfg *rest.Config
|
||||
k8sClient client.Client
|
||||
)
|
||||
|
||||
func TestControllers(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
|
||||
RunSpecs(t, "Controller Suite")
|
||||
}
|
||||
|
||||
var _ = BeforeSuite(func() {
|
||||
logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
|
||||
|
||||
ctx, cancel = context.WithCancel(context.TODO())
|
||||
|
||||
var err error
|
||||
err = secretsv1alpha1.AddToScheme(scheme.Scheme)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// +kubebuilder:scaffold:scheme
|
||||
|
||||
By("bootstrapping test environment")
|
||||
testEnv = &envtest.Environment{
|
||||
CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")},
|
||||
ErrorIfCRDPathMissing: true,
|
||||
}
|
||||
|
||||
// Retrieve the first found binary directory to allow running tests from IDEs
|
||||
if getFirstFoundEnvTestBinaryDir() != "" {
|
||||
testEnv.BinaryAssetsDirectory = getFirstFoundEnvTestBinaryDir()
|
||||
}
|
||||
|
||||
// cfg is defined in this file globally.
|
||||
cfg, err = testEnv.Start()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(cfg).NotTo(BeNil())
|
||||
|
||||
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(k8sClient).NotTo(BeNil())
|
||||
})
|
||||
|
||||
var _ = AfterSuite(func() {
|
||||
By("tearing down the test environment")
|
||||
cancel()
|
||||
err := testEnv.Stop()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
// getFirstFoundEnvTestBinaryDir locates the first binary in the specified path.
|
||||
// ENVTEST-based tests depend on specific binaries, usually located in paths set by
|
||||
// controller-runtime. When running tests directly (e.g., via an IDE) without using
|
||||
// Makefile targets, the 'BinaryAssetsDirectory' must be explicitly configured.
|
||||
//
|
||||
// This function streamlines the process by finding the required binaries, similar to
|
||||
// setting the 'KUBEBUILDER_ASSETS' environment variable. To ensure the binaries are
|
||||
// properly set up, run 'make setup-envtest' beforehand.
|
||||
func getFirstFoundEnvTestBinaryDir() string {
|
||||
basePath := filepath.Join("..", "..", "bin", "k8s")
|
||||
entries, err := os.ReadDir(basePath)
|
||||
if err != nil {
|
||||
logf.Log.Error(err, "Failed to read directory", "path", basePath)
|
||||
return ""
|
||||
}
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
return filepath.Join(basePath, entry.Name())
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -1,304 +0,0 @@
|
||||
package controllerhelpers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
||||
"github.com/Infisical/infisical/k8-operator/internal/constants"
|
||||
"github.com/Infisical/infisical/k8-operator/internal/util"
|
||||
"github.com/go-logr/logr"
|
||||
v1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
k8Errors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
controllerClient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
const DEPLOYMENT_SECRET_NAME_ANNOTATION_PREFIX = "secrets.infisical.com/managed-secret"
|
||||
const AUTO_RELOAD_DEPLOYMENT_ANNOTATION = "secrets.infisical.com/auto-reload" // needs to be set to true for a deployment to start auto redeploying
|
||||
|
||||
func ReconcileDeploymentsWithManagedSecrets(ctx context.Context, client controllerClient.Client, logger logr.Logger, managedSecret v1alpha1.ManagedKubeSecretConfig, isNamespaceScoped bool) (int, error) {
|
||||
listOfDeployments := &v1.DeploymentList{}
|
||||
|
||||
err := client.List(ctx, listOfDeployments, &controllerClient.ListOptions{Namespace: managedSecret.SecretNamespace})
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("unable to get deployments in the [namespace=%v] [err=%v]", managedSecret.SecretNamespace, err)
|
||||
}
|
||||
|
||||
listOfDaemonSets := &v1.DaemonSetList{}
|
||||
err = client.List(ctx, listOfDaemonSets, &controllerClient.ListOptions{Namespace: managedSecret.SecretNamespace})
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("unable to get daemonSets in the [namespace=%v] [err=%v]", managedSecret.SecretNamespace, err)
|
||||
}
|
||||
|
||||
listOfStatefulSets := &v1.StatefulSetList{}
|
||||
err = client.List(ctx, listOfStatefulSets, &controllerClient.ListOptions{Namespace: managedSecret.SecretNamespace})
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("unable to get statefulSets in the [namespace=%v] [err=%v]", managedSecret.SecretNamespace, err)
|
||||
}
|
||||
|
||||
managedKubeSecretNameAndNamespace := types.NamespacedName{
|
||||
Namespace: managedSecret.SecretNamespace,
|
||||
Name: managedSecret.SecretName,
|
||||
}
|
||||
|
||||
managedKubeSecret := &corev1.Secret{}
|
||||
err = client.Get(ctx, managedKubeSecretNameAndNamespace, managedKubeSecret)
|
||||
if err != nil {
|
||||
if util.IsNamespaceScopedError(err, isNamespaceScoped) {
|
||||
return 0, fmt.Errorf("unable to fetch Kubernetes secret to update deployment. Your Operator is namespace scoped, and cannot read secrets outside of its namespace. Please ensure the secret is in the same namespace as the operator. [err=%v]", err)
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("unable to fetch Kubernetes secret to update deployment: %v", err)
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
// Iterate over the deployments and check if they use the managed secret
|
||||
for _, deployment := range listOfDeployments.Items {
|
||||
deployment := deployment
|
||||
if deployment.Annotations[AUTO_RELOAD_DEPLOYMENT_ANNOTATION] == "true" && IsDeploymentUsingManagedSecret(deployment, managedSecret) {
|
||||
// Start a goroutine to reconcile the deployment
|
||||
wg.Add(1)
|
||||
go func(deployment v1.Deployment, managedSecret corev1.Secret) {
|
||||
defer wg.Done()
|
||||
if err := ReconcileDeployment(ctx, client, logger, deployment, managedSecret); err != nil {
|
||||
logger.Error(err, fmt.Sprintf("unable to reconcile deployment with [name=%v]. Will try next requeue", deployment.ObjectMeta.Name))
|
||||
}
|
||||
}(deployment, *managedKubeSecret)
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate over the daemonSets and check if they use the managed secret
|
||||
for _, daemonSet := range listOfDaemonSets.Items {
|
||||
daemonSet := daemonSet
|
||||
if daemonSet.Annotations[AUTO_RELOAD_DEPLOYMENT_ANNOTATION] == "true" && IsDaemonSetUsingManagedSecret(daemonSet, managedSecret) {
|
||||
wg.Add(1)
|
||||
go func(deployment v1.DaemonSet, managedSecret corev1.Secret) {
|
||||
defer wg.Done()
|
||||
if err := ReconcileDaemonSet(ctx, client, logger, daemonSet, managedSecret); err != nil {
|
||||
logger.Error(err, fmt.Sprintf("unable to reconcile daemonset with [name=%v]. Will try next requeue", deployment.ObjectMeta.Name))
|
||||
}
|
||||
}(daemonSet, *managedKubeSecret)
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate over the statefulSets and check if they use the managed secret
|
||||
for _, statefulSet := range listOfStatefulSets.Items {
|
||||
statefulSet := statefulSet
|
||||
if statefulSet.Annotations[AUTO_RELOAD_DEPLOYMENT_ANNOTATION] == "true" && IsStatefulSetUsingManagedSecret(statefulSet, managedSecret) {
|
||||
wg.Add(1)
|
||||
go func(statefulSet v1.StatefulSet, managedSecret corev1.Secret) {
|
||||
defer wg.Done()
|
||||
if err := ReconcileStatefulSet(ctx, client, logger, statefulSet, managedSecret); err != nil {
|
||||
logger.Error(err, fmt.Sprintf("unable to reconcile statefulset with [name=%v]. Will try next requeue", statefulSet.ObjectMeta.Name))
|
||||
}
|
||||
}(statefulSet, *managedKubeSecret)
|
||||
}
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func ReconcileDeploymentsWithMultipleManagedSecrets(ctx context.Context, client controllerClient.Client, logger logr.Logger, managedSecrets []v1alpha1.ManagedKubeSecretConfig, isNamespaceScoped bool) (int, error) {
|
||||
for _, managedSecret := range managedSecrets {
|
||||
_, err := ReconcileDeploymentsWithManagedSecrets(ctx, client, logger, managedSecret, isNamespaceScoped)
|
||||
if err != nil {
|
||||
logger.Error(err, fmt.Sprintf("unable to reconcile deployments with managed secret [name=%v]", managedSecret.SecretName))
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// Check if the deployment uses managed secrets
|
||||
func IsDeploymentUsingManagedSecret(deployment v1.Deployment, managedSecret v1alpha1.ManagedKubeSecretConfig) bool {
|
||||
managedSecretName := managedSecret.SecretName
|
||||
for _, container := range deployment.Spec.Template.Spec.Containers {
|
||||
for _, envFrom := range container.EnvFrom {
|
||||
if envFrom.SecretRef != nil && envFrom.SecretRef.LocalObjectReference.Name == managedSecretName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, env := range container.Env {
|
||||
if env.ValueFrom != nil && env.ValueFrom.SecretKeyRef != nil && env.ValueFrom.SecretKeyRef.LocalObjectReference.Name == managedSecretName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, volume := range deployment.Spec.Template.Spec.Volumes {
|
||||
if volume.Secret != nil && volume.Secret.SecretName == managedSecretName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func IsDaemonSetUsingManagedSecret(daemonSet v1.DaemonSet, managedSecret v1alpha1.ManagedKubeSecretConfig) bool {
|
||||
managedSecretName := managedSecret.SecretName
|
||||
for _, container := range daemonSet.Spec.Template.Spec.Containers {
|
||||
for _, envFrom := range container.EnvFrom {
|
||||
if envFrom.SecretRef != nil && envFrom.SecretRef.LocalObjectReference.Name == managedSecretName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, env := range container.Env {
|
||||
if env.ValueFrom != nil && env.ValueFrom.SecretKeyRef != nil && env.ValueFrom.SecretKeyRef.LocalObjectReference.Name == managedSecretName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, volume := range daemonSet.Spec.Template.Spec.Volumes {
|
||||
if volume.Secret != nil && volume.Secret.SecretName == managedSecretName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func IsStatefulSetUsingManagedSecret(statefulSet v1.StatefulSet, managedSecret v1alpha1.ManagedKubeSecretConfig) bool {
|
||||
managedSecretName := managedSecret.SecretName
|
||||
for _, container := range statefulSet.Spec.Template.Spec.Containers {
|
||||
for _, envFrom := range container.EnvFrom {
|
||||
if envFrom.SecretRef != nil && envFrom.SecretRef.LocalObjectReference.Name == managedSecretName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, env := range container.Env {
|
||||
if env.ValueFrom != nil && env.ValueFrom.SecretKeyRef != nil && env.ValueFrom.SecretKeyRef.LocalObjectReference.Name == managedSecretName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, volume := range statefulSet.Spec.Template.Spec.Volumes {
|
||||
if volume.Secret != nil && volume.Secret.SecretName == managedSecretName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// This function ensures that a deployment is in sync with a Kubernetes secret by comparing their versions.
|
||||
// If the version of the secret is different from the version annotation on the deployment, the annotation is updated to trigger a restart of the deployment.
|
||||
func ReconcileDeployment(ctx context.Context, client controllerClient.Client, logger logr.Logger, deployment v1.Deployment, secret corev1.Secret) error {
|
||||
annotationKey := fmt.Sprintf("%s.%s", DEPLOYMENT_SECRET_NAME_ANNOTATION_PREFIX, secret.Name)
|
||||
annotationValue := secret.Annotations[constants.SECRET_VERSION_ANNOTATION]
|
||||
|
||||
if deployment.Annotations[annotationKey] == annotationValue &&
|
||||
deployment.Spec.Template.Annotations[annotationKey] == annotationValue {
|
||||
logger.Info(fmt.Sprintf("The [deploymentName=%v] is already using the most up to date managed secrets. No action required.", deployment.ObjectMeta.Name))
|
||||
return nil
|
||||
}
|
||||
|
||||
logger.Info(fmt.Sprintf("Deployment is using outdated managed secret. Starting re-deployment [deploymentName=%v]", deployment.ObjectMeta.Name))
|
||||
|
||||
if deployment.Spec.Template.Annotations == nil {
|
||||
deployment.Spec.Template.Annotations = make(map[string]string)
|
||||
}
|
||||
|
||||
deployment.Annotations[annotationKey] = annotationValue
|
||||
deployment.Spec.Template.Annotations[annotationKey] = annotationValue
|
||||
|
||||
if err := client.Update(ctx, &deployment); err != nil {
|
||||
return fmt.Errorf("failed to update deployment annotation: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ReconcileDaemonSet(ctx context.Context, client controllerClient.Client, logger logr.Logger, daemonSet v1.DaemonSet, secret corev1.Secret) error {
|
||||
annotationKey := fmt.Sprintf("%s.%s", DEPLOYMENT_SECRET_NAME_ANNOTATION_PREFIX, secret.Name)
|
||||
annotationValue := secret.Annotations[constants.SECRET_VERSION_ANNOTATION]
|
||||
|
||||
if daemonSet.Annotations[annotationKey] == annotationValue &&
|
||||
daemonSet.Spec.Template.Annotations[annotationKey] == annotationValue {
|
||||
logger.Info(fmt.Sprintf("The [daemonSetName=%v] is already using the most up to date managed secrets. No action required.", daemonSet.ObjectMeta.Name))
|
||||
return nil
|
||||
}
|
||||
|
||||
logger.Info(fmt.Sprintf("DaemonSet is using outdated managed secret. Starting re-deployment [daemonSetName=%v]", daemonSet.ObjectMeta.Name))
|
||||
|
||||
if daemonSet.Spec.Template.Annotations == nil {
|
||||
daemonSet.Spec.Template.Annotations = make(map[string]string)
|
||||
}
|
||||
|
||||
daemonSet.Annotations[annotationKey] = annotationValue
|
||||
daemonSet.Spec.Template.Annotations[annotationKey] = annotationValue
|
||||
|
||||
if err := client.Update(ctx, &daemonSet); err != nil {
|
||||
return fmt.Errorf("failed to update daemonSet annotation: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ReconcileStatefulSet(ctx context.Context, client controllerClient.Client, logger logr.Logger, statefulSet v1.StatefulSet, secret corev1.Secret) error {
|
||||
annotationKey := fmt.Sprintf("%s.%s", DEPLOYMENT_SECRET_NAME_ANNOTATION_PREFIX, secret.Name)
|
||||
annotationValue := secret.Annotations[constants.SECRET_VERSION_ANNOTATION]
|
||||
|
||||
if statefulSet.Annotations[annotationKey] == annotationValue &&
|
||||
statefulSet.Spec.Template.Annotations[annotationKey] == annotationValue {
|
||||
logger.Info(fmt.Sprintf("The [statefulSetName=%v] is already using the most up to date managed secrets. No action required.", statefulSet.ObjectMeta.Name))
|
||||
return nil
|
||||
}
|
||||
|
||||
logger.Info(fmt.Sprintf("StatefulSet is using outdated managed secret. Starting re-deployment [statefulSetName=%v]", statefulSet.ObjectMeta.Name))
|
||||
|
||||
if statefulSet.Spec.Template.Annotations == nil {
|
||||
statefulSet.Spec.Template.Annotations = make(map[string]string)
|
||||
}
|
||||
|
||||
statefulSet.Annotations[annotationKey] = annotationValue
|
||||
statefulSet.Spec.Template.Annotations[annotationKey] = annotationValue
|
||||
|
||||
if err := client.Update(ctx, &statefulSet); err != nil {
|
||||
return fmt.Errorf("failed to update statefulSet annotation: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetInfisicalConfigMap(ctx context.Context, client client.Client, isNamespaceScoped bool) (configMap map[string]string, errToReturn error) {
|
||||
// default key values
|
||||
defaultConfigMapData := make(map[string]string)
|
||||
defaultConfigMapData["hostAPI"] = constants.INFISICAL_DOMAIN
|
||||
|
||||
// this will never work if we're namespace scoped, because the operator can't read outside of its namespace by our current RBAC rules.
|
||||
// This is how it has always worked, but the error has been masked as 'not found' in V3 kubebuilder.
|
||||
if isNamespaceScoped {
|
||||
return defaultConfigMapData, nil
|
||||
}
|
||||
|
||||
kubeConfigMap := &corev1.ConfigMap{}
|
||||
err := client.Get(ctx, types.NamespacedName{
|
||||
Namespace: constants.OPERATOR_SETTINGS_CONFIGMAP_NAMESPACE,
|
||||
Name: constants.OPERATOR_SETTINGS_CONFIGMAP_NAME,
|
||||
}, kubeConfigMap)
|
||||
|
||||
if err != nil {
|
||||
if k8Errors.IsNotFound(err) {
|
||||
kubeConfigMap = nil
|
||||
} else {
|
||||
return nil, fmt.Errorf("GetConfigMapByNamespacedName: unable to fetch config map in [namespacedName=%s] [err=%s]", constants.OPERATOR_SETTINGS_CONFIGMAP_NAMESPACE, err)
|
||||
}
|
||||
}
|
||||
|
||||
if kubeConfigMap == nil {
|
||||
return defaultConfigMapData, nil
|
||||
} else {
|
||||
for key, value := range defaultConfigMapData {
|
||||
_, exists := kubeConfigMap.Data[key]
|
||||
if !exists {
|
||||
kubeConfigMap.Data[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
return kubeConfigMap.Data, nil
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"fmt"
|
||||
"hash/crc32"
|
||||
|
||||
"golang.org/x/crypto/nacl/box"
|
||||
)
|
||||
|
||||
func DecryptSymmetric(key []byte, encryptedPrivateKey []byte, tag []byte, IV []byte) ([]byte, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
aesgcm, err := cipher.NewGCMWithNonceSize(block, len(IV))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var nonce = IV
|
||||
var ciphertext = append(encryptedPrivateKey, tag...)
|
||||
|
||||
plaintext, err := aesgcm.Open(nil, nonce, ciphertext, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
func DecryptAsymmetric(ciphertext []byte, nonce []byte, publicKey []byte, privateKey []byte) (plainText []byte) {
|
||||
plainTextToReturn, _ := box.Open(nil, ciphertext, (*[24]byte)(nonce), (*[32]byte)(publicKey), (*[32]byte)(privateKey))
|
||||
return plainTextToReturn
|
||||
}
|
||||
|
||||
func ComputeEtag(data []byte) string {
|
||||
crc := crc32.ChecksumIEEE(data)
|
||||
return fmt.Sprintf(`W/"secrets-%d-%08X"`, len(data), crc)
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
package generator
|
||||
@@ -1,76 +0,0 @@
|
||||
package generator
|
||||
|
||||
import (
|
||||
"github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
||||
"github.com/sethvargo/go-password/password"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultLength = 24
|
||||
defaultSymbolChars = "~!@#$%^&*()_+`-={}|[]\\:\"<>?,./"
|
||||
digitFactor = 0.25
|
||||
symbolFactor = 0.25
|
||||
)
|
||||
|
||||
func generateSafePassword(
|
||||
passLen int,
|
||||
symbols int,
|
||||
symbolCharacters string,
|
||||
digits int,
|
||||
noUpper bool,
|
||||
allowRepeat bool,
|
||||
) (string, error) {
|
||||
gen, err := password.NewGenerator(&password.GeneratorInput{
|
||||
Symbols: symbolCharacters,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return gen.Generate(
|
||||
passLen,
|
||||
digits,
|
||||
symbols,
|
||||
noUpper,
|
||||
allowRepeat,
|
||||
)
|
||||
}
|
||||
|
||||
func GeneratorPassword(spec v1alpha1.PasswordSpec) (string, error) {
|
||||
|
||||
symbolCharacters := defaultSymbolChars
|
||||
|
||||
if spec.SymbolCharacters != nil && *spec.SymbolCharacters != "" {
|
||||
symbolCharacters = *spec.SymbolCharacters
|
||||
}
|
||||
|
||||
passwordLength := defaultLength
|
||||
|
||||
if spec.Length != 0 {
|
||||
passwordLength = spec.Length
|
||||
}
|
||||
|
||||
digits := int(float32(passwordLength) * digitFactor)
|
||||
if spec.Digits != nil {
|
||||
digits = *spec.Digits
|
||||
}
|
||||
|
||||
symbols := int(float32(passwordLength) * symbolFactor)
|
||||
if spec.Symbols != nil {
|
||||
symbols = *spec.Symbols
|
||||
}
|
||||
|
||||
pass, err := generateSafePassword(
|
||||
passwordLength,
|
||||
symbols,
|
||||
symbolCharacters,
|
||||
digits,
|
||||
spec.NoUpper,
|
||||
spec.AllowRepeat,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return pass, nil
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package generator
|
||||
|
||||
import (
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func GeneratorUUID() (string, error) {
|
||||
uuid := uuid.New().String()
|
||||
return uuid, nil
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package model
|
||||
|
||||
type ServiceAccountDetails struct {
|
||||
AccessKey string
|
||||
PublicKey string
|
||||
PrivateKey string
|
||||
}
|
||||
|
||||
type UniversalAuthIdentityDetails struct {
|
||||
ClientId string
|
||||
ClientSecret string
|
||||
}
|
||||
|
||||
type LdapIdentityDetails struct {
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
type SingleEnvironmentVariable struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
SecretPath string `json:"secretPath"`
|
||||
Type string `json:"type"`
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
type SecretTemplateOptions struct {
|
||||
Value string `json:"value"`
|
||||
SecretPath string `json:"secretPath"`
|
||||
}
|
||||
|
||||
type Project struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"slug"`
|
||||
OrgID string `json:"orgId"`
|
||||
Environments []struct {
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"slug"`
|
||||
ID string `json:"id"`
|
||||
}
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
package infisicaldynamicsecret
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
||||
"github.com/go-logr/logr"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func (r *InfisicalDynamicSecretReconciler) SetReconcileAutoRedeploymentConditionStatus(ctx context.Context, logger logr.Logger, infisicalDynamicSecret *v1alpha1.InfisicalDynamicSecret, numDeployments int, errorToConditionOn error) {
|
||||
if infisicalDynamicSecret.Status.Conditions == nil {
|
||||
infisicalDynamicSecret.Status.Conditions = []metav1.Condition{}
|
||||
}
|
||||
|
||||
if errorToConditionOn == nil {
|
||||
meta.SetStatusCondition(&infisicalDynamicSecret.Status.Conditions, metav1.Condition{
|
||||
Type: "secrets.infisical.com/AutoRedeployReady",
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: "OK",
|
||||
Message: fmt.Sprintf("Infisical has found %v deployments which are ready to be auto redeployed when dynamic secret lease changes", numDeployments),
|
||||
})
|
||||
} else {
|
||||
meta.SetStatusCondition(&infisicalDynamicSecret.Status.Conditions, metav1.Condition{
|
||||
Type: "secrets.infisical.com/AutoRedeployReady",
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: "Error",
|
||||
Message: fmt.Sprintf("Failed reconcile deployments because: %v", errorToConditionOn),
|
||||
})
|
||||
}
|
||||
|
||||
err := r.Client.Status().Update(ctx, infisicalDynamicSecret)
|
||||
if err != nil {
|
||||
logger.Error(err, "Could not set condition for AutoRedeployReady")
|
||||
}
|
||||
}
|
||||
|
||||
func (r *InfisicalDynamicSecretReconciler) SetAuthenticatedConditionStatus(ctx context.Context, logger logr.Logger, infisicalDynamicSecret *v1alpha1.InfisicalDynamicSecret, errorToConditionOn error) {
|
||||
if infisicalDynamicSecret.Status.Conditions == nil {
|
||||
infisicalDynamicSecret.Status.Conditions = []metav1.Condition{}
|
||||
}
|
||||
|
||||
if errorToConditionOn == nil {
|
||||
meta.SetStatusCondition(&infisicalDynamicSecret.Status.Conditions, metav1.Condition{
|
||||
Type: "secrets.infisical.com/Authenticated",
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: "OK",
|
||||
Message: "Infisical has successfully authenticated with the Infisical API",
|
||||
})
|
||||
} else {
|
||||
meta.SetStatusCondition(&infisicalDynamicSecret.Status.Conditions, metav1.Condition{
|
||||
Type: "secrets.infisical.com/Authenticated",
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: "Error",
|
||||
Message: fmt.Sprintf("Failed to authenticate with Infisical API because: %v", errorToConditionOn),
|
||||
})
|
||||
}
|
||||
|
||||
err := r.Client.Status().Update(ctx, infisicalDynamicSecret)
|
||||
if err != nil {
|
||||
logger.Error(err, "Could not set condition for Authenticated")
|
||||
}
|
||||
}
|
||||
|
||||
func (r *InfisicalDynamicSecretReconciler) SetLeaseRenewalConditionStatus(ctx context.Context, logger logr.Logger, infisicalDynamicSecret *v1alpha1.InfisicalDynamicSecret, errorToConditionOn error) {
|
||||
if infisicalDynamicSecret.Status.Conditions == nil {
|
||||
infisicalDynamicSecret.Status.Conditions = []metav1.Condition{}
|
||||
}
|
||||
|
||||
if errorToConditionOn == nil {
|
||||
meta.SetStatusCondition(&infisicalDynamicSecret.Status.Conditions, metav1.Condition{
|
||||
Type: "secrets.infisical.com/LeaseRenewal",
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: "OK",
|
||||
Message: "Infisical has successfully renewed the lease",
|
||||
})
|
||||
} else {
|
||||
meta.SetStatusCondition(&infisicalDynamicSecret.Status.Conditions, metav1.Condition{
|
||||
Type: "secrets.infisical.com/LeaseRenewal",
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: "Error",
|
||||
Message: fmt.Sprintf("Failed to renew the lease because: %v", errorToConditionOn),
|
||||
})
|
||||
}
|
||||
|
||||
err := r.Client.Status().Update(ctx, infisicalDynamicSecret)
|
||||
if err != nil {
|
||||
logger.Error(err, "Could not set condition for LeaseRenewal")
|
||||
}
|
||||
}
|
||||
|
||||
func (r *InfisicalDynamicSecretReconciler) SetCreatedLeaseConditionStatus(ctx context.Context, logger logr.Logger, infisicalDynamicSecret *v1alpha1.InfisicalDynamicSecret, errorToConditionOn error) {
|
||||
if infisicalDynamicSecret.Status.Conditions == nil {
|
||||
infisicalDynamicSecret.Status.Conditions = []metav1.Condition{}
|
||||
}
|
||||
|
||||
if errorToConditionOn == nil {
|
||||
meta.SetStatusCondition(&infisicalDynamicSecret.Status.Conditions, metav1.Condition{
|
||||
Type: "secrets.infisical.com/LeaseCreated",
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: "OK",
|
||||
Message: "Infisical has successfully created the lease",
|
||||
})
|
||||
} else {
|
||||
meta.SetStatusCondition(&infisicalDynamicSecret.Status.Conditions, metav1.Condition{
|
||||
Type: "secrets.infisical.com/LeaseCreated",
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: "Error",
|
||||
Message: fmt.Sprintf("Failed to create the lease because: %v", errorToConditionOn),
|
||||
})
|
||||
}
|
||||
|
||||
err := r.Client.Status().Update(ctx, infisicalDynamicSecret)
|
||||
if err != nil {
|
||||
logger.Error(err, "Could not set condition for LeaseCreated")
|
||||
}
|
||||
}
|
||||
|
||||
func (r *InfisicalDynamicSecretReconciler) SetReconcileConditionStatus(ctx context.Context, logger logr.Logger, infisicalDynamicSecret *v1alpha1.InfisicalDynamicSecret, errorToConditionOn error) {
|
||||
if infisicalDynamicSecret.Status.Conditions == nil {
|
||||
infisicalDynamicSecret.Status.Conditions = []metav1.Condition{}
|
||||
}
|
||||
|
||||
if errorToConditionOn == nil {
|
||||
meta.SetStatusCondition(&infisicalDynamicSecret.Status.Conditions, metav1.Condition{
|
||||
Type: "secrets.infisical.com/Reconcile",
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: "OK",
|
||||
Message: "Infisical has successfully reconciled the InfisicalDynamicSecret",
|
||||
})
|
||||
} else {
|
||||
meta.SetStatusCondition(&infisicalDynamicSecret.Status.Conditions, metav1.Condition{
|
||||
Type: "secrets.infisical.com/Reconcile",
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: "Error",
|
||||
Message: fmt.Sprintf("Failed to reconcile the InfisicalDynamicSecret because: %v", errorToConditionOn),
|
||||
})
|
||||
}
|
||||
|
||||
err := r.Client.Status().Update(ctx, infisicalDynamicSecret)
|
||||
if err != nil {
|
||||
logger.Error(err, "Could not set condition for Reconcile")
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
package infisicaldynamicsecret
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
||||
"github.com/Infisical/infisical/k8-operator/internal/api"
|
||||
"github.com/Infisical/infisical/k8-operator/internal/util"
|
||||
"github.com/go-logr/logr"
|
||||
k8Errors "k8s.io/apimachinery/pkg/api/errors"
|
||||
)
|
||||
|
||||
type InfisicalDynamicSecretHandler struct {
|
||||
client.Client
|
||||
Scheme *runtime.Scheme
|
||||
Random *rand.Rand
|
||||
IsNamespaceScoped bool
|
||||
}
|
||||
|
||||
func NewInfisicalDynamicSecretHandler(client client.Client, scheme *runtime.Scheme, isNamespaceScoped bool) *InfisicalDynamicSecretHandler {
|
||||
return &InfisicalDynamicSecretHandler{
|
||||
Client: client,
|
||||
Scheme: scheme,
|
||||
Random: rand.New(rand.NewSource(time.Now().UnixNano())),
|
||||
IsNamespaceScoped: isNamespaceScoped,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *InfisicalDynamicSecretHandler) SetupAPIConfig(infisicalDynamicSecret v1alpha1.InfisicalDynamicSecret, infisicalConfig map[string]string) error {
|
||||
if infisicalDynamicSecret.Spec.HostAPI == "" {
|
||||
api.API_HOST_URL = infisicalConfig["hostAPI"]
|
||||
} else {
|
||||
api.API_HOST_URL = util.AppendAPIEndpoint(infisicalDynamicSecret.Spec.HostAPI)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *InfisicalDynamicSecretHandler) getInfisicalCaCertificateFromKubeSecret(ctx context.Context, infisicalDynamicSecret v1alpha1.InfisicalDynamicSecret) (caCertificate string, err error) {
|
||||
|
||||
caCertificateFromKubeSecret, err := util.GetKubeSecretByNamespacedName(ctx, h.Client, types.NamespacedName{
|
||||
Namespace: infisicalDynamicSecret.Spec.TLS.CaRef.SecretNamespace,
|
||||
Name: infisicalDynamicSecret.Spec.TLS.CaRef.SecretName,
|
||||
})
|
||||
|
||||
if k8Errors.IsNotFound(err) {
|
||||
return "", fmt.Errorf("kubernetes secret containing custom CA certificate cannot be found. [err=%s]", err)
|
||||
}
|
||||
|
||||
if util.IsNamespaceScopedError(err, h.IsNamespaceScoped) {
|
||||
return "", fmt.Errorf("unable to fetch Kubernetes CA certificate secret. Your Operator installation is namespace scoped, and cannot read secrets outside of the namespace it is installed in. Please ensure the CA certificate secret is in the same namespace as the operator. [err=%v]", err)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("something went wrong when fetching your CA certificate [err=%s]", err)
|
||||
}
|
||||
|
||||
caCertificateFromSecret := string(caCertificateFromKubeSecret.Data[infisicalDynamicSecret.Spec.TLS.CaRef.SecretKey])
|
||||
|
||||
return caCertificateFromSecret, nil
|
||||
}
|
||||
|
||||
func (h *InfisicalDynamicSecretHandler) HandleCACertificate(ctx context.Context, infisicalDynamicSecret v1alpha1.InfisicalDynamicSecret) error {
|
||||
if infisicalDynamicSecret.Spec.TLS.CaRef.SecretName != "" {
|
||||
caCert, err := h.getInfisicalCaCertificateFromKubeSecret(ctx, infisicalDynamicSecret)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
api.API_CA_CERTIFICATE = caCert
|
||||
} else {
|
||||
api.API_CA_CERTIFICATE = ""
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *InfisicalDynamicSecretHandler) ReconcileInfisicalDynamicSecret(ctx context.Context, logger logr.Logger, infisicalDynamicSecret *v1alpha1.InfisicalDynamicSecret, resourceVariablesMap map[string]util.ResourceVariables) (time.Duration, error) {
|
||||
reconciler := &InfisicalDynamicSecretReconciler{
|
||||
Client: h.Client,
|
||||
Scheme: h.Scheme,
|
||||
Random: h.Random,
|
||||
IsNamespaceScoped: h.IsNamespaceScoped,
|
||||
}
|
||||
return reconciler.ReconcileInfisicalDynamicSecret(ctx, logger, infisicalDynamicSecret, resourceVariablesMap)
|
||||
}
|
||||
|
||||
func (h *InfisicalDynamicSecretHandler) HandleLeaseRevocation(ctx context.Context, logger logr.Logger, infisicalDynamicSecret *v1alpha1.InfisicalDynamicSecret, resourceVariablesMap map[string]util.ResourceVariables) error {
|
||||
reconciler := &InfisicalDynamicSecretReconciler{
|
||||
Client: h.Client,
|
||||
Scheme: h.Scheme,
|
||||
Random: h.Random,
|
||||
IsNamespaceScoped: h.IsNamespaceScoped,
|
||||
}
|
||||
return reconciler.HandleLeaseRevocation(ctx, logger, infisicalDynamicSecret, resourceVariablesMap)
|
||||
}
|
||||
|
||||
func (h *InfisicalDynamicSecretHandler) SetReconcileConditionStatus(ctx context.Context, logger logr.Logger, infisicalDynamicSecret *v1alpha1.InfisicalDynamicSecret, errorToConditionOn error) {
|
||||
reconciler := &InfisicalDynamicSecretReconciler{
|
||||
Client: h.Client,
|
||||
Scheme: h.Scheme,
|
||||
Random: h.Random,
|
||||
IsNamespaceScoped: h.IsNamespaceScoped,
|
||||
}
|
||||
reconciler.SetReconcileConditionStatus(ctx, logger, infisicalDynamicSecret, errorToConditionOn)
|
||||
}
|
||||
|
||||
func (h *InfisicalDynamicSecretHandler) SetReconcileAutoRedeploymentConditionStatus(ctx context.Context, logger logr.Logger, infisicalDynamicSecret *v1alpha1.InfisicalDynamicSecret, numDeployments int, errorToConditionOn error) {
|
||||
reconciler := &InfisicalDynamicSecretReconciler{
|
||||
Client: h.Client,
|
||||
Scheme: h.Scheme,
|
||||
Random: h.Random,
|
||||
IsNamespaceScoped: h.IsNamespaceScoped,
|
||||
}
|
||||
reconciler.SetReconcileAutoRedeploymentConditionStatus(ctx, logger, infisicalDynamicSecret, numDeployments, errorToConditionOn)
|
||||
}
|
||||
@@ -1,431 +0,0 @@
|
||||
package infisicaldynamicsecret
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
||||
"github.com/Infisical/infisical/k8-operator/internal/api"
|
||||
"github.com/Infisical/infisical/k8-operator/internal/constants"
|
||||
"github.com/Infisical/infisical/k8-operator/internal/util"
|
||||
"github.com/go-logr/logr"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
||||
infisicalSdk "github.com/infisical/go-sdk"
|
||||
k8Errors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
)
|
||||
|
||||
type InfisicalDynamicSecretReconciler struct {
|
||||
client.Client
|
||||
Scheme *runtime.Scheme
|
||||
Random *rand.Rand
|
||||
IsNamespaceScoped bool
|
||||
}
|
||||
|
||||
func (r *InfisicalDynamicSecretReconciler) createInfisicalManagedKubeSecret(ctx context.Context, logger logr.Logger, infisicalDynamicSecret v1alpha1.InfisicalDynamicSecret, versionAnnotationValue string) error {
|
||||
secretType := infisicalDynamicSecret.Spec.ManagedSecretReference.SecretType
|
||||
|
||||
// copy labels and annotations from InfisicalSecret CRD
|
||||
labels := map[string]string{}
|
||||
for k, v := range infisicalDynamicSecret.Labels {
|
||||
labels[k] = v
|
||||
}
|
||||
|
||||
annotations := map[string]string{}
|
||||
systemPrefixes := []string{"kubectl.kubernetes.io/", "kubernetes.io/", "k8s.io/", "helm.sh/"}
|
||||
for k, v := range infisicalDynamicSecret.Annotations {
|
||||
isSystem := false
|
||||
for _, prefix := range systemPrefixes {
|
||||
if strings.HasPrefix(k, prefix) {
|
||||
isSystem = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !isSystem {
|
||||
annotations[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
annotations[constants.SECRET_VERSION_ANNOTATION] = versionAnnotationValue
|
||||
|
||||
// create a new secret as specified by the managed secret spec of CRD
|
||||
newKubeSecretInstance := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: infisicalDynamicSecret.Spec.ManagedSecretReference.SecretName,
|
||||
Namespace: infisicalDynamicSecret.Spec.ManagedSecretReference.SecretNamespace,
|
||||
Annotations: annotations,
|
||||
Labels: labels,
|
||||
},
|
||||
Type: corev1.SecretType(secretType),
|
||||
}
|
||||
|
||||
if infisicalDynamicSecret.Spec.ManagedSecretReference.CreationPolicy == "Owner" {
|
||||
// Set InfisicalSecret instance as the owner and controller of the managed secret
|
||||
err := ctrl.SetControllerReference(&infisicalDynamicSecret, newKubeSecretInstance, r.Scheme)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err := r.Client.Create(ctx, newKubeSecretInstance)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create the managed Kubernetes secret : %w", err)
|
||||
}
|
||||
|
||||
logger.Info(fmt.Sprintf("Successfully created a managed Kubernetes secret. [type: %s]", secretType))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *InfisicalDynamicSecretReconciler) getResourceVariables(infisicalDynamicSecret v1alpha1.InfisicalDynamicSecret, resourceVariablesMap map[string]util.ResourceVariables) util.ResourceVariables {
|
||||
|
||||
var resourceVariables util.ResourceVariables
|
||||
|
||||
if _, ok := resourceVariablesMap[string(infisicalDynamicSecret.UID)]; !ok {
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
client := infisicalSdk.NewInfisicalClient(ctx, infisicalSdk.Config{
|
||||
SiteUrl: api.API_HOST_URL,
|
||||
CaCertificate: api.API_CA_CERTIFICATE,
|
||||
UserAgent: api.USER_AGENT_NAME,
|
||||
})
|
||||
|
||||
resourceVariablesMap[string(infisicalDynamicSecret.UID)] = util.ResourceVariables{
|
||||
InfisicalClient: client,
|
||||
CancelCtx: cancel,
|
||||
AuthDetails: util.AuthenticationDetails{},
|
||||
}
|
||||
|
||||
resourceVariables = resourceVariablesMap[string(infisicalDynamicSecret.UID)]
|
||||
|
||||
} else {
|
||||
resourceVariables = resourceVariablesMap[string(infisicalDynamicSecret.UID)]
|
||||
}
|
||||
|
||||
return resourceVariables
|
||||
}
|
||||
|
||||
func (r *InfisicalDynamicSecretReconciler) CreateDynamicSecretLease(ctx context.Context, logger logr.Logger, infisicalClient infisicalSdk.InfisicalClientInterface, infisicalDynamicSecret *v1alpha1.InfisicalDynamicSecret, destination *corev1.Secret) error {
|
||||
project, err := util.GetProjectByID(infisicalClient.Auth().GetAccessToken(), infisicalDynamicSecret.Spec.DynamicSecret.ProjectID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
request := infisicalSdk.CreateDynamicSecretLeaseOptions{
|
||||
DynamicSecretName: infisicalDynamicSecret.Spec.DynamicSecret.SecretName,
|
||||
ProjectSlug: project.Slug,
|
||||
SecretPath: infisicalDynamicSecret.Spec.DynamicSecret.SecretPath,
|
||||
EnvironmentSlug: infisicalDynamicSecret.Spec.DynamicSecret.EnvironmentSlug,
|
||||
}
|
||||
|
||||
if infisicalDynamicSecret.Spec.LeaseTTL != "" {
|
||||
request.TTL = infisicalDynamicSecret.Spec.LeaseTTL
|
||||
}
|
||||
|
||||
leaseData, dynamicSecret, lease, err := infisicalClient.DynamicSecrets().Leases().Create(request)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create lease [err=%s]", err)
|
||||
}
|
||||
|
||||
newLeaseStatus := &v1alpha1.InfisicalDynamicSecretLease{
|
||||
ID: lease.Id,
|
||||
ExpiresAt: metav1.NewTime(lease.ExpireAt),
|
||||
CreationTimestamp: metav1.NewTime(time.Now()),
|
||||
Version: int64(lease.Version),
|
||||
}
|
||||
|
||||
infisicalDynamicSecret.Status.DynamicSecretID = dynamicSecret.Id
|
||||
infisicalDynamicSecret.Status.MaxTTL = dynamicSecret.MaxTTL
|
||||
infisicalDynamicSecret.Status.Lease = newLeaseStatus
|
||||
|
||||
// write the leaseData to the destination secret
|
||||
destinationData := map[string]string{}
|
||||
|
||||
for key, value := range leaseData {
|
||||
if strValue, ok := value.(string); ok {
|
||||
destinationData[key] = strValue
|
||||
} else {
|
||||
return fmt.Errorf("unable to convert value to string for key %s", key)
|
||||
}
|
||||
}
|
||||
|
||||
destination.StringData = destinationData
|
||||
destination.Annotations[constants.SECRET_VERSION_ANNOTATION] = fmt.Sprintf("%s-%d", lease.Id, lease.Version)
|
||||
|
||||
if err := r.Client.Update(ctx, destination); err != nil {
|
||||
return fmt.Errorf("unable to update destination secret [err=%s]", err)
|
||||
}
|
||||
|
||||
logger.Info(fmt.Sprintf("New lease successfully created [leaseId=%s]", lease.Id))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *InfisicalDynamicSecretReconciler) RenewDynamicSecretLease(ctx context.Context, logger logr.Logger, infisicalClient infisicalSdk.InfisicalClientInterface, infisicalDynamicSecret *v1alpha1.InfisicalDynamicSecret, destination *corev1.Secret) error {
|
||||
project, err := util.GetProjectByID(infisicalClient.Auth().GetAccessToken(), infisicalDynamicSecret.Spec.DynamicSecret.ProjectID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
request := infisicalSdk.RenewDynamicSecretLeaseOptions{
|
||||
LeaseId: infisicalDynamicSecret.Status.Lease.ID,
|
||||
ProjectSlug: project.Slug,
|
||||
SecretPath: infisicalDynamicSecret.Spec.DynamicSecret.SecretPath,
|
||||
EnvironmentSlug: infisicalDynamicSecret.Spec.DynamicSecret.EnvironmentSlug,
|
||||
}
|
||||
|
||||
if infisicalDynamicSecret.Spec.LeaseTTL != "" {
|
||||
request.TTL = infisicalDynamicSecret.Spec.LeaseTTL
|
||||
}
|
||||
|
||||
lease, err := infisicalClient.DynamicSecrets().Leases().RenewById(request)
|
||||
|
||||
if err != nil {
|
||||
|
||||
if strings.Contains(err.Error(), "TTL cannot be larger than max ttl") || // Case 1: TTL is larger than the max TTL
|
||||
strings.Contains(err.Error(), "Dynamic secret lease with ID") { // Case 2: The lease has already expired and has been deleted
|
||||
return constants.ErrInvalidLease
|
||||
}
|
||||
|
||||
return fmt.Errorf("unable to renew lease [err=%s]", err)
|
||||
}
|
||||
|
||||
infisicalDynamicSecret.Status.Lease.ExpiresAt = metav1.NewTime(lease.ExpireAt)
|
||||
|
||||
// update the infisicalDynamicSecret status
|
||||
if err := r.Client.Status().Update(ctx, infisicalDynamicSecret); err != nil {
|
||||
return fmt.Errorf("unable to update InfisicalDynamicSecret status [err=%s]", err)
|
||||
}
|
||||
|
||||
logger.Info(fmt.Sprintf("Lease successfully renewed [leaseId=%s]", lease.Id))
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (r *InfisicalDynamicSecretReconciler) updateResourceVariables(infisicalDynamicSecret v1alpha1.InfisicalDynamicSecret, resourceVariables util.ResourceVariables, resourceVariablesMap map[string]util.ResourceVariables) {
|
||||
resourceVariablesMap[string(infisicalDynamicSecret.UID)] = resourceVariables
|
||||
}
|
||||
|
||||
func (r *InfisicalDynamicSecretReconciler) HandleLeaseRevocation(ctx context.Context, logger logr.Logger, infisicalDynamicSecret *v1alpha1.InfisicalDynamicSecret, resourceVariablesMap map[string]util.ResourceVariables) error {
|
||||
if infisicalDynamicSecret.Spec.LeaseRevocationPolicy != string(constants.DYNAMIC_SECRET_LEASE_REVOCATION_POLICY_ENABLED) {
|
||||
return nil
|
||||
}
|
||||
|
||||
resourceVariables := r.getResourceVariables(*infisicalDynamicSecret, resourceVariablesMap)
|
||||
infisicalClient := resourceVariables.InfisicalClient
|
||||
|
||||
logger.Info("Authenticating for lease revocation")
|
||||
authDetails, err := util.HandleAuthentication(ctx, util.SecretAuthInput{
|
||||
Secret: *infisicalDynamicSecret,
|
||||
Type: util.SecretCrd.INFISICAL_DYNAMIC_SECRET,
|
||||
}, r.Client, infisicalClient, r.IsNamespaceScoped)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to authenticate for lease revocation [err=%s]", err)
|
||||
}
|
||||
|
||||
r.updateResourceVariables(*infisicalDynamicSecret, util.ResourceVariables{
|
||||
InfisicalClient: infisicalClient,
|
||||
CancelCtx: resourceVariables.CancelCtx,
|
||||
AuthDetails: authDetails,
|
||||
}, resourceVariablesMap)
|
||||
|
||||
if infisicalDynamicSecret.Status.Lease == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
project, err := util.GetProjectByID(infisicalClient.Auth().GetAccessToken(), infisicalDynamicSecret.Spec.DynamicSecret.ProjectID)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
infisicalClient.DynamicSecrets().Leases().DeleteById(infisicalSdk.DeleteDynamicSecretLeaseOptions{
|
||||
LeaseId: infisicalDynamicSecret.Status.Lease.ID,
|
||||
ProjectSlug: project.Slug,
|
||||
SecretPath: infisicalDynamicSecret.Spec.DynamicSecret.SecretPath,
|
||||
EnvironmentSlug: infisicalDynamicSecret.Spec.DynamicSecret.EnvironmentSlug,
|
||||
})
|
||||
|
||||
// update the destination data to remove the lease data
|
||||
destination, err := util.GetKubeSecretByNamespacedName(ctx, r.Client, types.NamespacedName{
|
||||
Name: infisicalDynamicSecret.Spec.ManagedSecretReference.SecretName,
|
||||
Namespace: infisicalDynamicSecret.Spec.ManagedSecretReference.SecretNamespace,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
if util.IsNamespaceScopedError(err, r.IsNamespaceScoped) {
|
||||
return fmt.Errorf("unable to fetch Kubernetes destination secret. Your Operator installation is namespace scoped, and cannot read secrets outside of the namespace it is installed in. Please ensure the destination secret is in the same namespace as the operator. [err=%v]", err)
|
||||
}
|
||||
return fmt.Errorf("unable to fetch destination secret [err=%s]", err)
|
||||
}
|
||||
|
||||
destination.Data = map[string][]byte{}
|
||||
|
||||
if err := r.Client.Update(ctx, destination); err != nil {
|
||||
return fmt.Errorf("unable to update destination secret [err=%s]", err)
|
||||
}
|
||||
|
||||
logger.Info(fmt.Sprintf("Lease successfully revoked [leaseId=%s]", infisicalDynamicSecret.Status.Lease.ID))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *InfisicalDynamicSecretReconciler) ReconcileInfisicalDynamicSecret(ctx context.Context, logger logr.Logger, infisicalDynamicSecret *v1alpha1.InfisicalDynamicSecret, resourceVariablesMap map[string]util.ResourceVariables) (time.Duration, error) {
|
||||
|
||||
resourceVariables := r.getResourceVariables(*infisicalDynamicSecret, resourceVariablesMap)
|
||||
infisicalClient := resourceVariables.InfisicalClient
|
||||
cancelCtx := resourceVariables.CancelCtx
|
||||
authDetails := resourceVariables.AuthDetails
|
||||
|
||||
defaultNextReconcile := 5 * time.Second
|
||||
nextReconcile := defaultNextReconcile
|
||||
|
||||
var err error
|
||||
|
||||
if authDetails.AuthStrategy == "" {
|
||||
logger.Info("No authentication strategy found. Attempting to authenticate")
|
||||
authDetails, err = util.HandleAuthentication(ctx, util.SecretAuthInput{
|
||||
Secret: *infisicalDynamicSecret,
|
||||
Type: util.SecretCrd.INFISICAL_DYNAMIC_SECRET,
|
||||
}, r.Client, infisicalClient, r.IsNamespaceScoped)
|
||||
|
||||
if err != nil {
|
||||
return nextReconcile, fmt.Errorf("unable to authenticate [err=%s]", err)
|
||||
}
|
||||
|
||||
r.updateResourceVariables(*infisicalDynamicSecret, util.ResourceVariables{
|
||||
InfisicalClient: infisicalClient,
|
||||
CancelCtx: cancelCtx,
|
||||
AuthDetails: authDetails,
|
||||
}, resourceVariablesMap)
|
||||
}
|
||||
|
||||
destination, err := util.GetKubeSecretByNamespacedName(ctx, r.Client, types.NamespacedName{
|
||||
Name: infisicalDynamicSecret.Spec.ManagedSecretReference.SecretName,
|
||||
Namespace: infisicalDynamicSecret.Spec.ManagedSecretReference.SecretNamespace,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
if util.IsNamespaceScopedError(err, r.IsNamespaceScoped) {
|
||||
return nextReconcile, fmt.Errorf("unable to fetch Kubernetes destination secret. Your Operator installation is namespace scoped, and cannot read secrets outside of the namespace it is installed in. Please ensure the destination secret is in the same namespace as the operator. [err=%v]", err)
|
||||
}
|
||||
if k8Errors.IsNotFound(err) {
|
||||
|
||||
annotationValue := ""
|
||||
if infisicalDynamicSecret.Status.Lease != nil {
|
||||
annotationValue = fmt.Sprintf("%s-%d", infisicalDynamicSecret.Status.Lease.ID, infisicalDynamicSecret.Status.Lease.Version)
|
||||
}
|
||||
|
||||
r.createInfisicalManagedKubeSecret(ctx, logger, *infisicalDynamicSecret, annotationValue)
|
||||
|
||||
destination, err = util.GetKubeSecretByNamespacedName(ctx, r.Client, types.NamespacedName{
|
||||
Name: infisicalDynamicSecret.Spec.ManagedSecretReference.SecretName,
|
||||
Namespace: infisicalDynamicSecret.Spec.ManagedSecretReference.SecretNamespace,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
if util.IsNamespaceScopedError(err, r.IsNamespaceScoped) {
|
||||
return nextReconcile, fmt.Errorf("unable to fetch Kubernetes destination secret after creation. Your Operator installation is namespace scoped, and cannot read secrets outside of the namespace it is installed in. Please ensure the destination secret is in the same namespace as the operator. [err=%v]", err)
|
||||
}
|
||||
return nextReconcile, fmt.Errorf("unable to fetch destination secret after creation [err=%s]", err)
|
||||
}
|
||||
|
||||
} else {
|
||||
return nextReconcile, fmt.Errorf("unable to fetch destination secret")
|
||||
}
|
||||
}
|
||||
|
||||
if infisicalDynamicSecret.Status.Lease == nil {
|
||||
err := r.CreateDynamicSecretLease(ctx, logger, infisicalClient, infisicalDynamicSecret, destination)
|
||||
|
||||
return defaultNextReconcile, err // Short requeue after creation
|
||||
} else {
|
||||
now := time.Now()
|
||||
leaseExpiresAt := infisicalDynamicSecret.Status.Lease.ExpiresAt.Time
|
||||
|
||||
// Calculate from creation to expiration
|
||||
originalLeaseDuration := leaseExpiresAt.Sub(infisicalDynamicSecret.Status.Lease.CreationTimestamp.Time)
|
||||
|
||||
// Generate a random percentage between 20% and 30%
|
||||
jitterPercentage := 20 + r.Random.Intn(11) // Random int from 0 to 10, then add 20
|
||||
renewalThreshold := originalLeaseDuration * time.Duration(jitterPercentage) / 100
|
||||
timeUntilExpiration := time.Until(leaseExpiresAt)
|
||||
|
||||
nextReconcile = timeUntilExpiration / 2
|
||||
|
||||
// Max TTL
|
||||
if infisicalDynamicSecret.Status.MaxTTL != "" {
|
||||
maxTTLDuration, err := util.ConvertIntervalToDuration(&infisicalDynamicSecret.Status.MaxTTL)
|
||||
if err != nil {
|
||||
return defaultNextReconcile, fmt.Errorf("unable to parse MaxTTL duration: %w", err)
|
||||
}
|
||||
|
||||
// Calculate when this dynamic secret will hit its max TTL
|
||||
maxTTLExpirationTime := infisicalDynamicSecret.Status.Lease.CreationTimestamp.Add(maxTTLDuration)
|
||||
|
||||
// Calculate remaining time until max TTL
|
||||
timeUntilMaxTTL := maxTTLExpirationTime.Sub(now)
|
||||
maxTTLThreshold := maxTTLDuration * 40 / 100
|
||||
|
||||
// If we have less than 40% of max TTL remaining or have exceeded it, create new lease
|
||||
if timeUntilMaxTTL <= maxTTLThreshold || now.After(maxTTLExpirationTime) {
|
||||
logger.Info(fmt.Sprintf("Approaching or exceeded max TTL [timeUntilMaxTTL=%v] [maxTTLThreshold=%v], creating new lease...",
|
||||
timeUntilMaxTTL,
|
||||
maxTTLThreshold))
|
||||
|
||||
err := r.CreateDynamicSecretLease(ctx, logger, infisicalClient, infisicalDynamicSecret, destination)
|
||||
return defaultNextReconcile, err // Short requeue after creation
|
||||
}
|
||||
}
|
||||
|
||||
// Fail-safe: If the lease has expired we create a new dynamic secret directly.
|
||||
if now.After(leaseExpiresAt) {
|
||||
logger.Info("Lease has expired, creating new lease...")
|
||||
err = r.CreateDynamicSecretLease(ctx, logger, infisicalClient, infisicalDynamicSecret, destination)
|
||||
return defaultNextReconcile, err // Short requeue after creation
|
||||
}
|
||||
|
||||
if timeUntilExpiration < renewalThreshold || timeUntilExpiration < 30*time.Second {
|
||||
logger.Info(fmt.Sprintf("Lease renewal needed [leaseId=%s] [timeUntilExpiration=%v] [threshold=%v]",
|
||||
infisicalDynamicSecret.Status.Lease.ID,
|
||||
timeUntilExpiration,
|
||||
renewalThreshold))
|
||||
|
||||
err = r.RenewDynamicSecretLease(ctx, logger, infisicalClient, infisicalDynamicSecret, destination)
|
||||
|
||||
if err == constants.ErrInvalidLease {
|
||||
logger.Info("Failed to renew expired lease, creating new lease...")
|
||||
err = r.CreateDynamicSecretLease(ctx, logger, infisicalClient, infisicalDynamicSecret, destination)
|
||||
}
|
||||
return defaultNextReconcile, err // Short requeue after renewal/creation
|
||||
|
||||
} else {
|
||||
logger.Info(fmt.Sprintf("Lease renewal not needed yet [leaseId=%s] [timeUntilExpiration=%v] [threshold=%v]",
|
||||
infisicalDynamicSecret.Status.Lease.ID,
|
||||
timeUntilExpiration,
|
||||
renewalThreshold))
|
||||
}
|
||||
|
||||
// Small buffer (20% of the calculated time) to ensure we don't cut it too close
|
||||
nextReconcile = nextReconcile * 8 / 10
|
||||
|
||||
// Minimum and maximum bounds for the reconcile interval (5 min max, 5 min minimum)
|
||||
nextReconcile = max(5*time.Second, min(nextReconcile, 5*time.Minute))
|
||||
}
|
||||
|
||||
if err := r.Client.Status().Update(ctx, infisicalDynamicSecret); err != nil {
|
||||
return nextReconcile, fmt.Errorf("unable to update InfisicalDynamicSecret status [err=%s]", err)
|
||||
}
|
||||
|
||||
return nextReconcile, nil
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
package infisicalpushsecret
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func (r *InfisicalPushSecretReconciler) SetReconcileStatusCondition(ctx context.Context, infisicalPushSecret *v1alpha1.InfisicalPushSecret, err error) error {
|
||||
|
||||
if infisicalPushSecret.Status.Conditions == nil {
|
||||
infisicalPushSecret.Status.Conditions = []metav1.Condition{}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
meta.SetStatusCondition(&infisicalPushSecret.Status.Conditions, metav1.Condition{
|
||||
Type: "secrets.infisical.com/Reconcile",
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: "Error",
|
||||
Message: fmt.Sprintf("Reconcile failed, secrets were not pushed to Infisical. Error: %s", err.Error()),
|
||||
})
|
||||
} else {
|
||||
meta.SetStatusCondition(&infisicalPushSecret.Status.Conditions, metav1.Condition{
|
||||
Type: "secrets.infisical.com/Reconcile",
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: "OK",
|
||||
Message: "Reconcile succeeded, secrets were pushed to Infisical",
|
||||
})
|
||||
}
|
||||
|
||||
return r.Client.Status().Update(ctx, infisicalPushSecret)
|
||||
|
||||
}
|
||||
|
||||
func (r *InfisicalPushSecretReconciler) SetFailedToReplaceSecretsStatusCondition(ctx context.Context, infisicalPushSecret *v1alpha1.InfisicalPushSecret, failMessage string) error {
|
||||
if infisicalPushSecret.Status.Conditions == nil {
|
||||
infisicalPushSecret.Status.Conditions = []metav1.Condition{}
|
||||
}
|
||||
|
||||
if failMessage != "" {
|
||||
meta.SetStatusCondition(&infisicalPushSecret.Status.Conditions, metav1.Condition{
|
||||
Type: "secrets.infisical.com/FailedToReplaceSecrets",
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: "Error",
|
||||
Message: failMessage,
|
||||
})
|
||||
} else {
|
||||
meta.SetStatusCondition(&infisicalPushSecret.Status.Conditions, metav1.Condition{
|
||||
Type: "secrets.infisical.com/FailedToReplaceSecrets",
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: "OK",
|
||||
Message: "No errors, no secrets failed to be replaced in Infisical",
|
||||
})
|
||||
}
|
||||
|
||||
return r.Client.Status().Update(ctx, infisicalPushSecret)
|
||||
}
|
||||
|
||||
func (r *InfisicalPushSecretReconciler) SetFailedToCreateSecretsStatusCondition(ctx context.Context, infisicalPushSecret *v1alpha1.InfisicalPushSecret, failMessage string) error {
|
||||
if infisicalPushSecret.Status.Conditions == nil {
|
||||
infisicalPushSecret.Status.Conditions = []metav1.Condition{}
|
||||
}
|
||||
|
||||
if failMessage != "" {
|
||||
meta.SetStatusCondition(&infisicalPushSecret.Status.Conditions, metav1.Condition{
|
||||
Type: "secrets.infisical.com/FailedToCreateSecrets",
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: "Error",
|
||||
Message: failMessage,
|
||||
})
|
||||
} else {
|
||||
meta.SetStatusCondition(&infisicalPushSecret.Status.Conditions, metav1.Condition{
|
||||
Type: "secrets.infisical.com/FailedToCreateSecrets",
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: "OK",
|
||||
Message: "No errors encountered, no secrets failed to be created in Infisical",
|
||||
})
|
||||
}
|
||||
|
||||
return r.Client.Status().Update(ctx, infisicalPushSecret)
|
||||
}
|
||||
|
||||
func (r *InfisicalPushSecretReconciler) SetFailedToUpdateSecretsStatusCondition(ctx context.Context, infisicalPushSecret *v1alpha1.InfisicalPushSecret, failMessage string) error {
|
||||
if infisicalPushSecret.Status.Conditions == nil {
|
||||
infisicalPushSecret.Status.Conditions = []metav1.Condition{}
|
||||
}
|
||||
|
||||
if failMessage != "" {
|
||||
meta.SetStatusCondition(&infisicalPushSecret.Status.Conditions, metav1.Condition{
|
||||
Type: "secrets.infisical.com/FailedToUpdateSecrets",
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: "Error",
|
||||
Message: failMessage,
|
||||
})
|
||||
} else {
|
||||
meta.SetStatusCondition(&infisicalPushSecret.Status.Conditions, metav1.Condition{
|
||||
Type: "secrets.infisical.com/FailedToUpdateSecrets",
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: "OK",
|
||||
Message: "No errors encountered, no secrets failed to be updated in Infisical",
|
||||
})
|
||||
}
|
||||
|
||||
return r.Client.Status().Update(ctx, infisicalPushSecret)
|
||||
}
|
||||
|
||||
func (r *InfisicalPushSecretReconciler) SetFailedToDeleteSecretsStatusCondition(ctx context.Context, infisicalPushSecret *v1alpha1.InfisicalPushSecret, failMessage string) error {
|
||||
if infisicalPushSecret.Status.Conditions == nil {
|
||||
infisicalPushSecret.Status.Conditions = []metav1.Condition{}
|
||||
}
|
||||
|
||||
if failMessage != "" {
|
||||
meta.SetStatusCondition(&infisicalPushSecret.Status.Conditions, metav1.Condition{
|
||||
Type: "secrets.infisical.com/FailedToDeleteSecrets",
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: "Error",
|
||||
Message: failMessage,
|
||||
})
|
||||
} else {
|
||||
meta.SetStatusCondition(&infisicalPushSecret.Status.Conditions, metav1.Condition{
|
||||
Type: "secrets.infisical.com/FailedToDeleteSecrets",
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: "OK",
|
||||
Message: "No errors encountered, no secrets failed to be deleted",
|
||||
})
|
||||
}
|
||||
|
||||
return r.Client.Status().Update(ctx, infisicalPushSecret)
|
||||
}
|
||||
|
||||
func (r *InfisicalPushSecretReconciler) SetAuthenticatedStatusCondition(ctx context.Context, infisicalPushSecret *v1alpha1.InfisicalPushSecret, errorToConditionOn error) error {
|
||||
if infisicalPushSecret.Status.Conditions == nil {
|
||||
infisicalPushSecret.Status.Conditions = []metav1.Condition{}
|
||||
}
|
||||
|
||||
if errorToConditionOn != nil {
|
||||
meta.SetStatusCondition(&infisicalPushSecret.Status.Conditions, metav1.Condition{
|
||||
Type: "secrets.infisical.com/Authenticated",
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: "Error",
|
||||
Message: "Failed to authenticate with Infisical API. This can be caused by invalid service token or an invalid API host that is set. Check operator logs for more info",
|
||||
})
|
||||
} else {
|
||||
meta.SetStatusCondition(&infisicalPushSecret.Status.Conditions, metav1.Condition{
|
||||
Type: "secrets.infisical.com/Authenticated",
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: "OK",
|
||||
Message: "Successfully authenticated with Infisical API",
|
||||
})
|
||||
}
|
||||
|
||||
return r.Client.Status().Update(ctx, infisicalPushSecret)
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
package infisicalpushsecret
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
||||
"github.com/Infisical/infisical/k8-operator/internal/api"
|
||||
"github.com/Infisical/infisical/k8-operator/internal/util"
|
||||
"github.com/go-logr/logr"
|
||||
k8Errors "k8s.io/apimachinery/pkg/api/errors"
|
||||
)
|
||||
|
||||
type InfisicalPushSecretHandler struct {
|
||||
client.Client
|
||||
Scheme *runtime.Scheme
|
||||
IsNamespaceScoped bool
|
||||
}
|
||||
|
||||
func NewInfisicalPushSecretHandler(client client.Client, scheme *runtime.Scheme, isNamespaceScoped bool) *InfisicalPushSecretHandler {
|
||||
return &InfisicalPushSecretHandler{
|
||||
Client: client,
|
||||
Scheme: scheme,
|
||||
IsNamespaceScoped: isNamespaceScoped,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *InfisicalPushSecretHandler) SetupAPIConfig(infisicalPushSecret v1alpha1.InfisicalPushSecret, infisicalConfig map[string]string) error {
|
||||
if infisicalPushSecret.Spec.HostAPI == "" {
|
||||
api.API_HOST_URL = infisicalConfig["hostAPI"]
|
||||
} else {
|
||||
api.API_HOST_URL = util.AppendAPIEndpoint(infisicalPushSecret.Spec.HostAPI)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *InfisicalPushSecretHandler) getInfisicalCaCertificateFromKubeSecret(ctx context.Context, infisicalPushSecret v1alpha1.InfisicalPushSecret) (caCertificate string, err error) {
|
||||
|
||||
caCertificateFromKubeSecret, err := util.GetKubeSecretByNamespacedName(ctx, h.Client, types.NamespacedName{
|
||||
Namespace: infisicalPushSecret.Spec.TLS.CaRef.SecretNamespace,
|
||||
Name: infisicalPushSecret.Spec.TLS.CaRef.SecretName,
|
||||
})
|
||||
|
||||
if k8Errors.IsNotFound(err) {
|
||||
return "", fmt.Errorf("kubernetes secret containing custom CA certificate cannot be found. [err=%s]", err)
|
||||
}
|
||||
|
||||
if util.IsNamespaceScopedError(err, h.IsNamespaceScoped) {
|
||||
return "", fmt.Errorf("unable to fetch Kubernetes CA certificate secret. Your Operator installation is namespace scoped, and cannot read secrets outside of the namespace it is installed in. Please ensure the CA certificate secret is in the same namespace as the operator. [err=%v]", err)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("something went wrong when fetching your CA certificate [err=%s]", err)
|
||||
}
|
||||
|
||||
caCertificateFromSecret := string(caCertificateFromKubeSecret.Data[infisicalPushSecret.Spec.TLS.CaRef.SecretKey])
|
||||
|
||||
return caCertificateFromSecret, nil
|
||||
}
|
||||
|
||||
func (h *InfisicalPushSecretHandler) HandleCACertificate(ctx context.Context, infisicalPushSecret v1alpha1.InfisicalPushSecret) error {
|
||||
if infisicalPushSecret.Spec.TLS.CaRef.SecretName != "" {
|
||||
caCert, err := h.getInfisicalCaCertificateFromKubeSecret(ctx, infisicalPushSecret)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
api.API_CA_CERTIFICATE = caCert
|
||||
} else {
|
||||
api.API_CA_CERTIFICATE = ""
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *InfisicalPushSecretHandler) ReconcileInfisicalPushSecret(ctx context.Context, logger logr.Logger, infisicalPushSecret *v1alpha1.InfisicalPushSecret, resourceVariablesMap map[string]util.ResourceVariables) error {
|
||||
reconciler := &InfisicalPushSecretReconciler{
|
||||
Client: h.Client,
|
||||
Scheme: h.Scheme,
|
||||
IsNamespaceScoped: h.IsNamespaceScoped,
|
||||
}
|
||||
return reconciler.ReconcileInfisicalPushSecret(ctx, logger, infisicalPushSecret, resourceVariablesMap)
|
||||
}
|
||||
|
||||
func (h *InfisicalPushSecretHandler) DeleteManagedSecrets(ctx context.Context, logger logr.Logger, infisicalPushSecret *v1alpha1.InfisicalPushSecret, resourceVariablesMap map[string]util.ResourceVariables) error {
|
||||
reconciler := &InfisicalPushSecretReconciler{
|
||||
Client: h.Client,
|
||||
Scheme: h.Scheme,
|
||||
IsNamespaceScoped: h.IsNamespaceScoped,
|
||||
}
|
||||
return reconciler.DeleteManagedSecrets(ctx, logger, infisicalPushSecret, resourceVariablesMap)
|
||||
}
|
||||
|
||||
func (h *InfisicalPushSecretHandler) SetReconcileStatusCondition(ctx context.Context, infisicalPushSecret *v1alpha1.InfisicalPushSecret, err error) {
|
||||
reconciler := &InfisicalPushSecretReconciler{
|
||||
Client: h.Client,
|
||||
Scheme: h.Scheme,
|
||||
IsNamespaceScoped: h.IsNamespaceScoped,
|
||||
}
|
||||
reconciler.SetReconcileStatusCondition(ctx, infisicalPushSecret, err)
|
||||
}
|
||||
@@ -1,573 +0,0 @@
|
||||
package infisicalpushsecret
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
tpl "text/template"
|
||||
|
||||
"github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
||||
"github.com/Infisical/infisical/k8-operator/internal/api"
|
||||
"github.com/Infisical/infisical/k8-operator/internal/constants"
|
||||
"github.com/Infisical/infisical/k8-operator/internal/model"
|
||||
"github.com/Infisical/infisical/k8-operator/internal/template"
|
||||
"github.com/Infisical/infisical/k8-operator/internal/util"
|
||||
"github.com/go-logr/logr"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
generatorUtil "github.com/Infisical/infisical/k8-operator/internal/generator"
|
||||
infisicalSdk "github.com/infisical/go-sdk"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
type InfisicalPushSecretReconciler struct {
|
||||
client.Client
|
||||
Scheme *runtime.Scheme
|
||||
IsNamespaceScoped bool
|
||||
}
|
||||
|
||||
func (r *InfisicalPushSecretReconciler) getResourceVariables(infisicalPushSecret v1alpha1.InfisicalPushSecret, resourceVariablesMap map[string]util.ResourceVariables) util.ResourceVariables {
|
||||
|
||||
var resourceVariables util.ResourceVariables
|
||||
|
||||
if _, ok := resourceVariablesMap[string(infisicalPushSecret.UID)]; !ok {
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
client := infisicalSdk.NewInfisicalClient(ctx, infisicalSdk.Config{
|
||||
SiteUrl: api.API_HOST_URL,
|
||||
CaCertificate: api.API_CA_CERTIFICATE,
|
||||
UserAgent: api.USER_AGENT_NAME,
|
||||
})
|
||||
|
||||
resourceVariablesMap[string(infisicalPushSecret.UID)] = util.ResourceVariables{
|
||||
InfisicalClient: client,
|
||||
CancelCtx: cancel,
|
||||
AuthDetails: util.AuthenticationDetails{},
|
||||
}
|
||||
|
||||
resourceVariables = resourceVariablesMap[string(infisicalPushSecret.UID)]
|
||||
|
||||
} else {
|
||||
resourceVariables = resourceVariablesMap[string(infisicalPushSecret.UID)]
|
||||
}
|
||||
|
||||
return resourceVariables
|
||||
|
||||
}
|
||||
|
||||
func (r *InfisicalPushSecretReconciler) updateResourceVariables(infisicalPushSecret v1alpha1.InfisicalPushSecret, resourceVariables util.ResourceVariables, resourceVariablesMap map[string]util.ResourceVariables) {
|
||||
resourceVariablesMap[string(infisicalPushSecret.UID)] = resourceVariables
|
||||
}
|
||||
|
||||
func (r *InfisicalPushSecretReconciler) processGenerators(ctx context.Context, infisicalPushSecret v1alpha1.InfisicalPushSecret) (map[string]string, error) {
|
||||
|
||||
processedSecrets := make(map[string]string)
|
||||
|
||||
if len(infisicalPushSecret.Spec.Push.Generators) == 0 {
|
||||
return processedSecrets, nil
|
||||
}
|
||||
|
||||
for _, generator := range infisicalPushSecret.Spec.Push.Generators {
|
||||
generatorRef := generator.GeneratorRef
|
||||
|
||||
clusterGenerator := &v1alpha1.ClusterGenerator{}
|
||||
err := r.Client.Get(ctx, types.NamespacedName{Name: generatorRef.Name}, clusterGenerator)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to get ClusterGenerator resource [err=%s]", err)
|
||||
}
|
||||
if generatorRef.Kind == v1alpha1.GeneratorKindPassword {
|
||||
// get the custom ClusterGenerator resource from the cluster
|
||||
|
||||
if clusterGenerator.Spec.Generator.PasswordSpec == nil {
|
||||
return nil, fmt.Errorf("password spec is not defined in the ClusterGenerator resource")
|
||||
}
|
||||
|
||||
password, err := generatorUtil.GeneratorPassword(*clusterGenerator.Spec.Generator.PasswordSpec)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to generate password [err=%s]", err)
|
||||
}
|
||||
|
||||
processedSecrets[generator.DestinationSecretName] = password
|
||||
}
|
||||
|
||||
if generatorRef.Kind == v1alpha1.GeneratorKindUUID {
|
||||
|
||||
uuid, err := generatorUtil.GeneratorUUID()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to generate UUID [err=%s]", err)
|
||||
}
|
||||
|
||||
processedSecrets[generator.DestinationSecretName] = uuid
|
||||
}
|
||||
}
|
||||
|
||||
return processedSecrets, nil
|
||||
|
||||
}
|
||||
|
||||
func (r *InfisicalPushSecretReconciler) processTemplatedSecrets(infisicalPushSecret v1alpha1.InfisicalPushSecret, kubePushSecret *corev1.Secret, destination v1alpha1.InfisicalPushSecretDestination) (map[string]string, error) {
|
||||
|
||||
processedSecrets := make(map[string]string)
|
||||
|
||||
sourceSecrets := make(map[string]model.SecretTemplateOptions)
|
||||
for key, value := range kubePushSecret.Data {
|
||||
|
||||
sourceSecrets[key] = model.SecretTemplateOptions{
|
||||
Value: string(value),
|
||||
SecretPath: destination.SecretsPath,
|
||||
}
|
||||
}
|
||||
|
||||
if infisicalPushSecret.Spec.Push.Secret.Template == nil || (infisicalPushSecret.Spec.Push.Secret.Template != nil && infisicalPushSecret.Spec.Push.Secret.Template.IncludeAllSecrets) {
|
||||
for key, value := range kubePushSecret.Data {
|
||||
processedSecrets[key] = string(value)
|
||||
}
|
||||
}
|
||||
|
||||
if infisicalPushSecret.Spec.Push.Secret.Template != nil &&
|
||||
len(infisicalPushSecret.Spec.Push.Secret.Template.Data) > 0 {
|
||||
|
||||
for templateKey, userTemplate := range infisicalPushSecret.Spec.Push.Secret.Template.Data {
|
||||
|
||||
tmpl, err := tpl.New("push-secret-templates").Funcs(template.GetTemplateFunctions()).Parse(userTemplate)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to compile template: %s [err=%v]", templateKey, err)
|
||||
}
|
||||
|
||||
buf := bytes.NewBuffer(nil)
|
||||
err = tmpl.Execute(buf, sourceSecrets)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to execute template: %s [err=%v]", templateKey, err)
|
||||
}
|
||||
|
||||
processedSecrets[templateKey] = buf.String()
|
||||
}
|
||||
}
|
||||
|
||||
return processedSecrets, nil
|
||||
}
|
||||
|
||||
func (r *InfisicalPushSecretReconciler) ReconcileInfisicalPushSecret(ctx context.Context, logger logr.Logger, infisicalPushSecret *v1alpha1.InfisicalPushSecret, resourceVariablesMap map[string]util.ResourceVariables) error {
|
||||
|
||||
resourceVariables := r.getResourceVariables(*infisicalPushSecret, resourceVariablesMap)
|
||||
infisicalClient := resourceVariables.InfisicalClient
|
||||
cancelCtx := resourceVariables.CancelCtx
|
||||
authDetails := resourceVariables.AuthDetails
|
||||
var err error
|
||||
|
||||
if authDetails.AuthStrategy == "" {
|
||||
logger.Info("No authentication strategy found. Attempting to authenticate")
|
||||
authDetails, err = util.HandleAuthentication(ctx, util.SecretAuthInput{
|
||||
Secret: *infisicalPushSecret,
|
||||
Type: util.SecretCrd.INFISICAL_PUSH_SECRET,
|
||||
}, r.Client, infisicalClient, r.IsNamespaceScoped)
|
||||
r.SetAuthenticatedStatusCondition(ctx, infisicalPushSecret, err)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to authenticate [err=%s]", err)
|
||||
}
|
||||
|
||||
r.updateResourceVariables(*infisicalPushSecret, util.ResourceVariables{
|
||||
InfisicalClient: infisicalClient,
|
||||
CancelCtx: cancelCtx,
|
||||
AuthDetails: authDetails,
|
||||
}, resourceVariablesMap)
|
||||
}
|
||||
|
||||
processedSecrets := make(map[string]string)
|
||||
|
||||
if infisicalPushSecret.Spec.Push.Secret != nil {
|
||||
kubePushSecret, err := util.GetKubeSecretByNamespacedName(ctx, r.Client, types.NamespacedName{
|
||||
Namespace: infisicalPushSecret.Spec.Push.Secret.SecretNamespace,
|
||||
Name: infisicalPushSecret.Spec.Push.Secret.SecretName,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
if util.IsNamespaceScopedError(err, r.IsNamespaceScoped) {
|
||||
return fmt.Errorf("unable to fetch Kubernetes destination secret. Your Operator installation is namespace scoped, and cannot read secrets outside of the namespace it is installed in. Please ensure the destination secret is in the same namespace as the operator. [err=%v]", err)
|
||||
}
|
||||
|
||||
return fmt.Errorf("unable to fetch kube secret [err=%s]", err)
|
||||
}
|
||||
|
||||
processedSecrets, err = r.processTemplatedSecrets(*infisicalPushSecret, kubePushSecret, infisicalPushSecret.Spec.Destination)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to process templated secrets [err=%s]", err)
|
||||
}
|
||||
}
|
||||
|
||||
generatorSecrets, err := r.processGenerators(ctx, *infisicalPushSecret)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to process generators [err=%s]", err)
|
||||
}
|
||||
|
||||
for key, value := range generatorSecrets {
|
||||
processedSecrets[key] = value
|
||||
}
|
||||
|
||||
destination := infisicalPushSecret.Spec.Destination
|
||||
existingSecrets, err := infisicalClient.Secrets().List(infisicalSdk.ListSecretsOptions{
|
||||
ProjectID: destination.ProjectID,
|
||||
Environment: destination.EnvironmentSlug,
|
||||
SecretPath: destination.SecretsPath,
|
||||
IncludeImports: false,
|
||||
})
|
||||
|
||||
getExistingSecretByKey := func(key string) *infisicalSdk.Secret {
|
||||
for _, secret := range existingSecrets {
|
||||
if secret.SecretKey == key {
|
||||
return &secret
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
getExistingSecretById := func(id string) *infisicalSdk.Secret {
|
||||
for _, secret := range existingSecrets {
|
||||
if secret.ID == id {
|
||||
return &secret
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
updateExistingSecretByKey := func(key string, newSecretValue string) {
|
||||
for i := range existingSecrets {
|
||||
if existingSecrets[i].SecretKey == key {
|
||||
existingSecrets[i].SecretValue = newSecretValue
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to list secrets [err=%s]", err)
|
||||
}
|
||||
|
||||
updatePolicy := infisicalPushSecret.Spec.UpdatePolicy
|
||||
|
||||
var secretsFailedToCreate []string
|
||||
var secretsFailedToUpdate []string
|
||||
var secretsFailedToDelete []string
|
||||
var secretsFailedToReplaceById []string
|
||||
|
||||
// If the ManagedSecrets are nil, we know this is the first time the InfisicalPushSecret is being reconciled.
|
||||
if infisicalPushSecret.Status.ManagedSecrets == nil {
|
||||
|
||||
infisicalPushSecret.Status.ManagedSecrets = make(map[string]string) // (string[id], string[key] )
|
||||
|
||||
for secretKey, secretValue := range processedSecrets {
|
||||
if exists := getExistingSecretByKey(secretKey); exists != nil {
|
||||
|
||||
if updatePolicy == string(constants.PUSH_SECRET_REPLACE_POLICY_ENABLED) {
|
||||
updatedSecret, err := infisicalClient.Secrets().Update(infisicalSdk.UpdateSecretOptions{
|
||||
SecretKey: secretKey,
|
||||
ProjectID: destination.ProjectID,
|
||||
Environment: destination.EnvironmentSlug,
|
||||
SecretPath: destination.SecretsPath,
|
||||
NewSecretValue: secretValue,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
secretsFailedToUpdate = append(secretsFailedToUpdate, secretKey)
|
||||
logger.Info(fmt.Sprintf("unable to update secret [key=%s] [err=%s]", secretKey, err))
|
||||
continue
|
||||
}
|
||||
|
||||
infisicalPushSecret.Status.ManagedSecrets[updatedSecret.ID] = secretKey
|
||||
}
|
||||
} else {
|
||||
createdSecret, err := infisicalClient.Secrets().Create(infisicalSdk.CreateSecretOptions{
|
||||
SecretKey: secretKey,
|
||||
SecretValue: secretValue,
|
||||
ProjectID: destination.ProjectID,
|
||||
Environment: destination.EnvironmentSlug,
|
||||
SecretPath: destination.SecretsPath,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
secretsFailedToCreate = append(secretsFailedToCreate, secretKey)
|
||||
logger.Info(fmt.Sprintf("unable to create secret [key=%s] [err=%s]", secretKey, err))
|
||||
continue
|
||||
}
|
||||
|
||||
infisicalPushSecret.Status.ManagedSecrets[createdSecret.ID] = secretKey
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
// Loop over all the managed secrets, and find the corresponding existingSecret that has the same ID. If the key doesn't match, delete the secret, and re-create it with the correct key/value
|
||||
for managedSecretId, managedSecretKey := range infisicalPushSecret.Status.ManagedSecrets {
|
||||
|
||||
existingSecret := getExistingSecretById(managedSecretId)
|
||||
|
||||
if existingSecret != nil {
|
||||
|
||||
if existingSecret.SecretKey != managedSecretKey {
|
||||
// Secret key has changed, lets delete the secret and re-create it with the correct key
|
||||
|
||||
logger.Info(fmt.Sprintf("Secret with ID [id=%s] has changed key from [%s] to [%s]. Deleting and re-creating secret", managedSecretId, managedSecretKey, existingSecret.SecretKey))
|
||||
|
||||
deletedSecret, err := infisicalClient.Secrets().Delete(infisicalSdk.DeleteSecretOptions{
|
||||
SecretKey: existingSecret.SecretKey,
|
||||
ProjectID: destination.ProjectID,
|
||||
Environment: destination.EnvironmentSlug,
|
||||
SecretPath: destination.SecretsPath,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
secretsFailedToReplaceById = append(secretsFailedToReplaceById, managedSecretKey)
|
||||
logger.Info(fmt.Sprintf("unable to delete secret [key=%s] [err=%s]", managedSecretKey, err))
|
||||
continue
|
||||
}
|
||||
|
||||
createdSecret, err := infisicalClient.Secrets().Create(infisicalSdk.CreateSecretOptions{
|
||||
SecretKey: managedSecretKey,
|
||||
SecretValue: existingSecret.SecretValue,
|
||||
ProjectID: destination.ProjectID,
|
||||
Environment: destination.EnvironmentSlug,
|
||||
SecretPath: destination.SecretsPath,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
secretsFailedToReplaceById = append(secretsFailedToReplaceById, managedSecretKey)
|
||||
logger.Info(fmt.Sprintf("unable to create secret [key=%s] [err=%s]", managedSecretKey, err))
|
||||
continue
|
||||
}
|
||||
|
||||
delete(infisicalPushSecret.Status.ManagedSecrets, deletedSecret.ID)
|
||||
infisicalPushSecret.Status.ManagedSecrets[createdSecret.ID] = managedSecretKey
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// We need to check if any of the secrets have been removed in the new kube secret
|
||||
for _, managedSecretKey := range infisicalPushSecret.Status.ManagedSecrets {
|
||||
|
||||
if _, ok := processedSecrets[managedSecretKey]; !ok {
|
||||
|
||||
// Secret has been removed, verify that the secret is managed by the operator
|
||||
if getExistingSecretByKey(managedSecretKey) != nil {
|
||||
logger.Info(fmt.Sprintf("Secret with key [key=%s] has been removed from the kube secret. Deleting secret from Infisical", managedSecretKey))
|
||||
|
||||
deletedSecret, err := infisicalClient.Secrets().Delete(infisicalSdk.DeleteSecretOptions{
|
||||
SecretKey: managedSecretKey,
|
||||
ProjectID: destination.ProjectID,
|
||||
Environment: destination.EnvironmentSlug,
|
||||
SecretPath: destination.SecretsPath,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
secretsFailedToDelete = append(secretsFailedToDelete, managedSecretKey)
|
||||
logger.Info(fmt.Sprintf("unable to delete secret [key=%s] [err=%s]", managedSecretKey, err))
|
||||
continue
|
||||
}
|
||||
|
||||
delete(infisicalPushSecret.Status.ManagedSecrets, deletedSecret.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We need to check if any new secrets have been added in the kube secret
|
||||
for currentSecretKey := range processedSecrets {
|
||||
|
||||
if exists := getExistingSecretByKey(currentSecretKey); exists == nil {
|
||||
|
||||
// Some secrets has been added, verify that the secret that has been added is not already managed by the operator
|
||||
if _, ok := infisicalPushSecret.Status.ManagedSecrets[currentSecretKey]; !ok {
|
||||
|
||||
// Secret was not managed by the operator, lets add it
|
||||
logger.Info(fmt.Sprintf("Secret with key [key=%s] has been added to the kube secret. Creating secret in Infisical", currentSecretKey))
|
||||
|
||||
createdSecret, err := infisicalClient.Secrets().Create(infisicalSdk.CreateSecretOptions{
|
||||
SecretKey: currentSecretKey,
|
||||
SecretValue: processedSecrets[currentSecretKey],
|
||||
ProjectID: destination.ProjectID,
|
||||
Environment: destination.EnvironmentSlug,
|
||||
SecretPath: destination.SecretsPath,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
secretsFailedToCreate = append(secretsFailedToCreate, currentSecretKey)
|
||||
logger.Info(fmt.Sprintf("unable to create secret [key=%s] [err=%s]", currentSecretKey, err))
|
||||
continue
|
||||
}
|
||||
|
||||
infisicalPushSecret.Status.ManagedSecrets[createdSecret.ID] = currentSecretKey
|
||||
}
|
||||
} else {
|
||||
if updatePolicy == string(constants.PUSH_SECRET_REPLACE_POLICY_ENABLED) {
|
||||
|
||||
existingSecret := getExistingSecretByKey(currentSecretKey)
|
||||
|
||||
if existingSecret != nil && existingSecret.SecretValue != processedSecrets[currentSecretKey] {
|
||||
logger.Info(fmt.Sprintf("Secret with key [key=%s] has changed value. Updating secret in Infisical", currentSecretKey))
|
||||
|
||||
updatedSecret, err := infisicalClient.Secrets().Update(infisicalSdk.UpdateSecretOptions{
|
||||
SecretKey: currentSecretKey,
|
||||
NewSecretValue: processedSecrets[currentSecretKey],
|
||||
ProjectID: destination.ProjectID,
|
||||
Environment: destination.EnvironmentSlug,
|
||||
SecretPath: destination.SecretsPath,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
secretsFailedToUpdate = append(secretsFailedToUpdate, currentSecretKey)
|
||||
logger.Info(fmt.Sprintf("unable to update secret [key=%s] [err=%s]", currentSecretKey, err))
|
||||
continue
|
||||
}
|
||||
|
||||
updateExistingSecretByKey(currentSecretKey, processedSecrets[currentSecretKey])
|
||||
infisicalPushSecret.Status.ManagedSecrets[updatedSecret.ID] = currentSecretKey
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if any of the existing secrets values have changed
|
||||
for secretKey, secretValue := range processedSecrets {
|
||||
|
||||
existingSecret := getExistingSecretByKey(secretKey)
|
||||
|
||||
if existingSecret != nil {
|
||||
|
||||
_, managedByOperator := infisicalPushSecret.Status.ManagedSecrets[existingSecret.ID]
|
||||
|
||||
if secretValue != existingSecret.SecretValue {
|
||||
|
||||
if managedByOperator || updatePolicy == string(constants.PUSH_SECRET_REPLACE_POLICY_ENABLED) {
|
||||
logger.Info(fmt.Sprintf("Secret with key [key=%s] has changed value. Updating secret in Infisical", secretKey))
|
||||
|
||||
updatedSecret, err := infisicalClient.Secrets().Update(infisicalSdk.UpdateSecretOptions{
|
||||
SecretKey: secretKey,
|
||||
NewSecretValue: secretValue,
|
||||
ProjectID: destination.ProjectID,
|
||||
Environment: destination.EnvironmentSlug,
|
||||
SecretPath: destination.SecretsPath,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
secretsFailedToUpdate = append(secretsFailedToUpdate, secretKey)
|
||||
logger.Info(fmt.Sprintf("unable to update secret [key=%s] [err=%s]", secretKey, err))
|
||||
continue
|
||||
}
|
||||
|
||||
infisicalPushSecret.Status.ManagedSecrets[updatedSecret.ID] = secretKey
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var errorMessage string
|
||||
if len(secretsFailedToCreate) > 0 {
|
||||
errorMessage = fmt.Sprintf("Failed to create secrets: [%s]", strings.Join(secretsFailedToCreate, ", "))
|
||||
} else {
|
||||
errorMessage = ""
|
||||
}
|
||||
r.SetFailedToCreateSecretsStatusCondition(ctx, infisicalPushSecret, fmt.Sprintf("Failed to create secrets: [%s]", errorMessage))
|
||||
|
||||
if len(secretsFailedToUpdate) > 0 {
|
||||
errorMessage = fmt.Sprintf("Failed to update secrets: [%s]", strings.Join(secretsFailedToUpdate, ", "))
|
||||
} else {
|
||||
errorMessage = ""
|
||||
}
|
||||
r.SetFailedToUpdateSecretsStatusCondition(ctx, infisicalPushSecret, fmt.Sprintf("Failed to update secrets: [%s]", errorMessage))
|
||||
|
||||
if len(secretsFailedToDelete) > 0 {
|
||||
errorMessage = fmt.Sprintf("Failed to delete secrets: [%s]", strings.Join(secretsFailedToDelete, ", "))
|
||||
} else {
|
||||
errorMessage = ""
|
||||
}
|
||||
r.SetFailedToDeleteSecretsStatusCondition(ctx, infisicalPushSecret, errorMessage)
|
||||
|
||||
if len(secretsFailedToReplaceById) > 0 {
|
||||
errorMessage = fmt.Sprintf("Failed to replace secrets: [%s]", strings.Join(secretsFailedToReplaceById, ", "))
|
||||
} else {
|
||||
errorMessage = ""
|
||||
}
|
||||
r.SetFailedToReplaceSecretsStatusCondition(ctx, infisicalPushSecret, errorMessage)
|
||||
|
||||
// Update the status of the InfisicalPushSecret
|
||||
if err := r.Client.Status().Update(ctx, infisicalPushSecret); err != nil {
|
||||
return fmt.Errorf("unable to update status of InfisicalPushSecret [err=%s]", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (r *InfisicalPushSecretReconciler) DeleteManagedSecrets(ctx context.Context, logger logr.Logger, infisicalPushSecret *v1alpha1.InfisicalPushSecret, resourceVariablesMap map[string]util.ResourceVariables) error {
|
||||
if infisicalPushSecret.Spec.DeletionPolicy != string(constants.PUSH_SECRET_DELETE_POLICY_ENABLED) {
|
||||
return nil
|
||||
}
|
||||
|
||||
resourceVariables := r.getResourceVariables(*infisicalPushSecret, resourceVariablesMap)
|
||||
infisicalClient := resourceVariables.InfisicalClient
|
||||
cancelCtx := resourceVariables.CancelCtx
|
||||
authDetails := resourceVariables.AuthDetails
|
||||
var err error
|
||||
|
||||
if authDetails.AuthStrategy == "" {
|
||||
logger.Info("No authentication strategy found. Attempting to authenticate")
|
||||
authDetails, err = util.HandleAuthentication(ctx, util.SecretAuthInput{
|
||||
Secret: *infisicalPushSecret,
|
||||
Type: util.SecretCrd.INFISICAL_PUSH_SECRET,
|
||||
}, r.Client, infisicalClient, r.IsNamespaceScoped)
|
||||
r.SetAuthenticatedStatusCondition(ctx, infisicalPushSecret, err)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to authenticate [err=%s]", err)
|
||||
}
|
||||
|
||||
r.updateResourceVariables(*infisicalPushSecret, util.ResourceVariables{
|
||||
InfisicalClient: infisicalClient,
|
||||
CancelCtx: cancelCtx,
|
||||
AuthDetails: authDetails,
|
||||
}, resourceVariablesMap)
|
||||
}
|
||||
|
||||
destination := infisicalPushSecret.Spec.Destination
|
||||
existingSecrets, err := resourceVariables.InfisicalClient.Secrets().List(infisicalSdk.ListSecretsOptions{
|
||||
ProjectID: destination.ProjectID,
|
||||
Environment: destination.EnvironmentSlug,
|
||||
SecretPath: destination.SecretsPath,
|
||||
IncludeImports: false,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to list secrets [err=%s]", err)
|
||||
}
|
||||
|
||||
existingSecretsMappedById := make(map[string]infisicalSdk.Secret)
|
||||
for _, secret := range existingSecrets {
|
||||
existingSecretsMappedById[secret.ID] = secret
|
||||
}
|
||||
|
||||
for managedSecretId, managedSecretKey := range infisicalPushSecret.Status.ManagedSecrets {
|
||||
|
||||
if _, ok := existingSecretsMappedById[managedSecretId]; ok {
|
||||
logger.Info(fmt.Sprintf("Deleting secret with key [key=%s]", managedSecretKey))
|
||||
|
||||
_, err := infisicalClient.Secrets().Delete(infisicalSdk.DeleteSecretOptions{
|
||||
SecretKey: managedSecretKey,
|
||||
ProjectID: destination.ProjectID,
|
||||
Environment: destination.EnvironmentSlug,
|
||||
SecretPath: destination.SecretsPath,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
logger.Info(fmt.Sprintf("unable to delete secret [key=%s] [err=%s]", managedSecretKey, err))
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
package infisicalsecret
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
||||
"github.com/Infisical/infisical/k8-operator/internal/util"
|
||||
"github.com/go-logr/logr"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func (r *InfisicalSecretReconciler) SetReadyToSyncSecretsConditions(ctx context.Context, logger logr.Logger, infisicalSecret *v1alpha1.InfisicalSecret, secretsCount int, errorToConditionOn error) {
|
||||
if infisicalSecret.Status.Conditions == nil {
|
||||
infisicalSecret.Status.Conditions = []metav1.Condition{}
|
||||
}
|
||||
|
||||
if errorToConditionOn != nil {
|
||||
meta.SetStatusCondition(&infisicalSecret.Status.Conditions, metav1.Condition{
|
||||
Type: "secrets.infisical.com/ReadyToSyncSecrets",
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: "Error",
|
||||
Message: fmt.Sprintf("Failed to sync secrets. This can be caused by invalid access token or an invalid API host that is set. Error: %v", errorToConditionOn),
|
||||
})
|
||||
|
||||
meta.SetStatusCondition(&infisicalSecret.Status.Conditions, metav1.Condition{
|
||||
Type: "secrets.infisical.com/AutoRedeployReady",
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: "Stopped",
|
||||
Message: fmt.Sprintf("Auto redeployment has been stopped because the operator failed to sync secrets. Error: %v", errorToConditionOn),
|
||||
})
|
||||
} else {
|
||||
meta.SetStatusCondition(&infisicalSecret.Status.Conditions, metav1.Condition{
|
||||
Type: "secrets.infisical.com/ReadyToSyncSecrets",
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: "OK",
|
||||
Message: fmt.Sprintf("Infisical controller has started syncing your secrets. Last reconcile synced %d secrets", secretsCount),
|
||||
})
|
||||
}
|
||||
|
||||
err := r.Client.Status().Update(ctx, infisicalSecret)
|
||||
if err != nil {
|
||||
logger.Error(err, "Could not set condition for ReadyToSyncSecrets")
|
||||
}
|
||||
}
|
||||
|
||||
func (r *InfisicalSecretReconciler) SetInfisicalTokenLoadCondition(ctx context.Context, logger logr.Logger, infisicalSecret *v1alpha1.InfisicalSecret, authStrategy util.AuthStrategyType, errorToConditionOn error) {
|
||||
if infisicalSecret.Status.Conditions == nil {
|
||||
infisicalSecret.Status.Conditions = []metav1.Condition{}
|
||||
}
|
||||
|
||||
if errorToConditionOn == nil {
|
||||
meta.SetStatusCondition(&infisicalSecret.Status.Conditions, metav1.Condition{
|
||||
Type: "secrets.infisical.com/LoadedInfisicalToken",
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: "OK",
|
||||
Message: fmt.Sprintf("Infisical controller has loaded the Infisical token in provided Kubernetes secret, using %v authentication strategy", authStrategy),
|
||||
})
|
||||
} else {
|
||||
meta.SetStatusCondition(&infisicalSecret.Status.Conditions, metav1.Condition{
|
||||
Type: "secrets.infisical.com/LoadedInfisicalToken",
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: "Error",
|
||||
Message: fmt.Sprintf("Failed to load Infisical Token from the provided Kubernetes secret because: %v", errorToConditionOn),
|
||||
})
|
||||
}
|
||||
|
||||
err := r.Client.Status().Update(ctx, infisicalSecret)
|
||||
if err != nil {
|
||||
logger.Error(err, "Could not set condition for LoadedInfisicalToken")
|
||||
}
|
||||
}
|
||||
|
||||
func (r *InfisicalSecretReconciler) SetInfisicalAutoRedeploymentReady(ctx context.Context, logger logr.Logger, infisicalSecret *v1alpha1.InfisicalSecret, numDeployments int, errorToConditionOn error) {
|
||||
if infisicalSecret.Status.Conditions == nil {
|
||||
infisicalSecret.Status.Conditions = []metav1.Condition{}
|
||||
}
|
||||
|
||||
if errorToConditionOn == nil {
|
||||
meta.SetStatusCondition(&infisicalSecret.Status.Conditions, metav1.Condition{
|
||||
Type: "secrets.infisical.com/AutoRedeployReady",
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: "OK",
|
||||
Message: fmt.Sprintf("Infisical has found %v deployments which are ready to be auto redeployed when secrets change", numDeployments),
|
||||
})
|
||||
} else {
|
||||
meta.SetStatusCondition(&infisicalSecret.Status.Conditions, metav1.Condition{
|
||||
Type: "secrets.infisical.com/AutoRedeployReady",
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: "Error",
|
||||
Message: fmt.Sprintf("Failed reconcile deployments because: %v", errorToConditionOn),
|
||||
})
|
||||
}
|
||||
|
||||
err := r.Client.Status().Update(ctx, infisicalSecret)
|
||||
if err != nil {
|
||||
logger.Error(err, "Could not set condition for AutoRedeployReady")
|
||||
}
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
package infisicalsecret
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/event"
|
||||
|
||||
"github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
||||
"github.com/Infisical/infisical/k8-operator/internal/api"
|
||||
"github.com/Infisical/infisical/k8-operator/internal/util"
|
||||
"github.com/go-logr/logr"
|
||||
k8Errors "k8s.io/apimachinery/pkg/api/errors"
|
||||
)
|
||||
|
||||
type InfisicalSecretHandler struct {
|
||||
client.Client
|
||||
Scheme *runtime.Scheme
|
||||
IsNamespaceScoped bool
|
||||
}
|
||||
|
||||
func NewInfisicalSecretHandler(client client.Client, scheme *runtime.Scheme, isNamespaceScoped bool) *InfisicalSecretHandler {
|
||||
return &InfisicalSecretHandler{
|
||||
Client: client,
|
||||
Scheme: scheme,
|
||||
IsNamespaceScoped: isNamespaceScoped,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *InfisicalSecretHandler) SetupAPIConfig(infisicalSecret v1alpha1.InfisicalSecret, infisicalConfig map[string]string) error {
|
||||
if infisicalSecret.Spec.HostAPI == "" {
|
||||
api.API_HOST_URL = infisicalConfig["hostAPI"]
|
||||
} else {
|
||||
api.API_HOST_URL = util.AppendAPIEndpoint(infisicalSecret.Spec.HostAPI)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *InfisicalSecretHandler) getInfisicalCaCertificateFromKubeSecret(ctx context.Context, infisicalSecret v1alpha1.InfisicalSecret) (caCertificate string, err error) {
|
||||
|
||||
caCertificateFromKubeSecret, err := util.GetKubeSecretByNamespacedName(ctx, h.Client, types.NamespacedName{
|
||||
Namespace: infisicalSecret.Spec.TLS.CaRef.SecretNamespace,
|
||||
Name: infisicalSecret.Spec.TLS.CaRef.SecretName,
|
||||
})
|
||||
|
||||
if k8Errors.IsNotFound(err) {
|
||||
return "", fmt.Errorf("kubernetes secret containing custom CA certificate cannot be found. [err=%s]", err)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if util.IsNamespaceScopedError(err, h.IsNamespaceScoped) {
|
||||
return "", fmt.Errorf("unable to fetch Kubernetes CA certificate secret. Your Operator installation is namespace scoped, and cannot read secrets outside of the namespace it is installed in. Please ensure the CA certificate secret is in the same namespace as the operator. [err=%v]", err)
|
||||
}
|
||||
return "", fmt.Errorf("something went wrong when fetching your CA certificate [err=%s]", err)
|
||||
}
|
||||
|
||||
caCertificateFromSecret := string(caCertificateFromKubeSecret.Data[infisicalSecret.Spec.TLS.CaRef.SecretKey])
|
||||
|
||||
return caCertificateFromSecret, nil
|
||||
}
|
||||
|
||||
func (h *InfisicalSecretHandler) HandleCACertificate(ctx context.Context, infisicalSecret v1alpha1.InfisicalSecret) error {
|
||||
if infisicalSecret.Spec.TLS.CaRef.SecretName != "" {
|
||||
caCert, err := h.getInfisicalCaCertificateFromKubeSecret(ctx, infisicalSecret)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
api.API_CA_CERTIFICATE = caCert
|
||||
} else {
|
||||
api.API_CA_CERTIFICATE = ""
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *InfisicalSecretHandler) ReconcileInfisicalSecret(ctx context.Context, logger logr.Logger, infisicalSecret *v1alpha1.InfisicalSecret, managedKubeSecretReferences []v1alpha1.ManagedKubeSecretConfig, managedKubeConfigMapReferences []v1alpha1.ManagedKubeConfigMapConfig, resourceVariablesMap map[string]util.ResourceVariables) (int, error) {
|
||||
reconciler := &InfisicalSecretReconciler{
|
||||
Client: h.Client,
|
||||
Scheme: h.Scheme,
|
||||
IsNamespaceScoped: h.IsNamespaceScoped,
|
||||
}
|
||||
return reconciler.ReconcileInfisicalSecret(ctx, logger, infisicalSecret, managedKubeSecretReferences, managedKubeConfigMapReferences, resourceVariablesMap)
|
||||
}
|
||||
|
||||
func (h *InfisicalSecretHandler) SetReadyToSyncSecretsConditions(ctx context.Context, logger logr.Logger, infisicalSecret *v1alpha1.InfisicalSecret, secretsCount int, errorToConditionOn error) {
|
||||
reconciler := &InfisicalSecretReconciler{
|
||||
Client: h.Client,
|
||||
Scheme: h.Scheme,
|
||||
IsNamespaceScoped: h.IsNamespaceScoped,
|
||||
}
|
||||
reconciler.SetReadyToSyncSecretsConditions(ctx, logger, infisicalSecret, secretsCount, errorToConditionOn)
|
||||
}
|
||||
|
||||
func (h *InfisicalSecretHandler) SetInfisicalAutoRedeploymentReady(ctx context.Context, logger logr.Logger, infisicalSecret *v1alpha1.InfisicalSecret, numDeployments int, errorToConditionOn error) {
|
||||
reconciler := &InfisicalSecretReconciler{
|
||||
Client: h.Client,
|
||||
Scheme: h.Scheme,
|
||||
IsNamespaceScoped: h.IsNamespaceScoped,
|
||||
}
|
||||
reconciler.SetInfisicalAutoRedeploymentReady(ctx, logger, infisicalSecret, numDeployments, errorToConditionOn)
|
||||
}
|
||||
|
||||
func (h *InfisicalSecretHandler) CloseInstantUpdatesStream(ctx context.Context, logger logr.Logger, infisicalSecret *v1alpha1.InfisicalSecret, resourceVariablesMap map[string]util.ResourceVariables) error {
|
||||
reconciler := &InfisicalSecretReconciler{
|
||||
Client: h.Client,
|
||||
Scheme: h.Scheme,
|
||||
IsNamespaceScoped: h.IsNamespaceScoped,
|
||||
}
|
||||
return reconciler.CloseInstantUpdatesStream(ctx, logger, infisicalSecret, resourceVariablesMap)
|
||||
}
|
||||
|
||||
// Ensures that SSE stream is open, incase if the stream is already opened - this is a noop
|
||||
func (h *InfisicalSecretHandler) OpenInstantUpdatesStream(ctx context.Context, logger logr.Logger, infisicalSecret *v1alpha1.InfisicalSecret, resourceVariablesMap map[string]util.ResourceVariables, eventCh chan<- event.TypedGenericEvent[client.Object]) error {
|
||||
reconciler := &InfisicalSecretReconciler{
|
||||
Client: h.Client,
|
||||
Scheme: h.Scheme,
|
||||
IsNamespaceScoped: h.IsNamespaceScoped,
|
||||
}
|
||||
return reconciler.OpenInstantUpdatesStream(ctx, logger, infisicalSecret, resourceVariablesMap, eventCh)
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user