Init commit
This commit is contained in:
49
.gitignore
vendored
Normal file
49
.gitignore
vendored
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# These are some examples of commonly ignored file patterns.
|
||||||
|
# You should customize this list as applicable to your project.
|
||||||
|
# Learn more about .gitignore:
|
||||||
|
# https://www.atlassian.com/git/tutorials/saving-changes/gitignore
|
||||||
|
|
||||||
|
# Node artifact files
|
||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
|
||||||
|
# Compiled Java class files
|
||||||
|
*.class
|
||||||
|
|
||||||
|
# Compiled Python bytecode
|
||||||
|
*.py[cod]
|
||||||
|
|
||||||
|
# Log files
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Package files
|
||||||
|
*.jar
|
||||||
|
|
||||||
|
# Maven
|
||||||
|
target/
|
||||||
|
dist/
|
||||||
|
|
||||||
|
# JetBrains IDE
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# Unit test reports
|
||||||
|
TEST*.xml
|
||||||
|
|
||||||
|
# Generated by MacOS
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Generated by Windows
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Applications
|
||||||
|
*.app
|
||||||
|
*.exe
|
||||||
|
*.war
|
||||||
|
|
||||||
|
# Large media files
|
||||||
|
*.mp4
|
||||||
|
*.tiff
|
||||||
|
*.avi
|
||||||
|
*.flv
|
||||||
|
*.mov
|
||||||
|
*.wmv
|
||||||
73
build/flake.lock
generated
Normal file
73
build/flake.lock
generated
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1731533236,
|
||||||
|
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 0,
|
||||||
|
"narHash": "sha256-7Fu7oazPoYCbDzb9k8D/DdbKrC3aU1zlnc39Y8jy/s8=",
|
||||||
|
"path": "/nix/store/m4wcdchjxw2fdyzjp8i6irpc613pchkr-source",
|
||||||
|
"type": "path"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"id": "nixpkgs",
|
||||||
|
"type": "indirect"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs-unstable": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1743448293,
|
||||||
|
"narHash": "sha256-bmEPmSjJakAp/JojZRrUvNcDX2R5/nuX6bm+seVaGhs=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "77b584d61ff80b4cef9245829a6f1dfad5afdfa3",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"id": "nixpkgs",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"type": "indirect"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"nixpkgs-unstable": "nixpkgs-unstable"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
52
build/flake.nix
Normal file
52
build/flake.nix
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
description = "Zitadel Resources Operator";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
nixpkgs-unstable.url = "nixpkgs/nixos-unstable";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, nixpkgs-unstable, flake-utils }:
|
||||||
|
flake-utils.lib.eachDefaultSystem (system:
|
||||||
|
let
|
||||||
|
unstable = nixpkgs-unstable.legacyPackages.${system};
|
||||||
|
pkgs = nixpkgs.legacyPackages.${system};
|
||||||
|
package = unstable.buildGoModule {
|
||||||
|
pname = "zitadel-resources-operator";
|
||||||
|
version = "0.0.0";
|
||||||
|
src = ../src;
|
||||||
|
doCheck = false;
|
||||||
|
vendorHash = "sha256-HEXIHASdDC7chG9uF56f6pvZPVbxYs/fWFytDz6CAf4=";
|
||||||
|
installPhase = ''
|
||||||
|
runHook preInstall
|
||||||
|
|
||||||
|
mkdir -p $out/bin
|
||||||
|
dir="$GOPATH/bin"
|
||||||
|
[ -e "$dir" ] && cp -r $dir/cmd $out/manager
|
||||||
|
|
||||||
|
runHook postInstall
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
dockerPackage = pkgs.dockerTools.buildImage {
|
||||||
|
name = "zitadel-resources-operator";
|
||||||
|
fromImageName = "gcr.io/distroless/static";
|
||||||
|
fromImageTag = "nonroot";
|
||||||
|
copyToRoot = pkgs.buildEnv {
|
||||||
|
name = "operator";
|
||||||
|
paths = [ package ];
|
||||||
|
pathsToLink = [ "/" ];
|
||||||
|
};
|
||||||
|
config = {
|
||||||
|
Cmd = [ "/manager" ];
|
||||||
|
WorkingDir = "/";
|
||||||
|
User = "65532:65532";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in with pkgs; {
|
||||||
|
packages.default = package;
|
||||||
|
packages.dockerImage = dockerPackage;
|
||||||
|
devShells.default = mkShell {
|
||||||
|
buildInputs = [ nixfmt unstable.gopls operator-sdk unstable.go ];
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
63
build/push-image.sh
Executable file
63
build/push-image.sh
Executable file
@@ -0,0 +1,63 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -xeuo pipefail
|
||||||
|
|
||||||
|
# Setup client certificate for docker registry login
|
||||||
|
mkdir -p /.docker
|
||||||
|
mkdir -p /etc/docker/certs.d/$DOCKERREGISTRY_URL
|
||||||
|
echo $DOCKERREGISTRY_CACERT
|
||||||
|
(umask 077 ; echo $DOCKERREGISTRY_CACERT | base64 -d > /.docker/ca.pem)
|
||||||
|
(umask 077 ; echo $DOCKERREGISTRY_CACERT | base64 -d > /etc/docker/certs.d/$DOCKERREGISTRY_URL/ca.crt) #Don't ask why this is needed twice.
|
||||||
|
(umask 077 ; echo $DOCKERREGISTRY_CLIENTCERT | base64 -d > /etc/docker/certs.d/$DOCKERREGISTRY_URL/client.cert)
|
||||||
|
(umask 077 ; echo $DOCKERREGISTRY_CLIENTKEY | base64 -d > /etc/docker/certs.d/$DOCKERREGISTRY_URL/client.key)
|
||||||
|
|
||||||
|
docker --tls login -u $DOCKERREGISTRY_USER -p $DOCKERREGISTRY_PASSWORD $DOCKERREGISTRY_URL
|
||||||
|
|
||||||
|
export DOCKER_HOST=$DOCKERDAEMON_ADDRESS #Setup docker to use a specific daemon
|
||||||
|
|
||||||
|
BUILD_IMAGE_NAME=$(ls images | tee /dev/stderr | head -n 1)
|
||||||
|
|
||||||
|
IMAGE_ID=$(
|
||||||
|
docker load --input "images/$BUILD_IMAGE_NAME" |
|
||||||
|
sed -nr 's/^Loaded image: (.*)$/\1/p' |
|
||||||
|
xargs -I{} docker image ls "{}" --format="{{.ID}}" |
|
||||||
|
tee /dev/stderr
|
||||||
|
)
|
||||||
|
|
||||||
|
DOCKER_IMAGE_NAME=$DOCKERREGISTRY_URL/$BITBUCKET_REPO_SLUG
|
||||||
|
VERSION=$BITBUCKET_BUILD_NUMBER
|
||||||
|
|
||||||
|
if [[ "${BITBUCKET_BRANCH:-""}" == "master" ]]; then
|
||||||
|
LATEST="latest"
|
||||||
|
else
|
||||||
|
unset LATEST
|
||||||
|
fi
|
||||||
|
|
||||||
|
escapeTag(){ echo "${1//[^a-zA-Z0-9._\-]/-}"; }
|
||||||
|
|
||||||
|
tagPush(){
|
||||||
|
if [ -n "$1" ]; then
|
||||||
|
local tag=$(escapeTag "$1")
|
||||||
|
docker tag "$IMAGE_ID" "$DOCKER_IMAGE_NAME:$tag" && docker push "$DOCKER_IMAGE_NAME:$tag"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
tagRemove(){
|
||||||
|
if [ -n "$1" ]; then
|
||||||
|
local tag=$(escapeTag "$1")
|
||||||
|
docker rmi "$DOCKER_IMAGE_NAME:$tag"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
set +u
|
||||||
|
|
||||||
|
tagPush "$VERSION"
|
||||||
|
tagPush "$BITBUCKET_BRANCH"
|
||||||
|
tagPush "$BITBUCKET_TAG"
|
||||||
|
tagPush "$BITBUCKET_COMMIT"
|
||||||
|
tagPush "$LATEST"
|
||||||
|
|
||||||
|
tagRemove "$VERSION"
|
||||||
|
tagRemove "$BITBUCKET_BRANCH"
|
||||||
|
tagRemove "$BITBUCKET_TAG"
|
||||||
|
tagRemove "$BITBUCKET_COMMIT"
|
||||||
|
tagRemove "$LATEST"
|
||||||
4
src/.dockerignore
Normal file
4
src/.dockerignore
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file
|
||||||
|
# Ignore build and test binaries.
|
||||||
|
bin/
|
||||||
|
testbin/
|
||||||
26
src/.gitignore
vendored
Normal file
26
src/.gitignore
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
|
||||||
|
# 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
|
||||||
|
*~
|
||||||
33
src/Dockerfile
Normal file
33
src/Dockerfile
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# Build the manager binary
|
||||||
|
FROM golang:1.19 as builder
|
||||||
|
ARG TARGETOS
|
||||||
|
ARG TARGETARCH
|
||||||
|
|
||||||
|
WORKDIR /workspace
|
||||||
|
# Copy the Go Modules manifests
|
||||||
|
COPY go.mod go.mod
|
||||||
|
COPY go.sum go.sum
|
||||||
|
# cache deps before building and copying source so that we don't need to re-download as much
|
||||||
|
# and so that source changes don't invalidate our downloaded layer
|
||||||
|
RUN go mod download
|
||||||
|
|
||||||
|
# Copy the go source
|
||||||
|
COPY cmd/main.go cmd/main.go
|
||||||
|
COPY api/ api/
|
||||||
|
COPY internal/controller/ internal/controller/
|
||||||
|
|
||||||
|
# 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"]
|
||||||
291
src/Makefile
Normal file
291
src/Makefile
Normal file
@@ -0,0 +1,291 @@
|
|||||||
|
# VERSION defines the project version for the bundle.
|
||||||
|
# Update this value when you upgrade the version of your project.
|
||||||
|
# To re-generate a bundle for another specific version without changing the standard setup, you can:
|
||||||
|
# - use the VERSION as arg of the bundle target (e.g make bundle VERSION=0.0.2)
|
||||||
|
# - use environment variables to overwrite this value (e.g export VERSION=0.0.2)
|
||||||
|
VERSION ?= 0.0.1
|
||||||
|
|
||||||
|
# CHANNELS define the bundle channels used in the bundle.
|
||||||
|
# Add a new line here if you would like to change its default config. (E.g CHANNELS = "candidate,fast,stable")
|
||||||
|
# To re-generate a bundle for other specific channels without changing the standard setup, you can:
|
||||||
|
# - use the CHANNELS as arg of the bundle target (e.g make bundle CHANNELS=candidate,fast,stable)
|
||||||
|
# - use environment variables to overwrite this value (e.g export CHANNELS="candidate,fast,stable")
|
||||||
|
ifneq ($(origin CHANNELS), undefined)
|
||||||
|
BUNDLE_CHANNELS := --channels=$(CHANNELS)
|
||||||
|
endif
|
||||||
|
|
||||||
|
# DEFAULT_CHANNEL defines the default channel used in the bundle.
|
||||||
|
# Add a new line here if you would like to change its default config. (E.g DEFAULT_CHANNEL = "stable")
|
||||||
|
# To re-generate a bundle for any other default channel without changing the default setup, you can:
|
||||||
|
# - use the DEFAULT_CHANNEL as arg of the bundle target (e.g make bundle DEFAULT_CHANNEL=stable)
|
||||||
|
# - use environment variables to overwrite this value (e.g export DEFAULT_CHANNEL="stable")
|
||||||
|
ifneq ($(origin DEFAULT_CHANNEL), undefined)
|
||||||
|
BUNDLE_DEFAULT_CHANNEL := --default-channel=$(DEFAULT_CHANNEL)
|
||||||
|
endif
|
||||||
|
BUNDLE_METADATA_OPTS ?= $(BUNDLE_CHANNELS) $(BUNDLE_DEFAULT_CHANNEL)
|
||||||
|
|
||||||
|
# IMAGE_TAG_BASE defines the docker.io namespace and part of the image name for remote images.
|
||||||
|
# This variable is used to construct full image tags for bundle and catalog images.
|
||||||
|
#
|
||||||
|
# For example, running 'make bundle-build bundle-push catalog-build catalog-push' will build and push both
|
||||||
|
# github.com/src-bundle:$VERSION and github.com/src-catalog:$VERSION.
|
||||||
|
IMAGE_TAG_BASE ?= github.com/src
|
||||||
|
|
||||||
|
# BUNDLE_IMG defines the image:tag used for the bundle.
|
||||||
|
# You can use it as an arg. (E.g make bundle-build BUNDLE_IMG=<some-registry>/<project-name-bundle>:<tag>)
|
||||||
|
BUNDLE_IMG ?= $(IMAGE_TAG_BASE)-bundle:v$(VERSION)
|
||||||
|
|
||||||
|
# BUNDLE_GEN_FLAGS are the flags passed to the operator-sdk generate bundle command
|
||||||
|
BUNDLE_GEN_FLAGS ?= -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS)
|
||||||
|
|
||||||
|
# USE_IMAGE_DIGESTS defines if images are resolved via tags or digests
|
||||||
|
# You can enable this value if you would like to use SHA Based Digests
|
||||||
|
# To enable set flag to true
|
||||||
|
USE_IMAGE_DIGESTS ?= false
|
||||||
|
ifeq ($(USE_IMAGE_DIGESTS), true)
|
||||||
|
BUNDLE_GEN_FLAGS += --use-image-digests
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Set the Operator SDK version to use. By default, what is installed on the system is used.
|
||||||
|
# This is useful for CI or a project to utilize a specific version of the operator-sdk toolkit.
|
||||||
|
OPERATOR_SDK_VERSION ?= unknown
|
||||||
|
|
||||||
|
# Image URL to use all building/pushing image targets
|
||||||
|
IMG ?= controller:latest
|
||||||
|
# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary.
|
||||||
|
ENVTEST_K8S_VERSION = 1.26.0
|
||||||
|
|
||||||
|
# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
|
||||||
|
ifeq (,$(shell go env GOBIN))
|
||||||
|
GOBIN=$(shell go env GOPATH)/bin
|
||||||
|
else
|
||||||
|
GOBIN=$(shell go env GOBIN)
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Setting SHELL to bash allows bash commands to be executed by recipes.
|
||||||
|
# Options are set to exit when a recipe line exits non-zero or a piped command fails.
|
||||||
|
SHELL = /usr/bin/env bash -o pipefail
|
||||||
|
.SHELLFLAGS = -ec
|
||||||
|
|
||||||
|
.PHONY: all
|
||||||
|
all: build
|
||||||
|
|
||||||
|
##@ General
|
||||||
|
|
||||||
|
# The help target prints out all targets with their descriptions organized
|
||||||
|
# beneath their categories. The categories are represented by '##@' and the
|
||||||
|
# target descriptions by '##'. The awk commands 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 envtest ## Run tests.
|
||||||
|
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test ./... -coverprofile cover.out
|
||||||
|
|
||||||
|
##@ 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 built the manager image targeting other platforms you can use the --platform flag.
|
||||||
|
# (i.e. docker build --platform linux/arm64 ). However, you must enable docker buildKit for it.
|
||||||
|
# More info: https://docs.docker.com/develop/develop-images/build_enhancements/
|
||||||
|
.PHONY: docker-build
|
||||||
|
docker-build: test ## Build docker image with the manager.
|
||||||
|
docker build -t ${IMG} .
|
||||||
|
|
||||||
|
.PHONY: docker-push
|
||||||
|
docker-push: ## Push docker image with the manager.
|
||||||
|
docker push ${IMG}
|
||||||
|
|
||||||
|
# PLATFORMS defines the target platforms for the manager image be build to provide support to multiple
|
||||||
|
# architectures. (i.e. make docker-buildx IMG=myregistry/mypoperator:0.0.1). To use this option you need to:
|
||||||
|
# - able to use docker buildx . More info: https://docs.docker.com/build/buildx/
|
||||||
|
# - have enable BuildKit, More info: https://docs.docker.com/develop/develop-images/build_enhancements/
|
||||||
|
# - be able to push the image for your registry (i.e. if you do not inform a valid value via IMG=<myregistry/image:<tag>> then the export will fail)
|
||||||
|
# To properly provided solutions that supports more than one platform you should use this option.
|
||||||
|
PLATFORMS ?= linux/arm64,linux/amd64,linux/s390x,linux/ppc64le
|
||||||
|
.PHONY: docker-buildx
|
||||||
|
docker-buildx: test ## 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
|
||||||
|
- docker buildx create --name project-v3-builder
|
||||||
|
docker buildx use project-v3-builder
|
||||||
|
- docker buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile.cross .
|
||||||
|
- docker buildx rm project-v3-builder
|
||||||
|
rm Dockerfile.cross
|
||||||
|
|
||||||
|
##@ 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: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion.
|
||||||
|
$(KUSTOMIZE) build config/default | kubectl delete --ignore-not-found=$(ignore-not-found) -f -
|
||||||
|
|
||||||
|
##@ Build Dependencies
|
||||||
|
|
||||||
|
## Location to install dependencies to
|
||||||
|
LOCALBIN ?= $(shell pwd)/bin
|
||||||
|
$(LOCALBIN):
|
||||||
|
mkdir -p $(LOCALBIN)
|
||||||
|
|
||||||
|
## Tool Binaries
|
||||||
|
KUSTOMIZE ?= $(LOCALBIN)/kustomize
|
||||||
|
CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen
|
||||||
|
ENVTEST ?= $(LOCALBIN)/setup-envtest
|
||||||
|
|
||||||
|
## Tool Versions
|
||||||
|
KUSTOMIZE_VERSION ?= v4.5.7
|
||||||
|
CONTROLLER_TOOLS_VERSION ?= v0.17.3
|
||||||
|
|
||||||
|
KUSTOMIZE_INSTALL_SCRIPT ?= "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh"
|
||||||
|
.PHONY: kustomize
|
||||||
|
kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. If wrong version is installed, it will be removed before downloading.
|
||||||
|
$(KUSTOMIZE): $(LOCALBIN)
|
||||||
|
@if test -x $(LOCALBIN)/kustomize && ! $(LOCALBIN)/kustomize version | grep -q $(KUSTOMIZE_VERSION); then \
|
||||||
|
echo "$(LOCALBIN)/kustomize version is not expected $(KUSTOMIZE_VERSION). Removing it before installing."; \
|
||||||
|
rm -rf $(LOCALBIN)/kustomize; \
|
||||||
|
fi
|
||||||
|
test -s $(LOCALBIN)/kustomize || { curl -Ss $(KUSTOMIZE_INSTALL_SCRIPT) | bash -s -- $(subst v,,$(KUSTOMIZE_VERSION)) $(LOCALBIN); }
|
||||||
|
|
||||||
|
.PHONY: controller-gen
|
||||||
|
controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary. If wrong version is installed, it will be overwritten.
|
||||||
|
$(CONTROLLER_GEN): $(LOCALBIN)
|
||||||
|
test -s $(LOCALBIN)/controller-gen && $(LOCALBIN)/controller-gen --version | grep -q $(CONTROLLER_TOOLS_VERSION) || \
|
||||||
|
GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_TOOLS_VERSION)
|
||||||
|
|
||||||
|
.PHONY: envtest
|
||||||
|
envtest: $(ENVTEST) ## Download envtest-setup locally if necessary.
|
||||||
|
$(ENVTEST): $(LOCALBIN)
|
||||||
|
test -s $(LOCALBIN)/setup-envtest || GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest
|
||||||
|
|
||||||
|
.PHONY: operator-sdk
|
||||||
|
OPERATOR_SDK ?= $(LOCALBIN)/operator-sdk
|
||||||
|
operator-sdk: ## Download operator-sdk locally if necessary.
|
||||||
|
ifeq (,$(wildcard $(OPERATOR_SDK)))
|
||||||
|
ifeq (, $(shell which operator-sdk 2>/dev/null))
|
||||||
|
@{ \
|
||||||
|
set -e ;\
|
||||||
|
mkdir -p $(dir $(OPERATOR_SDK)) ;\
|
||||||
|
OS=$(shell go env GOOS) && ARCH=$(shell go env GOARCH) && \
|
||||||
|
curl -sSLo $(OPERATOR_SDK) https://github.com/operator-framework/operator-sdk/releases/download/$(OPERATOR_SDK_VERSION)/operator-sdk_$${OS}_$${ARCH} ;\
|
||||||
|
chmod +x $(OPERATOR_SDK) ;\
|
||||||
|
}
|
||||||
|
else
|
||||||
|
OPERATOR_SDK = $(shell which operator-sdk)
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
.PHONY: bundle
|
||||||
|
bundle: manifests kustomize operator-sdk ## Generate bundle manifests and metadata, then validate generated files.
|
||||||
|
$(OPERATOR_SDK) generate kustomize manifests -q
|
||||||
|
cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG)
|
||||||
|
$(KUSTOMIZE) build config/manifests | $(OPERATOR_SDK) generate bundle $(BUNDLE_GEN_FLAGS)
|
||||||
|
$(OPERATOR_SDK) bundle validate ./bundle
|
||||||
|
|
||||||
|
.PHONY: bundle-build
|
||||||
|
bundle-build: ## Build the bundle image.
|
||||||
|
docker build -f bundle.Dockerfile -t $(BUNDLE_IMG) .
|
||||||
|
|
||||||
|
.PHONY: bundle-push
|
||||||
|
bundle-push: ## Push the bundle image.
|
||||||
|
$(MAKE) docker-push IMG=$(BUNDLE_IMG)
|
||||||
|
|
||||||
|
.PHONY: opm
|
||||||
|
OPM = ./bin/opm
|
||||||
|
opm: ## Download opm locally if necessary.
|
||||||
|
ifeq (,$(wildcard $(OPM)))
|
||||||
|
ifeq (,$(shell which opm 2>/dev/null))
|
||||||
|
@{ \
|
||||||
|
set -e ;\
|
||||||
|
mkdir -p $(dir $(OPM)) ;\
|
||||||
|
OS=$(shell go env GOOS) && ARCH=$(shell go env GOARCH) && \
|
||||||
|
curl -sSLo $(OPM) https://github.com/operator-framework/operator-registry/releases/download/v1.23.0/$${OS}-$${ARCH}-opm ;\
|
||||||
|
chmod +x $(OPM) ;\
|
||||||
|
}
|
||||||
|
else
|
||||||
|
OPM = $(shell which opm)
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
# A comma-separated list of bundle images (e.g. make catalog-build BUNDLE_IMGS=example.com/operator-bundle:v0.1.0,example.com/operator-bundle:v0.2.0).
|
||||||
|
# These images MUST exist in a registry and be pull-able.
|
||||||
|
BUNDLE_IMGS ?= $(BUNDLE_IMG)
|
||||||
|
|
||||||
|
# The image tag given to the resulting catalog image (e.g. make catalog-build CATALOG_IMG=example.com/operator-catalog:v0.2.0).
|
||||||
|
CATALOG_IMG ?= $(IMAGE_TAG_BASE)-catalog:v$(VERSION)
|
||||||
|
|
||||||
|
# Set CATALOG_BASE_IMG to an existing catalog image tag to add $BUNDLE_IMGS to that image.
|
||||||
|
ifneq ($(origin CATALOG_BASE_IMG), undefined)
|
||||||
|
FROM_INDEX_OPT := --from-index $(CATALOG_BASE_IMG)
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Build a catalog image by adding bundle images to an empty catalog using the operator package manager tool, 'opm'.
|
||||||
|
# This recipe invokes 'opm' in 'semver' bundle add mode. For more information on add modes, see:
|
||||||
|
# https://github.com/operator-framework/community-operators/blob/7f1438c/docs/packaging-operator.md#updating-your-existing-operator
|
||||||
|
.PHONY: catalog-build
|
||||||
|
catalog-build: opm ## Build a catalog image.
|
||||||
|
$(OPM) index add --container-tool docker --mode semver --tag $(CATALOG_IMG) --bundles $(BUNDLE_IMGS) $(FROM_INDEX_OPT)
|
||||||
|
|
||||||
|
# Push the catalog image.
|
||||||
|
.PHONY: catalog-push
|
||||||
|
catalog-push: ## Push a catalog image.
|
||||||
|
$(MAKE) docker-push IMG=$(CATALOG_IMG)
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
helm: manifests kustomize helmify
|
||||||
|
$(KUSTOMIZE) build config/default | $(HELMIFY) -crd-dir zitadel-resources-operator
|
||||||
87
src/PROJECT
Normal file
87
src/PROJECT
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
# Code generated by tool. DO NOT EDIT.
|
||||||
|
# This file is used to track the info used to scaffold your project
|
||||||
|
# and allow the plugins properly work.
|
||||||
|
# More info: https://book.kubebuilder.io/reference/project-config.html
|
||||||
|
domain: github.com
|
||||||
|
layout:
|
||||||
|
- go.kubebuilder.io/v4
|
||||||
|
plugins:
|
||||||
|
manifests.sdk.operatorframework.io/v2: {}
|
||||||
|
scorecard.sdk.operatorframework.io/v2: {}
|
||||||
|
projectName: src
|
||||||
|
repo: github.com/HaimKortovich/zitadel-resources-operator
|
||||||
|
resources:
|
||||||
|
- api:
|
||||||
|
crdVersion: v1
|
||||||
|
namespaced: true
|
||||||
|
controller: true
|
||||||
|
domain: github.com
|
||||||
|
group: zitadel
|
||||||
|
kind: Organization
|
||||||
|
path: github.com/HaimKortovich/zitadel-k8s-operator/api/v1alpha1
|
||||||
|
version: v1alpha1
|
||||||
|
- api:
|
||||||
|
crdVersion: v1
|
||||||
|
namespaced: true
|
||||||
|
controller: true
|
||||||
|
domain: github.com
|
||||||
|
group: zitadel
|
||||||
|
kind: Project
|
||||||
|
path: github.com/HaimKortovich/zitadel-k8s-operator/api/v1alpha1
|
||||||
|
version: v1alpha1
|
||||||
|
- api:
|
||||||
|
crdVersion: v1
|
||||||
|
namespaced: true
|
||||||
|
controller: true
|
||||||
|
domain: github.com
|
||||||
|
group: zitadel
|
||||||
|
kind: OIDCApp
|
||||||
|
path: github.com/HaimKortovich/zitadel-k8s-operator/api/v1alpha1
|
||||||
|
version: v1alpha1
|
||||||
|
- api:
|
||||||
|
crdVersion: v1
|
||||||
|
namespaced: true
|
||||||
|
controller: true
|
||||||
|
domain: github.com
|
||||||
|
group: zitadel
|
||||||
|
kind: MachineUser
|
||||||
|
path: github.com/HaimKortovich/zitadel-k8s-operator/api/v1alpha1
|
||||||
|
version: v1alpha1
|
||||||
|
- api:
|
||||||
|
crdVersion: v1
|
||||||
|
namespaced: true
|
||||||
|
controller: true
|
||||||
|
domain: github.com
|
||||||
|
group: zitadel
|
||||||
|
kind: APIApp
|
||||||
|
path: github.com/HaimKortovich/zitadel-k8s-operator/api/v1alpha1
|
||||||
|
version: v1alpha1
|
||||||
|
- api:
|
||||||
|
crdVersion: v1
|
||||||
|
namespaced: true
|
||||||
|
controller: true
|
||||||
|
domain: github.com
|
||||||
|
group: zitadel
|
||||||
|
kind: Action
|
||||||
|
path: github.com/HaimKortovich/zitadel-k8s-operator/api/v1alpha1
|
||||||
|
version: v1alpha1
|
||||||
|
- api:
|
||||||
|
crdVersion: v1
|
||||||
|
namespaced: true
|
||||||
|
controller: true
|
||||||
|
domain: github.com
|
||||||
|
group: zitadel
|
||||||
|
kind: Flow
|
||||||
|
path: github.com/HaimKortovich/zitadel-k8s-operator/api/v1alpha1
|
||||||
|
version: v1alpha1
|
||||||
|
- api:
|
||||||
|
- api:
|
||||||
|
crdVersion: v1
|
||||||
|
namespaced: true
|
||||||
|
controller: true
|
||||||
|
domain: github.com
|
||||||
|
group: zitadel
|
||||||
|
kind: Connection
|
||||||
|
path: github.com/HaimKortovich/zitadel-k8s-operator/api/v1alpha1
|
||||||
|
version: v1alpha1
|
||||||
|
version: "3"
|
||||||
94
src/README.md
Normal file
94
src/README.md
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
# src
|
||||||
|
// TODO(user): Add simple overview of use/purpose
|
||||||
|
|
||||||
|
## Description
|
||||||
|
// TODO(user): An in-depth paragraph about your project and overview of use
|
||||||
|
|
||||||
|
## 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>/src:tag
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Deploy the controller to the cluster with the image specified by `IMG`:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make deploy IMG=<some-registry>/src:tag
|
||||||
|
```
|
||||||
|
|
||||||
|
### Uninstall CRDs
|
||||||
|
To delete the CRDs from the cluster:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make uninstall
|
||||||
|
```
|
||||||
|
|
||||||
|
### Undeploy controller
|
||||||
|
UnDeploy the controller from the cluster:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make undeploy
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
// TODO(user): Add detailed information on how you would like others to contribute to this project
|
||||||
|
|
||||||
|
### 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 provide a reconcile function responsible for synchronizing resources until 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
|
||||||
|
```
|
||||||
|
|
||||||
|
**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)
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Copyright 2024.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
100
src/api/v1alpha1/action_types.go
Normal file
100
src/api/v1alpha1/action_types.go
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2024.
|
||||||
|
|
||||||
|
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 (
|
||||||
|
"context"
|
||||||
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
|
||||||
|
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
|
||||||
|
|
||||||
|
// ActionSpec defines the desired state of Action
|
||||||
|
type ActionSpec struct {
|
||||||
|
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
|
||||||
|
// Important: Run "make" to regenerate code after modifying this file
|
||||||
|
// +kubebuilder:validation:Required
|
||||||
|
// +operator-sdk:csv:customresourcedefinitions:type=spec
|
||||||
|
OrganizationRef OrganizationRef `json:"organizationRef"`
|
||||||
|
Script string `json:"script"`
|
||||||
|
// +kubebuilder:default=true
|
||||||
|
AllowedToFail bool `json:"allowedToFail"`
|
||||||
|
// +kubebuilder:validation:Type=string
|
||||||
|
// +kubebuilder:validation:Format=duration
|
||||||
|
Timeout *metav1.Duration `json:"timeout"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ActionStatus defines the observed state of Action
|
||||||
|
type ActionStatus struct {
|
||||||
|
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
|
||||||
|
// Important: Run "make" to regenerate code after modifying this file
|
||||||
|
// +optional
|
||||||
|
// +operator-sdk:csv:customresourcedefinitions:type=status,xDescriptors={"urn:alm:descriptor:io.kubernetes.conditions"}
|
||||||
|
Conditions []metav1.Condition `json:"conditions,omitempty"`
|
||||||
|
// +kubebuilder:default=""
|
||||||
|
ActionId string `json:"actionId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *ActionStatus) SetCondition(condition metav1.Condition) {
|
||||||
|
if d.Conditions == nil {
|
||||||
|
d.Conditions = make([]metav1.Condition, 0)
|
||||||
|
}
|
||||||
|
meta.SetStatusCondition(&d.Conditions, condition)
|
||||||
|
}
|
||||||
|
|
||||||
|
//+kubebuilder:object:root=true
|
||||||
|
//+kubebuilder:subresource:status
|
||||||
|
|
||||||
|
// Action is the Schema for the actions API
|
||||||
|
type Action struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||||
|
|
||||||
|
Spec ActionSpec `json:"spec,omitempty"`
|
||||||
|
Status ActionStatus `json:"status,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Action) IsBeingDeleted() bool {
|
||||||
|
return !d.DeletionTimestamp.IsZero()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Action) IsReady() bool {
|
||||||
|
return meta.IsStatusConditionTrue(d.Status.Conditions, ConditionTypeReady)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Action) ConnectionRef(ctx context.Context, refresolver *RefResolver) (*ConnectionRef, error) {
|
||||||
|
org, err := refresolver.OrganizationRef(ctx, &d.Spec.OrganizationRef, d.Namespace)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &org.Spec.ConnectionRef, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//+kubebuilder:object:root=true
|
||||||
|
|
||||||
|
// ActionList contains a list of Action
|
||||||
|
type ActionList struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ListMeta `json:"metadata,omitempty"`
|
||||||
|
Items []Action `json:"items"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
SchemeBuilder.Register(&Action{}, &ActionList{})
|
||||||
|
}
|
||||||
122
src/api/v1alpha1/apiapp_types.go
Normal file
122
src/api/v1alpha1/apiapp_types.go
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2024.
|
||||||
|
|
||||||
|
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 (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
|
||||||
|
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
|
||||||
|
|
||||||
|
// APIAppSpec defines the desired state of APIApp
|
||||||
|
type APIAppSpec struct {
|
||||||
|
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
|
||||||
|
// Important: Run "make" to regenerate code after modifying this file
|
||||||
|
ProjectRef ProjectRef `json:"projectRef"`
|
||||||
|
// +kubebuilder:validation:Enum=API_AUTH_METHOD_TYPE_BASIC;API_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT
|
||||||
|
AuthMethodType string `json:"authMethodType"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// APIAppStatus defines the observed state of APIApp
|
||||||
|
type APIAppStatus struct {
|
||||||
|
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
|
||||||
|
// Important: Run "make" to regenerate code after modifying this file
|
||||||
|
// +optional
|
||||||
|
// +operator-sdk:csv:customresourcedefinitions:type=status,xDescriptors={"urn:alm:descriptor:io.kubernetes.conditions"}
|
||||||
|
Conditions []metav1.Condition `json:"conditions,omitempty"`
|
||||||
|
// +kubebuilder:default=""
|
||||||
|
AppId string `json:"appId"`
|
||||||
|
// +kubebuilder:default=""
|
||||||
|
KeyId string `json:"keyId"`
|
||||||
|
// +kubebuilder:default=""
|
||||||
|
ClientId string `json:"clientId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *APIAppStatus) SetCondition(condition metav1.Condition) {
|
||||||
|
if d.Conditions == nil {
|
||||||
|
d.Conditions = make([]metav1.Condition, 0)
|
||||||
|
}
|
||||||
|
meta.SetStatusCondition(&d.Conditions, condition)
|
||||||
|
}
|
||||||
|
|
||||||
|
//+kubebuilder:object:root=true
|
||||||
|
//+kubebuilder:subresource:status
|
||||||
|
|
||||||
|
// APIApp is the Schema for the apiapps API
|
||||||
|
type APIApp struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||||
|
|
||||||
|
Spec APIAppSpec `json:"spec,omitempty"`
|
||||||
|
Status APIAppStatus `json:"status,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *APIApp) IsBeingDeleted() bool {
|
||||||
|
return !d.DeletionTimestamp.IsZero()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *APIApp) IsReady() bool {
|
||||||
|
return meta.IsStatusConditionTrue(d.Status.Conditions, ConditionTypeReady)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *APIApp) ConnectionRef(ctx context.Context, refresolver *RefResolver) (*ConnectionRef, error) {
|
||||||
|
project, err := refresolver.ProjectRef(ctx, &d.Spec.ProjectRef, d.Namespace)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
org, err := refresolver.OrganizationRef(ctx, &project.Spec.OrganizationRef, d.Namespace)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &org.Spec.ConnectionRef, nil
|
||||||
|
}
|
||||||
|
func (d *APIApp) Organization(ctx context.Context, refresolver *RefResolver) (*Organization, error) {
|
||||||
|
project, err := refresolver.ProjectRef(ctx, &d.Spec.ProjectRef, d.Namespace)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
org, err := refresolver.OrganizationRef(ctx, &project.Spec.OrganizationRef, d.Namespace)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return org, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *APIApp) Project(ctx context.Context, refresolver *RefResolver) (*Project, error) {
|
||||||
|
project, err := refresolver.ProjectRef(ctx, &d.Spec.ProjectRef, d.Namespace)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return project, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//+kubebuilder:object:root=true
|
||||||
|
|
||||||
|
// APIAppList contains a list of APIApp
|
||||||
|
type APIAppList struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ListMeta `json:"metadata,omitempty"`
|
||||||
|
Items []APIApp `json:"items"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
SchemeBuilder.Register(&APIApp{}, &APIAppList{})
|
||||||
|
}
|
||||||
32
src/api/v1alpha1/condition_types.go
Normal file
32
src/api/v1alpha1/condition_types.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package v1alpha1
|
||||||
|
|
||||||
|
const (
|
||||||
|
ConditionTypeReady string = "Ready"
|
||||||
|
ConditionTypePATUpToDate string = "PATUpToDate"
|
||||||
|
|
||||||
|
ConditionReasonRolesChanged string = "RolesChanged"
|
||||||
|
ConditionReasonPATUpToDate string = "UpToDate"
|
||||||
|
|
||||||
|
ConditionReasonDeploymentNotReady string = "DeploymentNotReady"
|
||||||
|
ConditionReasonDeploymentReady string = "DeploymentReady"
|
||||||
|
ConditionReasonRestoreBackup string = "RestoreBackup"
|
||||||
|
|
||||||
|
ConditionReasonRestoreNotComplete string = "RestoreNotComplete"
|
||||||
|
ConditionReasonRestoreComplete string = "RestoreComplete"
|
||||||
|
|
||||||
|
ConditionReasonJobComplete string = "JobComplete"
|
||||||
|
ConditionReasonJobSuspended string = "JobSuspended"
|
||||||
|
ConditionReasonJobFailed string = "JobFailed"
|
||||||
|
ConditionReasonJobRunning string = "JobRunning"
|
||||||
|
|
||||||
|
ConditionReasonCronJobScheduled string = "CronJobScheduled"
|
||||||
|
ConditionReasonCronJobFailed string = "CronJobScheduled"
|
||||||
|
ConditionReasonCronJobRunning string = "CronJobRunning"
|
||||||
|
ConditionReasonCronJobSuccess string = "CronJobSucess"
|
||||||
|
|
||||||
|
ConditionReasonConnectionFailed string = "ConnectionFailed"
|
||||||
|
|
||||||
|
ConditionReasonCreated string = "Created"
|
||||||
|
ConditionReasonHealthy string = "Healthy"
|
||||||
|
ConditionReasonFailed string = "Failed"
|
||||||
|
)
|
||||||
122
src/api/v1alpha1/connection_types.go
Normal file
122
src/api/v1alpha1/connection_types.go
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2024.
|
||||||
|
|
||||||
|
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"
|
||||||
|
|
||||||
|
"context"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
|
||||||
|
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
|
||||||
|
type PAT struct {
|
||||||
|
TokenSecretKey corev1.SecretKeySelector `json:"tokenSecretKey"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type JWT struct {
|
||||||
|
JWTSecretKey corev1.SecretKeySelector `json:"jwtSecretKey"`
|
||||||
|
Scopes []string `json:"scopes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserPassword struct {
|
||||||
|
Username string `json:"username"`
|
||||||
|
PasswordSecretKey corev1.SecretKeySelector `json:"passwordSecretKey"`
|
||||||
|
Scopes []string `json:"scopes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// +kubebuilder:validation:XValidation:rule="[has(self.pat), has(self.password), has(self.jwt)].filter(x, x).size() == 1",message="exactly one of pat, password, or jwt must be specified"
|
||||||
|
type Authentication struct {
|
||||||
|
PAT *PAT `json:"pat,omitempty"`
|
||||||
|
Password *UserPassword `json:"password,omitempty"`
|
||||||
|
JWT *JWT `json:"jwt,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnectionSpec defines the desired state of Connection
|
||||||
|
type ConnectionSpec struct {
|
||||||
|
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
|
||||||
|
// Important: Run "make" to regenerate code after modifying this file
|
||||||
|
Host string `json:"host"`
|
||||||
|
Port *uint16 `json:"port,omitempty"`
|
||||||
|
// +kubebuilder:default=true
|
||||||
|
Secure bool `json:"secure"`
|
||||||
|
// +kubebuilder:default=false
|
||||||
|
InsecureSkipVerifyTLS bool `json:"insecureSkipVerifyTLS"`
|
||||||
|
Authentication Authentication `json:"authentication"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnectionStatus defines the observed state of Connection
|
||||||
|
type ConnectionStatus struct {
|
||||||
|
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
|
||||||
|
// Important: Run "make" to regenerate code after modifying this file
|
||||||
|
// Conditions for the Connection object.
|
||||||
|
// +optional
|
||||||
|
// +operator-sdk:csv:customresourcedefinitions:type=status,xDescriptors={"urn:alm:descriptor:io.kubernetes.conditions"}
|
||||||
|
Conditions []metav1.Condition `json:"conditions,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *ConnectionStatus) SetCondition(condition metav1.Condition) {
|
||||||
|
if d.Conditions == nil {
|
||||||
|
d.Conditions = make([]metav1.Condition, 0)
|
||||||
|
}
|
||||||
|
meta.SetStatusCondition(&d.Conditions, condition)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Connection) IsBeingDeleted() bool {
|
||||||
|
return !d.DeletionTimestamp.IsZero()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Connection) IsReady() bool {
|
||||||
|
return meta.IsStatusConditionTrue(d.Status.Conditions, ConditionTypeReady)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Connection) ConnectionRef(_ context.Context, _ *RefResolver) (*ConnectionRef, error) {
|
||||||
|
return &ConnectionRef{
|
||||||
|
ObjectReference: corev1.ObjectReference{
|
||||||
|
Kind: d.Kind,
|
||||||
|
Namespace: d.Namespace,
|
||||||
|
Name: d.Name,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// +kubebuilder:object:root=true
|
||||||
|
// +kubebuilder:subresource:status
|
||||||
|
|
||||||
|
// Connection is the Schema for the connections API
|
||||||
|
type Connection struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||||
|
|
||||||
|
Spec ConnectionSpec `json:"spec,omitempty"`
|
||||||
|
Status ConnectionStatus `json:"status,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// +kubebuilder:object:root=true
|
||||||
|
|
||||||
|
// ConnectionList contains a list of Connection
|
||||||
|
type ConnectionList struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ListMeta `json:"metadata,omitempty"`
|
||||||
|
Items []Connection `json:"items"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
SchemeBuilder.Register(&Connection{}, &ConnectionList{})
|
||||||
|
}
|
||||||
97
src/api/v1alpha1/flow_types.go
Normal file
97
src/api/v1alpha1/flow_types.go
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2024.
|
||||||
|
|
||||||
|
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 (
|
||||||
|
"context"
|
||||||
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
|
||||||
|
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
|
||||||
|
|
||||||
|
// FlowSpec defines the desired state of Flow
|
||||||
|
type FlowSpec struct {
|
||||||
|
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
|
||||||
|
// Important: Run "make" to regenerate code after modifying this file
|
||||||
|
// +kubebuilder:validation:Required
|
||||||
|
// +operator-sdk:csv:customresourcedefinitions:type=spec
|
||||||
|
OrganizationRef OrganizationRef `json:"organizationRef"`
|
||||||
|
// +kubebuilder:validation:Enum=FLOW_TYPE_EXTERNAL_AUTHENTICATION;"1";"2";"3";"4"
|
||||||
|
FlowType string `json:"flowType"`
|
||||||
|
// +kubebuilder:validation:Enum=TRIGGER_TYPE_POST_AUTHENTICATION;TRIGGER_TYPE_PRE_CREATION;TRIGGER_TYPE_POST_CREATION;TRIGGER_TYPE_POST_AUTHENTICATION;TRIGGER_TYPE_PRE_CREATION;TRIGGER_TYPE_POST_CREATION;"1";"2";"3";"4";"5";"6"
|
||||||
|
TriggerType string `json:"triggerType"`
|
||||||
|
ActionRefs []ActionRef `json:"actionRefs"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlowStatus defines the observed state of Flow
|
||||||
|
type FlowStatus struct {
|
||||||
|
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
|
||||||
|
// Important: Run "make" to regenerate code after modifying this file
|
||||||
|
// +optional
|
||||||
|
// +operator-sdk:csv:customresourcedefinitions:type=status,xDescriptors={"urn:alm:descriptor:io.kubernetes.conditions"}
|
||||||
|
Conditions []metav1.Condition `json:"conditions,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *FlowStatus) SetCondition(condition metav1.Condition) {
|
||||||
|
if d.Conditions == nil {
|
||||||
|
d.Conditions = make([]metav1.Condition, 0)
|
||||||
|
}
|
||||||
|
meta.SetStatusCondition(&d.Conditions, condition)
|
||||||
|
}
|
||||||
|
|
||||||
|
//+kubebuilder:object:root=true
|
||||||
|
//+kubebuilder:subresource:status
|
||||||
|
|
||||||
|
// Flow is the Schema for the flows API
|
||||||
|
type Flow struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||||
|
|
||||||
|
Spec FlowSpec `json:"spec,omitempty"`
|
||||||
|
Status FlowStatus `json:"status,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Flow) IsBeingDeleted() bool {
|
||||||
|
return !d.DeletionTimestamp.IsZero()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Flow) IsReady() bool {
|
||||||
|
return meta.IsStatusConditionTrue(d.Status.Conditions, ConditionTypeReady)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Flow) ConnectionRef(ctx context.Context, refresolver *RefResolver) (*ConnectionRef, error) {
|
||||||
|
org, err := refresolver.OrganizationRef(ctx, &d.Spec.OrganizationRef, d.Namespace)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &org.Spec.ConnectionRef, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//+kubebuilder:object:root=true
|
||||||
|
|
||||||
|
// FlowList contains a list of Flow
|
||||||
|
type FlowList struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ListMeta `json:"metadata,omitempty"`
|
||||||
|
Items []Flow `json:"items"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
SchemeBuilder.Register(&Flow{}, &FlowList{})
|
||||||
|
}
|
||||||
36
src/api/v1alpha1/groupversion_info.go
Normal file
36
src/api/v1alpha1/groupversion_info.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2024.
|
||||||
|
|
||||||
|
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 contains API Schema definitions for the zitadel v1alpha1 API group
|
||||||
|
// +kubebuilder:object:generate=true
|
||||||
|
// +groupName=zitadel.github.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: "zitadel.github.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
|
||||||
|
)
|
||||||
156
src/api/v1alpha1/machineuser_types.go
Normal file
156
src/api/v1alpha1/machineuser_types.go
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2024.
|
||||||
|
|
||||||
|
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 (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
|
||||||
|
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
|
||||||
|
|
||||||
|
type Authorization struct {
|
||||||
|
ProjectRef ProjectRef `json:"projectRef"`
|
||||||
|
RoleKeys []string `json:"roleKeys,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type OrganizationResource struct {
|
||||||
|
OrgID string `json:"orgId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProjectResource struct {
|
||||||
|
ProjectID string `json:"projectId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProjectGrantResource struct {
|
||||||
|
ProjectID string `json:"projectId"`
|
||||||
|
OrgID string `json:"orgId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConnectionResource struct{}
|
||||||
|
|
||||||
|
// +kubebuilder:validation:XValidation:rule="[has(self.organization), has(self.connection), has(self.project), has(self.projectGrant)].filter(x, x).size() == 1",message="exactly one of organization, connection, project, or projectGrant must be specified"
|
||||||
|
type Resource struct {
|
||||||
|
// +optional
|
||||||
|
Organization *OrganizationResource `json:"organization,omitempty"`
|
||||||
|
// +optional
|
||||||
|
Connection *ConnectionResource `json:"connection,omitempty"`
|
||||||
|
// +optional
|
||||||
|
Project *ProjectResource `json:"project,omitempty"`
|
||||||
|
// +optional
|
||||||
|
ProjectGrant *ProjectGrantResource `json:"projectGrant,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type InternalPermissions struct {
|
||||||
|
Resource Resource `json:"resource"`
|
||||||
|
// +kubebuilder:validation:MaxItems=50
|
||||||
|
Roles []string `json:"roles,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MachineUserSpec defines the desired state of MachineUser
|
||||||
|
type MachineUserSpec struct {
|
||||||
|
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
|
||||||
|
// Important: Run "make" to regenerate code after modifying this file
|
||||||
|
// +kubebuilder:validation:Required
|
||||||
|
// +operator-sdk:csv:customresourcedefinitions:type=spec
|
||||||
|
OrganizationRef OrganizationRef `json:"organizationRef" webhook:"inmutable"`
|
||||||
|
// +kubebuilder:validation:Enum=ACCESS_TOKEN_TYPE_BEARER;ACCESS_TOKEN_TYPE_JWT
|
||||||
|
AccessTokenType string `json:"accessTokenType"`
|
||||||
|
Authorizations []Authorization `json:"authorizations,omitempty"`
|
||||||
|
// +kubebuilder:validation:MaxItems=100
|
||||||
|
InternalPermissions []InternalPermissions `json:"internalPermissions,omitempty"`
|
||||||
|
Metadata []map[string]string `json:"metadata,omitempty"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MachineUserStatus defines the observed state of MachineUser
|
||||||
|
type MachineUserStatus struct {
|
||||||
|
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
|
||||||
|
// Important: Run "make" to regenerate code after modifying this file
|
||||||
|
// +optional
|
||||||
|
// +operator-sdk:csv:customresourcedefinitions:type=status,xDescriptors={"urn:alm:descriptor:io.kubernetes.conditions"}
|
||||||
|
Conditions []metav1.Condition `json:"conditions,omitempty"`
|
||||||
|
UserId *string `json:"userId,omitempty"`
|
||||||
|
KeyId *string `json:"keyId,omitempty"`
|
||||||
|
PATId *string `json:"patId,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *MachineUserStatus) SetCondition(condition metav1.Condition) {
|
||||||
|
if d.Conditions == nil {
|
||||||
|
d.Conditions = make([]metav1.Condition, 0)
|
||||||
|
}
|
||||||
|
meta.SetStatusCondition(&d.Conditions, condition)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *MachineUserStatus) GetConditionStatus(conditionType string) bool {
|
||||||
|
if d.Conditions == nil {
|
||||||
|
d.Conditions = make([]metav1.Condition, 0)
|
||||||
|
}
|
||||||
|
return meta.IsStatusConditionTrue(d.Conditions, conditionType)
|
||||||
|
}
|
||||||
|
|
||||||
|
//+kubebuilder:object:root=true
|
||||||
|
//+kubebuilder:subresource:status
|
||||||
|
|
||||||
|
// MachineUser is the Schema for the machineusers API
|
||||||
|
type MachineUser struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||||
|
|
||||||
|
Spec MachineUserSpec `json:"spec,omitempty"`
|
||||||
|
Status MachineUserStatus `json:"status,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *MachineUser) IsBeingDeleted() bool {
|
||||||
|
return !d.DeletionTimestamp.IsZero()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *MachineUser) IsReady() bool {
|
||||||
|
return meta.IsStatusConditionTrue(d.Status.Conditions, ConditionTypeReady)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *MachineUser) ConnectionRef(ctx context.Context, refresolver *RefResolver) (*ConnectionRef, error) {
|
||||||
|
org, err := refresolver.OrganizationRef(ctx, &d.Spec.OrganizationRef, d.Namespace)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &org.Spec.ConnectionRef, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *MachineUser) PatSecretName() string {
|
||||||
|
return d.Name + "-pat-secret"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *MachineUser) JWTSecretName() string {
|
||||||
|
return d.Name + "-machinekey-secret"
|
||||||
|
}
|
||||||
|
|
||||||
|
//+kubebuilder:object:root=true
|
||||||
|
|
||||||
|
// MachineUserList contains a list of MachineUser
|
||||||
|
type MachineUserList struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ListMeta `json:"metadata,omitempty"`
|
||||||
|
Items []MachineUser `json:"items"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
SchemeBuilder.Register(&MachineUser{}, &MachineUserList{})
|
||||||
|
}
|
||||||
149
src/api/v1alpha1/oidcapp_types.go
Normal file
149
src/api/v1alpha1/oidcapp_types.go
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2024.
|
||||||
|
|
||||||
|
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 (
|
||||||
|
"context"
|
||||||
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
|
||||||
|
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
|
||||||
|
|
||||||
|
// +kubebuilder:validation:Enum=OIDC_RESPONSE_TYPE_CODE;OIDC_RESPONSE_TYPE_ID_TOKEN;OIDC_RESPONSE_TYPE_ID_TOKEN_TOKEN
|
||||||
|
type ResponseType string
|
||||||
|
|
||||||
|
// +kubebuilder:validation:Enum=OIDC_GRANT_TYPE_AUTHORIZATION_CODE;OIDC_GRANT_TYPE_IMPLICIT;OIDC_GRANT_TYPE_REFRESH_TOKEN;OIDC_GRANT_TYPE_DEVICE_CODE;OIDC_GRANT_TYPE_TOKEN_EXCHANGE
|
||||||
|
type GrantType string
|
||||||
|
|
||||||
|
// OIDCAppSpec defines the desired state of OIDCApp
|
||||||
|
type OIDCAppSpec struct {
|
||||||
|
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
|
||||||
|
// Important: Run "make" to regenerate code after modifying this file
|
||||||
|
ProjectRef ProjectRef `json:"projectRef"`
|
||||||
|
OIDCAppName string `json:"oidcAppName"`
|
||||||
|
RedirectUris []string `json:"redirectUris"`
|
||||||
|
ResponseTypes []ResponseType `json:"responseTypes"`
|
||||||
|
GrantTypes []GrantType `json:"grantTypes"`
|
||||||
|
// +kubebuilder:validation:Enum=OIDC_APP_TYPE_WEB;OIDC_APP_TYPE_USER_AGENT;OIDC_APP_TYPE_NATIVE
|
||||||
|
AppType string `json:"appType"`
|
||||||
|
// +kubebuilder:validation:Enum=OIDC_AUTH_METHOD_TYPE_BASIC;OIDC_AUTH_METHOD_TYPE_POST;OIDC_AUTH_METHOD_TYPE_NONE;OIDC_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT
|
||||||
|
AuthMethodType string `json:"authMethodType"`
|
||||||
|
PostLogoutRedirectUris []string `json:"postLogoutRedirectUris"`
|
||||||
|
DevMode bool `json:"devMode"`
|
||||||
|
// +kubebuilder:validation:Enum=OIDC_TOKEN_TYPE_BEARER;OIDC_TOKEN_TYPE_JWT
|
||||||
|
AccessTokenType string `json:"accessTokenType"`
|
||||||
|
AccessTokenRoleAssertion bool `json:"accessTokenRoleAssertion"`
|
||||||
|
IdTokenRoleAssertion bool `json:"idTokenRoleAssertion"`
|
||||||
|
IdTokenUserinfoAssertion bool `json:"idTokenUserinfoAssertion"`
|
||||||
|
// +kubebuilder:validation:Type=string
|
||||||
|
// +kubebuilder:validation:Format=duration
|
||||||
|
ClockSkew *metav1.Duration `json:"clockSkew"`
|
||||||
|
// +optional
|
||||||
|
AdditionalOrigins []string `json:"additionalOrigins"`
|
||||||
|
SkipNativeAppSuccessPage bool `json:"skipNativeAppSuccessPage"`
|
||||||
|
BackChannelLogoutUri string `json:"backChannelLogoutUri,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OIDCAppStatus defines the observed state of OIDCApp
|
||||||
|
type OIDCAppStatus struct {
|
||||||
|
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
|
||||||
|
// Important: Run "make" to regenerate code after modifying this file
|
||||||
|
// +optional
|
||||||
|
// +operator-sdk:csv:customresourcedefinitions:type=status,xDescriptors={"urn:alm:descriptor:io.kubernetes.conditions"}
|
||||||
|
Conditions []metav1.Condition `json:"conditions,omitempty"`
|
||||||
|
AppId *string `json:"appId"`
|
||||||
|
ClientId *string `json:"clientId,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *OIDCAppStatus) SetCondition(condition metav1.Condition) {
|
||||||
|
if d.Conditions == nil {
|
||||||
|
d.Conditions = make([]metav1.Condition, 0)
|
||||||
|
}
|
||||||
|
meta.SetStatusCondition(&d.Conditions, condition)
|
||||||
|
}
|
||||||
|
|
||||||
|
//+kubebuilder:object:root=true
|
||||||
|
//+kubebuilder:subresource:status
|
||||||
|
|
||||||
|
// OIDCApp is the Schema for the oidcapps API
|
||||||
|
type OIDCApp struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||||
|
|
||||||
|
Spec OIDCAppSpec `json:"spec,omitempty"`
|
||||||
|
Status OIDCAppStatus `json:"status,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *OIDCApp) IsBeingDeleted() bool {
|
||||||
|
return !d.DeletionTimestamp.IsZero()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *OIDCApp) IsReady() bool {
|
||||||
|
return meta.IsStatusConditionTrue(d.Status.Conditions, ConditionTypeReady)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *OIDCApp) ConnectionRef(ctx context.Context, refresolver *RefResolver) (*ConnectionRef, error) {
|
||||||
|
project, err := refresolver.ProjectRef(ctx, &d.Spec.ProjectRef, d.Namespace)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
org, err := refresolver.OrganizationRef(ctx, &project.Spec.OrganizationRef, d.Namespace)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &org.Spec.ConnectionRef, nil
|
||||||
|
}
|
||||||
|
func (d *OIDCApp) Organization(ctx context.Context, refresolver *RefResolver) (*Organization, error) {
|
||||||
|
project, err := refresolver.ProjectRef(ctx, &d.Spec.ProjectRef, d.Namespace)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
org, err := refresolver.OrganizationRef(ctx, &project.Spec.OrganizationRef, d.Namespace)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return org, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *OIDCApp) Project(ctx context.Context, refresolver *RefResolver) (*Project, error) {
|
||||||
|
project, err := refresolver.ProjectRef(ctx, &d.Spec.ProjectRef, d.Namespace)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return project, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *OIDCApp) ClientSecretName() string {
|
||||||
|
return d.Name + "-client-secret"
|
||||||
|
}
|
||||||
|
|
||||||
|
//+kubebuilder:object:root=true
|
||||||
|
|
||||||
|
// OIDCAppList contains a list of OIDCApp
|
||||||
|
type OIDCAppList struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ListMeta `json:"metadata,omitempty"`
|
||||||
|
Items []OIDCApp `json:"items"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
SchemeBuilder.Register(&OIDCApp{}, &OIDCAppList{})
|
||||||
|
}
|
||||||
92
src/api/v1alpha1/organization_types.go
Normal file
92
src/api/v1alpha1/organization_types.go
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2024.
|
||||||
|
|
||||||
|
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 (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
|
||||||
|
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
|
||||||
|
|
||||||
|
// OrganizationSpec defines the desired state of Organization
|
||||||
|
type OrganizationSpec struct {
|
||||||
|
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
|
||||||
|
// Important: Run "make" to regenerate code after modifying this file
|
||||||
|
// +kubebuilder:validation:Required
|
||||||
|
// +operator-sdk:csv:customresourcedefinitions:type=spec
|
||||||
|
ConnectionRef ConnectionRef `json:"connectionRef" webhook:"inmutable"`
|
||||||
|
OrganzationName string `json:"organizationName"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrganizationStatus defines the observed state of Organization
|
||||||
|
type OrganizationStatus struct {
|
||||||
|
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
|
||||||
|
// Important: Run "make" to regenerate code after modifying this file
|
||||||
|
// Conditions for the Database object.
|
||||||
|
// +optional
|
||||||
|
// +operator-sdk:csv:customresourcedefinitions:type=status,xDescriptors={"urn:alm:descriptor:io.kubernetes.conditions"}
|
||||||
|
Conditions []metav1.Condition `json:"conditions,omitempty"`
|
||||||
|
OrganizationId *string `json:"organizationId,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *OrganizationStatus) SetCondition(condition metav1.Condition) {
|
||||||
|
if d.Conditions == nil {
|
||||||
|
d.Conditions = make([]metav1.Condition, 0)
|
||||||
|
}
|
||||||
|
meta.SetStatusCondition(&d.Conditions, condition)
|
||||||
|
}
|
||||||
|
|
||||||
|
//+kubebuilder:object:root=true
|
||||||
|
//+kubebuilder:subresource:status
|
||||||
|
|
||||||
|
// Organization is the Schema for the organizations API
|
||||||
|
type Organization struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||||
|
|
||||||
|
Spec OrganizationSpec `json:"spec,omitempty"`
|
||||||
|
Status OrganizationStatus `json:"status,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Organization) IsBeingDeleted() bool {
|
||||||
|
return !d.DeletionTimestamp.IsZero()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Organization) IsReady() bool {
|
||||||
|
return meta.IsStatusConditionTrue(d.Status.Conditions, ConditionTypeReady)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Organization) ConnectionRef(_ context.Context, _ *RefResolver) (*ConnectionRef, error) {
|
||||||
|
return &d.Spec.ConnectionRef, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//+kubebuilder:object:root=true
|
||||||
|
|
||||||
|
// OrganizationList contains a list of Organization
|
||||||
|
type OrganizationList struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ListMeta `json:"metadata,omitempty"`
|
||||||
|
Items []Organization `json:"items"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
SchemeBuilder.Register(&Organization{}, &OrganizationList{})
|
||||||
|
}
|
||||||
117
src/api/v1alpha1/project_types.go
Normal file
117
src/api/v1alpha1/project_types.go
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2024.
|
||||||
|
|
||||||
|
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 (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
|
||||||
|
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
|
||||||
|
|
||||||
|
type Role struct {
|
||||||
|
Key string `json:"key"`
|
||||||
|
DisplayName string `json:"displayName"`
|
||||||
|
Group string `json:"group"`
|
||||||
|
}
|
||||||
|
type Grant struct {
|
||||||
|
OrganizationRef OrganizationRef `json:"organizationRef"`
|
||||||
|
RoleKeys []string `json:"roleKeys"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProjectSpec defines the desired state of Project
|
||||||
|
type ProjectSpec struct {
|
||||||
|
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
|
||||||
|
// Important: Run "make" to regenerate code after modifying this file
|
||||||
|
// https://zitadel.com/docs/apis/resources/mgmt/management-service-add-project
|
||||||
|
// +kubebuilder:validation:Required
|
||||||
|
// +operator-sdk:csv:customresourcedefinitions:type=spec
|
||||||
|
OrganizationRef OrganizationRef `json:"organizationRef"`
|
||||||
|
// +optional
|
||||||
|
Roles []Role `json:"roles"`
|
||||||
|
// +optional
|
||||||
|
Grants []Grant `json:"grants"`
|
||||||
|
// +optional
|
||||||
|
ProjectRoleAssertion bool `json:"projectRoleAssertion,omitempty"`
|
||||||
|
// +optional
|
||||||
|
ProjectRoleCheck bool `json:"projectRoleCheck,omitempty"`
|
||||||
|
// +optional
|
||||||
|
HasProjectCheck bool `json:"hasProjectCheck,omitempty"`
|
||||||
|
ProjectName string `json:"projectName"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProjectStatus defines the observed state of Project
|
||||||
|
type ProjectStatus struct {
|
||||||
|
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
|
||||||
|
// Important: Run "make" to regenerate code after modifying this file
|
||||||
|
// Conditions for the Database object.
|
||||||
|
// +optional
|
||||||
|
// +operator-sdk:csv:customresourcedefinitions:type=status,xDescriptors={"urn:alm:descriptor:io.kubernetes.conditions"}
|
||||||
|
Conditions []metav1.Condition `json:"conditions,omitempty"`
|
||||||
|
ProjectId *string `json:"projectId,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *ProjectStatus) SetCondition(condition metav1.Condition) {
|
||||||
|
if d.Conditions == nil {
|
||||||
|
d.Conditions = make([]metav1.Condition, 0)
|
||||||
|
}
|
||||||
|
meta.SetStatusCondition(&d.Conditions, condition)
|
||||||
|
}
|
||||||
|
|
||||||
|
//+kubebuilder:object:root=true
|
||||||
|
//+kubebuilder:subresource:status
|
||||||
|
|
||||||
|
// Project is the Schema for the projects API
|
||||||
|
type Project struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||||
|
|
||||||
|
Spec ProjectSpec `json:"spec,omitempty"`
|
||||||
|
Status ProjectStatus `json:"status,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Project) IsBeingDeleted() bool {
|
||||||
|
return !d.DeletionTimestamp.IsZero()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Project) IsReady() bool {
|
||||||
|
return meta.IsStatusConditionTrue(d.Status.Conditions, ConditionTypeReady)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Project) ConnectionRef(ctx context.Context, refresolver *RefResolver) (*ConnectionRef, error) {
|
||||||
|
org, err := refresolver.OrganizationRef(ctx, &d.Spec.OrganizationRef, d.Namespace)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &org.Spec.ConnectionRef, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//+kubebuilder:object:root=true
|
||||||
|
|
||||||
|
// ProjectList contains a list of Project
|
||||||
|
type ProjectList struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ListMeta `json:"metadata,omitempty"`
|
||||||
|
Items []Project `json:"items"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
SchemeBuilder.Register(&Project{}, &ProjectList{})
|
||||||
|
}
|
||||||
35
src/api/v1alpha1/ref_types.go
Normal file
35
src/api/v1alpha1/ref_types.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package v1alpha1
|
||||||
|
|
||||||
|
import (
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ConnectionRef struct {
|
||||||
|
// ObjectReference is a reference to a object.
|
||||||
|
// +operator-sdk:csv:customresourcedefinitions:type=spec
|
||||||
|
corev1.ObjectReference `json:",inline"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type OIDCAppRef struct {
|
||||||
|
// ObjectReference is a reference to a object.
|
||||||
|
// +operator-sdk:csv:customresourcedefinitions:type=spec
|
||||||
|
corev1.ObjectReference `json:",inline"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type OrganizationRef struct {
|
||||||
|
// ObjectReference is a reference to a object.
|
||||||
|
// +operator-sdk:csv:customresourcedefinitions:type=spec
|
||||||
|
corev1.ObjectReference `json:",inline"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProjectRef struct {
|
||||||
|
// ObjectReference is a reference to a object.
|
||||||
|
// +operator-sdk:csv:customresourcedefinitions:type=spec
|
||||||
|
corev1.ObjectReference `json:",inline"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ActionRef struct {
|
||||||
|
// ObjectReference is a reference to a object.
|
||||||
|
// +operator-sdk:csv:customresourcedefinitions:type=spec
|
||||||
|
corev1.ObjectReference `json:",inline"`
|
||||||
|
}
|
||||||
143
src/api/v1alpha1/refresolver.go
Normal file
143
src/api/v1alpha1/refresolver.go
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
package v1alpha1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
v1 "k8s.io/api/core/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
// +kubebuilder:object:generate=false
|
||||||
|
type RefResolver struct {
|
||||||
|
client client.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRefResolver(client client.Client) *RefResolver {
|
||||||
|
return &RefResolver{
|
||||||
|
client: client,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RefResolver) OIDCAppRef(ctx context.Context, ref *OIDCAppRef,
|
||||||
|
namespace string) (*OIDCApp, error) {
|
||||||
|
if ref.Kind != "" && ref.Kind != "OIDCApp" {
|
||||||
|
return nil, fmt.Errorf("Unsupported reference kind: '%s'", ref.Kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
key := types.NamespacedName{
|
||||||
|
Name: ref.Name,
|
||||||
|
Namespace: namespace,
|
||||||
|
}
|
||||||
|
if ref.Namespace != "" {
|
||||||
|
key.Namespace = ref.Namespace
|
||||||
|
}
|
||||||
|
|
||||||
|
var zitadel OIDCApp
|
||||||
|
if err := r.client.Get(ctx, key, &zitadel); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &zitadel, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RefResolver) ActionRef(ctx context.Context, ref *ActionRef,
|
||||||
|
namespace string) (*Action, error) {
|
||||||
|
if ref.Kind != "" && ref.Kind != "Action" {
|
||||||
|
return nil, fmt.Errorf("Unsupported reference kind: '%s'", ref.Kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
key := types.NamespacedName{
|
||||||
|
Name: ref.Name,
|
||||||
|
Namespace: namespace,
|
||||||
|
}
|
||||||
|
if ref.Namespace != "" {
|
||||||
|
key.Namespace = ref.Namespace
|
||||||
|
}
|
||||||
|
|
||||||
|
var zitadel Action
|
||||||
|
if err := r.client.Get(ctx, key, &zitadel); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &zitadel, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RefResolver) ProjectRef(ctx context.Context, ref *ProjectRef,
|
||||||
|
namespace string) (*Project, error) {
|
||||||
|
if ref.Kind != "" && ref.Kind != "Project" {
|
||||||
|
return nil, fmt.Errorf("Unsupported reference kind: '%s'", ref.Kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
key := types.NamespacedName{
|
||||||
|
Name: ref.Name,
|
||||||
|
Namespace: namespace,
|
||||||
|
}
|
||||||
|
if ref.Namespace != "" {
|
||||||
|
key.Namespace = ref.Namespace
|
||||||
|
}
|
||||||
|
|
||||||
|
var zitadel Project
|
||||||
|
if err := r.client.Get(ctx, key, &zitadel); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &zitadel, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RefResolver) OrganizationRef(ctx context.Context, ref *OrganizationRef,
|
||||||
|
namespace string) (*Organization, error) {
|
||||||
|
if ref.Kind != "" && ref.Kind != "Organization" {
|
||||||
|
return nil, fmt.Errorf("Unsupported reference kind: '%s'", ref.Kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
key := types.NamespacedName{
|
||||||
|
Name: ref.Name,
|
||||||
|
Namespace: namespace,
|
||||||
|
}
|
||||||
|
if ref.Namespace != "" {
|
||||||
|
key.Namespace = ref.Namespace
|
||||||
|
}
|
||||||
|
|
||||||
|
var zitadel Organization
|
||||||
|
if err := r.client.Get(ctx, key, &zitadel); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &zitadel, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RefResolver) ConnectionRef(ctx context.Context, ref *ConnectionRef, namespace string) (*Connection, error) {
|
||||||
|
if ref.Kind != "" && ref.Kind != "Connection" {
|
||||||
|
return nil, fmt.Errorf("Unsupported reference kind: '%s'", ref.Kind)
|
||||||
|
}
|
||||||
|
key := types.NamespacedName{
|
||||||
|
Name: ref.Name,
|
||||||
|
Namespace: namespace,
|
||||||
|
}
|
||||||
|
if ref.Namespace != "" {
|
||||||
|
key.Namespace = ref.Namespace
|
||||||
|
}
|
||||||
|
|
||||||
|
var connection Connection
|
||||||
|
if err := r.client.Get(ctx, key, &connection); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &connection, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RefResolver) SecretKeyRef(ctx context.Context, selector corev1.SecretKeySelector,
|
||||||
|
namespace string) (string, error) {
|
||||||
|
nn := types.NamespacedName{
|
||||||
|
Name: selector.Name,
|
||||||
|
Namespace: namespace,
|
||||||
|
}
|
||||||
|
var secret v1.Secret
|
||||||
|
if err := r.client.Get(ctx, nn, &secret); err != nil {
|
||||||
|
return "", fmt.Errorf("error getting secret: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
data, ok := secret.Data[selector.Key]
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("secret key \"%s\" not found", selector.Key)
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(data), nil
|
||||||
|
}
|
||||||
1262
src/api/v1alpha1/zz_generated.deepcopy.go
Normal file
1262
src/api/v1alpha1/zz_generated.deepcopy.go
Normal file
File diff suppressed because it is too large
Load Diff
150
src/cmd/main.go
Normal file
150
src/cmd/main.go
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2024.
|
||||||
|
|
||||||
|
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 (
|
||||||
|
"flag"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
// "time"
|
||||||
|
|
||||||
|
// 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/apimachinery/pkg/runtime"
|
||||||
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
|
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
||||||
|
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
||||||
|
ctrl "sigs.k8s.io/controller-runtime"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/healthz"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/log/zap"
|
||||||
|
server "sigs.k8s.io/controller-runtime/pkg/metrics/server"
|
||||||
|
|
||||||
|
zitadelv1alpha1 "github.com/HaimKortovich/zitadel-resources-operator/api/v1alpha1"
|
||||||
|
"github.com/HaimKortovich/zitadel-resources-operator/internal/controller"
|
||||||
|
"github.com/HaimKortovich/zitadel-resources-operator/pkg/builder"
|
||||||
|
conditions "github.com/HaimKortovich/zitadel-resources-operator/pkg/condition"
|
||||||
|
//+kubebuilder:scaffold:imports
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
scheme = runtime.NewScheme()
|
||||||
|
setupLog = ctrl.Log.WithName("setup")
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
|
||||||
|
//+kubebuilder:scaffold:scheme
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var metricsAddr string
|
||||||
|
var enableLeaderElection bool
|
||||||
|
var probeAddr string
|
||||||
|
flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.")
|
||||||
|
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
|
||||||
|
flag.BoolVar(&enableLeaderElection, "leader-elect", false,
|
||||||
|
"Enable leader election for controller manager. "+
|
||||||
|
"Enabling this will ensure there is only one active controller manager.")
|
||||||
|
opts := zap.Options{
|
||||||
|
Development: true,
|
||||||
|
}
|
||||||
|
opts.BindFlags(flag.CommandLine)
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
|
||||||
|
|
||||||
|
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
|
||||||
|
Scheme: scheme,
|
||||||
|
Metrics: server.Options{BindAddress: metricsAddr},
|
||||||
|
HealthProbeBindAddress: probeAddr,
|
||||||
|
LeaderElection: enableLeaderElection,
|
||||||
|
LeaderElectionID: "88a0b43c.github.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,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
setupLog.Error(err, "unable to start manager")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
client := mgr.GetClient()
|
||||||
|
scheme := mgr.GetScheme()
|
||||||
|
builder := builder.NewBuilder(scheme)
|
||||||
|
refResolver := zitadelv1alpha1.NewRefResolver(client)
|
||||||
|
conditionReady := conditions.NewReady()
|
||||||
|
requeueZitadel := 5 * time.Minute
|
||||||
|
|
||||||
|
if err = controller.NewConnectionReconciler(client, refResolver, builder, conditionReady, requeueZitadel).SetupWithManager(mgr); err != nil {
|
||||||
|
setupLog.Error(err, "unable to create controller", "controller", "Organization")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// if err = controller.NewOrganizationReconciler(client, refResolver, conditionReady, requeueZitadel).SetupWithManager(mgr); err != nil {
|
||||||
|
// setupLog.Error(err, "unable to create controller", "controller", "Organization")
|
||||||
|
// os.Exit(1)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if err = controller.NewProjectReconciler(client, refResolver, conditionReady, requeueZitadel).SetupWithManager(mgr); err != nil {
|
||||||
|
// setupLog.Error(err, "unable to create controller", "controller", "Project")
|
||||||
|
// os.Exit(1)
|
||||||
|
// }
|
||||||
|
// if err = controller.NewOIDCAppReconciler(client, refResolver, builder, conditionReady, requeueZitadel).SetupWithManager(mgr); err != nil {
|
||||||
|
// setupLog.Error(err, "unable to create controller", "controller", "OIDCApp")
|
||||||
|
// os.Exit(1)
|
||||||
|
// }
|
||||||
|
// if err = controller.NewMachineUserReconciler(client, refResolver, builder, conditionReady, requeueZitadel).SetupWithManager(mgr); err != nil {
|
||||||
|
// setupLog.Error(err, "unable to create controller", "controller", "MachineUser")
|
||||||
|
// os.Exit(1)
|
||||||
|
// }
|
||||||
|
// if err = controller.NewAPIAppReconciler(client, refResolver, builder, conditionReady, requeueZitadel).SetupWithManager(mgr); err != nil {
|
||||||
|
// setupLog.Error(err, "unable to create controller", "controller", "APIApp")
|
||||||
|
// os.Exit(1)
|
||||||
|
// }
|
||||||
|
// if err = controller.NewActionReconciler(client, refResolver, builder, conditionReady, requeueZitadel).SetupWithManager(mgr); err != nil {
|
||||||
|
// setupLog.Error(err, "unable to create controller", "controller", "Action")
|
||||||
|
// os.Exit(1)
|
||||||
|
// }
|
||||||
|
// if err = controller.NewFlowReconciler(client, refResolver, builder, conditionReady, requeueZitadel).SetupWithManager(mgr); err != nil {
|
||||||
|
// setupLog.Error(err, "unable to create controller", "controller", "Flow")
|
||||||
|
// os.Exit(1)
|
||||||
|
// }
|
||||||
|
//+kubebuilder:scaffold:builder
|
||||||
|
|
||||||
|
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
|
||||||
|
setupLog.Error(err, "unable to set up health check")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
|
||||||
|
setupLog.Error(err, "unable to set up ready check")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
setupLog.Info("starting manager")
|
||||||
|
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
|
||||||
|
setupLog.Error(err, "problem running manager")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
173
src/config/crd/bases/zitadel.github.com_actions.yaml
Normal file
173
src/config/crd/bases/zitadel.github.com_actions.yaml
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
---
|
||||||
|
apiVersion: apiextensions.k8s.io/v1
|
||||||
|
kind: CustomResourceDefinition
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
controller-gen.kubebuilder.io/version: v0.17.3
|
||||||
|
name: actions.zitadel.github.com
|
||||||
|
spec:
|
||||||
|
group: zitadel.github.com
|
||||||
|
names:
|
||||||
|
kind: Action
|
||||||
|
listKind: ActionList
|
||||||
|
plural: actions
|
||||||
|
singular: action
|
||||||
|
scope: Namespaced
|
||||||
|
versions:
|
||||||
|
- name: v1alpha1
|
||||||
|
schema:
|
||||||
|
openAPIV3Schema:
|
||||||
|
description: Action is the Schema for the actions 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: ActionSpec defines the desired state of Action
|
||||||
|
properties:
|
||||||
|
allowedToFail:
|
||||||
|
default: true
|
||||||
|
type: boolean
|
||||||
|
organizationRef:
|
||||||
|
description: |-
|
||||||
|
INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
|
||||||
|
Important: Run "make" to regenerate code after modifying this file
|
||||||
|
properties:
|
||||||
|
apiVersion:
|
||||||
|
description: API version of the referent.
|
||||||
|
type: string
|
||||||
|
fieldPath:
|
||||||
|
description: |-
|
||||||
|
If referring to a piece of an object instead of an entire object, this string
|
||||||
|
should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2].
|
||||||
|
For example, if the object reference is to a container within a pod, this would take on a value like:
|
||||||
|
"spec.containers{name}" (where "name" refers to the name of the container that triggered
|
||||||
|
the event) or if no container name is specified "spec.containers[2]" (container with
|
||||||
|
index 2 in this pod). This syntax is chosen only to have some well-defined way of
|
||||||
|
referencing a part of an object.
|
||||||
|
type: string
|
||||||
|
kind:
|
||||||
|
description: |-
|
||||||
|
Kind of the referent.
|
||||||
|
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
description: |-
|
||||||
|
Name of the referent.
|
||||||
|
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||||
|
type: string
|
||||||
|
namespace:
|
||||||
|
description: |-
|
||||||
|
Namespace of the referent.
|
||||||
|
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/
|
||||||
|
type: string
|
||||||
|
resourceVersion:
|
||||||
|
description: |-
|
||||||
|
Specific resourceVersion to which this reference is made, if any.
|
||||||
|
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency
|
||||||
|
type: string
|
||||||
|
uid:
|
||||||
|
description: |-
|
||||||
|
UID of the referent.
|
||||||
|
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
x-kubernetes-map-type: atomic
|
||||||
|
script:
|
||||||
|
type: string
|
||||||
|
timeout:
|
||||||
|
format: duration
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- allowedToFail
|
||||||
|
- organizationRef
|
||||||
|
- script
|
||||||
|
- timeout
|
||||||
|
type: object
|
||||||
|
status:
|
||||||
|
description: ActionStatus defines the observed state of Action
|
||||||
|
properties:
|
||||||
|
actionId:
|
||||||
|
default: ""
|
||||||
|
type: string
|
||||||
|
conditions:
|
||||||
|
description: |-
|
||||||
|
INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
|
||||||
|
Important: Run "make" to regenerate code after modifying this file
|
||||||
|
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:
|
||||||
|
- actionId
|
||||||
|
type: object
|
||||||
|
type: object
|
||||||
|
served: true
|
||||||
|
storage: true
|
||||||
|
subresources:
|
||||||
|
status: {}
|
||||||
176
src/config/crd/bases/zitadel.github.com_apiapps.yaml
Normal file
176
src/config/crd/bases/zitadel.github.com_apiapps.yaml
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
---
|
||||||
|
apiVersion: apiextensions.k8s.io/v1
|
||||||
|
kind: CustomResourceDefinition
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
controller-gen.kubebuilder.io/version: v0.17.3
|
||||||
|
name: apiapps.zitadel.github.com
|
||||||
|
spec:
|
||||||
|
group: zitadel.github.com
|
||||||
|
names:
|
||||||
|
kind: APIApp
|
||||||
|
listKind: APIAppList
|
||||||
|
plural: apiapps
|
||||||
|
singular: apiapp
|
||||||
|
scope: Namespaced
|
||||||
|
versions:
|
||||||
|
- name: v1alpha1
|
||||||
|
schema:
|
||||||
|
openAPIV3Schema:
|
||||||
|
description: APIApp is the Schema for the apiapps 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: APIAppSpec defines the desired state of APIApp
|
||||||
|
properties:
|
||||||
|
authMethodType:
|
||||||
|
enum:
|
||||||
|
- API_AUTH_METHOD_TYPE_BASIC
|
||||||
|
- API_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT
|
||||||
|
type: string
|
||||||
|
projectRef:
|
||||||
|
description: |-
|
||||||
|
INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
|
||||||
|
Important: Run "make" to regenerate code after modifying this file
|
||||||
|
properties:
|
||||||
|
apiVersion:
|
||||||
|
description: API version of the referent.
|
||||||
|
type: string
|
||||||
|
fieldPath:
|
||||||
|
description: |-
|
||||||
|
If referring to a piece of an object instead of an entire object, this string
|
||||||
|
should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2].
|
||||||
|
For example, if the object reference is to a container within a pod, this would take on a value like:
|
||||||
|
"spec.containers{name}" (where "name" refers to the name of the container that triggered
|
||||||
|
the event) or if no container name is specified "spec.containers[2]" (container with
|
||||||
|
index 2 in this pod). This syntax is chosen only to have some well-defined way of
|
||||||
|
referencing a part of an object.
|
||||||
|
type: string
|
||||||
|
kind:
|
||||||
|
description: |-
|
||||||
|
Kind of the referent.
|
||||||
|
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
description: |-
|
||||||
|
Name of the referent.
|
||||||
|
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||||
|
type: string
|
||||||
|
namespace:
|
||||||
|
description: |-
|
||||||
|
Namespace of the referent.
|
||||||
|
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/
|
||||||
|
type: string
|
||||||
|
resourceVersion:
|
||||||
|
description: |-
|
||||||
|
Specific resourceVersion to which this reference is made, if any.
|
||||||
|
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency
|
||||||
|
type: string
|
||||||
|
uid:
|
||||||
|
description: |-
|
||||||
|
UID of the referent.
|
||||||
|
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
x-kubernetes-map-type: atomic
|
||||||
|
required:
|
||||||
|
- authMethodType
|
||||||
|
- projectRef
|
||||||
|
type: object
|
||||||
|
status:
|
||||||
|
description: APIAppStatus defines the observed state of APIApp
|
||||||
|
properties:
|
||||||
|
appId:
|
||||||
|
default: ""
|
||||||
|
type: string
|
||||||
|
clientId:
|
||||||
|
default: ""
|
||||||
|
type: string
|
||||||
|
conditions:
|
||||||
|
description: |-
|
||||||
|
INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
|
||||||
|
Important: Run "make" to regenerate code after modifying this file
|
||||||
|
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
|
||||||
|
keyId:
|
||||||
|
default: ""
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- appId
|
||||||
|
- clientId
|
||||||
|
- keyId
|
||||||
|
type: object
|
||||||
|
type: object
|
||||||
|
served: true
|
||||||
|
storage: true
|
||||||
|
subresources:
|
||||||
|
status: {}
|
||||||
239
src/config/crd/bases/zitadel.github.com_connections.yaml
Normal file
239
src/config/crd/bases/zitadel.github.com_connections.yaml
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
---
|
||||||
|
apiVersion: apiextensions.k8s.io/v1
|
||||||
|
kind: CustomResourceDefinition
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
controller-gen.kubebuilder.io/version: v0.17.3
|
||||||
|
name: connections.zitadel.github.com
|
||||||
|
spec:
|
||||||
|
group: zitadel.github.com
|
||||||
|
names:
|
||||||
|
kind: Connection
|
||||||
|
listKind: ConnectionList
|
||||||
|
plural: connections
|
||||||
|
singular: connection
|
||||||
|
scope: Namespaced
|
||||||
|
versions:
|
||||||
|
- name: v1alpha1
|
||||||
|
schema:
|
||||||
|
openAPIV3Schema:
|
||||||
|
description: Connection is the Schema for the connections 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: ConnectionSpec defines the desired state of Connection
|
||||||
|
properties:
|
||||||
|
authentication:
|
||||||
|
properties:
|
||||||
|
jwt:
|
||||||
|
properties:
|
||||||
|
jwtSecretKey:
|
||||||
|
description: SecretKeySelector selects a key of a Secret.
|
||||||
|
properties:
|
||||||
|
key:
|
||||||
|
description: The key of the secret to select from. Must
|
||||||
|
be a valid secret key.
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
default: ""
|
||||||
|
description: |-
|
||||||
|
Name of the referent.
|
||||||
|
This field is effectively required, but due to backwards compatibility is
|
||||||
|
allowed to be empty. Instances of this type with an empty value here are
|
||||||
|
almost certainly wrong.
|
||||||
|
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||||
|
type: string
|
||||||
|
optional:
|
||||||
|
description: Specify whether the Secret or its key must
|
||||||
|
be defined
|
||||||
|
type: boolean
|
||||||
|
required:
|
||||||
|
- key
|
||||||
|
type: object
|
||||||
|
x-kubernetes-map-type: atomic
|
||||||
|
scopes:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
required:
|
||||||
|
- jwtSecretKey
|
||||||
|
- scopes
|
||||||
|
type: object
|
||||||
|
password:
|
||||||
|
properties:
|
||||||
|
passwordSecretKey:
|
||||||
|
description: SecretKeySelector selects a key of a Secret.
|
||||||
|
properties:
|
||||||
|
key:
|
||||||
|
description: The key of the secret to select from. Must
|
||||||
|
be a valid secret key.
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
default: ""
|
||||||
|
description: |-
|
||||||
|
Name of the referent.
|
||||||
|
This field is effectively required, but due to backwards compatibility is
|
||||||
|
allowed to be empty. Instances of this type with an empty value here are
|
||||||
|
almost certainly wrong.
|
||||||
|
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||||
|
type: string
|
||||||
|
optional:
|
||||||
|
description: Specify whether the Secret or its key must
|
||||||
|
be defined
|
||||||
|
type: boolean
|
||||||
|
required:
|
||||||
|
- key
|
||||||
|
type: object
|
||||||
|
x-kubernetes-map-type: atomic
|
||||||
|
scopes:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
username:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- passwordSecretKey
|
||||||
|
- scopes
|
||||||
|
- username
|
||||||
|
type: object
|
||||||
|
pat:
|
||||||
|
description: |-
|
||||||
|
EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
|
||||||
|
NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
|
||||||
|
properties:
|
||||||
|
tokenSecretKey:
|
||||||
|
description: SecretKeySelector selects a key of a Secret.
|
||||||
|
properties:
|
||||||
|
key:
|
||||||
|
description: The key of the secret to select from. Must
|
||||||
|
be a valid secret key.
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
default: ""
|
||||||
|
description: |-
|
||||||
|
Name of the referent.
|
||||||
|
This field is effectively required, but due to backwards compatibility is
|
||||||
|
allowed to be empty. Instances of this type with an empty value here are
|
||||||
|
almost certainly wrong.
|
||||||
|
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||||
|
type: string
|
||||||
|
optional:
|
||||||
|
description: Specify whether the Secret or its key must
|
||||||
|
be defined
|
||||||
|
type: boolean
|
||||||
|
required:
|
||||||
|
- key
|
||||||
|
type: object
|
||||||
|
x-kubernetes-map-type: atomic
|
||||||
|
required:
|
||||||
|
- tokenSecretKey
|
||||||
|
type: object
|
||||||
|
type: object
|
||||||
|
x-kubernetes-validations:
|
||||||
|
- message: exactly one of pat, password, or jwt must be specified
|
||||||
|
rule: '[has(self.pat), has(self.password), has(self.jwt)].filter(x,
|
||||||
|
x).size() == 1'
|
||||||
|
host:
|
||||||
|
description: |-
|
||||||
|
INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
|
||||||
|
Important: Run "make" to regenerate code after modifying this file
|
||||||
|
type: string
|
||||||
|
insecureSkipVerifyTLS:
|
||||||
|
default: false
|
||||||
|
type: boolean
|
||||||
|
port:
|
||||||
|
type: integer
|
||||||
|
secure:
|
||||||
|
default: true
|
||||||
|
type: boolean
|
||||||
|
required:
|
||||||
|
- authentication
|
||||||
|
- host
|
||||||
|
- insecureSkipVerifyTLS
|
||||||
|
- secure
|
||||||
|
type: object
|
||||||
|
status:
|
||||||
|
description: ConnectionStatus defines the observed state of Connection
|
||||||
|
properties:
|
||||||
|
conditions:
|
||||||
|
description: |-
|
||||||
|
INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
|
||||||
|
Important: Run "make" to regenerate code after modifying this file
|
||||||
|
Conditions for the Connection object.
|
||||||
|
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
|
||||||
|
type: object
|
||||||
|
type: object
|
||||||
|
served: true
|
||||||
|
storage: true
|
||||||
|
subresources:
|
||||||
|
status: {}
|
||||||
227
src/config/crd/bases/zitadel.github.com_flows.yaml
Normal file
227
src/config/crd/bases/zitadel.github.com_flows.yaml
Normal file
@@ -0,0 +1,227 @@
|
|||||||
|
---
|
||||||
|
apiVersion: apiextensions.k8s.io/v1
|
||||||
|
kind: CustomResourceDefinition
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
controller-gen.kubebuilder.io/version: v0.17.3
|
||||||
|
name: flows.zitadel.github.com
|
||||||
|
spec:
|
||||||
|
group: zitadel.github.com
|
||||||
|
names:
|
||||||
|
kind: Flow
|
||||||
|
listKind: FlowList
|
||||||
|
plural: flows
|
||||||
|
singular: flow
|
||||||
|
scope: Namespaced
|
||||||
|
versions:
|
||||||
|
- name: v1alpha1
|
||||||
|
schema:
|
||||||
|
openAPIV3Schema:
|
||||||
|
description: Flow is the Schema for the flows 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: FlowSpec defines the desired state of Flow
|
||||||
|
properties:
|
||||||
|
actionRefs:
|
||||||
|
items:
|
||||||
|
properties:
|
||||||
|
apiVersion:
|
||||||
|
description: API version of the referent.
|
||||||
|
type: string
|
||||||
|
fieldPath:
|
||||||
|
description: |-
|
||||||
|
If referring to a piece of an object instead of an entire object, this string
|
||||||
|
should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2].
|
||||||
|
For example, if the object reference is to a container within a pod, this would take on a value like:
|
||||||
|
"spec.containers{name}" (where "name" refers to the name of the container that triggered
|
||||||
|
the event) or if no container name is specified "spec.containers[2]" (container with
|
||||||
|
index 2 in this pod). This syntax is chosen only to have some well-defined way of
|
||||||
|
referencing a part of an object.
|
||||||
|
type: string
|
||||||
|
kind:
|
||||||
|
description: |-
|
||||||
|
Kind of the referent.
|
||||||
|
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
description: |-
|
||||||
|
Name of the referent.
|
||||||
|
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||||
|
type: string
|
||||||
|
namespace:
|
||||||
|
description: |-
|
||||||
|
Namespace of the referent.
|
||||||
|
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/
|
||||||
|
type: string
|
||||||
|
resourceVersion:
|
||||||
|
description: |-
|
||||||
|
Specific resourceVersion to which this reference is made, if any.
|
||||||
|
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency
|
||||||
|
type: string
|
||||||
|
uid:
|
||||||
|
description: |-
|
||||||
|
UID of the referent.
|
||||||
|
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
x-kubernetes-map-type: atomic
|
||||||
|
type: array
|
||||||
|
flowType:
|
||||||
|
enum:
|
||||||
|
- FLOW_TYPE_EXTERNAL_AUTHENTICATION
|
||||||
|
- "1"
|
||||||
|
- "2"
|
||||||
|
- "3"
|
||||||
|
- "4"
|
||||||
|
type: string
|
||||||
|
organizationRef:
|
||||||
|
description: |-
|
||||||
|
INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
|
||||||
|
Important: Run "make" to regenerate code after modifying this file
|
||||||
|
properties:
|
||||||
|
apiVersion:
|
||||||
|
description: API version of the referent.
|
||||||
|
type: string
|
||||||
|
fieldPath:
|
||||||
|
description: |-
|
||||||
|
If referring to a piece of an object instead of an entire object, this string
|
||||||
|
should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2].
|
||||||
|
For example, if the object reference is to a container within a pod, this would take on a value like:
|
||||||
|
"spec.containers{name}" (where "name" refers to the name of the container that triggered
|
||||||
|
the event) or if no container name is specified "spec.containers[2]" (container with
|
||||||
|
index 2 in this pod). This syntax is chosen only to have some well-defined way of
|
||||||
|
referencing a part of an object.
|
||||||
|
type: string
|
||||||
|
kind:
|
||||||
|
description: |-
|
||||||
|
Kind of the referent.
|
||||||
|
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
description: |-
|
||||||
|
Name of the referent.
|
||||||
|
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||||
|
type: string
|
||||||
|
namespace:
|
||||||
|
description: |-
|
||||||
|
Namespace of the referent.
|
||||||
|
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/
|
||||||
|
type: string
|
||||||
|
resourceVersion:
|
||||||
|
description: |-
|
||||||
|
Specific resourceVersion to which this reference is made, if any.
|
||||||
|
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency
|
||||||
|
type: string
|
||||||
|
uid:
|
||||||
|
description: |-
|
||||||
|
UID of the referent.
|
||||||
|
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
x-kubernetes-map-type: atomic
|
||||||
|
triggerType:
|
||||||
|
enum:
|
||||||
|
- TRIGGER_TYPE_POST_AUTHENTICATION
|
||||||
|
- TRIGGER_TYPE_PRE_CREATION
|
||||||
|
- TRIGGER_TYPE_POST_CREATION
|
||||||
|
- TRIGGER_TYPE_POST_AUTHENTICATION
|
||||||
|
- TRIGGER_TYPE_PRE_CREATION
|
||||||
|
- TRIGGER_TYPE_POST_CREATION
|
||||||
|
- "1"
|
||||||
|
- "2"
|
||||||
|
- "3"
|
||||||
|
- "4"
|
||||||
|
- "5"
|
||||||
|
- "6"
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- actionRefs
|
||||||
|
- flowType
|
||||||
|
- organizationRef
|
||||||
|
- triggerType
|
||||||
|
type: object
|
||||||
|
status:
|
||||||
|
description: FlowStatus defines the observed state of Flow
|
||||||
|
properties:
|
||||||
|
conditions:
|
||||||
|
description: |-
|
||||||
|
INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
|
||||||
|
Important: Run "make" to regenerate code after modifying this file
|
||||||
|
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
|
||||||
|
type: object
|
||||||
|
type: object
|
||||||
|
served: true
|
||||||
|
storage: true
|
||||||
|
subresources:
|
||||||
|
status: {}
|
||||||
278
src/config/crd/bases/zitadel.github.com_machineusers.yaml
Normal file
278
src/config/crd/bases/zitadel.github.com_machineusers.yaml
Normal file
@@ -0,0 +1,278 @@
|
|||||||
|
---
|
||||||
|
apiVersion: apiextensions.k8s.io/v1
|
||||||
|
kind: CustomResourceDefinition
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
controller-gen.kubebuilder.io/version: v0.17.3
|
||||||
|
name: machineusers.zitadel.github.com
|
||||||
|
spec:
|
||||||
|
group: zitadel.github.com
|
||||||
|
names:
|
||||||
|
kind: MachineUser
|
||||||
|
listKind: MachineUserList
|
||||||
|
plural: machineusers
|
||||||
|
singular: machineuser
|
||||||
|
scope: Namespaced
|
||||||
|
versions:
|
||||||
|
- name: v1alpha1
|
||||||
|
schema:
|
||||||
|
openAPIV3Schema:
|
||||||
|
description: MachineUser is the Schema for the machineusers 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: MachineUserSpec defines the desired state of MachineUser
|
||||||
|
properties:
|
||||||
|
accessTokenType:
|
||||||
|
enum:
|
||||||
|
- ACCESS_TOKEN_TYPE_BEARER
|
||||||
|
- ACCESS_TOKEN_TYPE_JWT
|
||||||
|
type: string
|
||||||
|
authorizations:
|
||||||
|
items:
|
||||||
|
properties:
|
||||||
|
projectRef:
|
||||||
|
properties:
|
||||||
|
apiVersion:
|
||||||
|
description: API version of the referent.
|
||||||
|
type: string
|
||||||
|
fieldPath:
|
||||||
|
description: |-
|
||||||
|
If referring to a piece of an object instead of an entire object, this string
|
||||||
|
should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2].
|
||||||
|
For example, if the object reference is to a container within a pod, this would take on a value like:
|
||||||
|
"spec.containers{name}" (where "name" refers to the name of the container that triggered
|
||||||
|
the event) or if no container name is specified "spec.containers[2]" (container with
|
||||||
|
index 2 in this pod). This syntax is chosen only to have some well-defined way of
|
||||||
|
referencing a part of an object.
|
||||||
|
type: string
|
||||||
|
kind:
|
||||||
|
description: |-
|
||||||
|
Kind of the referent.
|
||||||
|
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
description: |-
|
||||||
|
Name of the referent.
|
||||||
|
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||||
|
type: string
|
||||||
|
namespace:
|
||||||
|
description: |-
|
||||||
|
Namespace of the referent.
|
||||||
|
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/
|
||||||
|
type: string
|
||||||
|
resourceVersion:
|
||||||
|
description: |-
|
||||||
|
Specific resourceVersion to which this reference is made, if any.
|
||||||
|
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency
|
||||||
|
type: string
|
||||||
|
uid:
|
||||||
|
description: |-
|
||||||
|
UID of the referent.
|
||||||
|
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
x-kubernetes-map-type: atomic
|
||||||
|
roleKeys:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
required:
|
||||||
|
- projectRef
|
||||||
|
type: object
|
||||||
|
type: array
|
||||||
|
internalPermissions:
|
||||||
|
items:
|
||||||
|
properties:
|
||||||
|
resource:
|
||||||
|
properties:
|
||||||
|
connection:
|
||||||
|
type: object
|
||||||
|
organization:
|
||||||
|
properties:
|
||||||
|
orgId:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- orgId
|
||||||
|
type: object
|
||||||
|
project:
|
||||||
|
properties:
|
||||||
|
projectId:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- projectId
|
||||||
|
type: object
|
||||||
|
projectGrant:
|
||||||
|
properties:
|
||||||
|
orgId:
|
||||||
|
type: string
|
||||||
|
projectId:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- orgId
|
||||||
|
- projectId
|
||||||
|
type: object
|
||||||
|
type: object
|
||||||
|
x-kubernetes-validations:
|
||||||
|
- message: exactly one of organization, connection, project,
|
||||||
|
or projectGrant must be specified
|
||||||
|
rule: '[has(self.organization), has(self.connection), has(self.project),
|
||||||
|
has(self.projectGrant)].filter(x, x).size() == 1'
|
||||||
|
roles:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
maxItems: 50
|
||||||
|
type: array
|
||||||
|
required:
|
||||||
|
- resource
|
||||||
|
type: object
|
||||||
|
maxItems: 100
|
||||||
|
type: array
|
||||||
|
metadata:
|
||||||
|
items:
|
||||||
|
additionalProperties:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
type: array
|
||||||
|
organizationRef:
|
||||||
|
description: |-
|
||||||
|
INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
|
||||||
|
Important: Run "make" to regenerate code after modifying this file
|
||||||
|
properties:
|
||||||
|
apiVersion:
|
||||||
|
description: API version of the referent.
|
||||||
|
type: string
|
||||||
|
fieldPath:
|
||||||
|
description: |-
|
||||||
|
If referring to a piece of an object instead of an entire object, this string
|
||||||
|
should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2].
|
||||||
|
For example, if the object reference is to a container within a pod, this would take on a value like:
|
||||||
|
"spec.containers{name}" (where "name" refers to the name of the container that triggered
|
||||||
|
the event) or if no container name is specified "spec.containers[2]" (container with
|
||||||
|
index 2 in this pod). This syntax is chosen only to have some well-defined way of
|
||||||
|
referencing a part of an object.
|
||||||
|
type: string
|
||||||
|
kind:
|
||||||
|
description: |-
|
||||||
|
Kind of the referent.
|
||||||
|
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
description: |-
|
||||||
|
Name of the referent.
|
||||||
|
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||||
|
type: string
|
||||||
|
namespace:
|
||||||
|
description: |-
|
||||||
|
Namespace of the referent.
|
||||||
|
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/
|
||||||
|
type: string
|
||||||
|
resourceVersion:
|
||||||
|
description: |-
|
||||||
|
Specific resourceVersion to which this reference is made, if any.
|
||||||
|
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency
|
||||||
|
type: string
|
||||||
|
uid:
|
||||||
|
description: |-
|
||||||
|
UID of the referent.
|
||||||
|
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
x-kubernetes-map-type: atomic
|
||||||
|
username:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- accessTokenType
|
||||||
|
- organizationRef
|
||||||
|
- username
|
||||||
|
type: object
|
||||||
|
status:
|
||||||
|
description: MachineUserStatus defines the observed state of MachineUser
|
||||||
|
properties:
|
||||||
|
conditions:
|
||||||
|
description: |-
|
||||||
|
INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
|
||||||
|
Important: Run "make" to regenerate code after modifying this file
|
||||||
|
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
|
||||||
|
keyId:
|
||||||
|
type: string
|
||||||
|
patId:
|
||||||
|
type: string
|
||||||
|
userId:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
type: object
|
||||||
|
served: true
|
||||||
|
storage: true
|
||||||
|
subresources:
|
||||||
|
status: {}
|
||||||
242
src/config/crd/bases/zitadel.github.com_oidcapps.yaml
Normal file
242
src/config/crd/bases/zitadel.github.com_oidcapps.yaml
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
---
|
||||||
|
apiVersion: apiextensions.k8s.io/v1
|
||||||
|
kind: CustomResourceDefinition
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
controller-gen.kubebuilder.io/version: v0.17.3
|
||||||
|
name: oidcapps.zitadel.github.com
|
||||||
|
spec:
|
||||||
|
group: zitadel.github.com
|
||||||
|
names:
|
||||||
|
kind: OIDCApp
|
||||||
|
listKind: OIDCAppList
|
||||||
|
plural: oidcapps
|
||||||
|
singular: oidcapp
|
||||||
|
scope: Namespaced
|
||||||
|
versions:
|
||||||
|
- name: v1alpha1
|
||||||
|
schema:
|
||||||
|
openAPIV3Schema:
|
||||||
|
description: OIDCApp is the Schema for the oidcapps 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: OIDCAppSpec defines the desired state of OIDCApp
|
||||||
|
properties:
|
||||||
|
accessTokenRoleAssertion:
|
||||||
|
type: boolean
|
||||||
|
accessTokenType:
|
||||||
|
enum:
|
||||||
|
- OIDC_TOKEN_TYPE_BEARER
|
||||||
|
- OIDC_TOKEN_TYPE_JWT
|
||||||
|
type: string
|
||||||
|
additionalOrigins:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
appType:
|
||||||
|
enum:
|
||||||
|
- OIDC_APP_TYPE_WEB
|
||||||
|
- OIDC_APP_TYPE_USER_AGENT
|
||||||
|
- OIDC_APP_TYPE_NATIVE
|
||||||
|
type: string
|
||||||
|
authMethodType:
|
||||||
|
enum:
|
||||||
|
- OIDC_AUTH_METHOD_TYPE_BASIC
|
||||||
|
- OIDC_AUTH_METHOD_TYPE_POST
|
||||||
|
- OIDC_AUTH_METHOD_TYPE_NONE
|
||||||
|
- OIDC_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT
|
||||||
|
type: string
|
||||||
|
backChannelLogoutUri:
|
||||||
|
type: string
|
||||||
|
clockSkew:
|
||||||
|
format: duration
|
||||||
|
type: string
|
||||||
|
devMode:
|
||||||
|
type: boolean
|
||||||
|
grantTypes:
|
||||||
|
items:
|
||||||
|
enum:
|
||||||
|
- OIDC_GRANT_TYPE_AUTHORIZATION_CODE
|
||||||
|
- OIDC_GRANT_TYPE_IMPLICIT
|
||||||
|
- OIDC_GRANT_TYPE_REFRESH_TOKEN
|
||||||
|
- OIDC_GRANT_TYPE_DEVICE_CODE
|
||||||
|
- OIDC_GRANT_TYPE_TOKEN_EXCHANGE
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
idTokenRoleAssertion:
|
||||||
|
type: boolean
|
||||||
|
idTokenUserinfoAssertion:
|
||||||
|
type: boolean
|
||||||
|
oidcAppName:
|
||||||
|
type: string
|
||||||
|
postLogoutRedirectUris:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
projectRef:
|
||||||
|
description: |-
|
||||||
|
INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
|
||||||
|
Important: Run "make" to regenerate code after modifying this file
|
||||||
|
properties:
|
||||||
|
apiVersion:
|
||||||
|
description: API version of the referent.
|
||||||
|
type: string
|
||||||
|
fieldPath:
|
||||||
|
description: |-
|
||||||
|
If referring to a piece of an object instead of an entire object, this string
|
||||||
|
should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2].
|
||||||
|
For example, if the object reference is to a container within a pod, this would take on a value like:
|
||||||
|
"spec.containers{name}" (where "name" refers to the name of the container that triggered
|
||||||
|
the event) or if no container name is specified "spec.containers[2]" (container with
|
||||||
|
index 2 in this pod). This syntax is chosen only to have some well-defined way of
|
||||||
|
referencing a part of an object.
|
||||||
|
type: string
|
||||||
|
kind:
|
||||||
|
description: |-
|
||||||
|
Kind of the referent.
|
||||||
|
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
description: |-
|
||||||
|
Name of the referent.
|
||||||
|
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||||
|
type: string
|
||||||
|
namespace:
|
||||||
|
description: |-
|
||||||
|
Namespace of the referent.
|
||||||
|
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/
|
||||||
|
type: string
|
||||||
|
resourceVersion:
|
||||||
|
description: |-
|
||||||
|
Specific resourceVersion to which this reference is made, if any.
|
||||||
|
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency
|
||||||
|
type: string
|
||||||
|
uid:
|
||||||
|
description: |-
|
||||||
|
UID of the referent.
|
||||||
|
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
x-kubernetes-map-type: atomic
|
||||||
|
redirectUris:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
responseTypes:
|
||||||
|
items:
|
||||||
|
enum:
|
||||||
|
- OIDC_RESPONSE_TYPE_CODE
|
||||||
|
- OIDC_RESPONSE_TYPE_ID_TOKEN
|
||||||
|
- OIDC_RESPONSE_TYPE_ID_TOKEN_TOKEN
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
skipNativeAppSuccessPage:
|
||||||
|
type: boolean
|
||||||
|
required:
|
||||||
|
- accessTokenRoleAssertion
|
||||||
|
- accessTokenType
|
||||||
|
- appType
|
||||||
|
- authMethodType
|
||||||
|
- clockSkew
|
||||||
|
- devMode
|
||||||
|
- grantTypes
|
||||||
|
- idTokenRoleAssertion
|
||||||
|
- idTokenUserinfoAssertion
|
||||||
|
- oidcAppName
|
||||||
|
- postLogoutRedirectUris
|
||||||
|
- projectRef
|
||||||
|
- redirectUris
|
||||||
|
- responseTypes
|
||||||
|
- skipNativeAppSuccessPage
|
||||||
|
type: object
|
||||||
|
status:
|
||||||
|
description: OIDCAppStatus defines the observed state of OIDCApp
|
||||||
|
properties:
|
||||||
|
appId:
|
||||||
|
type: string
|
||||||
|
clientId:
|
||||||
|
type: string
|
||||||
|
conditions:
|
||||||
|
description: |-
|
||||||
|
INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
|
||||||
|
Important: Run "make" to regenerate code after modifying this file
|
||||||
|
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:
|
||||||
|
- appId
|
||||||
|
type: object
|
||||||
|
type: object
|
||||||
|
served: true
|
||||||
|
storage: true
|
||||||
|
subresources:
|
||||||
|
status: {}
|
||||||
163
src/config/crd/bases/zitadel.github.com_organizations.yaml
Normal file
163
src/config/crd/bases/zitadel.github.com_organizations.yaml
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
---
|
||||||
|
apiVersion: apiextensions.k8s.io/v1
|
||||||
|
kind: CustomResourceDefinition
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
controller-gen.kubebuilder.io/version: v0.17.3
|
||||||
|
name: organizations.zitadel.github.com
|
||||||
|
spec:
|
||||||
|
group: zitadel.github.com
|
||||||
|
names:
|
||||||
|
kind: Organization
|
||||||
|
listKind: OrganizationList
|
||||||
|
plural: organizations
|
||||||
|
singular: organization
|
||||||
|
scope: Namespaced
|
||||||
|
versions:
|
||||||
|
- name: v1alpha1
|
||||||
|
schema:
|
||||||
|
openAPIV3Schema:
|
||||||
|
description: Organization is the Schema for the organizations 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: OrganizationSpec defines the desired state of Organization
|
||||||
|
properties:
|
||||||
|
connectionRef:
|
||||||
|
description: |-
|
||||||
|
INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
|
||||||
|
Important: Run "make" to regenerate code after modifying this file
|
||||||
|
properties:
|
||||||
|
apiVersion:
|
||||||
|
description: API version of the referent.
|
||||||
|
type: string
|
||||||
|
fieldPath:
|
||||||
|
description: |-
|
||||||
|
If referring to a piece of an object instead of an entire object, this string
|
||||||
|
should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2].
|
||||||
|
For example, if the object reference is to a container within a pod, this would take on a value like:
|
||||||
|
"spec.containers{name}" (where "name" refers to the name of the container that triggered
|
||||||
|
the event) or if no container name is specified "spec.containers[2]" (container with
|
||||||
|
index 2 in this pod). This syntax is chosen only to have some well-defined way of
|
||||||
|
referencing a part of an object.
|
||||||
|
type: string
|
||||||
|
kind:
|
||||||
|
description: |-
|
||||||
|
Kind of the referent.
|
||||||
|
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
description: |-
|
||||||
|
Name of the referent.
|
||||||
|
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||||
|
type: string
|
||||||
|
namespace:
|
||||||
|
description: |-
|
||||||
|
Namespace of the referent.
|
||||||
|
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/
|
||||||
|
type: string
|
||||||
|
resourceVersion:
|
||||||
|
description: |-
|
||||||
|
Specific resourceVersion to which this reference is made, if any.
|
||||||
|
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency
|
||||||
|
type: string
|
||||||
|
uid:
|
||||||
|
description: |-
|
||||||
|
UID of the referent.
|
||||||
|
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
x-kubernetes-map-type: atomic
|
||||||
|
organizationName:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- connectionRef
|
||||||
|
- organizationName
|
||||||
|
type: object
|
||||||
|
status:
|
||||||
|
description: OrganizationStatus defines the observed state of Organization
|
||||||
|
properties:
|
||||||
|
conditions:
|
||||||
|
description: |-
|
||||||
|
INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
|
||||||
|
Important: Run "make" to regenerate code after modifying this file
|
||||||
|
Conditions for the Database object.
|
||||||
|
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
|
||||||
|
organizationId:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
type: object
|
||||||
|
served: true
|
||||||
|
storage: true
|
||||||
|
subresources:
|
||||||
|
status: {}
|
||||||
239
src/config/crd/bases/zitadel.github.com_projects.yaml
Normal file
239
src/config/crd/bases/zitadel.github.com_projects.yaml
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
---
|
||||||
|
apiVersion: apiextensions.k8s.io/v1
|
||||||
|
kind: CustomResourceDefinition
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
controller-gen.kubebuilder.io/version: v0.17.3
|
||||||
|
name: projects.zitadel.github.com
|
||||||
|
spec:
|
||||||
|
group: zitadel.github.com
|
||||||
|
names:
|
||||||
|
kind: Project
|
||||||
|
listKind: ProjectList
|
||||||
|
plural: projects
|
||||||
|
singular: project
|
||||||
|
scope: Namespaced
|
||||||
|
versions:
|
||||||
|
- name: v1alpha1
|
||||||
|
schema:
|
||||||
|
openAPIV3Schema:
|
||||||
|
description: Project is the Schema for the projects 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: ProjectSpec defines the desired state of Project
|
||||||
|
properties:
|
||||||
|
grants:
|
||||||
|
items:
|
||||||
|
properties:
|
||||||
|
organizationRef:
|
||||||
|
properties:
|
||||||
|
apiVersion:
|
||||||
|
description: API version of the referent.
|
||||||
|
type: string
|
||||||
|
fieldPath:
|
||||||
|
description: |-
|
||||||
|
If referring to a piece of an object instead of an entire object, this string
|
||||||
|
should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2].
|
||||||
|
For example, if the object reference is to a container within a pod, this would take on a value like:
|
||||||
|
"spec.containers{name}" (where "name" refers to the name of the container that triggered
|
||||||
|
the event) or if no container name is specified "spec.containers[2]" (container with
|
||||||
|
index 2 in this pod). This syntax is chosen only to have some well-defined way of
|
||||||
|
referencing a part of an object.
|
||||||
|
type: string
|
||||||
|
kind:
|
||||||
|
description: |-
|
||||||
|
Kind of the referent.
|
||||||
|
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
description: |-
|
||||||
|
Name of the referent.
|
||||||
|
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||||
|
type: string
|
||||||
|
namespace:
|
||||||
|
description: |-
|
||||||
|
Namespace of the referent.
|
||||||
|
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/
|
||||||
|
type: string
|
||||||
|
resourceVersion:
|
||||||
|
description: |-
|
||||||
|
Specific resourceVersion to which this reference is made, if any.
|
||||||
|
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency
|
||||||
|
type: string
|
||||||
|
uid:
|
||||||
|
description: |-
|
||||||
|
UID of the referent.
|
||||||
|
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
x-kubernetes-map-type: atomic
|
||||||
|
roleKeys:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
required:
|
||||||
|
- organizationRef
|
||||||
|
- roleKeys
|
||||||
|
type: object
|
||||||
|
type: array
|
||||||
|
hasProjectCheck:
|
||||||
|
type: boolean
|
||||||
|
organizationRef:
|
||||||
|
description: |-
|
||||||
|
INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
|
||||||
|
Important: Run "make" to regenerate code after modifying this file
|
||||||
|
https://zitadel.com/docs/apis/resources/mgmt/management-service-add-project
|
||||||
|
properties:
|
||||||
|
apiVersion:
|
||||||
|
description: API version of the referent.
|
||||||
|
type: string
|
||||||
|
fieldPath:
|
||||||
|
description: |-
|
||||||
|
If referring to a piece of an object instead of an entire object, this string
|
||||||
|
should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2].
|
||||||
|
For example, if the object reference is to a container within a pod, this would take on a value like:
|
||||||
|
"spec.containers{name}" (where "name" refers to the name of the container that triggered
|
||||||
|
the event) or if no container name is specified "spec.containers[2]" (container with
|
||||||
|
index 2 in this pod). This syntax is chosen only to have some well-defined way of
|
||||||
|
referencing a part of an object.
|
||||||
|
type: string
|
||||||
|
kind:
|
||||||
|
description: |-
|
||||||
|
Kind of the referent.
|
||||||
|
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
description: |-
|
||||||
|
Name of the referent.
|
||||||
|
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||||
|
type: string
|
||||||
|
namespace:
|
||||||
|
description: |-
|
||||||
|
Namespace of the referent.
|
||||||
|
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/
|
||||||
|
type: string
|
||||||
|
resourceVersion:
|
||||||
|
description: |-
|
||||||
|
Specific resourceVersion to which this reference is made, if any.
|
||||||
|
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency
|
||||||
|
type: string
|
||||||
|
uid:
|
||||||
|
description: |-
|
||||||
|
UID of the referent.
|
||||||
|
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
x-kubernetes-map-type: atomic
|
||||||
|
projectName:
|
||||||
|
type: string
|
||||||
|
projectRoleAssertion:
|
||||||
|
type: boolean
|
||||||
|
projectRoleCheck:
|
||||||
|
type: boolean
|
||||||
|
roles:
|
||||||
|
items:
|
||||||
|
properties:
|
||||||
|
displayName:
|
||||||
|
type: string
|
||||||
|
group:
|
||||||
|
type: string
|
||||||
|
key:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- displayName
|
||||||
|
- group
|
||||||
|
- key
|
||||||
|
type: object
|
||||||
|
type: array
|
||||||
|
required:
|
||||||
|
- organizationRef
|
||||||
|
- projectName
|
||||||
|
type: object
|
||||||
|
status:
|
||||||
|
description: ProjectStatus defines the observed state of Project
|
||||||
|
properties:
|
||||||
|
conditions:
|
||||||
|
description: |-
|
||||||
|
INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
|
||||||
|
Important: Run "make" to regenerate code after modifying this file
|
||||||
|
Conditions for the Database object.
|
||||||
|
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
|
||||||
|
projectId:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
type: object
|
||||||
|
served: true
|
||||||
|
storage: true
|
||||||
|
subresources:
|
||||||
|
status: {}
|
||||||
43
src/config/crd/kustomization.yaml
Normal file
43
src/config/crd/kustomization.yaml
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# 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/zitadel.github.com_organizations.yaml
|
||||||
|
- bases/zitadel.github.com_projects.yaml
|
||||||
|
- bases/zitadel.github.com_oidcapps.yaml
|
||||||
|
- bases/zitadel.github.com_machineusers.yaml
|
||||||
|
- bases/zitadel.github.com_apiapps.yaml
|
||||||
|
- bases/zitadel.github.com_actions.yaml
|
||||||
|
- bases/zitadel.github.com_flows.yaml
|
||||||
|
- bases/zitadel.github.com_connections.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_zitadelclusters.yaml
|
||||||
|
#- patches/webhook_in_organizations.yaml
|
||||||
|
#- patches/webhook_in_projects.yaml
|
||||||
|
#- patches/webhook_in_oidcapps.yaml
|
||||||
|
#- patches/webhook_in_machineusers.yaml
|
||||||
|
#- patches/webhook_in_apiapps.yaml
|
||||||
|
#- patches/webhook_in_actions.yaml
|
||||||
|
#- patches/webhook_in_flows.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_zitadelclusters.yaml
|
||||||
|
#- patches/cainjection_in_organizations.yaml
|
||||||
|
#- patches/cainjection_in_projects.yaml
|
||||||
|
#- patches/cainjection_in_oidcapps.yaml
|
||||||
|
#- patches/cainjection_in_machineusers.yaml
|
||||||
|
#- patches/cainjection_in_apiapps.yaml
|
||||||
|
#- patches/cainjection_in_actions.yaml
|
||||||
|
#- patches/cainjection_in_flows.yaml
|
||||||
|
#- path: patches/cainjection_in_connections.yaml
|
||||||
|
#+kubebuilder:scaffold:crdkustomizecainjectionpatch
|
||||||
|
|
||||||
|
# the following config is for teaching kustomize how to do kustomization for CRDs.
|
||||||
|
configurations:
|
||||||
|
- kustomizeconfig.yaml
|
||||||
19
src/config/crd/kustomizeconfig.yaml
Normal file
19
src/config/crd/kustomizeconfig.yaml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# This file is for teaching kustomize how to substitute name and namespace reference in CRD
|
||||||
|
nameReference:
|
||||||
|
- kind: Service
|
||||||
|
version: v1
|
||||||
|
fieldSpecs:
|
||||||
|
- kind: CustomResourceDefinition
|
||||||
|
version: v1
|
||||||
|
group: apiextensions.k8s.io
|
||||||
|
path: spec/conversion/webhook/clientConfig/service/name
|
||||||
|
|
||||||
|
namespace:
|
||||||
|
- kind: CustomResourceDefinition
|
||||||
|
version: v1
|
||||||
|
group: apiextensions.k8s.io
|
||||||
|
path: spec/conversion/webhook/clientConfig/service/namespace
|
||||||
|
create: false
|
||||||
|
|
||||||
|
varReference:
|
||||||
|
- path: metadata/annotations
|
||||||
144
src/config/default/kustomization.yaml
Normal file
144
src/config/default/kustomization.yaml
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
# Adds namespace to all resources.
|
||||||
|
namespace: src-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: src-
|
||||||
|
|
||||||
|
# Labels to add to all resources and selectors.
|
||||||
|
#labels:
|
||||||
|
#- includeSelectors: true
|
||||||
|
# pairs:
|
||||||
|
# someName: someValue
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- ../crd
|
||||||
|
- ../rbac
|
||||||
|
- ../manager
|
||||||
|
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
|
||||||
|
# crd/kustomization.yaml
|
||||||
|
#- ../webhook
|
||||||
|
# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required.
|
||||||
|
#- ../certmanager
|
||||||
|
# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'.
|
||||||
|
#- ../prometheus
|
||||||
|
|
||||||
|
patchesStrategicMerge:
|
||||||
|
# Protect the /metrics endpoint by putting it behind auth.
|
||||||
|
# If you want your controller-manager to expose the /metrics
|
||||||
|
# endpoint w/o any authn/z, please comment the following line.
|
||||||
|
- manager_auth_proxy_patch.yaml
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
|
||||||
|
# crd/kustomization.yaml
|
||||||
|
#- manager_webhook_patch.yaml
|
||||||
|
|
||||||
|
# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'.
|
||||||
|
# Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks.
|
||||||
|
# 'CERTMANAGER' needs to be enabled to use ca injection
|
||||||
|
#- webhookcainjection_patch.yaml
|
||||||
|
|
||||||
|
# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix.
|
||||||
|
# Uncomment the following replacements to add the cert-manager CA injection annotations
|
||||||
|
#replacements:
|
||||||
|
# - source: # Add cert-manager annotation to ValidatingWebhookConfiguration, MutatingWebhookConfiguration and CRDs
|
||||||
|
# kind: Certificate
|
||||||
|
# group: cert-manager.io
|
||||||
|
# version: v1
|
||||||
|
# name: serving-cert # this name should match the one in certificate.yaml
|
||||||
|
# fieldPath: .metadata.namespace # namespace of the certificate CR
|
||||||
|
# targets:
|
||||||
|
# - select:
|
||||||
|
# kind: ValidatingWebhookConfiguration
|
||||||
|
# fieldPaths:
|
||||||
|
# - .metadata.annotations.[cert-manager.io/inject-ca-from]
|
||||||
|
# options:
|
||||||
|
# delimiter: '/'
|
||||||
|
# index: 0
|
||||||
|
# create: true
|
||||||
|
# - select:
|
||||||
|
# kind: MutatingWebhookConfiguration
|
||||||
|
# fieldPaths:
|
||||||
|
# - .metadata.annotations.[cert-manager.io/inject-ca-from]
|
||||||
|
# options:
|
||||||
|
# delimiter: '/'
|
||||||
|
# index: 0
|
||||||
|
# create: true
|
||||||
|
# - select:
|
||||||
|
# kind: CustomResourceDefinition
|
||||||
|
# fieldPaths:
|
||||||
|
# - .metadata.annotations.[cert-manager.io/inject-ca-from]
|
||||||
|
# options:
|
||||||
|
# delimiter: '/'
|
||||||
|
# index: 0
|
||||||
|
# create: true
|
||||||
|
# - source:
|
||||||
|
# kind: Certificate
|
||||||
|
# group: cert-manager.io
|
||||||
|
# version: v1
|
||||||
|
# name: serving-cert # this name should match the one in certificate.yaml
|
||||||
|
# fieldPath: .metadata.name
|
||||||
|
# targets:
|
||||||
|
# - select:
|
||||||
|
# kind: ValidatingWebhookConfiguration
|
||||||
|
# fieldPaths:
|
||||||
|
# - .metadata.annotations.[cert-manager.io/inject-ca-from]
|
||||||
|
# options:
|
||||||
|
# delimiter: '/'
|
||||||
|
# index: 1
|
||||||
|
# create: true
|
||||||
|
# - select:
|
||||||
|
# kind: MutatingWebhookConfiguration
|
||||||
|
# fieldPaths:
|
||||||
|
# - .metadata.annotations.[cert-manager.io/inject-ca-from]
|
||||||
|
# options:
|
||||||
|
# delimiter: '/'
|
||||||
|
# index: 1
|
||||||
|
# create: true
|
||||||
|
# - select:
|
||||||
|
# kind: CustomResourceDefinition
|
||||||
|
# fieldPaths:
|
||||||
|
# - .metadata.annotations.[cert-manager.io/inject-ca-from]
|
||||||
|
# options:
|
||||||
|
# delimiter: '/'
|
||||||
|
# index: 1
|
||||||
|
# create: true
|
||||||
|
# - source: # Add cert-manager annotation to the webhook Service
|
||||||
|
# kind: Service
|
||||||
|
# version: v1
|
||||||
|
# name: webhook-service
|
||||||
|
# fieldPath: .metadata.name # namespace of the service
|
||||||
|
# targets:
|
||||||
|
# - select:
|
||||||
|
# kind: Certificate
|
||||||
|
# group: cert-manager.io
|
||||||
|
# version: v1
|
||||||
|
# fieldPaths:
|
||||||
|
# - .spec.dnsNames.0
|
||||||
|
# - .spec.dnsNames.1
|
||||||
|
# options:
|
||||||
|
# delimiter: '.'
|
||||||
|
# index: 0
|
||||||
|
# create: true
|
||||||
|
# - source:
|
||||||
|
# kind: Service
|
||||||
|
# version: v1
|
||||||
|
# name: webhook-service
|
||||||
|
# fieldPath: .metadata.namespace # namespace of the service
|
||||||
|
# targets:
|
||||||
|
# - select:
|
||||||
|
# kind: Certificate
|
||||||
|
# group: cert-manager.io
|
||||||
|
# version: v1
|
||||||
|
# fieldPaths:
|
||||||
|
# - .spec.dnsNames.0
|
||||||
|
# - .spec.dnsNames.1
|
||||||
|
# options:
|
||||||
|
# delimiter: '.'
|
||||||
|
# index: 1
|
||||||
|
# create: true
|
||||||
55
src/config/default/manager_auth_proxy_patch.yaml
Normal file
55
src/config/default/manager_auth_proxy_patch.yaml
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
# This patch inject a sidecar container which is a HTTP proxy for the
|
||||||
|
# controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews.
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: controller-manager
|
||||||
|
namespace: system
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
affinity:
|
||||||
|
nodeAffinity:
|
||||||
|
requiredDuringSchedulingIgnoredDuringExecution:
|
||||||
|
nodeSelectorTerms:
|
||||||
|
- matchExpressions:
|
||||||
|
- key: kubernetes.io/arch
|
||||||
|
operator: In
|
||||||
|
values:
|
||||||
|
- amd64
|
||||||
|
- arm64
|
||||||
|
- ppc64le
|
||||||
|
- s390x
|
||||||
|
- key: kubernetes.io/os
|
||||||
|
operator: In
|
||||||
|
values:
|
||||||
|
- linux
|
||||||
|
containers:
|
||||||
|
- name: kube-rbac-proxy
|
||||||
|
securityContext:
|
||||||
|
allowPrivilegeEscalation: false
|
||||||
|
capabilities:
|
||||||
|
drop:
|
||||||
|
- "ALL"
|
||||||
|
image: gcr.io/kubebuilder/kube-rbac-proxy:v0.13.1
|
||||||
|
args:
|
||||||
|
- "--secure-listen-address=0.0.0.0:8443"
|
||||||
|
- "--upstream=http://127.0.0.1:8080/"
|
||||||
|
- "--logtostderr=true"
|
||||||
|
- "--v=0"
|
||||||
|
ports:
|
||||||
|
- containerPort: 8443
|
||||||
|
protocol: TCP
|
||||||
|
name: https
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpu: 500m
|
||||||
|
memory: 128Mi
|
||||||
|
requests:
|
||||||
|
cpu: 5m
|
||||||
|
memory: 64Mi
|
||||||
|
- name: manager
|
||||||
|
args:
|
||||||
|
- "--health-probe-bind-address=:8081"
|
||||||
|
- "--metrics-bind-address=127.0.0.1:8080"
|
||||||
|
- "--leader-elect"
|
||||||
10
src/config/default/manager_config_patch.yaml
Normal file
10
src/config/default/manager_config_patch.yaml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: controller-manager
|
||||||
|
namespace: system
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: manager
|
||||||
2
src/config/manager/kustomization.yaml
Normal file
2
src/config/manager/kustomization.yaml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
resources:
|
||||||
|
- manager.yaml
|
||||||
102
src/config/manager/manager.yaml
Normal file
102
src/config/manager/manager.yaml
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
control-plane: controller-manager
|
||||||
|
app.kubernetes.io/name: namespace
|
||||||
|
app.kubernetes.io/instance: system
|
||||||
|
app.kubernetes.io/component: manager
|
||||||
|
app.kubernetes.io/created-by: src
|
||||||
|
app.kubernetes.io/part-of: src
|
||||||
|
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: deployment
|
||||||
|
app.kubernetes.io/instance: controller-manager
|
||||||
|
app.kubernetes.io/component: manager
|
||||||
|
app.kubernetes.io/created-by: src
|
||||||
|
app.kubernetes.io/part-of: src
|
||||||
|
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:
|
||||||
|
runAsNonRoot: true
|
||||||
|
# TODO(user): For common cases that do not require escalating privileges
|
||||||
|
# it is recommended to ensure that all your Pods/Containers are restrictive.
|
||||||
|
# More info: https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted
|
||||||
|
# Please uncomment the following code if your project does NOT have to work on old Kubernetes
|
||||||
|
# versions < 1.19 or on vendors versions which do NOT support this field by default (i.e. Openshift < 4.11 ).
|
||||||
|
# seccompProfile:
|
||||||
|
# type: RuntimeDefault
|
||||||
|
containers:
|
||||||
|
- command:
|
||||||
|
- /manager
|
||||||
|
args:
|
||||||
|
- --leader-elect
|
||||||
|
image: controller:latest
|
||||||
|
name: manager
|
||||||
|
securityContext:
|
||||||
|
allowPrivilegeEscalation: false
|
||||||
|
capabilities:
|
||||||
|
drop:
|
||||||
|
- "ALL"
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /healthz
|
||||||
|
port: 8081
|
||||||
|
initialDelaySeconds: 15
|
||||||
|
periodSeconds: 20
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /readyz
|
||||||
|
port: 8081
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 10
|
||||||
|
# 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
|
||||||
|
serviceAccountName: controller-manager
|
||||||
|
terminationGracePeriodSeconds: 10
|
||||||
31
src/config/rbac/action_editor_role.yaml
Normal file
31
src/config/rbac/action_editor_role.yaml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# permissions for end users to edit actions.
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: clusterrole
|
||||||
|
app.kubernetes.io/instance: action-editor-role
|
||||||
|
app.kubernetes.io/component: rbac
|
||||||
|
app.kubernetes.io/created-by: src
|
||||||
|
app.kubernetes.io/part-of: src
|
||||||
|
app.kubernetes.io/managed-by: kustomize
|
||||||
|
name: action-editor-role
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- zitadel.github.com
|
||||||
|
resources:
|
||||||
|
- actions
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- zitadel.github.com
|
||||||
|
resources:
|
||||||
|
- actions/status
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
27
src/config/rbac/action_viewer_role.yaml
Normal file
27
src/config/rbac/action_viewer_role.yaml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# permissions for end users to view actions.
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: clusterrole
|
||||||
|
app.kubernetes.io/instance: action-viewer-role
|
||||||
|
app.kubernetes.io/component: rbac
|
||||||
|
app.kubernetes.io/created-by: src
|
||||||
|
app.kubernetes.io/part-of: src
|
||||||
|
app.kubernetes.io/managed-by: kustomize
|
||||||
|
name: action-viewer-role
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- zitadel.github.com
|
||||||
|
resources:
|
||||||
|
- actions
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- zitadel.github.com
|
||||||
|
resources:
|
||||||
|
- actions/status
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
31
src/config/rbac/apiapp_editor_role.yaml
Normal file
31
src/config/rbac/apiapp_editor_role.yaml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# permissions for end users to edit apiapps.
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: clusterrole
|
||||||
|
app.kubernetes.io/instance: apiapp-editor-role
|
||||||
|
app.kubernetes.io/component: rbac
|
||||||
|
app.kubernetes.io/created-by: src
|
||||||
|
app.kubernetes.io/part-of: src
|
||||||
|
app.kubernetes.io/managed-by: kustomize
|
||||||
|
name: apiapp-editor-role
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- zitadel.github.com
|
||||||
|
resources:
|
||||||
|
- apiapps
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- zitadel.github.com
|
||||||
|
resources:
|
||||||
|
- apiapps/status
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
27
src/config/rbac/apiapp_viewer_role.yaml
Normal file
27
src/config/rbac/apiapp_viewer_role.yaml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# permissions for end users to view apiapps.
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: clusterrole
|
||||||
|
app.kubernetes.io/instance: apiapp-viewer-role
|
||||||
|
app.kubernetes.io/component: rbac
|
||||||
|
app.kubernetes.io/created-by: src
|
||||||
|
app.kubernetes.io/part-of: src
|
||||||
|
app.kubernetes.io/managed-by: kustomize
|
||||||
|
name: apiapp-viewer-role
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- zitadel.github.com
|
||||||
|
resources:
|
||||||
|
- apiapps
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- zitadel.github.com
|
||||||
|
resources:
|
||||||
|
- apiapps/status
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
16
src/config/rbac/auth_proxy_client_clusterrole.yaml
Normal file
16
src/config/rbac/auth_proxy_client_clusterrole.yaml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: clusterrole
|
||||||
|
app.kubernetes.io/instance: metrics-reader
|
||||||
|
app.kubernetes.io/component: kube-rbac-proxy
|
||||||
|
app.kubernetes.io/created-by: src
|
||||||
|
app.kubernetes.io/part-of: src
|
||||||
|
app.kubernetes.io/managed-by: kustomize
|
||||||
|
name: metrics-reader
|
||||||
|
rules:
|
||||||
|
- nonResourceURLs:
|
||||||
|
- "/metrics"
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
24
src/config/rbac/auth_proxy_role.yaml
Normal file
24
src/config/rbac/auth_proxy_role.yaml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: clusterrole
|
||||||
|
app.kubernetes.io/instance: proxy-role
|
||||||
|
app.kubernetes.io/component: kube-rbac-proxy
|
||||||
|
app.kubernetes.io/created-by: src
|
||||||
|
app.kubernetes.io/part-of: src
|
||||||
|
app.kubernetes.io/managed-by: kustomize
|
||||||
|
name: proxy-role
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- authentication.k8s.io
|
||||||
|
resources:
|
||||||
|
- tokenreviews
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- apiGroups:
|
||||||
|
- authorization.k8s.io
|
||||||
|
resources:
|
||||||
|
- subjectaccessreviews
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
19
src/config/rbac/auth_proxy_role_binding.yaml
Normal file
19
src/config/rbac/auth_proxy_role_binding.yaml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: clusterrolebinding
|
||||||
|
app.kubernetes.io/instance: proxy-rolebinding
|
||||||
|
app.kubernetes.io/component: kube-rbac-proxy
|
||||||
|
app.kubernetes.io/created-by: src
|
||||||
|
app.kubernetes.io/part-of: src
|
||||||
|
app.kubernetes.io/managed-by: kustomize
|
||||||
|
name: proxy-rolebinding
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: ClusterRole
|
||||||
|
name: proxy-role
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: controller-manager
|
||||||
|
namespace: system
|
||||||
21
src/config/rbac/auth_proxy_service.yaml
Normal file
21
src/config/rbac/auth_proxy_service.yaml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
control-plane: controller-manager
|
||||||
|
app.kubernetes.io/name: service
|
||||||
|
app.kubernetes.io/instance: controller-manager-metrics-service
|
||||||
|
app.kubernetes.io/component: kube-rbac-proxy
|
||||||
|
app.kubernetes.io/created-by: src
|
||||||
|
app.kubernetes.io/part-of: src
|
||||||
|
app.kubernetes.io/managed-by: kustomize
|
||||||
|
name: controller-manager-metrics-service
|
||||||
|
namespace: system
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- name: https
|
||||||
|
port: 8443
|
||||||
|
protocol: TCP
|
||||||
|
targetPort: https
|
||||||
|
selector:
|
||||||
|
control-plane: controller-manager
|
||||||
31
src/config/rbac/cluster_editor_role.yaml
Normal file
31
src/config/rbac/cluster_editor_role.yaml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# permissions for end users to edit zitadelclusters.
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: clusterrole
|
||||||
|
app.kubernetes.io/instance: zitadelcluster-editor-role
|
||||||
|
app.kubernetes.io/component: rbac
|
||||||
|
app.kubernetes.io/created-by: src
|
||||||
|
app.kubernetes.io/part-of: src
|
||||||
|
app.kubernetes.io/managed-by: kustomize
|
||||||
|
name: zitadelcluster-editor-role
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- zitadel.github.com
|
||||||
|
resources:
|
||||||
|
- clusters
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- zitadel.github.com
|
||||||
|
resources:
|
||||||
|
- clusters/status
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
27
src/config/rbac/connection_editor_role.yaml
Normal file
27
src/config/rbac/connection_editor_role.yaml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# permissions for end users to edit connections.
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: src
|
||||||
|
app.kubernetes.io/managed-by: kustomize
|
||||||
|
name: connection-editor-role
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- zitadel.github.com
|
||||||
|
resources:
|
||||||
|
- connections
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- zitadel.github.com
|
||||||
|
resources:
|
||||||
|
- connections/status
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
23
src/config/rbac/connection_viewer_role.yaml
Normal file
23
src/config/rbac/connection_viewer_role.yaml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# permissions for end users to view connections.
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: src
|
||||||
|
app.kubernetes.io/managed-by: kustomize
|
||||||
|
name: connection-viewer-role
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- zitadel.github.com
|
||||||
|
resources:
|
||||||
|
- connections
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- zitadel.github.com
|
||||||
|
resources:
|
||||||
|
- connections/status
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
31
src/config/rbac/flow_editor_role.yaml
Normal file
31
src/config/rbac/flow_editor_role.yaml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# permissions for end users to edit flows.
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: clusterrole
|
||||||
|
app.kubernetes.io/instance: flow-editor-role
|
||||||
|
app.kubernetes.io/component: rbac
|
||||||
|
app.kubernetes.io/created-by: src
|
||||||
|
app.kubernetes.io/part-of: src
|
||||||
|
app.kubernetes.io/managed-by: kustomize
|
||||||
|
name: flow-editor-role
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- zitadel.github.com
|
||||||
|
resources:
|
||||||
|
- flows
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- zitadel.github.com
|
||||||
|
resources:
|
||||||
|
- flows/status
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
27
src/config/rbac/flow_viewer_role.yaml
Normal file
27
src/config/rbac/flow_viewer_role.yaml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# permissions for end users to view flows.
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: clusterrole
|
||||||
|
app.kubernetes.io/instance: flow-viewer-role
|
||||||
|
app.kubernetes.io/component: rbac
|
||||||
|
app.kubernetes.io/created-by: src
|
||||||
|
app.kubernetes.io/part-of: src
|
||||||
|
app.kubernetes.io/managed-by: kustomize
|
||||||
|
name: flow-viewer-role
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- zitadel.github.com
|
||||||
|
resources:
|
||||||
|
- flows
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- zitadel.github.com
|
||||||
|
resources:
|
||||||
|
- flows/status
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
25
src/config/rbac/kustomization.yaml
Normal file
25
src/config/rbac/kustomization.yaml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
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
|
||||||
|
# Comment the following 4 lines if you want to disable
|
||||||
|
# the auth proxy (https://github.com/brancz/kube-rbac-proxy)
|
||||||
|
# which protects your /metrics endpoint.
|
||||||
|
- auth_proxy_service.yaml
|
||||||
|
- auth_proxy_role.yaml
|
||||||
|
- auth_proxy_role_binding.yaml
|
||||||
|
- auth_proxy_client_clusterrole.yaml
|
||||||
|
# For each CRD, "Editor" and "Viewer" roles are scaffolded by
|
||||||
|
# default, aiding admins in cluster management. Those roles are
|
||||||
|
# not used by the Project itself. You can comment the following lines
|
||||||
|
# if you do not want those helpers be installed with your Project.
|
||||||
|
- connection_editor_role.yaml
|
||||||
|
- connection_viewer_role.yaml
|
||||||
|
|
||||||
44
src/config/rbac/leader_election_role.yaml
Normal file
44
src/config/rbac/leader_election_role.yaml
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
# permissions to do leader election.
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: role
|
||||||
|
app.kubernetes.io/instance: leader-election-role
|
||||||
|
app.kubernetes.io/component: rbac
|
||||||
|
app.kubernetes.io/created-by: src
|
||||||
|
app.kubernetes.io/part-of: src
|
||||||
|
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
|
||||||
19
src/config/rbac/leader_election_role_binding.yaml
Normal file
19
src/config/rbac/leader_election_role_binding.yaml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: RoleBinding
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: rolebinding
|
||||||
|
app.kubernetes.io/instance: leader-election-rolebinding
|
||||||
|
app.kubernetes.io/component: rbac
|
||||||
|
app.kubernetes.io/created-by: src
|
||||||
|
app.kubernetes.io/part-of: src
|
||||||
|
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
|
||||||
31
src/config/rbac/machineuser_editor_role.yaml
Normal file
31
src/config/rbac/machineuser_editor_role.yaml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# permissions for end users to edit machineusers.
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: clusterrole
|
||||||
|
app.kubernetes.io/instance: machineuser-editor-role
|
||||||
|
app.kubernetes.io/component: rbac
|
||||||
|
app.kubernetes.io/created-by: src
|
||||||
|
app.kubernetes.io/part-of: src
|
||||||
|
app.kubernetes.io/managed-by: kustomize
|
||||||
|
name: machineuser-editor-role
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- zitadel.github.com
|
||||||
|
resources:
|
||||||
|
- machineusers
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- zitadel.github.com
|
||||||
|
resources:
|
||||||
|
- machineusers/status
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
27
src/config/rbac/machineuser_viewer_role.yaml
Normal file
27
src/config/rbac/machineuser_viewer_role.yaml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# permissions for end users to view machineusers.
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: clusterrole
|
||||||
|
app.kubernetes.io/instance: machineuser-viewer-role
|
||||||
|
app.kubernetes.io/component: rbac
|
||||||
|
app.kubernetes.io/created-by: src
|
||||||
|
app.kubernetes.io/part-of: src
|
||||||
|
app.kubernetes.io/managed-by: kustomize
|
||||||
|
name: machineuser-viewer-role
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- zitadel.github.com
|
||||||
|
resources:
|
||||||
|
- machineusers
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- zitadel.github.com
|
||||||
|
resources:
|
||||||
|
- machineusers/status
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
31
src/config/rbac/oidcapp_editor_role.yaml
Normal file
31
src/config/rbac/oidcapp_editor_role.yaml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# permissions for end users to edit oidcapps.
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: clusterrole
|
||||||
|
app.kubernetes.io/instance: oidcapp-editor-role
|
||||||
|
app.kubernetes.io/component: rbac
|
||||||
|
app.kubernetes.io/created-by: src
|
||||||
|
app.kubernetes.io/part-of: src
|
||||||
|
app.kubernetes.io/managed-by: kustomize
|
||||||
|
name: oidcapp-editor-role
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- zitadel.github.com
|
||||||
|
resources:
|
||||||
|
- oidcapps
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- zitadel.github.com
|
||||||
|
resources:
|
||||||
|
- oidcapps/status
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
27
src/config/rbac/oidcapp_viewer_role.yaml
Normal file
27
src/config/rbac/oidcapp_viewer_role.yaml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# permissions for end users to view oidcapps.
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: clusterrole
|
||||||
|
app.kubernetes.io/instance: oidcapp-viewer-role
|
||||||
|
app.kubernetes.io/component: rbac
|
||||||
|
app.kubernetes.io/created-by: src
|
||||||
|
app.kubernetes.io/part-of: src
|
||||||
|
app.kubernetes.io/managed-by: kustomize
|
||||||
|
name: oidcapp-viewer-role
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- zitadel.github.com
|
||||||
|
resources:
|
||||||
|
- oidcapps
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- zitadel.github.com
|
||||||
|
resources:
|
||||||
|
- oidcapps/status
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
31
src/config/rbac/organization_editor_role.yaml
Normal file
31
src/config/rbac/organization_editor_role.yaml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# permissions for end users to edit organizations.
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: clusterrole
|
||||||
|
app.kubernetes.io/instance: organization-editor-role
|
||||||
|
app.kubernetes.io/component: rbac
|
||||||
|
app.kubernetes.io/created-by: src
|
||||||
|
app.kubernetes.io/part-of: src
|
||||||
|
app.kubernetes.io/managed-by: kustomize
|
||||||
|
name: organization-editor-role
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- zitadel.github.com
|
||||||
|
resources:
|
||||||
|
- organizations
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- zitadel.github.com
|
||||||
|
resources:
|
||||||
|
- organizations/status
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
27
src/config/rbac/organization_viewer_role.yaml
Normal file
27
src/config/rbac/organization_viewer_role.yaml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# permissions for end users to view organizations.
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: clusterrole
|
||||||
|
app.kubernetes.io/instance: organization-viewer-role
|
||||||
|
app.kubernetes.io/component: rbac
|
||||||
|
app.kubernetes.io/created-by: src
|
||||||
|
app.kubernetes.io/part-of: src
|
||||||
|
app.kubernetes.io/managed-by: kustomize
|
||||||
|
name: organization-viewer-role
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- zitadel.github.com
|
||||||
|
resources:
|
||||||
|
- organizations
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- zitadel.github.com
|
||||||
|
resources:
|
||||||
|
- organizations/status
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
31
src/config/rbac/project_editor_role.yaml
Normal file
31
src/config/rbac/project_editor_role.yaml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# permissions for end users to edit projects.
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: clusterrole
|
||||||
|
app.kubernetes.io/instance: project-editor-role
|
||||||
|
app.kubernetes.io/component: rbac
|
||||||
|
app.kubernetes.io/created-by: src
|
||||||
|
app.kubernetes.io/part-of: src
|
||||||
|
app.kubernetes.io/managed-by: kustomize
|
||||||
|
name: project-editor-role
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- zitadel.github.com
|
||||||
|
resources:
|
||||||
|
- projects
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- zitadel.github.com
|
||||||
|
resources:
|
||||||
|
- projects/status
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
27
src/config/rbac/project_viewer_role.yaml
Normal file
27
src/config/rbac/project_viewer_role.yaml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# permissions for end users to view projects.
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: clusterrole
|
||||||
|
app.kubernetes.io/instance: project-viewer-role
|
||||||
|
app.kubernetes.io/component: rbac
|
||||||
|
app.kubernetes.io/created-by: src
|
||||||
|
app.kubernetes.io/part-of: src
|
||||||
|
app.kubernetes.io/managed-by: kustomize
|
||||||
|
name: project-viewer-role
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- zitadel.github.com
|
||||||
|
resources:
|
||||||
|
- projects
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- zitadel.github.com
|
||||||
|
resources:
|
||||||
|
- projects/status
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
32
src/config/rbac/role.yaml
Normal file
32
src/config/rbac/role.yaml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
name: manager-role
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- zitadel.github.com
|
||||||
|
resources:
|
||||||
|
- connections
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- zitadel.github.com
|
||||||
|
resources:
|
||||||
|
- connections/finalizers
|
||||||
|
verbs:
|
||||||
|
- update
|
||||||
|
- apiGroups:
|
||||||
|
- zitadel.github.com
|
||||||
|
resources:
|
||||||
|
- connections/status
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
19
src/config/rbac/role_binding.yaml
Normal file
19
src/config/rbac/role_binding.yaml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: clusterrolebinding
|
||||||
|
app.kubernetes.io/instance: manager-rolebinding
|
||||||
|
app.kubernetes.io/component: rbac
|
||||||
|
app.kubernetes.io/created-by: src
|
||||||
|
app.kubernetes.io/part-of: src
|
||||||
|
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
|
||||||
12
src/config/rbac/service_account.yaml
Normal file
12
src/config/rbac/service_account.yaml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: serviceaccount
|
||||||
|
app.kubernetes.io/instance: controller-manager-sa
|
||||||
|
app.kubernetes.io/component: rbac
|
||||||
|
app.kubernetes.io/created-by: src
|
||||||
|
app.kubernetes.io/part-of: src
|
||||||
|
app.kubernetes.io/managed-by: kustomize
|
||||||
|
name: controller-manager
|
||||||
|
namespace: system
|
||||||
4
src/config/samples/kustomization.yaml
Normal file
4
src/config/samples/kustomization.yaml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
## Append samples of your project ##
|
||||||
|
resources:
|
||||||
|
- zitadel_v1alpha1_connection.yaml
|
||||||
|
# +kubebuilder:scaffold:manifestskustomizesamples
|
||||||
9
src/config/samples/zitadel_v1alpha1_connection.yaml
Normal file
9
src/config/samples/zitadel_v1alpha1_connection.yaml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
apiVersion: zitadel.github.com/v1alpha1
|
||||||
|
kind: Connection
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: src
|
||||||
|
app.kubernetes.io/managed-by: kustomize
|
||||||
|
name: connection-sample
|
||||||
|
spec:
|
||||||
|
# TODO(user): Add fields here
|
||||||
101
src/go.mod
Normal file
101
src/go.mod
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
module github.com/HaimKortovich/zitadel-resources-operator
|
||||||
|
|
||||||
|
go 1.25.8
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/hashicorp/go-multierror v1.1.1
|
||||||
|
github.com/onsi/ginkgo/v2 v2.23.3
|
||||||
|
github.com/onsi/gomega v1.36.3
|
||||||
|
github.com/sethvargo/go-password v0.3.1
|
||||||
|
github.com/zitadel/zitadel-go/v3 v3.27.0
|
||||||
|
google.golang.org/grpc v1.79.3
|
||||||
|
google.golang.org/protobuf v1.36.11
|
||||||
|
k8s.io/api v0.32.3
|
||||||
|
k8s.io/apimachinery v0.32.3
|
||||||
|
k8s.io/client-go v0.32.3
|
||||||
|
k8s.io/utils v0.0.0-20241210054802-24370beab758
|
||||||
|
sigs.k8s.io/controller-runtime v0.20.4
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
|
github.com/cloudnative-pg/barman-cloud v0.1.0 // indirect
|
||||||
|
github.com/cloudnative-pg/machinery v0.1.0 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
|
github.com/emicklei/go-restful/v3 v3.12.1 // indirect
|
||||||
|
github.com/envoyproxy/protoc-gen-validate v1.3.3 // indirect
|
||||||
|
github.com/evanphx/json-patch/v5 v5.9.11 // indirect
|
||||||
|
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||||
|
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||||
|
github.com/go-jose/go-jose/v4 v4.1.3 // indirect
|
||||||
|
github.com/go-logr/logr v1.4.3 // indirect
|
||||||
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
|
github.com/go-logr/zapr v1.3.0 // indirect
|
||||||
|
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||||
|
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
||||||
|
github.com/go-openapi/swag v0.23.0 // indirect
|
||||||
|
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||||
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
|
github.com/golang/protobuf v1.5.4 // indirect
|
||||||
|
github.com/google/btree v1.1.3 // indirect
|
||||||
|
github.com/google/gnostic-models v0.6.9 // indirect
|
||||||
|
github.com/google/go-cmp v0.7.0 // indirect
|
||||||
|
github.com/google/gofuzz v1.2.0 // indirect
|
||||||
|
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||||
|
github.com/gorilla/websocket v1.5.0 // indirect
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect
|
||||||
|
github.com/hashicorp/errwrap v1.0.0 // indirect
|
||||||
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
|
github.com/klauspost/compress v1.17.11 // indirect
|
||||||
|
github.com/kubernetes-csi/external-snapshotter/client/v8 v8.2.0 // indirect
|
||||||
|
github.com/lib/pq v1.10.9 // indirect
|
||||||
|
github.com/mailru/easyjson v0.9.0 // indirect
|
||||||
|
github.com/moby/spdystream v0.5.0 // indirect
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
|
github.com/muhlemmer/gu v0.3.1 // indirect
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
|
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
|
||||||
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
|
github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.80.1 // indirect
|
||||||
|
github.com/prometheus/client_golang v1.21.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/sirupsen/logrus v1.9.4 // indirect
|
||||||
|
github.com/spf13/pflag v1.0.6 // indirect
|
||||||
|
github.com/x448/float16 v0.8.4 // indirect
|
||||||
|
github.com/zitadel/logging v0.7.0 // indirect
|
||||||
|
github.com/zitadel/oidc/v3 v3.45.5 // indirect
|
||||||
|
github.com/zitadel/schema v1.3.2 // indirect
|
||||||
|
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||||
|
go.opentelemetry.io/otel v1.40.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/metric v1.40.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/trace v1.40.0 // indirect
|
||||||
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
|
go.uber.org/zap v1.27.0 // indirect
|
||||||
|
golang.org/x/net v0.49.0 // indirect
|
||||||
|
golang.org/x/oauth2 v0.35.0 // indirect
|
||||||
|
golang.org/x/sync v0.19.0 // indirect
|
||||||
|
golang.org/x/sys v0.40.0 // indirect
|
||||||
|
golang.org/x/term v0.39.0 // indirect
|
||||||
|
golang.org/x/text v0.34.0 // indirect
|
||||||
|
golang.org/x/time v0.9.0 // indirect
|
||||||
|
golang.org/x/tools v0.41.0 // indirect
|
||||||
|
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 // indirect
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect
|
||||||
|
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||||
|
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
k8s.io/apiextensions-apiserver v0.32.2 // indirect
|
||||||
|
k8s.io/klog/v2 v2.130.1 // indirect
|
||||||
|
k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 // indirect
|
||||||
|
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
|
||||||
|
sigs.k8s.io/structured-merge-diff/v4 v4.5.0 // indirect
|
||||||
|
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||||
|
)
|
||||||
265
src/go.sum
Normal file
265
src/go.sum
Normal file
@@ -0,0 +1,265 @@
|
|||||||
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||||
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||||
|
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/bmatcuk/doublestar/v4 v4.10.0 h1:zU9WiOla1YA122oLM6i4EXvGW62DvKZVxIe6TYWexEs=
|
||||||
|
github.com/bmatcuk/doublestar/v4 v4.10.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||||
|
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/cloudnative-pg/barman-cloud v0.1.0 h1:e/z52CehMBIh1LjZqNBJnncWJbS+1JYvRMBR8Js6Uiw=
|
||||||
|
github.com/cloudnative-pg/barman-cloud v0.1.0/go.mod h1:rJUJO/f1yNckLZiVxHAyRmKY+4EPJkYRJsGbTZRJQSY=
|
||||||
|
github.com/cloudnative-pg/cloudnative-pg v1.25.1 h1:Yc6T7ikQ1AiWXBQht+6C3DoihrIpUN2OkM1dIwqadTo=
|
||||||
|
github.com/cloudnative-pg/cloudnative-pg v1.25.1/go.mod h1:96b9bRFLSr3uFWHjhytPdcvKIKwy9H6AG7cH0O6jefs=
|
||||||
|
github.com/cloudnative-pg/machinery v0.1.0 h1:tjRmsqQmsO/OlaT0uFmkEtVqgr+SGPM88cKZOHYKLBo=
|
||||||
|
github.com/cloudnative-pg/machinery v0.1.0/go.mod h1:0V3vm44FaIsY+x4pm8ORry7xCC3AJiO+ebfPNxeP5Ck=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU=
|
||||||
|
github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||||
|
github.com/envoyproxy/protoc-gen-validate v1.3.3 h1:MVQghNeW+LZcmXe7SY1V36Z+WFMDjpqGAGacLe2T0ds=
|
||||||
|
github.com/envoyproxy/protoc-gen-validate v1.3.3/go.mod h1:TsndJ/ngyIdQRhMcVVGDDHINPLWB7C82oDArY51KfB0=
|
||||||
|
github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U=
|
||||||
|
github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||||
|
github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU=
|
||||||
|
github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM=
|
||||||
|
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-chi/chi/v5 v5.2.5 h1:Eg4myHZBjyvJmAFjFvWgrqDTXFyOzjj7YIm3L3mu6Ug=
|
||||||
|
github.com/go-chi/chi/v5 v5.2.5/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0=
|
||||||
|
github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=
|
||||||
|
github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
|
||||||
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
|
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||||
|
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
|
github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
|
||||||
|
github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg=
|
||||||
|
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
||||||
|
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
||||||
|
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
|
||||||
|
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
|
||||||
|
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
|
||||||
|
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||||
|
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||||
|
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||||
|
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||||
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
|
github.com/golang/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/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
|
||||||
|
github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
|
||||||
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||||
|
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg=
|
||||||
|
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
|
||||||
|
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
|
||||||
|
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||||
|
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c=
|
||||||
|
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
|
||||||
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
|
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||||
|
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||||
|
github.com/jeremija/gosubmit v0.2.8 h1:mmSITBz9JxVtu8eqbN+zmmwX7Ij2RidQxhcwRVI4wqA=
|
||||||
|
github.com/jeremija/gosubmit v0.2.8/go.mod h1:Ui+HS073lCFREXBbdfrJzMB57OI/bdxTiLtrDHHhFPI=
|
||||||
|
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.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||||
|
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||||
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/kubernetes-csi/external-snapshotter/client/v8 v8.2.0 h1:Q3jQ1NkFqv5o+F8dMmHd8SfEmlcwNeo1immFApntEwE=
|
||||||
|
github.com/kubernetes-csi/external-snapshotter/client/v8 v8.2.0/go.mod h1:E3vdYxHj2C2q6qo8/Da4g7P+IcwqRZyy3gJBzYybV9Y=
|
||||||
|
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/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||||
|
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
|
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
|
||||||
|
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
|
||||||
|
github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU=
|
||||||
|
github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
|
||||||
|
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/muhlemmer/gu v0.3.1 h1:7EAqmFrW7n3hETvuAdmFmn4hS8W+z3LgKtrnow+YzNM=
|
||||||
|
github.com/muhlemmer/gu v0.3.1/go.mod h1:YHtHR+gxM+bKEIIs7Hmi9sPT3ZDUvTN/i88wQpZkrdM=
|
||||||
|
github.com/muhlemmer/httpforwarded v0.1.0 h1:x4DLrzXdliq8mprgUMR0olDvHGkou5BJsK/vWUetyzY=
|
||||||
|
github.com/muhlemmer/httpforwarded v0.1.0/go.mod h1:yo9czKedo2pdZhoXe+yDkGVbU0TJ0q9oQ90BVoDEtw0=
|
||||||
|
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/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
|
||||||
|
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||||
|
github.com/onsi/ginkgo/v2 v2.23.3 h1:edHxnszytJ4lD9D5Jjc4tiDkPBZ3siDeJJkUZJJVkp0=
|
||||||
|
github.com/onsi/ginkgo/v2 v2.23.3/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM=
|
||||||
|
github.com/onsi/gomega v1.36.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU=
|
||||||
|
github.com/onsi/gomega v1.36.3/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
|
||||||
|
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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||||
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.80.1 h1:DP+PUNVOc+Bkft8a4QunLzaZ0RspWuD3tBbcPHr2PeE=
|
||||||
|
github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.80.1/go.mod h1:6x4x0t9BP35g4XcjkHE9EB3RxhyfxpdpmZKd/Qyk8+M=
|
||||||
|
github.com/prometheus/client_golang v1.21.0 h1:DIsaGmiaBkSangBgMtWdNfxbMNdku5IK6iNhrEqWvdA=
|
||||||
|
github.com/prometheus/client_golang v1.21.0/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg=
|
||||||
|
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.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||||
|
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||||
|
github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA=
|
||||||
|
github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
|
||||||
|
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/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
|
||||||
|
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
|
||||||
|
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||||
|
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
|
github.com/thoas/go-funk v0.9.3 h1:7+nAEx3kn5ZJcnDm2Bh23N2yOtweO14bi//dvRtgLpw=
|
||||||
|
github.com/thoas/go-funk v0.9.3/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q=
|
||||||
|
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||||
|
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||||
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/zitadel/logging v0.7.0 h1:eugftwMM95Wgqwftsvj81isL0JK/hoScVqp/7iA2adQ=
|
||||||
|
github.com/zitadel/logging v0.7.0/go.mod h1:9A6h9feBF/3u0IhA4uffdzSDY7mBaf7RE78H5sFMINQ=
|
||||||
|
github.com/zitadel/oidc/v3 v3.45.5 h1:CubfcXQiqtysk+FZyIcvj1+1ayvdSV89v5xWu5asrDQ=
|
||||||
|
github.com/zitadel/oidc/v3 v3.45.5/go.mod h1:MKHUazeiNX/jxRc6HD/Dv9qhL/wNuzrJAadBEGXiBeE=
|
||||||
|
github.com/zitadel/schema v1.3.2 h1:gfJvt7dOMfTmxzhscZ9KkapKo3Nei3B6cAxjav+lyjI=
|
||||||
|
github.com/zitadel/schema v1.3.2/go.mod h1:IZmdfF9Wu62Zu6tJJTH3UsArevs3Y4smfJIj3L8fzxw=
|
||||||
|
github.com/zitadel/zitadel-go/v3 v3.27.0 h1:1BumImnIk3D9JYTq+IlVq793vZCXuMZuz1meWzeMQN4=
|
||||||
|
github.com/zitadel/zitadel-go/v3 v3.27.0/go.mod h1:8UaWIIUR+c9jstT6bjoiknaxttxFZKNc7RYs32v03jw=
|
||||||
|
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||||
|
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||||
|
go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
|
||||||
|
go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=
|
||||||
|
go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=
|
||||||
|
go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
|
||||||
|
go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
|
||||||
|
go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
|
||||||
|
go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
|
||||||
|
go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=
|
||||||
|
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/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
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.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
|
||||||
|
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
|
||||||
|
golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ=
|
||||||
|
golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||||
|
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.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
|
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
|
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.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||||
|
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
|
golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=
|
||||||
|
golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
|
||||||
|
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.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
|
||||||
|
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
|
||||||
|
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
|
||||||
|
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
|
||||||
|
golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
|
||||||
|
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=
|
||||||
|
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||||
|
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 h1:JLQynH/LBHfCTSbDWl+py8C+Rg/k1OVH3xfcaiANuF0=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:kSJwQxqmFXeo79zOmbrALdflXQeAYcUbgS7PbpMknCY=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||||
|
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
|
||||||
|
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||||
|
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||||
|
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
|
||||||
|
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
|
||||||
|
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||||
|
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls=
|
||||||
|
k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k=
|
||||||
|
k8s.io/apiextensions-apiserver v0.32.2 h1:2YMk285jWMk2188V2AERy5yDwBYrjgWYggscghPCvV4=
|
||||||
|
k8s.io/apiextensions-apiserver v0.32.2/go.mod h1:GPwf8sph7YlJT3H6aKUWtd0E+oyShk/YHWQHf/OOgCA=
|
||||||
|
k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U=
|
||||||
|
k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
|
||||||
|
k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU=
|
||||||
|
k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY=
|
||||||
|
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-20241212222426-2c72e554b1e7 h1:hcha5B1kVACrLujCKLbr8XWMxCxzQx42DY8QKYJrDLg=
|
||||||
|
k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7/go.mod h1:GewRfANuJ70iYzvn+i4lezLDAFzvjxZYK1gn1lWcfas=
|
||||||
|
k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0=
|
||||||
|
k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||||
|
sigs.k8s.io/controller-runtime v0.20.4 h1:X3c+Odnxz+iPTRobG4tp092+CvBU9UK0t/bRf+n0DGU=
|
||||||
|
sigs.k8s.io/controller-runtime v0.20.4/go.mod h1:xg2XB0K5ShQzAgsoujxuKN4LNXR2LfwwHsPj7Iaw+XY=
|
||||||
|
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
|
||||||
|
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
|
||||||
|
sigs.k8s.io/structured-merge-diff/v4 v4.5.0 h1:nbCitCK2hfnhyiKo6uf2HxUPTCodY6Qaf85SbDIaMBk=
|
||||||
|
sigs.k8s.io/structured-merge-diff/v4 v4.5.0/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
|
||||||
|
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||||
|
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||||
15
src/hack/boilerplate.go.txt
Normal file
15
src/hack/boilerplate.go.txt
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2024.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
192
src/internal/controller/action_controller.gold
Normal file
192
src/internal/controller/action_controller.gold
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2024.
|
||||||
|
|
||||||
|
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"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
zitadelv1alpha1 "github.com/HaimKortovich/zitadel-k8s-operator/api/v1alpha1"
|
||||||
|
"github.com/HaimKortovich/zitadel-k8s-operator/pkg/builder"
|
||||||
|
condition "github.com/HaimKortovich/zitadel-k8s-operator/pkg/condition"
|
||||||
|
"github.com/HaimKortovich/zitadel-k8s-operator/pkg/controller/zitadel"
|
||||||
|
"github.com/zitadel/zitadel-go/v3/pkg/client/management"
|
||||||
|
"github.com/zitadel/zitadel-go/v3/pkg/client/middleware"
|
||||||
|
pb "github.com/zitadel/zitadel-go/v3/pkg/client/zitadel/management"
|
||||||
|
durationpb "google.golang.org/protobuf/types/known/durationpb"
|
||||||
|
"k8s.io/client-go/util/workqueue"
|
||||||
|
ctrl "sigs.k8s.io/controller-runtime"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
ctrlClient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ActionReconciler reconciles a Action object
|
||||||
|
type ActionReconciler struct {
|
||||||
|
client.Client
|
||||||
|
RefResolver *zitadelv1alpha1.RefResolver
|
||||||
|
ConditionReady *condition.Ready
|
||||||
|
RequeueInterval time.Duration
|
||||||
|
Builder *builder.Builder
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewActionReconciler(client client.Client, refResolver *zitadelv1alpha1.RefResolver, builder *builder.Builder, conditionReady *condition.Ready,
|
||||||
|
requeueInterval time.Duration) *ActionReconciler {
|
||||||
|
return &ActionReconciler{
|
||||||
|
Client: client,
|
||||||
|
RefResolver: refResolver,
|
||||||
|
ConditionReady: conditionReady,
|
||||||
|
RequeueInterval: requeueInterval,
|
||||||
|
Builder: builder,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//+kubebuilder:rbac:groups=zitadel.topmanage.com,resources=actions,verbs=get;list;watch;create;update;patch;delete
|
||||||
|
//+kubebuilder:rbac:groups=zitadel.topmanage.com,resources=actions/status,verbs=get;update;patch
|
||||||
|
//+kubebuilder:rbac:groups=zitadel.topmanage.com,resources=actions/finalizers,verbs=update
|
||||||
|
|
||||||
|
// Reconcile is part of the main kubernetes reconciliation loop which aims to
|
||||||
|
// move the current state of the cluster closer to the desired state.
|
||||||
|
func (r *ActionReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||||
|
var Action zitadelv1alpha1.Action
|
||||||
|
if err := r.Get(ctx, req.NamespacedName, &Action); err != nil {
|
||||||
|
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||||
|
}
|
||||||
|
wr := newWrappedActionReconciler(r.Client, r.RefResolver, r.Builder, &Action)
|
||||||
|
wf := newWrappedActionFinalizer(r.Client, &Action, r.RefResolver)
|
||||||
|
tf := zitadel.NewZitadelFinalizer(r.Client, wf)
|
||||||
|
tr := zitadel.NewZitadelReconciler(r.Client, r.ConditionReady, wr, tf, r.RequeueInterval)
|
||||||
|
|
||||||
|
result, err := tr.Reconcile(ctx, &Action)
|
||||||
|
if err != nil {
|
||||||
|
return result, fmt.Errorf("error reconciling in ActionReconciler: %v", err)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type wrappedActionReconciler struct {
|
||||||
|
client.Client
|
||||||
|
refResolver *zitadelv1alpha1.RefResolver
|
||||||
|
Action *zitadelv1alpha1.Action
|
||||||
|
Builder *builder.Builder
|
||||||
|
}
|
||||||
|
|
||||||
|
func newWrappedActionReconciler(client client.Client, refResolver *zitadelv1alpha1.RefResolver, builder *builder.Builder,
|
||||||
|
Action *zitadelv1alpha1.Action) zitadel.WrappedReconciler {
|
||||||
|
return &wrappedActionReconciler{
|
||||||
|
Client: client,
|
||||||
|
refResolver: refResolver,
|
||||||
|
Action: Action,
|
||||||
|
Builder: builder,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type actionReoncilePhase struct {
|
||||||
|
Name string
|
||||||
|
Reconcile func(context.Context, *management.Client) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedActionReconciler) Reconcile(ctx context.Context, ztdClient *management.Client) error {
|
||||||
|
phases := []actionReoncilePhase{
|
||||||
|
{
|
||||||
|
Name: "action",
|
||||||
|
Reconcile: wr.reconcileAction,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, p := range phases {
|
||||||
|
err := p.Reconcile(ctx, ztdClient)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedActionReconciler) reconcileAction(ctx context.Context, ztdClient *management.Client) error {
|
||||||
|
org, err := wr.refResolver.OrganizationRef(ctx, &wr.Action.Spec.OrganizationRef, wr.Action.Namespace)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ctx = middleware.SetOrgID(ctx, org.Status.OrgId)
|
||||||
|
|
||||||
|
if wr.Action.Status.ActionId != "" {
|
||||||
|
p, err := ztdClient.GetAction(ctx, &pb.GetActionRequest{Id: wr.Action.Status.ActionId})
|
||||||
|
if p != nil {
|
||||||
|
_, err := ztdClient.UpdateAction(ctx,
|
||||||
|
&pb.UpdateActionRequest{
|
||||||
|
Id: p.Action.Id,
|
||||||
|
Name: wr.Action.Name,
|
||||||
|
Script: wr.Action.Spec.Script,
|
||||||
|
Timeout: durationpb.New(wr.Action.Spec.Timeout.Duration),
|
||||||
|
AllowedToFail: wr.Action.Spec.AllowedToFail,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if !strings.Contains(err.Error(), "No changes") {
|
||||||
|
return fmt.Errorf("Error updating Action: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
if !strings.Contains(err.Error(), "not found") {
|
||||||
|
return fmt.Errorf("Error getting Action: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := ztdClient.CreateAction(ctx,
|
||||||
|
&pb.CreateActionRequest{
|
||||||
|
Name: wr.Action.Name,
|
||||||
|
Script: wr.Action.Spec.Script,
|
||||||
|
Timeout: durationpb.New(wr.Action.Spec.Timeout.Duration),
|
||||||
|
AllowedToFail: wr.Action.Spec.AllowedToFail,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if strings.Contains(err.Error(), "AlreadyExists") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("error creating action in Zitadel: %v", err)
|
||||||
|
}
|
||||||
|
patch := ctrlClient.MergeFrom(wr.Action.DeepCopy())
|
||||||
|
wr.Action.Status.ActionId = resp.Id
|
||||||
|
return wr.Client.Status().Patch(ctx, wr.Action, patch)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedActionReconciler) PatchStatus(ctx context.Context, patcher condition.Patcher) error {
|
||||||
|
patch := client.MergeFrom(wr.Action.DeepCopy())
|
||||||
|
patcher(&wr.Action.Status)
|
||||||
|
|
||||||
|
if err := wr.Client.Status().Patch(ctx, wr.Action, patch); err != nil {
|
||||||
|
return fmt.Errorf("error patching Action status: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetupWithManager sets up the controller with the Manager.
|
||||||
|
func (r *ActionReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||||
|
return ctrl.NewControllerManagedBy(mgr).
|
||||||
|
For(&zitadelv1alpha1.Action{}).
|
||||||
|
WithOptions(controller.Options{RateLimiter: workqueue.NewTypedItemExponentialFailureRateLimiter[reconcile.Request](time.Millisecond*500, time.Minute*3)}).
|
||||||
|
Complete(r)
|
||||||
|
}
|
||||||
94
src/internal/controller/action_controller_finalizer.gold
Normal file
94
src/internal/controller/action_controller_finalizer.gold
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
package controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
zitadelv1alpha1 "github.com/HaimKortovich/zitadel-k8s-operator/api/v1alpha1"
|
||||||
|
"github.com/HaimKortovich/zitadel-k8s-operator/pkg/controller/zitadel"
|
||||||
|
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel-go/v3/pkg/client/management"
|
||||||
|
"github.com/zitadel/zitadel-go/v3/pkg/client/middleware"
|
||||||
|
pb "github.com/zitadel/zitadel-go/v3/pkg/client/zitadel/management"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
ctrlClient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
actionFinalizerName = "action.zitadel.topmanage.com/action"
|
||||||
|
)
|
||||||
|
|
||||||
|
type wrappedActionFinalizer struct {
|
||||||
|
client.Client
|
||||||
|
action *zitadelv1alpha1.Action
|
||||||
|
refresolver *zitadelv1alpha1.RefResolver
|
||||||
|
}
|
||||||
|
|
||||||
|
func newWrappedActionFinalizer(client client.Client, action *zitadelv1alpha1.Action, refresolver *zitadelv1alpha1.RefResolver) zitadel.WrappedFinalizer {
|
||||||
|
return &wrappedActionFinalizer{
|
||||||
|
Client: client,
|
||||||
|
action: action,
|
||||||
|
refresolver: refresolver,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wf *wrappedActionFinalizer) AddFinalizer(ctx context.Context) error {
|
||||||
|
if wf.ContainsFinalizer() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return wf.patch(ctx, wf.action, func(action *zitadelv1alpha1.Action) {
|
||||||
|
controllerutil.AddFinalizer(action, actionFinalizerName)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wf *wrappedActionFinalizer) RemoveFinalizer(ctx context.Context) error {
|
||||||
|
if !wf.ContainsFinalizer() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return wf.patch(ctx, wf.action, func(action *zitadelv1alpha1.Action) {
|
||||||
|
controllerutil.RemoveFinalizer(wf.action, actionFinalizerName)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedActionFinalizer) ContainsFinalizer() bool {
|
||||||
|
return controllerutil.ContainsFinalizer(wr.action, actionFinalizerName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wf *wrappedActionFinalizer) Reconcile(ctx context.Context, ztdClient *management.Client) error {
|
||||||
|
if wf.action.Status.ActionId == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
org, err := wf.refresolver.OrganizationRef(ctx, &wf.action.Spec.OrganizationRef, wf.action.Namespace)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
{
|
||||||
|
|
||||||
|
_, err := ztdClient.GetAction(middleware.SetOrgID(ctx, org.Status.OrgId), &pb.GetActionRequest{Id: wf.action.Status.ActionId})
|
||||||
|
if err != nil {
|
||||||
|
if strings.Contains(err.Error(), `not found`) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, err = ztdClient.DeleteAction(middleware.SetOrgID(ctx, org.Status.OrgId), &pb.DeleteActionRequest{Id: wf.action.Status.ActionId})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedActionFinalizer) patch(ctx context.Context, action *zitadelv1alpha1.Action,
|
||||||
|
patchFn func(*zitadelv1alpha1.Action)) error {
|
||||||
|
patch := ctrlClient.MergeFrom(action.DeepCopy())
|
||||||
|
patchFn(action)
|
||||||
|
|
||||||
|
if err := wr.Client.Patch(ctx, action, patch); err != nil {
|
||||||
|
return fmt.Errorf("error patching Action finalizer: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
282
src/internal/controller/apiapp_controller.gold
Normal file
282
src/internal/controller/apiapp_controller.gold
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2024.
|
||||||
|
|
||||||
|
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"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
zitadelv1alpha1 "github.com/HaimKortovich/zitadel-k8s-operator/api/v1alpha1"
|
||||||
|
"github.com/HaimKortovich/zitadel-k8s-operator/pkg/builder"
|
||||||
|
condition "github.com/HaimKortovich/zitadel-k8s-operator/pkg/condition"
|
||||||
|
"github.com/HaimKortovich/zitadel-k8s-operator/pkg/controller/zitadel"
|
||||||
|
"github.com/zitadel/zitadel-go/v3/pkg/client/management"
|
||||||
|
"github.com/zitadel/zitadel-go/v3/pkg/client/middleware"
|
||||||
|
app "github.com/zitadel/zitadel-go/v3/pkg/client/zitadel/app"
|
||||||
|
"github.com/zitadel/zitadel-go/v3/pkg/client/zitadel/authn"
|
||||||
|
pb "github.com/zitadel/zitadel-go/v3/pkg/client/zitadel/management"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
"k8s.io/client-go/util/workqueue"
|
||||||
|
ctrl "sigs.k8s.io/controller-runtime"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
ctrlClient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||||
|
)
|
||||||
|
|
||||||
|
// APIAppReconciler reconciles a APIApp object
|
||||||
|
type APIAppReconciler struct {
|
||||||
|
client.Client
|
||||||
|
RefResolver *zitadelv1alpha1.RefResolver
|
||||||
|
ConditionReady *condition.Ready
|
||||||
|
RequeueInterval time.Duration
|
||||||
|
Builder *builder.Builder
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAPIAppReconciler(client client.Client, refResolver *zitadelv1alpha1.RefResolver, builder *builder.Builder, conditionReady *condition.Ready,
|
||||||
|
requeueInterval time.Duration) *APIAppReconciler {
|
||||||
|
return &APIAppReconciler{
|
||||||
|
Client: client,
|
||||||
|
RefResolver: refResolver,
|
||||||
|
ConditionReady: conditionReady,
|
||||||
|
RequeueInterval: requeueInterval,
|
||||||
|
Builder: builder,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//+kubebuilder:rbac:groups=zitadel.topmanage.com,resources=apiapps,verbs=get;list;watch;create;update;patch;delete
|
||||||
|
//+kubebuilder:rbac:groups=zitadel.topmanage.com,resources=apiapps/status,verbs=get;update;patch
|
||||||
|
//+kubebuilder:rbac:groups=zitadel.topmanage.com,resources=apiapps/finalizers,verbs=update
|
||||||
|
|
||||||
|
// Reconcile is part of the main kubernetes reconciliation loop which aims to
|
||||||
|
// move the current state of the cluster closer to the desired state.
|
||||||
|
func (r *APIAppReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||||
|
var APIApp zitadelv1alpha1.APIApp
|
||||||
|
if err := r.Get(ctx, req.NamespacedName, &APIApp); err != nil {
|
||||||
|
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||||
|
}
|
||||||
|
wr := newWrappedAPIAppReconciler(r.Client, r.RefResolver, r.Builder, &APIApp)
|
||||||
|
wf := newWrappedAPIAppFinalizer(r.Client, &APIApp, r.RefResolver)
|
||||||
|
tf := zitadel.NewZitadelFinalizer(r.Client, wf)
|
||||||
|
tr := zitadel.NewZitadelReconciler(r.Client, r.ConditionReady, wr, tf, r.RequeueInterval)
|
||||||
|
|
||||||
|
result, err := tr.Reconcile(ctx, &APIApp)
|
||||||
|
if err != nil {
|
||||||
|
return result, fmt.Errorf("error reconciling in APIAppReconciler: %v", err)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type wrappedAPIAppReconciler struct {
|
||||||
|
client.Client
|
||||||
|
refResolver *zitadelv1alpha1.RefResolver
|
||||||
|
APIApp *zitadelv1alpha1.APIApp
|
||||||
|
Builder *builder.Builder
|
||||||
|
}
|
||||||
|
|
||||||
|
func newWrappedAPIAppReconciler(client client.Client, refResolver *zitadelv1alpha1.RefResolver, builder *builder.Builder,
|
||||||
|
APIApp *zitadelv1alpha1.APIApp) zitadel.WrappedReconciler {
|
||||||
|
return &wrappedAPIAppReconciler{
|
||||||
|
Client: client,
|
||||||
|
refResolver: refResolver,
|
||||||
|
APIApp: APIApp,
|
||||||
|
Builder: builder,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type apiAppReoncilePhase struct {
|
||||||
|
Name string
|
||||||
|
Reconcile func(context.Context, *management.Client) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedAPIAppReconciler) Reconcile(ctx context.Context, ztdClient *management.Client) error {
|
||||||
|
phases := []projectReconcilePhase{
|
||||||
|
{
|
||||||
|
Name: "apiapp",
|
||||||
|
Reconcile: wr.reconcileApp,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "keys",
|
||||||
|
Reconcile: wr.reconcileKeys,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, p := range phases {
|
||||||
|
err := p.Reconcile(ctx, ztdClient)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedAPIAppReconciler) reconcileApp(ctx context.Context, ztdClient *management.Client) error {
|
||||||
|
org, err := wr.APIApp.Organization(ctx, wr.refResolver)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
project, err := wr.APIApp.Project(ctx, wr.refResolver)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if wr.APIApp.Status.AppId != "" {
|
||||||
|
appResp, err := ztdClient.GetAppByID(middleware.SetOrgID(ctx, org.Status.OrgId), &pb.GetAppByIDRequest{
|
||||||
|
ProjectId: project.Status.ProjectId,
|
||||||
|
AppId: string(wr.APIApp.Status.AppId),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error getting APIApp: %v", err)
|
||||||
|
}
|
||||||
|
if appResp.App != nil {
|
||||||
|
_, err := ztdClient.UpdateAPIAppConfig(middleware.SetOrgID(ctx, org.Status.OrgId), &pb.UpdateAPIAppConfigRequest{ProjectId: project.Status.ProjectId, AppId: wr.APIApp.Status.AppId,
|
||||||
|
AuthMethodType: app.APIAuthMethodType(app.APIAuthMethodType_value[wr.APIApp.Spec.AuthMethodType]),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
if !strings.Contains(err.Error(), "No changes") {
|
||||||
|
return fmt.Errorf("Error updating APIApp: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := ztdClient.AddAPIApp(middleware.SetOrgID(ctx, org.Status.OrgId),
|
||||||
|
&pb.AddAPIAppRequest{
|
||||||
|
Name: wr.APIApp.Name,
|
||||||
|
ProjectId: project.Status.ProjectId,
|
||||||
|
AuthMethodType: app.APIAuthMethodType(app.APIAuthMethodType_value[wr.APIApp.Spec.AuthMethodType]),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
if strings.Contains(err.Error(), "AlreadyExists") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("error creating APIApp in Zitadel: %v", err)
|
||||||
|
}
|
||||||
|
key := types.NamespacedName{
|
||||||
|
Name: wr.APIApp.Name + "-client-secret",
|
||||||
|
Namespace: wr.APIApp.Namespace,
|
||||||
|
}
|
||||||
|
|
||||||
|
secretData := map[string][]byte{"client-secret": []byte(resp.ClientSecret)}
|
||||||
|
secret, err := wr.Builder.BuildSecret(builder.SecretOpts{Immutable: false, Zitadel: nil, Key: key, Data: secretData}, wr.APIApp)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error building Secret: %v", err)
|
||||||
|
}
|
||||||
|
if err := wr.Create(ctx, secret); err != nil {
|
||||||
|
return fmt.Errorf("error creating Client-secret Secret: %v", err)
|
||||||
|
}
|
||||||
|
patch := ctrlClient.MergeFrom(wr.APIApp.DeepCopy())
|
||||||
|
wr.APIApp.Status.AppId = resp.AppId
|
||||||
|
wr.APIApp.Status.ClientId = resp.ClientId
|
||||||
|
return wr.Client.Status().Patch(ctx, wr.APIApp, patch)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Key struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
KeyID string `json:"keyId"`
|
||||||
|
Key string `json:"key"`
|
||||||
|
AppID string `json:"appId"`
|
||||||
|
ClientID string `json:"clientId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedAPIAppReconciler) reconcileKeys(ctx context.Context, ztdClient *management.Client) error {
|
||||||
|
if wr.APIApp.Spec.AuthMethodType == "API_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT" {
|
||||||
|
org, err := wr.APIApp.Organization(ctx, wr.refResolver)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
project, err := wr.APIApp.Project(ctx, wr.refResolver)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if wr.APIApp.Status.KeyId != "" {
|
||||||
|
appKey, err := ztdClient.GetAppKey(middleware.SetOrgID(ctx, org.Status.OrgId), &pb.GetAppKeyRequest{
|
||||||
|
ProjectId: project.Status.ProjectId,
|
||||||
|
AppId: wr.APIApp.Status.AppId,
|
||||||
|
KeyId: wr.APIApp.Status.KeyId,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
if !strings.Contains(err.Error(), "not found") {
|
||||||
|
return fmt.Errorf("Could not get key: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if appKey.Key != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resp, err := ztdClient.AddAppKey(middleware.SetOrgID(ctx, org.Status.OrgId), &pb.AddAppKeyRequest{
|
||||||
|
ProjectId: project.Status.ProjectId,
|
||||||
|
AppId: wr.APIApp.Status.AppId,
|
||||||
|
Type: authn.KeyType_KEY_TYPE_JSON,
|
||||||
|
ExpirationDate: nil,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error adding Key to app: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
key := types.NamespacedName{
|
||||||
|
Name: wr.APIApp.Name + "-privatekey-secret",
|
||||||
|
Namespace: wr.APIApp.Namespace,
|
||||||
|
}
|
||||||
|
var jsonKey Key
|
||||||
|
if err = json.Unmarshal(resp.KeyDetails, &jsonKey); err != nil {
|
||||||
|
return fmt.Errorf("Could not unmarshal key details: %v", err)
|
||||||
|
}
|
||||||
|
secretData := map[string][]byte{
|
||||||
|
"clientId": []byte(jsonKey.ClientID),
|
||||||
|
"type": []byte(jsonKey.Type),
|
||||||
|
"keyId": []byte(jsonKey.KeyID),
|
||||||
|
"appId": []byte(jsonKey.AppID),
|
||||||
|
"key": []byte(jsonKey.Key),
|
||||||
|
}
|
||||||
|
secret, err := wr.Builder.BuildSecret(builder.SecretOpts{Immutable: false, Zitadel: nil, Key: key, Data: secretData}, wr.APIApp)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error building Secret: %v", err)
|
||||||
|
}
|
||||||
|
if err := wr.Create(ctx, secret); err != nil {
|
||||||
|
return fmt.Errorf("error creating private-key Secret: %v", err)
|
||||||
|
}
|
||||||
|
patch := ctrlClient.MergeFrom(wr.APIApp.DeepCopy())
|
||||||
|
wr.APIApp.Status.KeyId = resp.Id
|
||||||
|
return wr.Client.Status().Patch(ctx, wr.APIApp, patch)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedAPIAppReconciler) PatchStatus(ctx context.Context, patcher condition.Patcher) error {
|
||||||
|
patch := client.MergeFrom(wr.APIApp.DeepCopy())
|
||||||
|
patcher(&wr.APIApp.Status)
|
||||||
|
|
||||||
|
if err := wr.Client.Status().Patch(ctx, wr.APIApp, patch); err != nil {
|
||||||
|
return fmt.Errorf("error patching APIApp status: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetupWithManager sets up the controller with the Manager.
|
||||||
|
func (r *APIAppReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||||
|
return ctrl.NewControllerManagedBy(mgr).
|
||||||
|
For(&zitadelv1alpha1.APIApp{}).
|
||||||
|
Owns(&corev1.Secret{}).
|
||||||
|
WithOptions(controller.Options{RateLimiter: workqueue.NewTypedItemExponentialFailureRateLimiter[reconcile.Request](time.Millisecond*500, time.Minute*3)}).
|
||||||
|
Complete(r)
|
||||||
|
}
|
||||||
91
src/internal/controller/apiapp_controller_finalizer.gold
Normal file
91
src/internal/controller/apiapp_controller_finalizer.gold
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
package controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
zitadelv1alpha1 "github.com/HaimKortovich/zitadel-k8s-operator/api/v1alpha1"
|
||||||
|
"github.com/HaimKortovich/zitadel-k8s-operator/pkg/controller/zitadel"
|
||||||
|
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel-go/v3/pkg/client/management"
|
||||||
|
"github.com/zitadel/zitadel-go/v3/pkg/client/middleware"
|
||||||
|
pb "github.com/zitadel/zitadel-go/v3/pkg/client/zitadel/management"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
ctrlClient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
APIAppFinalizerName = "apiapp.zitadel.topmanage.com/apiapp"
|
||||||
|
)
|
||||||
|
|
||||||
|
type wrappedAPIAppFinalizer struct {
|
||||||
|
client.Client
|
||||||
|
APIApp *zitadelv1alpha1.APIApp
|
||||||
|
refresolver *zitadelv1alpha1.RefResolver
|
||||||
|
}
|
||||||
|
|
||||||
|
func newWrappedAPIAppFinalizer(client client.Client, APIApp *zitadelv1alpha1.APIApp, refresolver *zitadelv1alpha1.RefResolver) zitadel.WrappedFinalizer {
|
||||||
|
return &wrappedAPIAppFinalizer{
|
||||||
|
Client: client,
|
||||||
|
APIApp: APIApp,
|
||||||
|
refresolver: refresolver,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wf *wrappedAPIAppFinalizer) AddFinalizer(ctx context.Context) error {
|
||||||
|
if wf.ContainsFinalizer() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return wf.patch(ctx, wf.APIApp, func(APIApp *zitadelv1alpha1.APIApp) {
|
||||||
|
controllerutil.AddFinalizer(APIApp, APIAppFinalizerName)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wf *wrappedAPIAppFinalizer) RemoveFinalizer(ctx context.Context) error {
|
||||||
|
if !wf.ContainsFinalizer() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return wf.patch(ctx, wf.APIApp, func(APIApp *zitadelv1alpha1.APIApp) {
|
||||||
|
controllerutil.RemoveFinalizer(wf.APIApp, APIAppFinalizerName)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedAPIAppFinalizer) ContainsFinalizer() bool {
|
||||||
|
return controllerutil.ContainsFinalizer(wr.APIApp, APIAppFinalizerName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wf *wrappedAPIAppFinalizer) Reconcile(ctx context.Context, ztdClient *management.Client) error {
|
||||||
|
if wf.APIApp.Status.AppId == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
org, err := wf.APIApp.Organization(ctx, wf.refresolver)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
project, err := wf.APIApp.Project(ctx, wf.refresolver)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = ztdClient.RemoveApp(middleware.SetOrgID(ctx, org.Status.OrgId), &pb.RemoveAppRequest{ProjectId: project.Status.ProjectId, AppId: wf.APIApp.Status.AppId})
|
||||||
|
if err != nil {
|
||||||
|
if strings.Contains(err.Error(), "doesn't exist") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedAPIAppFinalizer) patch(ctx context.Context, APIApp *zitadelv1alpha1.APIApp,
|
||||||
|
patchFn func(*zitadelv1alpha1.APIApp)) error {
|
||||||
|
patch := ctrlClient.MergeFrom(APIApp.DeepCopy())
|
||||||
|
patchFn(APIApp)
|
||||||
|
|
||||||
|
if err := wr.Client.Patch(ctx, APIApp, patch); err != nil {
|
||||||
|
return fmt.Errorf("error patching APIApp finalizer: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
116
src/internal/controller/connection_controller.go
Normal file
116
src/internal/controller/connection_controller.go
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2024.
|
||||||
|
|
||||||
|
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"
|
||||||
|
|
||||||
|
zitadelv1alpha1 "github.com/HaimKortovich/zitadel-resources-operator/api/v1alpha1"
|
||||||
|
"github.com/HaimKortovich/zitadel-resources-operator/pkg/builder"
|
||||||
|
condition "github.com/HaimKortovich/zitadel-resources-operator/pkg/condition"
|
||||||
|
"github.com/HaimKortovich/zitadel-resources-operator/pkg/controller/core"
|
||||||
|
clientv2 "github.com/zitadel/zitadel-go/v3/pkg/client"
|
||||||
|
"k8s.io/client-go/util/workqueue"
|
||||||
|
ctrl "sigs.k8s.io/controller-runtime"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ConnectionReconciler reconciles a Connection object
|
||||||
|
type ConnectionReconciler struct {
|
||||||
|
client.Client
|
||||||
|
RefResolver *zitadelv1alpha1.RefResolver
|
||||||
|
ConditionReady *condition.Ready
|
||||||
|
RequeueInterval time.Duration
|
||||||
|
Builder *builder.Builder
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConnectionReconciler(client client.Client, refResolver *zitadelv1alpha1.RefResolver, builder *builder.Builder, conditionReady *condition.Ready,
|
||||||
|
requeueInterval time.Duration) *ConnectionReconciler {
|
||||||
|
return &ConnectionReconciler{
|
||||||
|
Client: client,
|
||||||
|
RefResolver: refResolver,
|
||||||
|
ConditionReady: conditionReady,
|
||||||
|
RequeueInterval: requeueInterval,
|
||||||
|
Builder: builder,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//+kubebuilder:rbac:groups=zitadel.github.com,resources=connections,verbs=get;list;watch;create;update;patch;delete
|
||||||
|
//+kubebuilder:rbac:groups=zitadel.github.com,resources=connections/status,verbs=get;update;patch
|
||||||
|
//+kubebuilder:rbac:groups=zitadel.github.com,resources=connections/finalizers,verbs=update
|
||||||
|
|
||||||
|
// Reconcile is part of the main kubernetes reconciliation loop which aims to
|
||||||
|
// move the current state of the cluster closer to the desired state.
|
||||||
|
func (r *ConnectionReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||||
|
var Connection zitadelv1alpha1.Connection
|
||||||
|
if err := r.Get(ctx, req.NamespacedName, &Connection); err != nil {
|
||||||
|
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||||
|
}
|
||||||
|
wr := newWrappedConnectionReconciler(r.Client, r.RefResolver, r.Builder, &Connection)
|
||||||
|
wf := newWrappedConnectionFinalizer(r.Client, &Connection, r.RefResolver)
|
||||||
|
tf := core.NewCoreFinalizer(r.Client, wf)
|
||||||
|
tr := core.NewCoreReconciler(r.Client, r.ConditionReady, wr, tf, r.RequeueInterval)
|
||||||
|
|
||||||
|
result, err := tr.Reconcile(ctx, &Connection)
|
||||||
|
if err != nil {
|
||||||
|
return result, fmt.Errorf("error reconciling in ConnectionReconciler: %v", err)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type wrappedConnectionReconciler struct {
|
||||||
|
client.Client
|
||||||
|
refResolver *zitadelv1alpha1.RefResolver
|
||||||
|
Connection *zitadelv1alpha1.Connection
|
||||||
|
Builder *builder.Builder
|
||||||
|
}
|
||||||
|
|
||||||
|
func newWrappedConnectionReconciler(client client.Client, refResolver *zitadelv1alpha1.RefResolver, builder *builder.Builder,
|
||||||
|
Connection *zitadelv1alpha1.Connection) core.WrappedCoreReconciler {
|
||||||
|
return &wrappedConnectionReconciler{
|
||||||
|
Client: client,
|
||||||
|
refResolver: refResolver,
|
||||||
|
Connection: Connection,
|
||||||
|
Builder: builder,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedConnectionReconciler) Reconcile(ctx context.Context, ztdClient *clientv2.Client) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedConnectionReconciler) PatchStatus(ctx context.Context, patcher condition.Patcher) error {
|
||||||
|
patch := client.MergeFrom(wr.Connection.DeepCopy())
|
||||||
|
patcher(&wr.Connection.Status)
|
||||||
|
|
||||||
|
if err := wr.Client.Status().Patch(ctx, wr.Connection, patch); err != nil {
|
||||||
|
return fmt.Errorf("error patching Connection status: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetupWithManager sets up the controller with the Manager.
|
||||||
|
func (r *ConnectionReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||||
|
return ctrl.NewControllerManagedBy(mgr).
|
||||||
|
For(&zitadelv1alpha1.Connection{}).
|
||||||
|
WithOptions(controller.Options{RateLimiter: workqueue.NewTypedItemExponentialFailureRateLimiter[reconcile.Request](time.Millisecond*500, time.Minute*3)}).
|
||||||
|
Complete(r)
|
||||||
|
}
|
||||||
69
src/internal/controller/connection_controller_finalizer.go
Normal file
69
src/internal/controller/connection_controller_finalizer.go
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
package controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
zitadelv1alpha1 "github.com/HaimKortovich/zitadel-resources-operator/api/v1alpha1"
|
||||||
|
"github.com/HaimKortovich/zitadel-resources-operator/pkg/controller/core"
|
||||||
|
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
clientv2 "github.com/zitadel/zitadel-go/v3/pkg/client"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
ctrlClient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ConnectionFinalizerName = "connection.zitadel.github.com/connection"
|
||||||
|
)
|
||||||
|
|
||||||
|
type wrappedConnectionFinalizer struct {
|
||||||
|
client.Client
|
||||||
|
Connection *zitadelv1alpha1.Connection
|
||||||
|
refresolver *zitadelv1alpha1.RefResolver
|
||||||
|
}
|
||||||
|
|
||||||
|
func newWrappedConnectionFinalizer(client client.Client, Connection *zitadelv1alpha1.Connection, refresolver *zitadelv1alpha1.RefResolver) core.WrappedCoreFinalizer {
|
||||||
|
return &wrappedConnectionFinalizer{
|
||||||
|
Client: client,
|
||||||
|
Connection: Connection,
|
||||||
|
refresolver: refresolver,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wf *wrappedConnectionFinalizer) AddFinalizer(ctx context.Context) error {
|
||||||
|
if wf.ContainsFinalizer() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return wf.patch(ctx, wf.Connection, func(Connection *zitadelv1alpha1.Connection) {
|
||||||
|
controllerutil.AddFinalizer(Connection, ConnectionFinalizerName)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wf *wrappedConnectionFinalizer) RemoveFinalizer(ctx context.Context) error {
|
||||||
|
if !wf.ContainsFinalizer() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return wf.patch(ctx, wf.Connection, func(Connection *zitadelv1alpha1.Connection) {
|
||||||
|
controllerutil.RemoveFinalizer(wf.Connection, ConnectionFinalizerName)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedConnectionFinalizer) ContainsFinalizer() bool {
|
||||||
|
return controllerutil.ContainsFinalizer(wr.Connection, ConnectionFinalizerName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wf *wrappedConnectionFinalizer) Reconcile(ctx context.Context, ztdClient *clientv2.Client) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedConnectionFinalizer) patch(ctx context.Context, Connection *zitadelv1alpha1.Connection,
|
||||||
|
patchFn func(*zitadelv1alpha1.Connection)) error {
|
||||||
|
patch := ctrlClient.MergeFrom(Connection.DeepCopy())
|
||||||
|
patchFn(Connection)
|
||||||
|
|
||||||
|
if err := wr.Client.Patch(ctx, Connection, patch); err != nil {
|
||||||
|
return fmt.Errorf("error patching Connection finalizer: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
168
src/internal/controller/flow_controller.gold
Normal file
168
src/internal/controller/flow_controller.gold
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2024.
|
||||||
|
|
||||||
|
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"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
zitadelv1alpha1 "github.com/HaimKortovich/zitadel-k8s-operator/api/v1alpha1"
|
||||||
|
"github.com/HaimKortovich/zitadel-k8s-operator/pkg/builder"
|
||||||
|
condition "github.com/HaimKortovich/zitadel-k8s-operator/pkg/condition"
|
||||||
|
"github.com/HaimKortovich/zitadel-k8s-operator/pkg/controller/zitadel"
|
||||||
|
"github.com/zitadel/zitadel-go/v3/pkg/client/management"
|
||||||
|
"github.com/zitadel/zitadel-go/v3/pkg/client/middleware"
|
||||||
|
pb "github.com/zitadel/zitadel-go/v3/pkg/client/zitadel/management"
|
||||||
|
"k8s.io/client-go/util/workqueue"
|
||||||
|
ctrl "sigs.k8s.io/controller-runtime"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FlowReconciler reconciles a Flow object
|
||||||
|
type FlowReconciler struct {
|
||||||
|
client.Client
|
||||||
|
RefResolver *zitadelv1alpha1.RefResolver
|
||||||
|
ConditionReady *condition.Ready
|
||||||
|
RequeueInterval time.Duration
|
||||||
|
Builder *builder.Builder
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFlowReconciler(client client.Client, refResolver *zitadelv1alpha1.RefResolver, builder *builder.Builder, conditionReady *condition.Ready,
|
||||||
|
requeueInterval time.Duration) *FlowReconciler {
|
||||||
|
return &FlowReconciler{
|
||||||
|
Client: client,
|
||||||
|
RefResolver: refResolver,
|
||||||
|
ConditionReady: conditionReady,
|
||||||
|
RequeueInterval: requeueInterval,
|
||||||
|
Builder: builder,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//+kubebuilder:rbac:groups=zitadel.topmanage.com,resources=flows,verbs=get;list;watch;create;update;patch;delete
|
||||||
|
//+kubebuilder:rbac:groups=zitadel.topmanage.com,resources=flows/status,verbs=get;update;patch
|
||||||
|
//+kubebuilder:rbac:groups=zitadel.topmanage.com,resources=flows/finalizers,verbs=update
|
||||||
|
|
||||||
|
// Reconcile is part of the main kubernetes reconciliation loop which aims to
|
||||||
|
// move the current state of the cluster closer to the desired state.
|
||||||
|
func (r *FlowReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||||
|
var Flow zitadelv1alpha1.Flow
|
||||||
|
if err := r.Get(ctx, req.NamespacedName, &Flow); err != nil {
|
||||||
|
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||||
|
}
|
||||||
|
wr := newWrappedFlowReconciler(r.Client, r.RefResolver, r.Builder, &Flow)
|
||||||
|
wf := newWrappedFlowFinalizer(r.Client, &Flow, r.RefResolver)
|
||||||
|
tf := zitadel.NewZitadelFinalizer(r.Client, wf)
|
||||||
|
tr := zitadel.NewZitadelReconciler(r.Client, r.ConditionReady, wr, tf, r.RequeueInterval)
|
||||||
|
|
||||||
|
result, err := tr.Reconcile(ctx, &Flow)
|
||||||
|
if err != nil {
|
||||||
|
return result, fmt.Errorf("error reconciling in FlowReconciler: %v", err)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type wrappedFlowReconciler struct {
|
||||||
|
client.Client
|
||||||
|
refResolver *zitadelv1alpha1.RefResolver
|
||||||
|
Flow *zitadelv1alpha1.Flow
|
||||||
|
Builder *builder.Builder
|
||||||
|
}
|
||||||
|
|
||||||
|
func newWrappedFlowReconciler(client client.Client, refResolver *zitadelv1alpha1.RefResolver, builder *builder.Builder,
|
||||||
|
Flow *zitadelv1alpha1.Flow) zitadel.WrappedReconciler {
|
||||||
|
return &wrappedFlowReconciler{
|
||||||
|
Client: client,
|
||||||
|
refResolver: refResolver,
|
||||||
|
Flow: Flow,
|
||||||
|
Builder: builder,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type flowReoncilePhase struct {
|
||||||
|
Name string
|
||||||
|
Reconcile func(context.Context, *management.Client) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedFlowReconciler) Reconcile(ctx context.Context, ztdClient *management.Client) error {
|
||||||
|
phases := []flowReoncilePhase{
|
||||||
|
{
|
||||||
|
Name: "flow",
|
||||||
|
Reconcile: wr.reconcileFlow,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, p := range phases {
|
||||||
|
err := p.Reconcile(ctx, ztdClient)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedFlowReconciler) reconcileFlow(ctx context.Context, ztdClient *management.Client) error {
|
||||||
|
org, err := wr.refResolver.OrganizationRef(ctx, &wr.Flow.Spec.OrganizationRef, wr.Flow.Namespace)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ctx = middleware.SetOrgID(ctx, org.Status.OrgId)
|
||||||
|
|
||||||
|
actionIds := []string{}
|
||||||
|
for _, actionRef := range wr.Flow.Spec.ActionRefs {
|
||||||
|
action, err := wr.refResolver.ActionRef(ctx, &actionRef, wr.Flow.Namespace)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error resolving action reference: %v", err)
|
||||||
|
}
|
||||||
|
if action.Status.ActionId == "" {
|
||||||
|
return fmt.Errorf("Action with name: %s not ready for trigger", action.Name)
|
||||||
|
}
|
||||||
|
actionIds = append(actionIds, action.Status.ActionId)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = ztdClient.SetTriggerActions(ctx, &pb.SetTriggerActionsRequest{
|
||||||
|
FlowType: wr.Flow.Spec.FlowType,
|
||||||
|
TriggerType: wr.Flow.Spec.TriggerType,
|
||||||
|
ActionIds: actionIds,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
if !strings.Contains(err.Error(), "No Changes") {
|
||||||
|
return fmt.Errorf("Error triggering action flow: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedFlowReconciler) PatchStatus(ctx context.Context, patcher condition.Patcher) error {
|
||||||
|
patch := client.MergeFrom(wr.Flow.DeepCopy())
|
||||||
|
patcher(&wr.Flow.Status)
|
||||||
|
|
||||||
|
if err := wr.Client.Status().Patch(ctx, wr.Flow, patch); err != nil {
|
||||||
|
return fmt.Errorf("error patching Flow status: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetupWithManager sets up the controller with the Manager.
|
||||||
|
func (r *FlowReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||||
|
return ctrl.NewControllerManagedBy(mgr).
|
||||||
|
For(&zitadelv1alpha1.Flow{}).
|
||||||
|
WithOptions(controller.Options{RateLimiter: workqueue.NewTypedItemExponentialFailureRateLimiter[reconcile.Request](time.Millisecond*500, time.Minute*3)}).
|
||||||
|
Complete(r)
|
||||||
|
}
|
||||||
82
src/internal/controller/flow_controller_finalizer.gold
Normal file
82
src/internal/controller/flow_controller_finalizer.gold
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
package controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
zitadelv1alpha1 "github.com/HaimKortovich/zitadel-k8s-operator/api/v1alpha1"
|
||||||
|
"github.com/HaimKortovich/zitadel-k8s-operator/pkg/controller/zitadel"
|
||||||
|
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel-go/v3/pkg/client/management"
|
||||||
|
"github.com/zitadel/zitadel-go/v3/pkg/client/middleware"
|
||||||
|
pb "github.com/zitadel/zitadel-go/v3/pkg/client/zitadel/management"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
ctrlClient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
flowFinalizerName = "flow.zitadel.topmanage.com/flow"
|
||||||
|
)
|
||||||
|
|
||||||
|
type wrappedFlowFinalizer struct {
|
||||||
|
client.Client
|
||||||
|
flow *zitadelv1alpha1.Flow
|
||||||
|
refresolver *zitadelv1alpha1.RefResolver
|
||||||
|
}
|
||||||
|
|
||||||
|
func newWrappedFlowFinalizer(client client.Client, flow *zitadelv1alpha1.Flow, refresolver *zitadelv1alpha1.RefResolver) zitadel.WrappedFinalizer {
|
||||||
|
return &wrappedFlowFinalizer{
|
||||||
|
Client: client,
|
||||||
|
flow: flow,
|
||||||
|
refresolver: refresolver,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wf *wrappedFlowFinalizer) AddFinalizer(ctx context.Context) error {
|
||||||
|
if wf.ContainsFinalizer() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return wf.patch(ctx, wf.flow, func(flow *zitadelv1alpha1.Flow) {
|
||||||
|
controllerutil.AddFinalizer(flow, flowFinalizerName)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wf *wrappedFlowFinalizer) RemoveFinalizer(ctx context.Context) error {
|
||||||
|
if !wf.ContainsFinalizer() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return wf.patch(ctx, wf.flow, func(flow *zitadelv1alpha1.Flow) {
|
||||||
|
controllerutil.RemoveFinalizer(wf.flow, flowFinalizerName)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedFlowFinalizer) ContainsFinalizer() bool {
|
||||||
|
return controllerutil.ContainsFinalizer(wr.flow, flowFinalizerName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wf *wrappedFlowFinalizer) Reconcile(ctx context.Context, ztdClient *management.Client) error {
|
||||||
|
org, err := wf.refresolver.OrganizationRef(ctx, &wf.flow.Spec.OrganizationRef, wf.flow.Namespace)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = ztdClient.ClearFlow(middleware.SetOrgID(ctx, org.Status.OrgId), &pb.ClearFlowRequest{
|
||||||
|
Type: wf.flow.Spec.FlowType,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedFlowFinalizer) patch(ctx context.Context, flow *zitadelv1alpha1.Flow,
|
||||||
|
patchFn func(*zitadelv1alpha1.Flow)) error {
|
||||||
|
patch := ctrlClient.MergeFrom(flow.DeepCopy())
|
||||||
|
patchFn(flow)
|
||||||
|
|
||||||
|
if err := wr.Client.Patch(ctx, flow, patch); err != nil {
|
||||||
|
return fmt.Errorf("error patching Flow finalizer: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
84
src/internal/controller/instance_controller_test.gold
Normal file
84
src/internal/controller/instance_controller_test.gold
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2024.
|
||||||
|
|
||||||
|
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"
|
||||||
|
|
||||||
|
zitadelv1alpha1 "github.com/HaimKortovich/zitadel-k8s-operator/api/v1alpha1"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = Describe("ZitadelInstance 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
|
||||||
|
}
|
||||||
|
zitadelinstance := &zitadelv1alpha1.ZitadelInstance{}
|
||||||
|
|
||||||
|
BeforeEach(func() {
|
||||||
|
By("creating the custom resource for the Kind ZitadelInstance")
|
||||||
|
err := k8sClient.Get(ctx, typeNamespacedName, zitadelinstance)
|
||||||
|
if err != nil && errors.IsNotFound(err) {
|
||||||
|
resource := &zitadelv1alpha1.ZitadelInstance{
|
||||||
|
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 := &zitadelv1alpha1.ZitadelInstance{}
|
||||||
|
err := k8sClient.Get(ctx, typeNamespacedName, resource)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
By("Cleanup the specific resource instance ZitadelInstance")
|
||||||
|
Expect(k8sClient.Delete(ctx, resource)).To(Succeed())
|
||||||
|
})
|
||||||
|
It("should successfully reconcile the resource", func() {
|
||||||
|
By("Reconciling the created resource")
|
||||||
|
controllerReconciler := &ZitadelInstanceReconciler{
|
||||||
|
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.
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
417
src/internal/controller/machineuser_controller.gold
Normal file
417
src/internal/controller/machineuser_controller.gold
Normal file
@@ -0,0 +1,417 @@
|
|||||||
|
package controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"slices"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
zitadelv1alpha1 "github.com/HaimKortovich/zitadel-k8s-operator/api/v1alpha1"
|
||||||
|
"github.com/HaimKortovich/zitadel-k8s-operator/pkg/builder"
|
||||||
|
condition "github.com/HaimKortovich/zitadel-k8s-operator/pkg/condition"
|
||||||
|
"github.com/HaimKortovich/zitadel-k8s-operator/pkg/controller/core"
|
||||||
|
clientv2 "github.com/zitadel/zitadel-go/v3/pkg/client"
|
||||||
|
"github.com/zitadel/zitadel-go/v3/pkg/client/zitadel/filter/v2"
|
||||||
|
"github.com/zitadel/zitadel-go/v3/pkg/client/zitadel/internal_permission/v2"
|
||||||
|
"github.com/zitadel/zitadel-go/v3/pkg/client/zitadel/object/v2"
|
||||||
|
"google.golang.org/protobuf/types/known/timestamppb"
|
||||||
|
|
||||||
|
user "github.com/zitadel/zitadel-go/v3/pkg/client/zitadel/user/v2"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
"k8s.io/client-go/util/workqueue"
|
||||||
|
ctrl "sigs.k8s.io/controller-runtime"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
ctrlClient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MachineUserReconciler reconciles a MachineUser object
|
||||||
|
type MachineUserReconciler struct {
|
||||||
|
client.Client
|
||||||
|
RefResolver *zitadelv1alpha1.RefResolver
|
||||||
|
ConditionReady *condition.Ready
|
||||||
|
RequeueInterval time.Duration
|
||||||
|
Builder *builder.Builder
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMachineUserReconciler(client client.Client, refResolver *zitadelv1alpha1.RefResolver, builder *builder.Builder, conditionReady *condition.Ready,
|
||||||
|
requeueInterval time.Duration) *MachineUserReconciler {
|
||||||
|
return &MachineUserReconciler{
|
||||||
|
Client: client,
|
||||||
|
RefResolver: refResolver,
|
||||||
|
ConditionReady: conditionReady,
|
||||||
|
RequeueInterval: requeueInterval,
|
||||||
|
Builder: builder,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//+kubebuilder:rbac:groups=zitadel.topmanage.com,resources=machineusers,verbs=get;list;watch;create;update;patch;delete
|
||||||
|
//+kubebuilder:rbac:groups=zitadel.topmanage.com,resources=machineusers/status,verbs=get;update;patch
|
||||||
|
//+kubebuilder:rbac:groups=zitadel.topmanage.com,resources=machineusers/finalizers,verbs=update
|
||||||
|
|
||||||
|
// Reconcile is part of the main kubernetes reconciliation loop which aims to
|
||||||
|
// move the current state of the cluster closer to the desired state.
|
||||||
|
func (r *MachineUserReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||||
|
var MachineUser zitadelv1alpha1.MachineUser
|
||||||
|
if err := r.Get(ctx, req.NamespacedName, &MachineUser); err != nil {
|
||||||
|
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||||
|
}
|
||||||
|
wr := newWrappedMachineUserReconciler(r.Client, r.RefResolver, r.Builder, &MachineUser)
|
||||||
|
wf := newWrappedMachineUserFinalizer(r.Client, &MachineUser)
|
||||||
|
tf := core.NewCoreFinalizer(r.Client, wf)
|
||||||
|
tr := core.NewCoreReconciler(r.Client, r.ConditionReady, wr, tf, r.RequeueInterval)
|
||||||
|
|
||||||
|
result, err := tr.Reconcile(ctx, &MachineUser)
|
||||||
|
if err != nil {
|
||||||
|
return result, fmt.Errorf("error reconciling in MachineUserReconciler: %v", err)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type wrappedMachineUserReconciler struct {
|
||||||
|
client.Client
|
||||||
|
refResolver *zitadelv1alpha1.RefResolver
|
||||||
|
MachineUser *zitadelv1alpha1.MachineUser
|
||||||
|
Builder *builder.Builder
|
||||||
|
}
|
||||||
|
|
||||||
|
func newWrappedMachineUserReconciler(client client.Client, refResolver *zitadelv1alpha1.RefResolver, builder *builder.Builder,
|
||||||
|
MachineUser *zitadelv1alpha1.MachineUser) core.WrappedCoreReconciler {
|
||||||
|
return &wrappedMachineUserReconciler{
|
||||||
|
Client: client,
|
||||||
|
refResolver: refResolver,
|
||||||
|
MachineUser: MachineUser,
|
||||||
|
Builder: builder,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type machineUserReconcilePhase struct {
|
||||||
|
Name string
|
||||||
|
Reconcile func(context.Context, *clientv2.Client) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedMachineUserReconciler) Reconcile(ctx context.Context, ztdClient *clientv2.Client) error {
|
||||||
|
phases := []machineUserReconcilePhase{
|
||||||
|
{
|
||||||
|
Name: "machineUser",
|
||||||
|
Reconcile: wr.reconcileMachineUser,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "internalPermissions",
|
||||||
|
Reconcile: wr.reconcileInternalPermissions,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "pat",
|
||||||
|
Reconcile: wr.reconcilePAT,
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// Name: "jwt",
|
||||||
|
// Reconcile: wr.reconcileJWT,
|
||||||
|
// },
|
||||||
|
}
|
||||||
|
for _, p := range phases {
|
||||||
|
err := p.Reconcile(ctx, ztdClient)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedMachineUserReconciler) reconcileMachineUser(ctx context.Context, ztdClient *clientv2.Client) error {
|
||||||
|
org, err := wr.refResolver.OrganizationRef(ctx, &wr.MachineUser.Spec.OrganizationRef, wr.MachineUser.Namespace)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if org.Status.OrganizationId == nil {
|
||||||
|
return fmt.Errorf("Organization not created yet")
|
||||||
|
}
|
||||||
|
|
||||||
|
var userId *string
|
||||||
|
userList, err := ztdClient.UserServiceV2().ListUsers(ctx, &user.ListUsersRequest{
|
||||||
|
Queries: []*user.SearchQuery{{
|
||||||
|
Query: &user.SearchQuery_AndQuery{
|
||||||
|
AndQuery: &user.AndQuery{
|
||||||
|
Queries: []*user.SearchQuery{
|
||||||
|
&user.SearchQuery{
|
||||||
|
Query: &user.SearchQuery_UserNameQuery{
|
||||||
|
UserNameQuery: &user.UserNameQuery{
|
||||||
|
UserName: wr.MachineUser.Spec.Username,
|
||||||
|
Method: object.TextQueryMethod_TEXT_QUERY_METHOD_EQUALS,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&user.SearchQuery{
|
||||||
|
Query: &user.SearchQuery_OrganizationIdQuery{
|
||||||
|
OrganizationIdQuery: &user.OrganizationIdQuery{
|
||||||
|
OrganizationId: *org.Status.OrganizationId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error listing users: %v", err)
|
||||||
|
}
|
||||||
|
if len(userList.Result) > 0 {
|
||||||
|
userId = &userList.Result[0].UserId
|
||||||
|
}
|
||||||
|
|
||||||
|
if userId == nil {
|
||||||
|
accesTokenType := user.AccessTokenType_ACCESS_TOKEN_TYPE_BEARER
|
||||||
|
switch wr.MachineUser.Spec.AccessTokenType {
|
||||||
|
case "ACCESS_TOKEN_TYPE_BEARER":
|
||||||
|
accesTokenType = user.AccessTokenType_ACCESS_TOKEN_TYPE_BEARER
|
||||||
|
case "ACCESS_TOKEN_TYPE_JWT":
|
||||||
|
accesTokenType = user.AccessTokenType_ACCESS_TOKEN_TYPE_JWT
|
||||||
|
}
|
||||||
|
ztdClient.UserServiceV2().CreateUser(ctx, &user.CreateUserRequest{
|
||||||
|
OrganizationId: *org.Status.OrganizationId,
|
||||||
|
Username: &wr.MachineUser.Spec.Username,
|
||||||
|
UserType: &user.CreateUserRequest_Machine_{
|
||||||
|
Machine: &user.CreateUserRequest_Machine{
|
||||||
|
Name: wr.MachineUser.Spec.Username,
|
||||||
|
AccessTokenType: accesTokenType,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
patch := ctrlClient.MergeFrom(wr.MachineUser.DeepCopy())
|
||||||
|
wr.MachineUser.Status.UserId = userId
|
||||||
|
return wr.Client.Status().Patch(ctx, wr.MachineUser, patch)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedMachineUserReconciler) reconcilePAT(ctx context.Context, ztdClient *clientv2.Client) error {
|
||||||
|
pats, err := ztdClient.UserServiceV2().ListPersonalAccessTokens(ctx, &user.ListPersonalAccessTokensRequest{
|
||||||
|
Filters: []*user.PersonalAccessTokensSearchFilter{
|
||||||
|
{
|
||||||
|
Filter: &user.PersonalAccessTokensSearchFilter_UserIdFilter{
|
||||||
|
UserIdFilter: &filter.IDFilter{
|
||||||
|
Id: *wr.MachineUser.Status.UserId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error getting PAT: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if pats.Result == nil || !wr.MachineUser.Status.GetConditionStatus(zitadelv1alpha1.ConditionTypePATUpToDate) {
|
||||||
|
resp, err := ztdClient.UserServiceV2().AddPersonalAccessToken(ctx, &user.AddPersonalAccessTokenRequest{
|
||||||
|
UserId: *wr.MachineUser.Status.UserId,
|
||||||
|
ExpirationDate: timestamppb.New(time.Now().AddDate(999, 1, 1)),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error adding PAT: %v", err)
|
||||||
|
}
|
||||||
|
key := types.NamespacedName{
|
||||||
|
Name: wr.MachineUser.PatSecretName(),
|
||||||
|
Namespace: wr.MachineUser.Namespace,
|
||||||
|
}
|
||||||
|
desiredPatSecret, err := wr.Builder.BuildSecret(builder.SecretOpts{
|
||||||
|
Key: key,
|
||||||
|
Immutable: false,
|
||||||
|
Data: map[string][]byte{
|
||||||
|
"pat": []byte(resp.Token),
|
||||||
|
},
|
||||||
|
}, wr.MachineUser)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error building PAT Secret: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var existingPatSecret corev1.Secret
|
||||||
|
if err := wr.Get(ctx, key, &existingPatSecret); err != nil {
|
||||||
|
if errors.IsNotFound(err) {
|
||||||
|
if err := wr.Create(ctx, desiredPatSecret); err != nil {
|
||||||
|
return fmt.Errorf("error creating PAT Secret: %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("error getting PAT Secret: %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
patch := client.MergeFrom(existingPatSecret.DeepCopy())
|
||||||
|
existingPatSecret.Data = desiredPatSecret.Data
|
||||||
|
if err = wr.Patch(ctx, &existingPatSecret, patch); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = wr.PatchStatus(ctx, condition.SetPatUpToDate); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
patch := ctrlClient.MergeFrom(wr.MachineUser.DeepCopy())
|
||||||
|
wr.MachineUser.Status.PATId = &resp.TokenId
|
||||||
|
return wr.Client.Status().Patch(ctx, wr.MachineUser, patch)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// func (wr *wrappedMachineUserReconciler) reconcileJWT(ctx context.Context, ztdClient *management.Client) error {
|
||||||
|
// org, err := wr.refResolver.OrganizationRef(ctx, &wr.MachineUser.Spec.OrganizationRef, wr.MachineUser.Namespace)
|
||||||
|
// if err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
// ctx = middleware.SetOrgID(ctx, org.Status.OrgId)
|
||||||
|
|
||||||
|
// token, err := ztdClient.GetMachineKeyByIDs(ctx, &pb.GetMachineKeyByIDsRequest{
|
||||||
|
// UserId: wr.MachineUser.Status.UserId,
|
||||||
|
// KeyId: wr.MachineUser.Status.KeyId,
|
||||||
|
// })
|
||||||
|
// if err != nil {
|
||||||
|
// if !(strings.Contains(err.Error(), "NotFound") || strings.Contains(err.Error(), "length must be between 1 and 200 runes")) {
|
||||||
|
// return fmt.Errorf("Error getting JWT: %v", err)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if token == nil {
|
||||||
|
// resp, err := ztdClient.AddMachineKey(ctx, &pb.AddMachineKeyRequest{
|
||||||
|
// UserId: wr.MachineUser.Status.UserId,
|
||||||
|
// Type: authn.KeyType_KEY_TYPE_JSON,
|
||||||
|
// })
|
||||||
|
// if err != nil {
|
||||||
|
// return fmt.Errorf("Error adding JWT: %v", err)
|
||||||
|
// }
|
||||||
|
// key := types.NamespacedName{
|
||||||
|
// Name: wr.MachineUser.JWTSecretName(),
|
||||||
|
// Namespace: wr.MachineUser.Namespace,
|
||||||
|
// }
|
||||||
|
|
||||||
|
// var jsonKey Key
|
||||||
|
// if err = json.Unmarshal(resp.KeyDetails, &jsonKey); err != nil {
|
||||||
|
// return fmt.Errorf("Could not unmarshal key details: %v", err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// secretData := map[string][]byte{
|
||||||
|
// "clientId": []byte(jsonKey.ClientID),
|
||||||
|
// "type": []byte(jsonKey.Type),
|
||||||
|
// "keyId": []byte(jsonKey.KeyID),
|
||||||
|
// "appId": []byte(jsonKey.AppID),
|
||||||
|
// "key": []byte(jsonKey.Key),
|
||||||
|
// }
|
||||||
|
// jwtSecret, err := wr.Builder.BuildSecret(builder.SecretOpts{
|
||||||
|
// Key: key,
|
||||||
|
// Immutable: false,
|
||||||
|
// Data: secretData,
|
||||||
|
// }, wr.MachineUser)
|
||||||
|
|
||||||
|
// if err != nil {
|
||||||
|
// return fmt.Errorf("error building machine key Secret: %v", err)
|
||||||
|
// }
|
||||||
|
// if err := wr.Create(ctx, jwtSecret); err != nil {
|
||||||
|
// return fmt.Errorf("error creating machine key Secret: %v", err)
|
||||||
|
// }
|
||||||
|
// patch := ctrlClient.MergeFrom(wr.MachineUser.DeepCopy())
|
||||||
|
// wr.MachineUser.Status.KeyId = resp.KeyId
|
||||||
|
// return wr.Client.Status().Patch(ctx, wr.MachineUser, patch)
|
||||||
|
// }
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
|
||||||
|
func (wr *wrappedMachineUserReconciler) reconcileInternalPermissions(ctx context.Context, ztdClient *clientv2.Client) error {
|
||||||
|
for _, permission := range wr.MachineUser.Spec.InternalPermissions {
|
||||||
|
permissionPbFilter :=
|
||||||
|
&internal_permission.AdministratorSearchFilter_Resource{
|
||||||
|
Resource: &internal_permission.ResourceFilter{},
|
||||||
|
}
|
||||||
|
permissionPb := &internal_permission.ResourceType{}
|
||||||
|
if permission.Resource.Instance != nil {
|
||||||
|
permissionPbFilter.Resource.Resource = &internal_permission.ResourceFilter_Instance{Instance: true}
|
||||||
|
permissionPb.Resource = &internal_permission.ResourceType_Instance{Instance: true}
|
||||||
|
} else if permission.Resource.Organization != nil {
|
||||||
|
permissionPbFilter.Resource.Resource = &internal_permission.ResourceFilter_OrganizationId{OrganizationId: permission.Resource.Organization.OrgID}
|
||||||
|
permissionPb.Resource = &internal_permission.ResourceType_OrganizationId{OrganizationId: permission.Resource.Organization.OrgID}
|
||||||
|
} else if permission.Resource.ProjectGrant != nil {
|
||||||
|
permissionPbFilter.Resource.Resource = &internal_permission.ResourceFilter_ProjectGrant_{ProjectGrant: &internal_permission.ResourceFilter_ProjectGrant{
|
||||||
|
ProjectId: permission.Resource.ProjectGrant.ProjectID, OrganizationId: permission.Resource.ProjectGrant.ProjectID,
|
||||||
|
}}
|
||||||
|
permissionPb.Resource = &internal_permission.ResourceType_ProjectGrant_{ProjectGrant: &internal_permission.ResourceType_ProjectGrant{
|
||||||
|
ProjectId: permission.Resource.ProjectGrant.ProjectID, OrganizationId: permission.Resource.ProjectGrant.ProjectID,
|
||||||
|
}}
|
||||||
|
} else if permission.Resource.Project != nil {
|
||||||
|
permissionPbFilter.Resource.Resource = &internal_permission.ResourceFilter_ProjectId{ProjectId: permission.Resource.Project.ProjectID}
|
||||||
|
permissionPb.Resource = &internal_permission.ResourceType_ProjectId{ProjectId: permission.Resource.Project.ProjectID}
|
||||||
|
}
|
||||||
|
|
||||||
|
var admin *internal_permission.Administrator
|
||||||
|
adminRoleList, err := ztdClient.InternalPermissionServiceV2().ListAdministrators(ctx, &internal_permission.ListAdministratorsRequest{
|
||||||
|
Filters: []*internal_permission.AdministratorSearchFilter{
|
||||||
|
{
|
||||||
|
Filter: permissionPbFilter,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Filter: &internal_permission.AdministratorSearchFilter_And{And: &internal_permission.AndFilter{Queries: []*internal_permission.AdministratorSearchFilter{
|
||||||
|
{
|
||||||
|
Filter: &internal_permission.AdministratorSearchFilter_InUserIdsFilter{
|
||||||
|
InUserIdsFilter: &filter.InIDsFilter{
|
||||||
|
Ids: []string{
|
||||||
|
*wr.MachineUser.Status.UserId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error listing admin list: %v", err)
|
||||||
|
}
|
||||||
|
if len(adminRoleList.Administrators) > 0 {
|
||||||
|
admin = adminRoleList.Administrators[0]
|
||||||
|
}
|
||||||
|
if admin == nil {
|
||||||
|
_, err := ztdClient.InternalPermissionServiceV2().CreateAdministrator(ctx, &internal_permission.CreateAdministratorRequest{
|
||||||
|
UserId: *wr.MachineUser.Status.UserId,
|
||||||
|
Roles: permission.Roles,
|
||||||
|
Resource: permissionPb,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error creating admin: %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uniqueRoles := permission.Roles
|
||||||
|
uniqueRoles = append(uniqueRoles, admin.Roles...)
|
||||||
|
uniqueRoles = slices.Compact(uniqueRoles)
|
||||||
|
_, err := ztdClient.InternalPermissionServiceV2().UpdateAdministrator(ctx, &internal_permission.UpdateAdministratorRequest{
|
||||||
|
UserId: *wr.MachineUser.Status.UserId,
|
||||||
|
Roles: uniqueRoles,
|
||||||
|
Resource: permissionPb,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error updating admin: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedMachineUserReconciler) PatchStatus(ctx context.Context, patcher condition.Patcher) error {
|
||||||
|
patch := client.MergeFrom(wr.MachineUser.DeepCopy())
|
||||||
|
patcher(&wr.MachineUser.Status)
|
||||||
|
|
||||||
|
if err := wr.Client.Status().Patch(ctx, wr.MachineUser, patch); err != nil {
|
||||||
|
return fmt.Errorf("error patching MachineUser status: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetupWithManager sets up the controller with the Manager.
|
||||||
|
func (r *MachineUserReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||||
|
return ctrl.NewControllerManagedBy(mgr).
|
||||||
|
For(&zitadelv1alpha1.MachineUser{}).
|
||||||
|
Owns(&corev1.Secret{}).
|
||||||
|
WithOptions(controller.Options{RateLimiter: workqueue.NewTypedItemExponentialFailureRateLimiter[reconcile.Request](time.Millisecond*500, time.Minute*3)}).
|
||||||
|
Complete(r)
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
zitadelv1alpha1 "github.com/HaimKortovich/zitadel-k8s-operator/api/v1alpha1"
|
||||||
|
"github.com/HaimKortovich/zitadel-k8s-operator/pkg/controller/core"
|
||||||
|
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
clientv2 "github.com/zitadel/zitadel-go/v3/pkg/client"
|
||||||
|
"github.com/zitadel/zitadel-go/v3/pkg/client/zitadel/user/v2"
|
||||||
|
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
ctrlClient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
machineuserFinalizerName = "machineuser.zitadel.topmanage.com/machineuser"
|
||||||
|
)
|
||||||
|
|
||||||
|
type wrappedMachineUserFinalizer struct {
|
||||||
|
client.Client
|
||||||
|
machineuser *zitadelv1alpha1.MachineUser
|
||||||
|
}
|
||||||
|
|
||||||
|
func newWrappedMachineUserFinalizer(client client.Client, machineuser *zitadelv1alpha1.MachineUser) core.WrappedCoreFinalizer {
|
||||||
|
return &wrappedMachineUserFinalizer{
|
||||||
|
Client: client,
|
||||||
|
machineuser: machineuser,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wf *wrappedMachineUserFinalizer) AddFinalizer(ctx context.Context) error {
|
||||||
|
if wf.ContainsFinalizer() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return wf.patch(ctx, wf.machineuser, func(machineuser *zitadelv1alpha1.MachineUser) {
|
||||||
|
controllerutil.AddFinalizer(machineuser, machineuserFinalizerName)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wf *wrappedMachineUserFinalizer) RemoveFinalizer(ctx context.Context) error {
|
||||||
|
if !wf.ContainsFinalizer() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return wf.patch(ctx, wf.machineuser, func(machineuser *zitadelv1alpha1.MachineUser) {
|
||||||
|
controllerutil.RemoveFinalizer(wf.machineuser, machineuserFinalizerName)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedMachineUserFinalizer) ContainsFinalizer() bool {
|
||||||
|
return controllerutil.ContainsFinalizer(wr.machineuser, machineuserFinalizerName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wf *wrappedMachineUserFinalizer) Reconcile(ctx context.Context, ztdClient *clientv2.Client) error {
|
||||||
|
if wf.machineuser.Status.UserId != nil {
|
||||||
|
if _, err := ztdClient.UserServiceV2().DeleteUser(ctx,
|
||||||
|
&user.DeleteUserRequest{
|
||||||
|
UserId: *wf.machineuser.Status.UserId,
|
||||||
|
},
|
||||||
|
); err != nil {
|
||||||
|
return fmt.Errorf("Error deleting organization: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedMachineUserFinalizer) patch(ctx context.Context, machineuser *zitadelv1alpha1.MachineUser,
|
||||||
|
patchFn func(*zitadelv1alpha1.MachineUser)) error {
|
||||||
|
patch := ctrlClient.MergeFrom(machineuser.DeepCopy())
|
||||||
|
patchFn(machineuser)
|
||||||
|
|
||||||
|
if err := wr.Client.Patch(ctx, machineuser, patch); err != nil {
|
||||||
|
return fmt.Errorf("error patching MachineUser finalizer: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
263
src/internal/controller/oidcapp_controller.gold
Normal file
263
src/internal/controller/oidcapp_controller.gold
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2024.
|
||||||
|
|
||||||
|
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"
|
||||||
|
|
||||||
|
zitadelv1alpha1 "github.com/HaimKortovich/zitadel-k8s-operator/api/v1alpha1"
|
||||||
|
"github.com/HaimKortovich/zitadel-k8s-operator/pkg/builder"
|
||||||
|
condition "github.com/HaimKortovich/zitadel-k8s-operator/pkg/condition"
|
||||||
|
"github.com/HaimKortovich/zitadel-k8s-operator/pkg/controller/core"
|
||||||
|
clientv2 "github.com/zitadel/zitadel-go/v3/pkg/client"
|
||||||
|
"github.com/zitadel/zitadel-go/v3/pkg/client/zitadel/application/v2"
|
||||||
|
"github.com/zitadel/zitadel-go/v3/pkg/client/zitadel/filter/v2"
|
||||||
|
durationpb "google.golang.org/protobuf/types/known/durationpb"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
"k8s.io/client-go/util/workqueue"
|
||||||
|
"k8s.io/utils/ptr"
|
||||||
|
ctrl "sigs.k8s.io/controller-runtime"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
ctrlClient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||||
|
)
|
||||||
|
|
||||||
|
// OIDCAppReconciler reconciles a OIDCApp object
|
||||||
|
type OIDCAppReconciler struct {
|
||||||
|
client.Client
|
||||||
|
RefResolver *zitadelv1alpha1.RefResolver
|
||||||
|
ConditionReady *condition.Ready
|
||||||
|
RequeueInterval time.Duration
|
||||||
|
Builder *builder.Builder
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewOIDCAppReconciler(client client.Client, refResolver *zitadelv1alpha1.RefResolver, builder *builder.Builder, conditionReady *condition.Ready,
|
||||||
|
requeueInterval time.Duration) *OIDCAppReconciler {
|
||||||
|
return &OIDCAppReconciler{
|
||||||
|
Client: client,
|
||||||
|
RefResolver: refResolver,
|
||||||
|
ConditionReady: conditionReady,
|
||||||
|
RequeueInterval: requeueInterval,
|
||||||
|
Builder: builder,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//+kubebuilder:rbac:groups=zitadel.topmanage.com,resources=oidcapps,verbs=get;list;watch;create;update;patch;delete
|
||||||
|
//+kubebuilder:rbac:groups=zitadel.topmanage.com,resources=oidcapps/status,verbs=get;update;patch
|
||||||
|
//+kubebuilder:rbac:groups=zitadel.topmanage.com,resources=oidcapps/finalizers,verbs=update
|
||||||
|
|
||||||
|
// Reconcile is part of the main kubernetes reconciliation loop which aims to
|
||||||
|
// move the current state of the cluster closer to the desired state.
|
||||||
|
func (r *OIDCAppReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||||
|
var OIDCApp zitadelv1alpha1.OIDCApp
|
||||||
|
if err := r.Get(ctx, req.NamespacedName, &OIDCApp); err != nil {
|
||||||
|
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||||
|
}
|
||||||
|
wr := newWrappedOIDCAppReconciler(r.Client, r.RefResolver, r.Builder, &OIDCApp)
|
||||||
|
wf := newWrappedOIDCAppFinalizer(r.Client, &OIDCApp, r.RefResolver)
|
||||||
|
tf := core.NewCoreFinalizer(r.Client, wf)
|
||||||
|
tr := core.NewCoreReconciler(r.Client, r.ConditionReady, wr, tf, r.RequeueInterval)
|
||||||
|
|
||||||
|
result, err := tr.Reconcile(ctx, &OIDCApp)
|
||||||
|
if err != nil {
|
||||||
|
return result, fmt.Errorf("error reconciling in OIDCAppReconciler: %v", err)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type wrappedOIDCAppReconciler struct {
|
||||||
|
client.Client
|
||||||
|
refResolver *zitadelv1alpha1.RefResolver
|
||||||
|
OIDCApp *zitadelv1alpha1.OIDCApp
|
||||||
|
Builder *builder.Builder
|
||||||
|
}
|
||||||
|
|
||||||
|
func newWrappedOIDCAppReconciler(client client.Client, refResolver *zitadelv1alpha1.RefResolver, builder *builder.Builder,
|
||||||
|
OIDCApp *zitadelv1alpha1.OIDCApp) core.WrappedCoreReconciler {
|
||||||
|
return &wrappedOIDCAppReconciler{
|
||||||
|
Client: client,
|
||||||
|
refResolver: refResolver,
|
||||||
|
OIDCApp: OIDCApp,
|
||||||
|
Builder: builder,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedOIDCAppReconciler) Reconcile(ctx context.Context, ztdClient *clientv2.Client) error {
|
||||||
|
project, err := wr.OIDCApp.Project(ctx, wr.refResolver)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if project.Status.ProjectId == nil {
|
||||||
|
return fmt.Errorf("Project has not been created yet...")
|
||||||
|
}
|
||||||
|
responseTypes := []application.OIDCResponseType{}
|
||||||
|
for _, r := range wr.OIDCApp.Spec.ResponseTypes {
|
||||||
|
responseTypes = append(responseTypes, application.OIDCResponseType(application.OIDCApplicationType_value[string(r)]))
|
||||||
|
}
|
||||||
|
grantTypes := []application.OIDCGrantType{}
|
||||||
|
for _, r := range wr.OIDCApp.Spec.GrantTypes {
|
||||||
|
grantTypes = append(grantTypes, application.OIDCGrantType(application.OIDCGrantType_value[string(r)]))
|
||||||
|
}
|
||||||
|
|
||||||
|
var appid *string
|
||||||
|
var clientid *string
|
||||||
|
appList, err := ztdClient.ApplicationServiceV2().ListApplications(ctx,
|
||||||
|
&application.ListApplicationsRequest{
|
||||||
|
Filters: []*application.ApplicationSearchFilter{
|
||||||
|
{
|
||||||
|
Filter: &application.ApplicationSearchFilter_NameFilter{
|
||||||
|
NameFilter: &application.ApplicationNameFilter{
|
||||||
|
Name: wr.OIDCApp.Spec.OIDCAppName,
|
||||||
|
Method: filter.TextFilterMethod_TEXT_FILTER_METHOD_EQUALS,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Filter: &application.ApplicationSearchFilter_ProjectIdFilter{
|
||||||
|
ProjectIdFilter: &application.ProjectIDFilter{
|
||||||
|
ProjectId: *project.Status.ProjectId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error listing OIDCApps: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(appList.Applications) > 0 {
|
||||||
|
appid = &appList.Applications[0].ApplicationId
|
||||||
|
clientid = &appList.Applications[0].GetApiConfiguration().ClientId
|
||||||
|
}
|
||||||
|
|
||||||
|
if appid == nil {
|
||||||
|
resp, err := ztdClient.ApplicationServiceV2().CreateApplication(ctx,
|
||||||
|
&application.CreateApplicationRequest{
|
||||||
|
Name: wr.OIDCApp.Name,
|
||||||
|
ProjectId: *project.Status.ProjectId,
|
||||||
|
ApplicationType: &application.CreateApplicationRequest_OidcConfiguration{
|
||||||
|
OidcConfiguration: &application.CreateOIDCApplicationRequest{
|
||||||
|
RedirectUris: wr.OIDCApp.Spec.RedirectUris,
|
||||||
|
ResponseTypes: responseTypes,
|
||||||
|
GrantTypes: grantTypes,
|
||||||
|
AuthMethodType: application.OIDCAuthMethodType(application.OIDCTokenType_value[wr.OIDCApp.Spec.AuthMethodType]),
|
||||||
|
PostLogoutRedirectUris: wr.OIDCApp.Spec.PostLogoutRedirectUris,
|
||||||
|
Version: application.OIDCVersion_OIDC_VERSION_1_0,
|
||||||
|
DevelopmentMode: wr.OIDCApp.Spec.DevMode,
|
||||||
|
AccessTokenType: application.OIDCTokenType(application.OIDCTokenType_value[wr.OIDCApp.Spec.AccessTokenType]),
|
||||||
|
AccessTokenRoleAssertion: wr.OIDCApp.Spec.AccessTokenRoleAssertion,
|
||||||
|
IdTokenRoleAssertion: wr.OIDCApp.Spec.IdTokenRoleAssertion,
|
||||||
|
IdTokenUserinfoAssertion: wr.OIDCApp.Spec.IdTokenUserinfoAssertion,
|
||||||
|
ClockSkew: durationpb.New(wr.OIDCApp.Spec.ClockSkew.Duration),
|
||||||
|
AdditionalOrigins: wr.OIDCApp.Spec.AdditionalOrigins,
|
||||||
|
SkipNativeAppSuccessPage: wr.OIDCApp.Spec.SkipNativeAppSuccessPage,
|
||||||
|
LoginVersion: &application.LoginVersion{
|
||||||
|
Version: &application.LoginVersion_LoginV2{
|
||||||
|
LoginV2: &application.LoginV2{
|
||||||
|
BaseUri: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
BackChannelLogoutUri: wr.OIDCApp.Spec.BackChannelLogoutUri,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error creating OIDCApp in Zitadel: %v", err)
|
||||||
|
}
|
||||||
|
key := types.NamespacedName{
|
||||||
|
Name: wr.OIDCApp.ClientSecretName(),
|
||||||
|
Namespace: wr.OIDCApp.Namespace,
|
||||||
|
}
|
||||||
|
|
||||||
|
secretData := map[string][]byte{"clientSecret": []byte(resp.GetApiConfiguration().ClientSecret), "appId": []byte(resp.ApplicationId), "clientId": []byte(resp.GetApiConfiguration().ClientId)}
|
||||||
|
secret, err := wr.Builder.BuildSecret(builder.SecretOpts{Immutable: false, Zitadel: nil, Key: key, Data: secretData}, wr.OIDCApp)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error building Secret: %v", err)
|
||||||
|
}
|
||||||
|
if err := wr.Create(ctx, secret); err != nil {
|
||||||
|
return fmt.Errorf("error creating Client-secret Secret: %v", err)
|
||||||
|
}
|
||||||
|
appid = &resp.ApplicationId
|
||||||
|
clientid = &resp.GetApiConfiguration().ClientId
|
||||||
|
} else {
|
||||||
|
_, err := ztdClient.ApplicationServiceV2().UpdateApplication(ctx,
|
||||||
|
&application.UpdateApplicationRequest{
|
||||||
|
Name: wr.OIDCApp.Name,
|
||||||
|
ProjectId: *project.Status.ProjectId,
|
||||||
|
ApplicationType: &application.UpdateApplicationRequest_OidcConfiguration{
|
||||||
|
OidcConfiguration: &application.UpdateOIDCApplicationConfigurationRequest{
|
||||||
|
RedirectUris: wr.OIDCApp.Spec.RedirectUris,
|
||||||
|
ResponseTypes: responseTypes,
|
||||||
|
GrantTypes: grantTypes,
|
||||||
|
AuthMethodType: ptr.To(application.OIDCAuthMethodType(application.OIDCTokenType_value[wr.OIDCApp.Spec.AuthMethodType])),
|
||||||
|
PostLogoutRedirectUris: wr.OIDCApp.Spec.PostLogoutRedirectUris,
|
||||||
|
Version: ptr.To(application.OIDCVersion_OIDC_VERSION_1_0),
|
||||||
|
DevelopmentMode: &wr.OIDCApp.Spec.DevMode,
|
||||||
|
AccessTokenType: ptr.To(application.OIDCTokenType(application.OIDCTokenType_value[wr.OIDCApp.Spec.AccessTokenType])),
|
||||||
|
AccessTokenRoleAssertion: &wr.OIDCApp.Spec.AccessTokenRoleAssertion,
|
||||||
|
IdTokenRoleAssertion: &wr.OIDCApp.Spec.IdTokenRoleAssertion,
|
||||||
|
IdTokenUserinfoAssertion: &wr.OIDCApp.Spec.IdTokenUserinfoAssertion,
|
||||||
|
ClockSkew: durationpb.New(wr.OIDCApp.Spec.ClockSkew.Duration),
|
||||||
|
AdditionalOrigins: wr.OIDCApp.Spec.AdditionalOrigins,
|
||||||
|
SkipNativeAppSuccessPage: &wr.OIDCApp.Spec.SkipNativeAppSuccessPage,
|
||||||
|
LoginVersion: &application.LoginVersion{
|
||||||
|
Version: &application.LoginVersion_LoginV2{
|
||||||
|
LoginV2: &application.LoginV2{
|
||||||
|
BaseUri: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
BackChannelLogoutUri: &wr.OIDCApp.Spec.BackChannelLogoutUri,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error updating OIDCApp in Zitadel: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
patch := ctrlClient.MergeFrom(wr.OIDCApp.DeepCopy())
|
||||||
|
wr.OIDCApp.Status.AppId = appid
|
||||||
|
wr.OIDCApp.Status.ClientId = clientid
|
||||||
|
return wr.Client.Status().Patch(ctx, wr.OIDCApp, patch)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedOIDCAppReconciler) PatchStatus(ctx context.Context, patcher condition.Patcher) error {
|
||||||
|
patch := client.MergeFrom(wr.OIDCApp.DeepCopy())
|
||||||
|
patcher(&wr.OIDCApp.Status)
|
||||||
|
|
||||||
|
if err := wr.Client.Status().Patch(ctx, wr.OIDCApp, patch); err != nil {
|
||||||
|
return fmt.Errorf("error patching OIDCApp status: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetupWithManager sets up the controller with the Manager.
|
||||||
|
func (r *OIDCAppReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||||
|
return ctrl.NewControllerManagedBy(mgr).
|
||||||
|
For(&zitadelv1alpha1.OIDCApp{}).
|
||||||
|
Owns(&corev1.Secret{}).
|
||||||
|
WithOptions(controller.Options{RateLimiter: workqueue.NewTypedItemExponentialFailureRateLimiter[reconcile.Request](time.Millisecond*500, time.Minute*3)}).
|
||||||
|
Complete(r)
|
||||||
|
}
|
||||||
88
src/internal/controller/oidcapp_controller_finalizer.gold
Normal file
88
src/internal/controller/oidcapp_controller_finalizer.gold
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
package controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
zitadelv1alpha1 "github.com/HaimKortovich/zitadel-k8s-operator/api/v1alpha1"
|
||||||
|
"github.com/HaimKortovich/zitadel-k8s-operator/pkg/controller/core"
|
||||||
|
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
clientv2 "github.com/zitadel/zitadel-go/v3/pkg/client"
|
||||||
|
"github.com/zitadel/zitadel-go/v3/pkg/client/zitadel/application/v2"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
ctrlClient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
OIDCAppFinalizerName = "oidcapp.zitadel.topmanage.com/oidcapp"
|
||||||
|
)
|
||||||
|
|
||||||
|
type wrappedOIDCAppFinalizer struct {
|
||||||
|
client.Client
|
||||||
|
OIDCApp *zitadelv1alpha1.OIDCApp
|
||||||
|
refresolver *zitadelv1alpha1.RefResolver
|
||||||
|
}
|
||||||
|
|
||||||
|
func newWrappedOIDCAppFinalizer(client client.Client, OIDCApp *zitadelv1alpha1.OIDCApp, refresolver *zitadelv1alpha1.RefResolver) core.WrappedCoreFinalizer {
|
||||||
|
return &wrappedOIDCAppFinalizer{
|
||||||
|
Client: client,
|
||||||
|
OIDCApp: OIDCApp,
|
||||||
|
refresolver: refresolver,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wf *wrappedOIDCAppFinalizer) AddFinalizer(ctx context.Context) error {
|
||||||
|
if wf.ContainsFinalizer() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return wf.patch(ctx, wf.OIDCApp, func(OIDCApp *zitadelv1alpha1.OIDCApp) {
|
||||||
|
controllerutil.AddFinalizer(OIDCApp, OIDCAppFinalizerName)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wf *wrappedOIDCAppFinalizer) RemoveFinalizer(ctx context.Context) error {
|
||||||
|
if !wf.ContainsFinalizer() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return wf.patch(ctx, wf.OIDCApp, func(OIDCApp *zitadelv1alpha1.OIDCApp) {
|
||||||
|
controllerutil.RemoveFinalizer(wf.OIDCApp, OIDCAppFinalizerName)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedOIDCAppFinalizer) ContainsFinalizer() bool {
|
||||||
|
return controllerutil.ContainsFinalizer(wr.OIDCApp, OIDCAppFinalizerName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wf *wrappedOIDCAppFinalizer) Reconcile(ctx context.Context, ztdClient *clientv2.Client) error {
|
||||||
|
if wf.OIDCApp.Status.AppId != nil {
|
||||||
|
project, err := wf.OIDCApp.Project(ctx, wf.refresolver)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = ztdClient.ApplicationServiceV2().DeleteApplication(ctx, &application.DeleteApplicationRequest{
|
||||||
|
ApplicationId: *wf.OIDCApp.Status.AppId,
|
||||||
|
ProjectId: *project.Status.ProjectId,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
if strings.Contains(err.Error(), "doesn't exist") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedOIDCAppFinalizer) patch(ctx context.Context, OIDCApp *zitadelv1alpha1.OIDCApp,
|
||||||
|
patchFn func(*zitadelv1alpha1.OIDCApp)) error {
|
||||||
|
patch := ctrlClient.MergeFrom(OIDCApp.DeepCopy())
|
||||||
|
patchFn(OIDCApp)
|
||||||
|
|
||||||
|
if err := wr.Client.Patch(ctx, OIDCApp, patch); err != nil {
|
||||||
|
return fmt.Errorf("error patching OIDCApp finalizer: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
167
src/internal/controller/organization_controller.gold
Normal file
167
src/internal/controller/organization_controller.gold
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2024.
|
||||||
|
|
||||||
|
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"
|
||||||
|
|
||||||
|
zitadelv1alpha1 "github.com/HaimKortovich/zitadel-k8s-operator/api/v1alpha1"
|
||||||
|
condition "github.com/HaimKortovich/zitadel-k8s-operator/pkg/condition"
|
||||||
|
"github.com/HaimKortovich/zitadel-k8s-operator/pkg/controller/core"
|
||||||
|
clientv2 "github.com/zitadel/zitadel-go/v3/pkg/client"
|
||||||
|
"github.com/zitadel/zitadel-go/v3/pkg/client/zitadel/object/v2"
|
||||||
|
"github.com/zitadel/zitadel-go/v3/pkg/client/zitadel/org/v2"
|
||||||
|
"k8s.io/client-go/util/workqueue"
|
||||||
|
ctrl "sigs.k8s.io/controller-runtime"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
ctrlClient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||||
|
)
|
||||||
|
|
||||||
|
// OrganizationReconciler reconciles a Organization object
|
||||||
|
type OrganizationReconciler struct {
|
||||||
|
client.Client
|
||||||
|
RefResolver *zitadelv1alpha1.RefResolver
|
||||||
|
ConditionReady *condition.Ready
|
||||||
|
RequeueInterval time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewOrganizationReconciler(client client.Client, refResolver *zitadelv1alpha1.RefResolver, conditionReady *condition.Ready,
|
||||||
|
requeueInterval time.Duration) *OrganizationReconciler {
|
||||||
|
return &OrganizationReconciler{
|
||||||
|
Client: client,
|
||||||
|
RefResolver: refResolver,
|
||||||
|
ConditionReady: conditionReady,
|
||||||
|
RequeueInterval: requeueInterval,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//+kubebuilder:rbac:groups=zitadel.topmanage.com,resources=organizations,verbs=get;list;watch;create;update;patch;delete
|
||||||
|
//+kubebuilder:rbac:groups=zitadel.topmanage.com,resources=organizations/status,verbs=get;update;patch
|
||||||
|
//+kubebuilder:rbac:groups=zitadel.topmanage.com,resources=organizations/finalizers,verbs=update
|
||||||
|
|
||||||
|
// Reconcile is part of the main kubernetes reconciliation loop which aims to
|
||||||
|
// move the current state of the cluster closer to the desired state.
|
||||||
|
func (r *OrganizationReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||||
|
var organization zitadelv1alpha1.Organization
|
||||||
|
if err := r.Get(ctx, req.NamespacedName, &organization); err != nil {
|
||||||
|
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||||
|
}
|
||||||
|
wr := newWrappedOrganizationReconciler(r.Client, r.RefResolver, &organization)
|
||||||
|
wf := newWrappedOrganizationFinalizer(r.Client, &organization)
|
||||||
|
tf := core.NewCoreFinalizer(r.Client, wf)
|
||||||
|
tr := core.NewCoreReconciler(r.Client, r.ConditionReady, wr, tf, r.RequeueInterval)
|
||||||
|
|
||||||
|
result, err := tr.Reconcile(ctx, &organization)
|
||||||
|
if err != nil {
|
||||||
|
return result, fmt.Errorf("error reconciling in OrganizationReconciler: %v", err)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type wrappedOrganizationReconciler struct {
|
||||||
|
client.Client
|
||||||
|
refResolver *zitadelv1alpha1.RefResolver
|
||||||
|
organization *zitadelv1alpha1.Organization
|
||||||
|
}
|
||||||
|
|
||||||
|
func newWrappedOrganizationReconciler(client client.Client, refResolver *zitadelv1alpha1.RefResolver,
|
||||||
|
organization *zitadelv1alpha1.Organization) core.WrappedCoreReconciler {
|
||||||
|
return &wrappedOrganizationReconciler{
|
||||||
|
Client: client,
|
||||||
|
refResolver: refResolver,
|
||||||
|
organization: organization,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type orgReconcilePhase struct {
|
||||||
|
Name string
|
||||||
|
Reconcile func(context.Context, *clientv2.Client) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedOrganizationReconciler) Reconcile(ctx context.Context, ztdClient *clientv2.Client) error {
|
||||||
|
phases := []orgReconcilePhase{
|
||||||
|
{
|
||||||
|
Name: "organization",
|
||||||
|
Reconcile: wr.reconcileOrg,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, p := range phases {
|
||||||
|
err := p.Reconcile(ctx, ztdClient)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedOrganizationReconciler) reconcileOrg(ctx context.Context, ztdClient *clientv2.Client) error {
|
||||||
|
var organizationId *string
|
||||||
|
orgList, err := ztdClient.OrganizationServiceV2().ListOrganizations(ctx, &org.ListOrganizationsRequest{
|
||||||
|
Queries: []*org.SearchQuery{
|
||||||
|
{
|
||||||
|
Query: &org.SearchQuery_NameQuery{
|
||||||
|
NameQuery: &org.OrganizationNameQuery{
|
||||||
|
Name: wr.organization.Spec.OrganzationName,
|
||||||
|
Method: object.TextQueryMethod_TEXT_QUERY_METHOD_EQUALS,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error listing organization: %v", err)
|
||||||
|
}
|
||||||
|
if len(orgList.Result) > 0 {
|
||||||
|
organizationId = &orgList.Result[0].Id
|
||||||
|
}
|
||||||
|
|
||||||
|
if organizationId == nil {
|
||||||
|
resp, err :=
|
||||||
|
ztdClient.OrganizationServiceV2().AddOrganization(ctx, &org.AddOrganizationRequest{
|
||||||
|
Name: wr.organization.Spec.OrganzationName,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error creating organization: %v", err)
|
||||||
|
}
|
||||||
|
organizationId = &resp.OrganizationId
|
||||||
|
}
|
||||||
|
patch := ctrlClient.MergeFrom(wr.organization.DeepCopy())
|
||||||
|
wr.organization.Status.OrganizationId = organizationId
|
||||||
|
return wr.Client.Status().Patch(ctx, wr.organization, patch)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedOrganizationReconciler) PatchStatus(ctx context.Context, patcher condition.Patcher) error {
|
||||||
|
patch := client.MergeFrom(wr.organization.DeepCopy())
|
||||||
|
patcher(&wr.organization.Status)
|
||||||
|
|
||||||
|
if err := wr.Client.Status().Patch(ctx, wr.organization, patch); err != nil {
|
||||||
|
return fmt.Errorf("error patching Organization status: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetupWithManager sets up the controller with the Manager.
|
||||||
|
func (r *OrganizationReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||||
|
return ctrl.NewControllerManagedBy(mgr).
|
||||||
|
For(&zitadelv1alpha1.Organization{}).
|
||||||
|
WithOptions(controller.Options{RateLimiter: workqueue.NewTypedItemExponentialFailureRateLimiter[reconcile.Request](time.Millisecond*500, time.Minute*3)}).
|
||||||
|
Complete(r)
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
zitadelv1alpha1 "github.com/HaimKortovich/zitadel-k8s-operator/api/v1alpha1"
|
||||||
|
"github.com/HaimKortovich/zitadel-k8s-operator/pkg/controller/core"
|
||||||
|
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
clientv2 "github.com/zitadel/zitadel-go/v3/pkg/client"
|
||||||
|
"github.com/zitadel/zitadel-go/v3/pkg/client/zitadel/org/v2"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
ctrlClient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
organizationFinalizerName = "organization.zitadel.github.com/organization"
|
||||||
|
)
|
||||||
|
|
||||||
|
type wrappedOrganizationFinalizer struct {
|
||||||
|
client.Client
|
||||||
|
organization *zitadelv1alpha1.Organization
|
||||||
|
}
|
||||||
|
|
||||||
|
func newWrappedOrganizationFinalizer(client client.Client, organization *zitadelv1alpha1.Organization) core.WrappedCoreFinalizer {
|
||||||
|
return &wrappedOrganizationFinalizer{
|
||||||
|
Client: client,
|
||||||
|
organization: organization,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wf *wrappedOrganizationFinalizer) AddFinalizer(ctx context.Context) error {
|
||||||
|
if wf.ContainsFinalizer() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return wf.patch(ctx, wf.organization, func(organization *zitadelv1alpha1.Organization) {
|
||||||
|
controllerutil.AddFinalizer(organization, organizationFinalizerName)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wf *wrappedOrganizationFinalizer) RemoveFinalizer(ctx context.Context) error {
|
||||||
|
if !wf.ContainsFinalizer() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return wf.patch(ctx, wf.organization, func(organization *zitadelv1alpha1.Organization) {
|
||||||
|
controllerutil.RemoveFinalizer(wf.organization, organizationFinalizerName)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedOrganizationFinalizer) ContainsFinalizer() bool {
|
||||||
|
return controllerutil.ContainsFinalizer(wr.organization, organizationFinalizerName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wf *wrappedOrganizationFinalizer) Reconcile(ctx context.Context, ztdClient *clientv2.Client) error {
|
||||||
|
if wf.organization.Status.OrganizationId != nil {
|
||||||
|
if _, err := ztdClient.OrganizationServiceV2().DeleteOrganization(ctx,
|
||||||
|
&org.DeleteOrganizationRequest{
|
||||||
|
OrganizationId: *wf.organization.Status.OrganizationId,
|
||||||
|
},
|
||||||
|
); err != nil {
|
||||||
|
return fmt.Errorf("Error deleting organization: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedOrganizationFinalizer) patch(ctx context.Context, organization *zitadelv1alpha1.Organization,
|
||||||
|
patchFn func(*zitadelv1alpha1.Organization)) error {
|
||||||
|
patch := ctrlClient.MergeFrom(organization.DeepCopy())
|
||||||
|
patchFn(organization)
|
||||||
|
|
||||||
|
if err := wr.Client.Patch(ctx, organization, patch); err != nil {
|
||||||
|
return fmt.Errorf("error patching Organization finalizer: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
314
src/internal/controller/project_controller.gold
Normal file
314
src/internal/controller/project_controller.gold
Normal file
@@ -0,0 +1,314 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2024.
|
||||||
|
|
||||||
|
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"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
zitadelv1alpha1 "github.com/HaimKortovich/zitadel-k8s-operator/api/v1alpha1"
|
||||||
|
condition "github.com/HaimKortovich/zitadel-k8s-operator/pkg/condition"
|
||||||
|
"github.com/HaimKortovich/zitadel-k8s-operator/pkg/controller/core"
|
||||||
|
|
||||||
|
clientv2 "github.com/zitadel/zitadel-go/v3/pkg/client"
|
||||||
|
"github.com/zitadel/zitadel-go/v3/pkg/client/zitadel/filter/v2"
|
||||||
|
"github.com/zitadel/zitadel-go/v3/pkg/client/zitadel/project/v2"
|
||||||
|
"k8s.io/client-go/util/workqueue"
|
||||||
|
ctrl "sigs.k8s.io/controller-runtime"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
ctrlClient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProjectReconciler reconciles a Project object
|
||||||
|
type ProjectReconciler struct {
|
||||||
|
client.Client
|
||||||
|
RefResolver *zitadelv1alpha1.RefResolver
|
||||||
|
ConditionReady *condition.Ready
|
||||||
|
RequeueInterval time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewProjectReconciler(client client.Client, refResolver *zitadelv1alpha1.RefResolver, conditionReady *condition.Ready,
|
||||||
|
requeueInterval time.Duration) *ProjectReconciler {
|
||||||
|
return &ProjectReconciler{
|
||||||
|
Client: client,
|
||||||
|
RefResolver: refResolver,
|
||||||
|
ConditionReady: conditionReady,
|
||||||
|
RequeueInterval: requeueInterval,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//+kubebuilder:rbac:groups=zitadel.topmanage.com,resources=projects,verbs=get;list;watch;create;update;patch;delete
|
||||||
|
//+kubebuilder:rbac:groups=zitadel.topmanage.com,resources=projects/status,verbs=get;update;patch
|
||||||
|
//+kubebuilder:rbac:groups=zitadel.topmanage.com,resources=projects/finalizers,verbs=update
|
||||||
|
|
||||||
|
// Reconcile is part of the main kubernetes reconciliation loop which aims to
|
||||||
|
// move the current state of the cluster closer to the desired state.
|
||||||
|
func (r *ProjectReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||||
|
var project zitadelv1alpha1.Project
|
||||||
|
if err := r.Get(ctx, req.NamespacedName, &project); err != nil {
|
||||||
|
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||||
|
}
|
||||||
|
wr := newWrappedProjectReconciler(r.Client, r.RefResolver, &project)
|
||||||
|
wf := newWrappedProjectFinalizer(r.Client, &project, r.RefResolver)
|
||||||
|
tf := core.NewCoreFinalizer(r.Client, wf)
|
||||||
|
tr := core.NewCoreReconciler(r.Client, r.ConditionReady, wr, tf, r.RequeueInterval)
|
||||||
|
|
||||||
|
result, err := tr.Reconcile(ctx, &project)
|
||||||
|
if err != nil {
|
||||||
|
return result, fmt.Errorf("error reconciling in ProjectReconciler: %v", err)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type wrappedProjectReconciler struct {
|
||||||
|
client.Client
|
||||||
|
refResolver *zitadelv1alpha1.RefResolver
|
||||||
|
project *zitadelv1alpha1.Project
|
||||||
|
}
|
||||||
|
|
||||||
|
func newWrappedProjectReconciler(client client.Client, refResolver *zitadelv1alpha1.RefResolver,
|
||||||
|
project *zitadelv1alpha1.Project) core.WrappedCoreReconciler {
|
||||||
|
return &wrappedProjectReconciler{
|
||||||
|
Client: client,
|
||||||
|
refResolver: refResolver,
|
||||||
|
project: project,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type projectReconcilePhase struct {
|
||||||
|
Name string
|
||||||
|
Reconcile func(context.Context, *clientv2.Client) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedProjectReconciler) Reconcile(ctx context.Context, ztdClient *clientv2.Client) error {
|
||||||
|
phases := []projectReconcilePhase{
|
||||||
|
{
|
||||||
|
Name: "project",
|
||||||
|
Reconcile: wr.reconcileProject,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "roles",
|
||||||
|
Reconcile: wr.reconcileRoles,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "grants",
|
||||||
|
Reconcile: wr.reconcileGrants,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, p := range phases {
|
||||||
|
err := p.Reconcile(ctx, ztdClient)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedProjectReconciler) reconcileProject(ctx context.Context, ztdClient *clientv2.Client) error {
|
||||||
|
org, err := wr.refResolver.OrganizationRef(ctx, &wr.project.Spec.OrganizationRef, wr.project.Namespace)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if org.Status.OrganizationId == nil {
|
||||||
|
return fmt.Errorf("Organization not created yet")
|
||||||
|
}
|
||||||
|
var projectId *string
|
||||||
|
projectList, err := ztdClient.ProjectServiceV2().ListProjects(ctx, &project.ListProjectsRequest{
|
||||||
|
Filters: []*project.ProjectSearchFilter{
|
||||||
|
&project.ProjectSearchFilter{
|
||||||
|
Filter: &project.ProjectSearchFilter_ProjectNameFilter{
|
||||||
|
ProjectNameFilter: &project.ProjectNameFilter{
|
||||||
|
ProjectName: wr.project.Spec.ProjectName,
|
||||||
|
Method: filter.TextFilterMethod_TEXT_FILTER_METHOD_EQUALS,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&project.ProjectSearchFilter{
|
||||||
|
Filter: &project.ProjectSearchFilter_OrganizationIdFilter{
|
||||||
|
OrganizationIdFilter: &project.ProjectOrganizationIDFilter{
|
||||||
|
OrganizationId: *org.Status.OrganizationId,
|
||||||
|
Type: project.ProjectOrganizationIDFilter_OWNED,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error listing project: %v", err)
|
||||||
|
}
|
||||||
|
if len(projectList.Projects) > 0 {
|
||||||
|
projectId = &projectList.Projects[0].ProjectId
|
||||||
|
}
|
||||||
|
|
||||||
|
if projectId == nil {
|
||||||
|
resp, err :=
|
||||||
|
ztdClient.ProjectServiceV2().CreateProject(ctx,
|
||||||
|
&project.CreateProjectRequest{
|
||||||
|
OrganizationId: *org.Status.OrganizationId,
|
||||||
|
Name: wr.project.Spec.ProjectName,
|
||||||
|
ProjectRoleAssertion: wr.project.Spec.ProjectRoleAssertion,
|
||||||
|
AuthorizationRequired: wr.project.Spec.ProjectRoleCheck,
|
||||||
|
ProjectAccessRequired: wr.project.Spec.HasProjectCheck,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error creating project: %v", err)
|
||||||
|
}
|
||||||
|
projectId = &resp.ProjectId
|
||||||
|
}
|
||||||
|
patch := ctrlClient.MergeFrom(wr.project.DeepCopy())
|
||||||
|
wr.project.Status.ProjectId = projectId
|
||||||
|
return wr.Client.Status().Patch(ctx, wr.project, patch)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedProjectReconciler) reconcileRoles(ctx context.Context, ztdClient *clientv2.Client) error {
|
||||||
|
resp, err := ztdClient.ProjectServiceV2().ListProjectRoles(ctx, &project.ListProjectRolesRequest{
|
||||||
|
ProjectId: *wr.project.Status.ProjectId,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Could not list project roles: %v", err)
|
||||||
|
}
|
||||||
|
roles := map[string]*project.ProjectRole{}
|
||||||
|
deleteRolesKeys := []string{}
|
||||||
|
for _, role := range wr.project.Spec.Roles {
|
||||||
|
roles[role.Key] = &project.ProjectRole{
|
||||||
|
Key: role.Key,
|
||||||
|
DisplayName: role.DisplayName,
|
||||||
|
Group: role.Group,
|
||||||
|
ProjectId: *wr.project.Status.ProjectId,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, role := range resp.ProjectRoles {
|
||||||
|
if r, ok := roles[role.Key]; ok {
|
||||||
|
if r.DisplayName != role.DisplayName || r.Group != role.Group {
|
||||||
|
deleteRolesKeys = append(deleteRolesKeys, role.Key)
|
||||||
|
} else {
|
||||||
|
delete(roles, role.Key)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
deleteRolesKeys = append(deleteRolesKeys, role.Key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(deleteRolesKeys) > 0 {
|
||||||
|
for _, key := range deleteRolesKeys {
|
||||||
|
if _, err = ztdClient.ProjectServiceV2().RemoveProjectRole(ctx, &project.RemoveProjectRoleRequest{
|
||||||
|
ProjectId: *wr.project.Status.ProjectId,
|
||||||
|
RoleKey: key,
|
||||||
|
}); err != nil {
|
||||||
|
return fmt.Errorf("Error removing project role: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(roles) > 0 {
|
||||||
|
for _, value := range roles {
|
||||||
|
if _, err = ztdClient.ProjectServiceV2().AddProjectRole(ctx, &project.AddProjectRoleRequest{
|
||||||
|
ProjectId: *wr.project.Status.ProjectId,
|
||||||
|
RoleKey: value.Key,
|
||||||
|
DisplayName: value.DisplayName,
|
||||||
|
Group: &value.Group,
|
||||||
|
}); err != nil {
|
||||||
|
return fmt.Errorf("Error adding project role: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedProjectReconciler) reconcileGrants(ctx context.Context, ztdClient *clientv2.Client) error {
|
||||||
|
existingGrants, err := ztdClient.ProjectServiceV2().ListProjectGrants(ctx, &project.ListProjectGrantsRequest{
|
||||||
|
Filters: []*project.ProjectGrantSearchFilter{
|
||||||
|
{
|
||||||
|
Filter: &project.ProjectGrantSearchFilter_InProjectIdsFilter{
|
||||||
|
InProjectIdsFilter: &filter.InIDsFilter{
|
||||||
|
Ids: []string{
|
||||||
|
*wr.project.Status.ProjectId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error listing project grants: %v", err)
|
||||||
|
}
|
||||||
|
for _, grant := range wr.project.DeepCopy().Spec.Grants {
|
||||||
|
grantedOrg, err := wr.refResolver.OrganizationRef(ctx, &grant.OrganizationRef, wr.project.Namespace)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if grantedOrg.Status.OrganizationId == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var existingGrant *project.ProjectGrant
|
||||||
|
for _, eGrant := range existingGrants.ProjectGrants {
|
||||||
|
if eGrant.GrantedOrganizationId == *grantedOrg.Status.OrganizationId {
|
||||||
|
existingGrant = eGrant
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if existingGrant == nil {
|
||||||
|
_, err := ztdClient.ProjectServiceV2().CreateProjectGrant(ctx, &project.CreateProjectGrantRequest{
|
||||||
|
ProjectId: *wr.project.Status.ProjectId,
|
||||||
|
GrantedOrganizationId: *grantedOrg.Status.OrganizationId,
|
||||||
|
RoleKeys: grant.RoleKeys,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error Adding project grant: %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sort.Strings(existingGrant.GrantedRoleKeys)
|
||||||
|
sort.Strings(grant.RoleKeys)
|
||||||
|
if !reflect.DeepEqual(existingGrant.GrantedRoleKeys, grant.RoleKeys) {
|
||||||
|
_, err := ztdClient.ProjectServiceV2().UpdateProjectGrant(ctx, &project.UpdateProjectGrantRequest{
|
||||||
|
ProjectId: *wr.project.Status.ProjectId,
|
||||||
|
GrantedOrganizationId: existingGrant.GrantedOrganizationId,
|
||||||
|
RoleKeys: grant.RoleKeys,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error Updating project grant: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedProjectReconciler) PatchStatus(ctx context.Context, patcher condition.Patcher) error {
|
||||||
|
patch := client.MergeFrom(wr.project.DeepCopy())
|
||||||
|
patcher(&wr.project.Status)
|
||||||
|
|
||||||
|
if err := wr.Client.Status().Patch(ctx, wr.project, patch); err != nil {
|
||||||
|
return fmt.Errorf("error patching Project status: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetupWithManager sets up the controller with the Manager.
|
||||||
|
func (r *ProjectReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||||
|
return ctrl.NewControllerManagedBy(mgr).
|
||||||
|
For(&zitadelv1alpha1.Project{}).
|
||||||
|
WithOptions(controller.Options{RateLimiter: workqueue.NewTypedItemExponentialFailureRateLimiter[reconcile.Request](time.Millisecond*500, time.Minute*3)}).
|
||||||
|
Complete(r)
|
||||||
|
}
|
||||||
76
src/internal/controller/project_controller_finalizer.gold
Normal file
76
src/internal/controller/project_controller_finalizer.gold
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
package controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
zitadelv1alpha1 "github.com/HaimKortovich/zitadel-k8s-operator/api/v1alpha1"
|
||||||
|
"github.com/HaimKortovich/zitadel-k8s-operator/pkg/controller/core"
|
||||||
|
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
clientv2 "github.com/zitadel/zitadel-go/v3/pkg/client"
|
||||||
|
"github.com/zitadel/zitadel-go/v3/pkg/client/zitadel/project/v2"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
ctrlClient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
projectFinalizerName = "project.zitadel.github.com/project"
|
||||||
|
)
|
||||||
|
|
||||||
|
type wrappedProjectFinalizer struct {
|
||||||
|
client.Client
|
||||||
|
project *zitadelv1alpha1.Project
|
||||||
|
refresolver *zitadelv1alpha1.RefResolver
|
||||||
|
}
|
||||||
|
|
||||||
|
func newWrappedProjectFinalizer(client client.Client, project *zitadelv1alpha1.Project, refresolver *zitadelv1alpha1.RefResolver) core.WrappedCoreFinalizer {
|
||||||
|
return &wrappedProjectFinalizer{
|
||||||
|
Client: client,
|
||||||
|
project: project,
|
||||||
|
refresolver: refresolver,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wf *wrappedProjectFinalizer) AddFinalizer(ctx context.Context) error {
|
||||||
|
if wf.ContainsFinalizer() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return wf.patch(ctx, wf.project, func(project *zitadelv1alpha1.Project) {
|
||||||
|
controllerutil.AddFinalizer(project, projectFinalizerName)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wf *wrappedProjectFinalizer) RemoveFinalizer(ctx context.Context) error {
|
||||||
|
if !wf.ContainsFinalizer() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return wf.patch(ctx, wf.project, func(project *zitadelv1alpha1.Project) {
|
||||||
|
controllerutil.RemoveFinalizer(wf.project, projectFinalizerName)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedProjectFinalizer) ContainsFinalizer() bool {
|
||||||
|
return controllerutil.ContainsFinalizer(wr.project, projectFinalizerName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wf *wrappedProjectFinalizer) Reconcile(ctx context.Context, ztdClient *clientv2.Client) error {
|
||||||
|
if wf.project.Status.ProjectId != nil {
|
||||||
|
_, err := ztdClient.ProjectServiceV2().DeleteProject(ctx, &project.DeleteProjectRequest{ProjectId: *wf.project.Status.ProjectId})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *wrappedProjectFinalizer) patch(ctx context.Context, project *zitadelv1alpha1.Project,
|
||||||
|
patchFn func(*zitadelv1alpha1.Project)) error {
|
||||||
|
patch := ctrlClient.MergeFrom(project.DeepCopy())
|
||||||
|
patchFn(project)
|
||||||
|
|
||||||
|
if err := wr.Client.Patch(ctx, project, patch); err != nil {
|
||||||
|
return fmt.Errorf("error patching Project finalizer: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
80
src/internal/controller/suite_test.go
Normal file
80
src/internal/controller/suite_test.go
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2024.
|
||||||
|
|
||||||
|
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 (
|
||||||
|
"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"
|
||||||
|
|
||||||
|
zitadelv1alpha1 "github.com/HaimKortovich/zitadel-resources-operator/api/v1alpha1"
|
||||||
|
//+kubebuilder:scaffold:imports
|
||||||
|
)
|
||||||
|
|
||||||
|
// These tests use Ginkgo (BDD-style Go testing framework). Refer to
|
||||||
|
// http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
|
||||||
|
|
||||||
|
var cfg *rest.Config
|
||||||
|
var k8sClient client.Client
|
||||||
|
var testEnv *envtest.Environment
|
||||||
|
|
||||||
|
func TestAPIs(t *testing.T) {
|
||||||
|
RegisterFailHandler(Fail)
|
||||||
|
|
||||||
|
RunSpecs(t, "Controller Suite")
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = BeforeSuite(func() {
|
||||||
|
logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
|
||||||
|
|
||||||
|
By("bootstrapping test environment")
|
||||||
|
testEnv = &envtest.Environment{
|
||||||
|
CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")},
|
||||||
|
ErrorIfCRDPathMissing: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
// cfg is defined in this file globally.
|
||||||
|
cfg, err = testEnv.Start()
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(cfg).NotTo(BeNil())
|
||||||
|
|
||||||
|
err = zitadelv1alpha1.AddToScheme(scheme.Scheme)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
//+kubebuilder:scaffold:scheme
|
||||||
|
|
||||||
|
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")
|
||||||
|
err := testEnv.Stop()
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
})
|
||||||
15
src/pkg/builder/builder.go
Normal file
15
src/pkg/builder/builder.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package builder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Builder struct {
|
||||||
|
scheme *runtime.Scheme
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBuilder(scheme *runtime.Scheme) *Builder {
|
||||||
|
return &Builder{
|
||||||
|
scheme: scheme,
|
||||||
|
}
|
||||||
|
}
|
||||||
21
src/pkg/builder/labels/labels.go
Normal file
21
src/pkg/builder/labels/labels.go
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package builder
|
||||||
|
|
||||||
|
type LabelsBuilder struct {
|
||||||
|
labels map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLabelsBuilder() *LabelsBuilder {
|
||||||
|
return &LabelsBuilder{
|
||||||
|
labels: map[string]string{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (b *LabelsBuilder) WithLabels(labels map[string]string) *LabelsBuilder {
|
||||||
|
for k, v := range labels {
|
||||||
|
b.labels[k] = v
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *LabelsBuilder) Build() map[string]string {
|
||||||
|
return b.labels
|
||||||
|
}
|
||||||
39
src/pkg/builder/metadata/metadata.go
Normal file
39
src/pkg/builder/metadata/metadata.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MetadataBuilder struct {
|
||||||
|
objMeta metav1.ObjectMeta
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMetadataBuilder(key types.NamespacedName) *MetadataBuilder {
|
||||||
|
return &MetadataBuilder{
|
||||||
|
objMeta: metav1.ObjectMeta{
|
||||||
|
Name: key.Name,
|
||||||
|
Namespace: key.Namespace,
|
||||||
|
Labels: map[string]string{},
|
||||||
|
Annotations: map[string]string{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *MetadataBuilder) WithLabels(labels map[string]string) *MetadataBuilder {
|
||||||
|
for k, v := range labels {
|
||||||
|
b.objMeta.Labels[k] = v
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *MetadataBuilder) WithAnnotations(annotations map[string]string) *MetadataBuilder {
|
||||||
|
for k, v := range annotations {
|
||||||
|
b.objMeta.Annotations[k] = v
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *MetadataBuilder) Build() metav1.ObjectMeta {
|
||||||
|
return b.objMeta
|
||||||
|
}
|
||||||
35
src/pkg/builder/secret_builder.go
Normal file
35
src/pkg/builder/secret_builder.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package builder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
metadata "github.com/HaimKortovich/zitadel-resources-operator/pkg/builder/metadata"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SecretOpts struct {
|
||||||
|
Key types.NamespacedName
|
||||||
|
Data map[string][]byte
|
||||||
|
Labels map[string]string
|
||||||
|
Annotations map[string]string
|
||||||
|
Immutable bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Builder) BuildSecret(opts SecretOpts, owner metav1.Object) (*corev1.Secret, error) {
|
||||||
|
objMeta :=
|
||||||
|
metadata.NewMetadataBuilder(opts.Key).
|
||||||
|
WithLabels(opts.Labels).
|
||||||
|
WithAnnotations(opts.Annotations).
|
||||||
|
Build()
|
||||||
|
secret := &corev1.Secret{
|
||||||
|
ObjectMeta: objMeta,
|
||||||
|
Data: opts.Data,
|
||||||
|
Immutable: &opts.Immutable,
|
||||||
|
}
|
||||||
|
if err := controllerutil.SetControllerReference(owner, secret, b.scheme); err != nil {
|
||||||
|
return nil, fmt.Errorf("error setting controller reference in Secret manifest: %v", err)
|
||||||
|
}
|
||||||
|
return secret, nil
|
||||||
|
}
|
||||||
68
src/pkg/condition/condition.go
Normal file
68
src/pkg/condition/condition.go
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
package conditions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Conditioner interface {
|
||||||
|
SetCondition(condition metav1.Condition)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Patcher func(Conditioner)
|
||||||
|
|
||||||
|
type Ready struct{}
|
||||||
|
|
||||||
|
func NewReady() *Ready {
|
||||||
|
return &Ready{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Ready) PatcherFailed(msg string) Patcher {
|
||||||
|
return func(c Conditioner) {
|
||||||
|
SetReadyFailedWithMessage(c, msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Ready) PatcherWithError(err error) Patcher {
|
||||||
|
return func(c Conditioner) {
|
||||||
|
if err == nil {
|
||||||
|
SetReadyCreated(c)
|
||||||
|
} else {
|
||||||
|
SetReadyFailed(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Ready) PatcherRefResolver(err error, obj interface{}) Patcher {
|
||||||
|
return func(c Conditioner) {
|
||||||
|
if err == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if apierrors.IsNotFound(err) {
|
||||||
|
SetReadyFailedWithMessage(c, fmt.Sprintf("%s not found", getType(obj)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
SetReadyFailedWithMessage(c, fmt.Sprintf("Error getting %s", getType(obj)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Ready) PatcherHealthy(err error) Patcher {
|
||||||
|
return func(c Conditioner) {
|
||||||
|
if err == nil {
|
||||||
|
SetReadyHealthty(c)
|
||||||
|
} else {
|
||||||
|
SetReadyUnhealthtyWithError(c, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getType(obj interface{}) string {
|
||||||
|
if t := reflect.TypeOf(obj); t.Kind() == reflect.Ptr {
|
||||||
|
return t.Elem().Name()
|
||||||
|
} else {
|
||||||
|
return t.Name()
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/pkg/condition/pat.go
Normal file
25
src/pkg/condition/pat.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package conditions
|
||||||
|
|
||||||
|
import (
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
|
zitadelv1alpha1 "github.com/HaimKortovich/zitadel-resources-operator/api/v1alpha1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SetPatOutOfDate(c Conditioner) {
|
||||||
|
c.SetCondition(metav1.Condition{
|
||||||
|
Type: zitadelv1alpha1.ConditionTypePATUpToDate,
|
||||||
|
Status: metav1.ConditionFalse,
|
||||||
|
Reason: zitadelv1alpha1.ConditionReasonRolesChanged,
|
||||||
|
Message: "PAT out of date",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetPatUpToDate(c Conditioner) {
|
||||||
|
c.SetCondition(metav1.Condition{
|
||||||
|
Type: zitadelv1alpha1.ConditionTypePATUpToDate,
|
||||||
|
Status: metav1.ConditionTrue,
|
||||||
|
Reason: zitadelv1alpha1.ConditionReasonPATUpToDate,
|
||||||
|
Message: "PAT up to date",
|
||||||
|
})
|
||||||
|
}
|
||||||
70
src/pkg/condition/ready.go
Normal file
70
src/pkg/condition/ready.go
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
package conditions
|
||||||
|
|
||||||
|
import (
|
||||||
|
appsv1 "k8s.io/api/apps/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
|
zitadelv1alpha1 "github.com/HaimKortovich/zitadel-resources-operator/api/v1alpha1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SetReadyHealthty(c Conditioner) {
|
||||||
|
c.SetCondition(metav1.Condition{
|
||||||
|
Type: zitadelv1alpha1.ConditionTypeReady,
|
||||||
|
Status: metav1.ConditionTrue,
|
||||||
|
Reason: zitadelv1alpha1.ConditionReasonHealthy,
|
||||||
|
Message: "Healthy",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetReadyUnhealthtyWithError(c Conditioner, err error) {
|
||||||
|
c.SetCondition(metav1.Condition{
|
||||||
|
Type: zitadelv1alpha1.ConditionTypeReady,
|
||||||
|
Status: metav1.ConditionFalse,
|
||||||
|
Reason: zitadelv1alpha1.ConditionReasonHealthy,
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetReadyCreatedWithMessage(c Conditioner, message string) {
|
||||||
|
c.SetCondition(metav1.Condition{
|
||||||
|
Type: zitadelv1alpha1.ConditionTypeReady,
|
||||||
|
Status: metav1.ConditionTrue,
|
||||||
|
Reason: zitadelv1alpha1.ConditionReasonCreated,
|
||||||
|
Message: message,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetReadyCreated(c Conditioner) {
|
||||||
|
SetReadyCreatedWithMessage(c, "Created")
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetReadyFailedWithMessage(c Conditioner, message string) {
|
||||||
|
c.SetCondition(metav1.Condition{
|
||||||
|
Type: zitadelv1alpha1.ConditionTypeReady,
|
||||||
|
Status: metav1.ConditionFalse,
|
||||||
|
Reason: zitadelv1alpha1.ConditionReasonFailed,
|
||||||
|
Message: message,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetReadyFailed(c Conditioner) {
|
||||||
|
SetReadyFailedWithMessage(c, "Failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetReadyWithDeployment(c Conditioner, sts *appsv1.Deployment) {
|
||||||
|
if sts.Status.Replicas == 0 || sts.Status.ReadyReplicas != sts.Status.Replicas {
|
||||||
|
c.SetCondition(metav1.Condition{
|
||||||
|
Type: zitadelv1alpha1.ConditionTypeReady,
|
||||||
|
Status: metav1.ConditionFalse,
|
||||||
|
Reason: zitadelv1alpha1.ConditionReasonDeploymentNotReady,
|
||||||
|
Message: "Not ready",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.SetCondition(metav1.Condition{
|
||||||
|
Type: zitadelv1alpha1.ConditionTypeReady,
|
||||||
|
Status: metav1.ConditionTrue,
|
||||||
|
Reason: zitadelv1alpha1.ConditionReasonDeploymentReady,
|
||||||
|
Message: "Running",
|
||||||
|
})
|
||||||
|
}
|
||||||
111
src/pkg/controller/core/controller.go
Normal file
111
src/pkg/controller/core/controller.go
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
zitadelv1alpha1 "github.com/HaimKortovich/zitadel-resources-operator/api/v1alpha1"
|
||||||
|
condition "github.com/HaimKortovich/zitadel-resources-operator/pkg/condition"
|
||||||
|
zitadelClient "github.com/HaimKortovich/zitadel-resources-operator/pkg/zitadel"
|
||||||
|
"github.com/hashicorp/go-multierror"
|
||||||
|
ctrl "sigs.k8s.io/controller-runtime"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CoreReconciler struct {
|
||||||
|
Client client.Client
|
||||||
|
RefResolver *zitadelv1alpha1.RefResolver
|
||||||
|
ConditionReady *condition.Ready
|
||||||
|
|
||||||
|
WrappedReconciler WrappedCoreReconciler
|
||||||
|
Finalizer Finalizer
|
||||||
|
RequeueInterval time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCoreReconciler(client client.Client, cr *condition.Ready, wr WrappedCoreReconciler, f Finalizer,
|
||||||
|
requeueInterval time.Duration) Reconciler {
|
||||||
|
return &CoreReconciler{
|
||||||
|
Client: client,
|
||||||
|
RefResolver: zitadelv1alpha1.NewRefResolver(client),
|
||||||
|
ConditionReady: cr,
|
||||||
|
WrappedReconciler: wr,
|
||||||
|
Finalizer: f,
|
||||||
|
RequeueInterval: requeueInterval,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *CoreReconciler) Reconcile(ctx context.Context, resource Resource) (ctrl.Result, error) {
|
||||||
|
if resource.IsBeingDeleted() {
|
||||||
|
if err := r.Finalizer.Finalize(ctx, resource); err != nil {
|
||||||
|
return ctrl.Result{}, fmt.Errorf("error finalizing %s: %v", resource.GetName(), err)
|
||||||
|
}
|
||||||
|
return ctrl.Result{}, nil
|
||||||
|
}
|
||||||
|
connectionRef, err := resource.ConnectionRef(ctx, r.RefResolver)
|
||||||
|
if err != nil {
|
||||||
|
return ctrl.Result{}, err
|
||||||
|
}
|
||||||
|
connection, err := r.RefResolver.ConnectionRef(ctx, connectionRef, resource.GetNamespace())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
var errBundle *multierror.Error
|
||||||
|
errBundle = multierror.Append(errBundle, err)
|
||||||
|
|
||||||
|
err = r.WrappedReconciler.PatchStatus(ctx, r.ConditionReady.PatcherRefResolver(err, connection))
|
||||||
|
errBundle = multierror.Append(errBundle, err)
|
||||||
|
|
||||||
|
return ctrl.Result{}, fmt.Errorf("error getting Connection: %v", errBundle)
|
||||||
|
}
|
||||||
|
|
||||||
|
ztdClient, err := zitadelClient.NewV2Client(ctx, connection, *r.RefResolver)
|
||||||
|
if err != nil {
|
||||||
|
var errBundle *multierror.Error
|
||||||
|
errBundle = multierror.Append(errBundle, err)
|
||||||
|
|
||||||
|
msg := fmt.Sprintf("Error connecting to Zitadel: %v", err)
|
||||||
|
err = r.WrappedReconciler.PatchStatus(ctx, r.ConditionReady.PatcherFailed(msg))
|
||||||
|
errBundle = multierror.Append(errBundle, err)
|
||||||
|
|
||||||
|
return r.retryResult(ctx, resource, errBundle)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer ztdClient.Close()
|
||||||
|
|
||||||
|
err = r.WrappedReconciler.Reconcile(ctx, ztdClient)
|
||||||
|
var errBundle *multierror.Error
|
||||||
|
errBundle = multierror.Append(errBundle, err)
|
||||||
|
|
||||||
|
if err := errBundle.ErrorOrNil(); err != nil {
|
||||||
|
msg := fmt.Sprintf("Error creating %s: %v", resource.GetName(), err)
|
||||||
|
err = r.WrappedReconciler.PatchStatus(ctx, r.ConditionReady.PatcherFailed(msg))
|
||||||
|
errBundle = multierror.Append(errBundle, err)
|
||||||
|
|
||||||
|
return r.retryResult(ctx, resource, errBundle)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = r.Finalizer.AddFinalizer(ctx); err != nil {
|
||||||
|
errBundle = multierror.Append(errBundle, fmt.Errorf("error adding finalizer to %s: %v", resource.GetName(), err))
|
||||||
|
}
|
||||||
|
|
||||||
|
err = r.WrappedReconciler.PatchStatus(ctx, r.ConditionReady.PatcherWithError(errBundle.ErrorOrNil()))
|
||||||
|
errBundle = multierror.Append(errBundle, err)
|
||||||
|
|
||||||
|
if err := errBundle.ErrorOrNil(); err != nil {
|
||||||
|
return ctrl.Result{}, err
|
||||||
|
}
|
||||||
|
return r.requeueResult(ctx, resource)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *CoreReconciler) retryResult(ctx context.Context, resource Resource, err error) (ctrl.Result, error) {
|
||||||
|
return ctrl.Result{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *CoreReconciler) requeueResult(ctx context.Context, resource Resource) (ctrl.Result, error) {
|
||||||
|
if r.RequeueInterval > 0 {
|
||||||
|
log.FromContext(ctx).V(1).Info("Requeuing CORE resource")
|
||||||
|
return ctrl.Result{RequeueAfter: r.RequeueInterval}, nil
|
||||||
|
}
|
||||||
|
return ctrl.Result{}, nil
|
||||||
|
}
|
||||||
73
src/pkg/controller/core/finalizer.go
Normal file
73
src/pkg/controller/core/finalizer.go
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
zitadelv1alpha1 "github.com/HaimKortovich/zitadel-resources-operator/api/v1alpha1"
|
||||||
|
zitadelClient "github.com/HaimKortovich/zitadel-resources-operator/pkg/zitadel"
|
||||||
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CoreFinalizer struct {
|
||||||
|
Client client.Client
|
||||||
|
RefResolver *zitadelv1alpha1.RefResolver
|
||||||
|
|
||||||
|
WrappedFinalizer WrappedCoreFinalizer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCoreFinalizer(client client.Client, wf WrappedCoreFinalizer) Finalizer {
|
||||||
|
return &CoreFinalizer{
|
||||||
|
Client: client,
|
||||||
|
RefResolver: zitadelv1alpha1.NewRefResolver(client),
|
||||||
|
WrappedFinalizer: wf,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tf *CoreFinalizer) AddFinalizer(ctx context.Context) error {
|
||||||
|
if tf.WrappedFinalizer.ContainsFinalizer() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err := tf.WrappedFinalizer.AddFinalizer(ctx); err != nil {
|
||||||
|
return fmt.Errorf("error adding finalizer in TemplateFinalizer: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tf *CoreFinalizer) Finalize(ctx context.Context, resource Resource) error {
|
||||||
|
if !tf.WrappedFinalizer.ContainsFinalizer() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
connectionRef, err := resource.ConnectionRef(ctx, tf.RefResolver)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
connection, err := tf.RefResolver.ConnectionRef(ctx, connectionRef, resource.GetNamespace())
|
||||||
|
if err != nil {
|
||||||
|
if apierrors.IsNotFound(err) {
|
||||||
|
if err := tf.WrappedFinalizer.RemoveFinalizer(ctx); err != nil {
|
||||||
|
return fmt.Errorf("error removing %s finalizer: %v", resource.GetName(), err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("error getting Cluster: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ztdClient, err := zitadelClient.NewV2Client(ctx, connection, *tf.RefResolver)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error connecting to Zitadel: %v", err)
|
||||||
|
}
|
||||||
|
defer ztdClient.Close()
|
||||||
|
|
||||||
|
if err := tf.WrappedFinalizer.Reconcile(ctx, ztdClient); err != nil {
|
||||||
|
return fmt.Errorf("error reconciling in TemplateFinalizer: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tf.WrappedFinalizer.RemoveFinalizer(ctx); err != nil {
|
||||||
|
return fmt.Errorf("error removing finalizer in TemplateFinalizer: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
39
src/pkg/controller/core/types.go
Normal file
39
src/pkg/controller/core/types.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
zitadelv1alpha1 "github.com/HaimKortovich/zitadel-resources-operator/api/v1alpha1"
|
||||||
|
"github.com/zitadel/zitadel-go/v3/pkg/client"
|
||||||
|
|
||||||
|
condition "github.com/HaimKortovich/zitadel-resources-operator/pkg/condition"
|
||||||
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
ctrl "sigs.k8s.io/controller-runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Resource interface {
|
||||||
|
v1.Object
|
||||||
|
ConnectionRef(context.Context, *zitadelv1alpha1.RefResolver) (*zitadelv1alpha1.ConnectionRef, error)
|
||||||
|
IsBeingDeleted() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type Reconciler interface {
|
||||||
|
Reconcile(ctx context.Context, resource Resource) (ctrl.Result, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type WrappedCoreReconciler interface {
|
||||||
|
Reconcile(context.Context, *client.Client) error
|
||||||
|
PatchStatus(context.Context, condition.Patcher) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type Finalizer interface {
|
||||||
|
AddFinalizer(context.Context) error
|
||||||
|
Finalize(context.Context, Resource) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type WrappedCoreFinalizer interface {
|
||||||
|
AddFinalizer(context.Context) error
|
||||||
|
RemoveFinalizer(context.Context) error
|
||||||
|
ContainsFinalizer() bool
|
||||||
|
Reconcile(context.Context, *client.Client) error
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user