diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml
index e6e7712..6b7ad79 100644
--- a/.github/workflows/pr.yaml
+++ b/.github/workflows/pr.yaml
@@ -12,7 +12,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@master
with:
- go-version: 1.17.x
+ go-version: 1.20.x
- name: Inject slug/short variables
uses: rlespinasse/github-slug-action@v4
@@ -53,4 +53,4 @@ jobs:
--platform linux/amd64,linux/arm64 \
--build-arg VERSION=pr-${GITHUB_REF_SLUG//-merge/} \
--push \
- -t ${DOCKER_REPO}:pr-${GITHUB_REF_SLUG//-merge/} .
\ No newline at end of file
+ -t ${DOCKER_REPO}:pr-${GITHUB_REF_SLUG//-merge/} .
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 78f6a70..1a40506 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -17,7 +17,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@master
with:
- go-version: 1.17.x
+ go-version: 1.20.x
- name: Inject slug/short variables
uses: rlespinasse/github-slug-action@v4
diff --git a/.gitignore b/.gitignore
index c0a7a54..8629446 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,7 @@
*.dylib
bin
testbin/*
+source
# Test binary, build with `go test -c`
*.test
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..9b44ff4
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "source/gitlab-runner"]
+ path = source/gitlab-runner
+ url = https://gitlab.com/gitlab-org/gitlab-runner/
diff --git a/.run/RunnerControllerTest.run.xml b/.run/RunnerControllerTest.run.xml
new file mode 100644
index 0000000..ce36f8f
--- /dev/null
+++ b/.run/RunnerControllerTest.run.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Makefile b/Makefile
index a4e7aa0..5e1ca13 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,8 @@
# Image URL to use all building/pushing image targets
IMG ?= controller:dev
+# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary.
+ENVTEST_K8S_VERSION = 1.25.0
# Produce CRDs that work back to Kubernetes 1.11 (no version conversion)
CRD_OPTIONS ?= "crd:trivialVersions=true,preserveUnknownFields=false"
@@ -18,6 +20,7 @@ endif
SHELL = /usr/bin/env bash -o pipefail
.SHELLFLAGS = -ec
+.PHONY: all
all: build
##@ General
@@ -33,77 +36,121 @@ all: build
# 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\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) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases
+ $(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 ./...
-ENVTEST_ASSETS_DIR=$(shell pwd)/testbin
-test: manifests generate fmt vet ## Run tests.
- mkdir -p ${ENVTEST_ASSETS_DIR}
- test -f ${ENVTEST_ASSETS_DIR}/setup-envtest.sh || curl -sSLo ${ENVTEST_ASSETS_DIR}/setup-envtest.sh https://raw.githubusercontent.com/kubernetes-sigs/controller-runtime/v0.8.3/hack/setup-envtest.sh
- source ${ENVTEST_ASSETS_DIR}/setup-envtest.sh; fetch_envtest_tools $(ENVTEST_ASSETS_DIR); setup_envtest_env $(ENVTEST_ASSETS_DIR); go test ./... -coverprofile cover.out
+.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: generate fmt vet ## Build manager binary.
go build -o bin/manager main.go
+.PHONY: run
run: manifests generate fmt vet ## Run a controller from your host.
go run ./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=> than 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 -
-uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config.
- $(KUSTOMIZE) build config/crd | kubectl delete -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 -
-undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config.
- $(KUSTOMIZE) build config/default | kubectl delete -f -
-
-
-CONTROLLER_GEN = $(shell pwd)/bin/controller-gen
-controller-gen: ## Download controller-gen locally if necessary.
- $(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.4.1)
-
-KUSTOMIZE = $(shell pwd)/bin/kustomize
-kustomize: ## Download kustomize locally if necessary.
- $(call go-get-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v3@v3.8.7)
-
-# go-get-tool will 'go get' any package $2 and install it to $1.
-PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST))))
-define go-get-tool
-@[ -f $(1) ] || { \
-set -e ;\
-TMP_DIR=$$(mktemp -d) ;\
-cd $$TMP_DIR ;\
-go mod init tmp ;\
-echo "Downloading $(2)" ;\
-GOBIN=$(PROJECT_DIR)/bin go get $(2) ;\
-rm -rf $$TMP_DIR ;\
-}
-endef
+.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 ?= v3.8.7
+CONTROLLER_TOOLS_VERSION ?= v0.9.2
+
+KUSTOMIZE_INSTALL_SCRIPT ?= "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh"
+.PHONY: kustomize
+kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary.
+$(KUSTOMIZE): $(LOCALBIN)
+ 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.
+$(CONTROLLER_GEN): $(LOCALBIN)
+ test -s $(LOCALBIN)/controller-gen || 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
diff --git a/PROJECT b/PROJECT
index 43efe8c..1b86722 100644
--- a/PROJECT
+++ b/PROJECT
@@ -17,4 +17,13 @@ resources:
defaulting: true
validation: true
webhookVersion: v1
+- api:
+ crdVersion: v1
+ namespaced: true
+ controller: true
+ domain: k8s.alekc.dev
+ group: gitlab
+ kind: MultiRunner
+ path: gitlab.k8s.alekc.dev/api/v1beta1
+ version: v1beta1
version: "3"
diff --git a/api/v1beta1/gitlab_types.go b/api/v1beta1/gitlab_types.go
new file mode 100644
index 0000000..04f5e8d
--- /dev/null
+++ b/api/v1beta1/gitlab_types.go
@@ -0,0 +1,320 @@
+package v1beta1
+
+import api "k8s.io/api/core/v1"
+
+type KubernetesConfig struct {
+ Host string `toml:"host" json:"host,omitempty" long:"host" env:"KUBERNETES_HOST" description:"Optional Kubernetes master host URL (auto-discovery attempted if not specified)"`
+ CertFile string `toml:"cert_file,omitempty" json:"cert_file,omitempty" long:"cert-file" env:"KUBERNETES_CERT_FILE" description:"Optional Kubernetes master auth certificate"`
+ KeyFile string `toml:"key_file,omitempty" json:"key_file,omitempty" long:"key-file" env:"KUBERNETES_KEY_FILE" description:"Optional Kubernetes master auth private key"`
+ CAFile string `toml:"ca_file,omitempty" json:"ca_file,omitempty" long:"ca-file" env:"KUBERNETES_CA_FILE" description:"Optional Kubernetes master auth ca certificate"`
+ BearerTokenOverwriteAllowed *bool `toml:"bearer_token_overwrite_allowed,omitempty" json:"bearer_token_overwrite_allowed,omitempty" long:"bearer_token_overwrite_allowed" env:"KUBERNETES_BEARER_TOKEN_OVERWRITE_ALLOWED" description:"Bool to authorize builds to specify their own bearer token for creation."`
+ BearerToken string `toml:"bearer_token,omitempty" json:"bearer_token,omitempty" long:"bearer_token" env:"KUBERNETES_BEARER_TOKEN" description:"Optional Kubernetes service account token used to start build pods."`
+ Image string `toml:"image,omitempty" json:"image,omitempty" long:"image" env:"KUBERNETES_IMAGE" description:"Default docker image to use for builds when none is specified"`
+ Namespace string `toml:"namespace,omitempty" json:"namespace,omitempty" long:"namespace" env:"KUBERNETES_NAMESPACE" description:"Namespace to run Kubernetes jobs in"`
+ NamespaceOverwriteAllowed string `toml:"namespace_overwrite_allowed,omitempty" json:"namespace_overwrite_allowed,omitempty" long:"namespace_overwrite_allowed" env:"KUBERNETES_NAMESPACE_OVERWRITE_ALLOWED" description:"Regex to validate 'KUBERNETES_NAMESPACE_OVERWRITE' value"`
+ Privileged *bool `toml:"privileged,omitzero" json:"privileged,omitempty" long:"privileged" env:"KUBERNETES_PRIVILEGED" description:"Run all containers with the privileged flag enabled"`
+ RuntimeClassName *string `toml:"runtime_class_name,omitempty" json:"runtime_class_name,omitempty" long:"runtime-class-name" env:"KUBERNETES_RUNTIME_CLASS_NAME" description:"A Runtime Class to use for all created pods, errors if the feature is unsupported by the cluster"`
+ AllowPrivilegeEscalation *bool `toml:"allow_privilege_escalation,omitzero" json:"allow_privilege_escalation,omitempty" long:"allow-privilege-escalation" env:"KUBERNETES_ALLOW_PRIVILEGE_ESCALATION" description:"Run all containers with the security context allowPrivilegeEscalation flag enabled. When empty, it does not define the allowPrivilegeEscalation flag in the container SecurityContext and allows Kubernetes to use the default privilege escalation behavior."`
+ CPULimit string `toml:"cpu_limit,omitempty" json:"cpu_limit,omitempty" long:"cpu-limit" env:"KUBERNETES_CPU_LIMIT" description:"The CPU allocation given to build containers"`
+ CPULimitOverwriteMaxAllowed string `toml:"cpu_limit_overwrite_max_allowed,omitempty" json:"cpu_limit_overwrite_max_allowed,omitempty" long:"cpu-limit-overwrite-max-allowed" env:"KUBERNETES_CPU_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the cpu limit can be set to. Used with the KUBERNETES_CPU_LIMIT variable in the build."`
+ CPURequest string `toml:"cpu_request,omitempty" json:"cpu_request,omitempty" long:"cpu-request" env:"KUBERNETES_CPU_REQUEST" description:"The CPU allocation requested for build containers"`
+ CPURequestOverwriteMaxAllowed string `toml:"cpu_request_overwrite_max_allowed,omitempty" json:"cpu_request_overwrite_max_allowed,omitempty" long:"cpu-request-overwrite-max-allowed" env:"KUBERNETES_CPU_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the cpu request can be set to. Used with the KUBERNETES_CPU_REQUEST variable in the build."`
+ MemoryLimit string `toml:"memory_limit,omitempty" json:"memory_limit,omitempty" long:"memory-limit" env:"KUBERNETES_MEMORY_LIMIT" description:"The amount of memory allocated to build containers"`
+ MemoryLimitOverwriteMaxAllowed string `toml:"memory_limit_overwrite_max_allowed,omitempty" json:"memory_limit_overwrite_max_allowed,omitempty" long:"memory-limit-overwrite-max-allowed" env:"KUBERNETES_MEMORY_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the memory limit can be set to. Used with the KUBERNETES_MEMORY_LIMIT variable in the build."`
+ MemoryRequest string `toml:"memory_request,omitempty" json:"memory_request,omitempty" long:"memory-request" env:"KUBERNETES_MEMORY_REQUEST" description:"The amount of memory requested from build containers"`
+ MemoryRequestOverwriteMaxAllowed string `toml:"memory_request_overwrite_max_allowed,omitempty" json:"memory_request_overwrite_max_allowed,omitempty" long:"memory-request-overwrite-max-allowed" env:"KUBERNETES_MEMORY_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the memory request can be set to. Used with the KUBERNETES_MEMORY_REQUEST variable in the build."`
+ EphemeralStorageLimit string `toml:"ephemeral_storage_limit,omitempty" json:"ephemeral_storage_limit,omitempty" long:"ephemeral-storage-limit" env:"KUBERNETES_EPHEMERAL_STORAGE_LIMIT" description:"The amount of ephemeral storage allocated to build containers"`
+ EphemeralStorageLimitOverwriteMaxAllowed string `toml:"ephemeral_storage_limit_overwrite_max_allowed,omitempty" json:"ephemeral_storage_limit_overwrite_max_allowed,omitempty" long:"ephemeral-storage-limit-overwrite-max-allowed" env:"KUBERNETES_EPHEMERAL_STORAGE_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the ephemeral limit can be set to. Used with the KUBERNETES_EPHEMERAL_STORAGE_LIMIT variable in the build."`
+ EphemeralStorageRequest string `toml:"ephemeral_storage_request,omitempty" json:"ephemeral_storage_request,omitempty" long:"ephemeral-storage-request" env:"KUBERNETES_EPHEMERAL_STORAGE_REQUEST" description:"The amount of ephemeral storage requested from build containers"`
+ EphemeralStorageRequestOverwriteMaxAllowed string `toml:"ephemeral_storage_request_overwrite_max_allowed,omitempty" json:"ephemeral_storage_request_overwrite_max_allowed,omitempty" long:"ephemeral-storage-request-overwrite-max-allowed" env:"KUBERNETES_EPHEMERAL_STORAGE_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the ephemeral storage request can be set to. Used with the KUBERNETES_EPHEMERAL_STORAGE_REQUEST variable in the build."`
+ ServiceCPULimit string `toml:"service_cpu_limit,omitempty" json:"service_cpu_limit,omitempty" long:"service-cpu-limit" env:"KUBERNETES_SERVICE_CPU_LIMIT" description:"The CPU allocation given to build service containers"`
+ ServiceCPULimitOverwriteMaxAllowed string `toml:"service_cpu_limit_overwrite_max_allowed,omitempty" json:"service_cpu_limit_overwrite_max_allowed,omitempty" long:"service-cpu-limit-overwrite-max-allowed" env:"KUBERNETES_SERVICE_CPU_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the service cpu limit can be set to. Used with the KUBERNETES_SERVICE_CPU_LIMIT variable in the build."`
+ ServiceCPURequest string `toml:"service_cpu_request,omitempty" json:"service_cpu_request,omitempty" long:"service-cpu-request" env:"KUBERNETES_SERVICE_CPU_REQUEST" description:"The CPU allocation requested for build service containers"`
+ ServiceCPURequestOverwriteMaxAllowed string `toml:"service_cpu_request_overwrite_max_allowed,omitempty" json:"service_cpu_request_overwrite_max_allowed,omitempty" long:"service-cpu-request-overwrite-max-allowed" env:"KUBERNETES_SERVICE_CPU_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the service cpu request can be set to. Used with the KUBERNETES_SERVICE_CPU_REQUEST variable in the build."`
+ ServiceMemoryLimit string `toml:"service_memory_limit,omitempty" json:"service_memory_limit,omitempty" long:"service-memory-limit" env:"KUBERNETES_SERVICE_MEMORY_LIMIT" description:"The amount of memory allocated to build service containers"`
+ ServiceMemoryLimitOverwriteMaxAllowed string `toml:"service_memory_limit_overwrite_max_allowed,omitempty" json:"service_memory_limit_overwrite_max_allowed,omitempty" long:"service-memory-limit-overwrite-max-allowed" env:"KUBERNETES_SERVICE_MEMORY_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the service memory limit can be set to. Used with the KUBERNETES_SERVICE_MEMORY_LIMIT variable in the build."`
+ ServiceMemoryRequest string `toml:"service_memory_request,omitempty" json:"service_memory_request,omitempty" long:"service-memory-request" env:"KUBERNETES_SERVICE_MEMORY_REQUEST" description:"The amount of memory requested for build service containers"`
+ ServiceMemoryRequestOverwriteMaxAllowed string `toml:"service_memory_request_overwrite_max_allowed,omitempty" json:"service_memory_request_overwrite_max_allowed,omitempty" long:"service-memory-request-overwrite-max-allowed" env:"KUBERNETES_SERVICE_MEMORY_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the service memory request can be set to. Used with the KUBERNETES_SERVICE_MEMORY_REQUEST variable in the build."`
+ ServiceEphemeralStorageLimit string `toml:"service_ephemeral_storage_limit,omitempty" json:"service_ephemeral_storage_limit,omitempty" long:"service-ephemeral_storage-limit" env:"KUBERNETES_SERVICE_EPHEMERAL_STORAGE_LIMIT" description:"The amount of ephemeral storage allocated to build service containers"`
+ ServiceEphemeralStorageLimitOverwriteMaxAllowed string `toml:"service_ephemeral_storage_limit_overwrite_max_allowed,omitempty" json:"service_ephemeral_storage_limit_overwrite_max_allowed,omitempty" long:"service-ephemeral_storage-limit-overwrite-max-allowed" env:"KUBERNETES_SERVICE_EPHEMERAL_STORAGE_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the service ephemeral storage limit can be set to. Used with the KUBERNETES_SERVICE_EPHEMERAL_STORAGE_LIMIT variable in the build."`
+ ServiceEphemeralStorageRequest string `toml:"service_ephemeral_storage_request,omitempty" json:"service_ephemeral_storage_request,omitempty" long:"service-ephemeral_storage-request" env:"KUBERNETES_SERVICE_EPHEMERAL_STORAGE_REQUEST" description:"The amount of ephemeral storage requested for build service containers"`
+ ServiceEphemeralStorageRequestOverwriteMaxAllowed string `toml:"service_ephemeral_storage_request_overwrite_max_allowed,omitempty" json:"service_ephemeral_storage_request_overwrite_max_allowed,omitempty" long:"service-ephemeral_storage-request-overwrite-max-allowed" env:"KUBERNETES_SERVICE_EPHEMERAL_STORAGE_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the service ephemeral storage request can be set to. Used with the KUBERNETES_SERVICE_EPHEMERAL_STORAGE_REQUEST variable in the build."`
+ HelperCPULimit string `toml:"helper_cpu_limit,omitempty" json:"helper_cpu_limit,omitempty" long:"helper-cpu-limit" env:"KUBERNETES_HELPER_CPU_LIMIT" description:"The CPU allocation given to build helper containers"`
+ HelperCPULimitOverwriteMaxAllowed string `toml:"helper_cpu_limit_overwrite_max_allowed,omitempty" json:"helper_cpu_limit_overwrite_max_allowed,omitempty" long:"helper-cpu-limit-overwrite-max-allowed" env:"KUBERNETES_HELPER_CPU_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the helper cpu limit can be set to. Used with the KUBERNETES_HELPER_CPU_LIMIT variable in the build."`
+ HelperCPURequest string `toml:"helper_cpu_request,omitempty" json:"helper_cpu_request,omitempty" long:"helper-cpu-request" env:"KUBERNETES_HELPER_CPU_REQUEST" description:"The CPU allocation requested for build helper containers"`
+ HelperCPURequestOverwriteMaxAllowed string `toml:"helper_cpu_request_overwrite_max_allowed,omitempty" json:"helper_cpu_request_overwrite_max_allowed,omitempty" long:"helper-cpu-request-overwrite-max-allowed" env:"KUBERNETES_HELPER_CPU_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the helper cpu request can be set to. Used with the KUBERNETES_HELPER_CPU_REQUEST variable in the build."`
+ HelperMemoryLimit string `toml:"helper_memory_limit,omitempty" json:"helper_memory_limit,omitempty" long:"helper-memory-limit" env:"KUBERNETES_HELPER_MEMORY_LIMIT" description:"The amount of memory allocated to build helper containers"`
+ HelperMemoryLimitOverwriteMaxAllowed string `toml:"helper_memory_limit_overwrite_max_allowed,omitempty" json:"helper_memory_limit_overwrite_max_allowed,omitempty" long:"helper-memory-limit-overwrite-max-allowed" env:"KUBERNETES_HELPER_MEMORY_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the helper memory limit can be set to. Used with the KUBERNETES_HELPER_MEMORY_LIMIT variable in the build."`
+ HelperMemoryRequest string `toml:"helper_memory_request,omitempty" json:"helper_memory_request,omitempty" long:"helper-memory-request" env:"KUBERNETES_HELPER_MEMORY_REQUEST" description:"The amount of memory requested for build helper containers"`
+ HelperMemoryRequestOverwriteMaxAllowed string `toml:"helper_memory_request_overwrite_max_allowed,omitempty" json:"helper_memory_request_overwrite_max_allowed,omitempty" long:"helper-memory-request-overwrite-max-allowed" env:"KUBERNETES_HELPER_MEMORY_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the helper memory request can be set to. Used with the KUBERNETES_HELPER_MEMORY_REQUEST variable in the build."`
+ HelperEphemeralStorageLimit string `toml:"helper_ephemeral_storage_limit,omitempty" json:"helper_ephemeral_storage_limit,omitempty" long:"helper-ephemeral_storage-limit" env:"KUBERNETES_HELPER_EPHEMERAL_STORAGE_LIMIT" description:"The amount of ephemeral storage allocated to build helper containers"`
+ HelperEphemeralStorageLimitOverwriteMaxAllowed string `toml:"helper_ephemeral_storage_limit_overwrite_max_allowed,omitempty" json:"helper_ephemeral_storage_limit_overwrite_max_allowed,omitempty" long:"helper-ephemeral_storage-limit-overwrite-max-allowed" env:"KUBERNETES_HELPER_EPHEMERAL_STORAGE_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the helper ephemeral storage limit can be set to. Used with the KUBERNETES_HELPER_EPHEMERAL_STORAGE_LIMIT variable in the build."`
+ HelperEphemeralStorageRequest string `toml:"helper_ephemeral_storage_request,omitempty" json:"helper_ephemeral_storage_request,omitempty" long:"helper-ephemeral_storage-request" env:"KUBERNETES_HELPER_EPHEMERAL_STORAGE_REQUEST" description:"The amount of ephemeral storage requested for build helper containers"`
+ HelperEphemeralStorageRequestOverwriteMaxAllowed string `toml:"helper_ephemeral_storage_request_overwrite_max_allowed,omitempty" json:"helper_ephemeral_storage_request_overwrite_max_allowed,omitempty" long:"helper-ephemeral_storage-request-overwrite-max-allowed" env:"KUBERNETES_HELPER_EPHEMERAL_STORAGE_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the helper ephemeral storage request can be set to. Used with the KUBERNETES_HELPER_EPHEMERAL_STORAGE_REQUEST variable in the build."`
+ AllowedImages []string `toml:"allowed_images,omitempty" json:"allowed_images,omitempty" long:"allowed-images" env:"KUBERNETES_ALLOWED_IMAGES" description:"Image allowlist"`
+ AllowedPullPolicies []string `toml:"allowed_pull_policies,omitempty" json:"allowed_pull_policies,omitempty" long:"allowed-pull-policies" env:"KUBERNETES_ALLOWED_PULL_POLICIES" description:"Pull policy allowlist"`
+ AllowedServices []string `toml:"allowed_services,omitempty" json:"allowed_services,omitempty" long:"allowed-services" env:"KUBERNETES_ALLOWED_SERVICES" description:"Service allowlist"`
+ PullPolicy []string `toml:"pull_policy,omitempty" json:"pull_policy,omitempty" long:"pull-policy" env:"KUBERNETES_PULL_POLICY" description:"Policy for if/when to pull a container image (never, if-not-present, always). The cluster default will be used if not set"`
+ NodeSelector map[string]string `toml:"node_selector,omitempty" json:"node_selector,omitempty" long:"node-selector" env:"KUBERNETES_NODE_SELECTOR" description:"A toml table/json object of key:value. Value is expected to be a string. When set this will create pods on k8s nodes that match all the key:value pairs. Only one selector is supported through environment variable configuration."`
+ NodeSelectorOverwriteAllowed string `toml:"node_selector_overwrite_allowed" json:"node_selector_overwrite_allowed,omitempty" long:"node_selector_overwrite_allowed" env:"KUBERNETES_NODE_SELECTOR_OVERWRITE_ALLOWED" description:"Regex to validate 'KUBERNETES_NODE_SELECTOR_*' values"`
+ NodeTolerations map[string]string `toml:"node_tolerations,omitempty" json:"node_tolerations,omitempty" long:"node-tolerations" env:"KUBERNETES_NODE_TOLERATIONS" description:"A toml table/json object of key=value:effect. Value and effect are expected to be strings. When set, pods will tolerate the given taints. Only one toleration is supported through environment variable configuration."`
+ Affinity *KubernetesAffinity `toml:"affinity,omitempty" json:"affinity,omitempty" long:"affinity" description:"Kubernetes Affinity setting that is used to select the node that spawns a pod"`
+ ImagePullSecrets []string `toml:"image_pull_secrets,omitempty" json:"image_pull_secrets,omitempty" long:"image-pull-secrets" env:"KUBERNETES_IMAGE_PULL_SECRETS" description:"A list of image pull secrets that are used for pulling docker image"`
+ HelperImage string `toml:"helper_image,omitempty" json:"helper_image,omitempty" long:"helper-image" env:"KUBERNETES_HELPER_IMAGE" description:"[ADVANCED] Override the default helper image used to clone repos and upload artifacts"`
+ HelperImageFlavor string `toml:"helper_image_flavor,omitempty" json:"helper_image_flavor,omitempty" long:"helper-image-flavor" env:"KUBERNETES_HELPER_IMAGE_FLAVOR" description:"Set helper image flavor (alpine, ubuntu), defaults to alpine"`
+ TerminationGracePeriodSeconds *int64 `toml:"terminationGracePeriodSeconds,omitzero" json:"terminationGracePeriodSeconds,omitempty" long:"terminationGracePeriodSeconds" env:"KUBERNETES_TERMINATIONGRACEPERIODSECONDS" description:"Duration after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal.DEPRECATED: use KUBERNETES_POD_TERMINATION_GRACE_PERIOD_SECONDS and KUBERNETES_CLEANUP_GRACE_PERIOD_SECONDS instead."`
+ PodTerminationGracePeriodSeconds *int64 `toml:"pod_termination_grace_period_seconds,omitzero" json:"pod_termination_grace_period_seconds,omitempty" long:"pod_termination_grace_period_seconds" env:"KUBERNETES_POD_TERMINATION_GRACE_PERIOD_SECONDS" description:"Pod-level setting which determines the duration in seconds which the pod has to terminate gracefully. After this, the processes are forcibly halted with a kill signal. Ignored if KUBERNETES_TERMINATIONGRACEPERIODSECONDS is specified."`
+ CleanupGracePeriodSeconds *int64 `toml:"cleanup_grace_period_seconds,omitzero" json:"cleanup_grace_period_seconds,omitempty" long:"cleanup_grace_period_seconds" env:"KUBERNETES_CLEANUP_GRACE_PERIOD_SECONDS" description:"When cleaning up a pod on completion of a job, the duration in seconds which the pod has to terminate gracefully. After this, the processes are forcibly halted with a kill signal. Ignored if KUBERNETES_TERMINATIONGRACEPERIODSECONDS is specified."`
+ PollInterval int `toml:"poll_interval,omitzero" json:"poll_interval,omitempty" long:"poll-interval" env:"KUBERNETES_POLL_INTERVAL" description:"How frequently, in seconds, the runner will poll the Kubernetes pod it has just created to check its status"`
+ PollTimeout int `toml:"poll_timeout,omitzero" json:"poll_timeout,omitempty" long:"poll-timeout" env:"KUBERNETES_POLL_TIMEOUT" description:"The total amount of time, in seconds, that needs to pass before the runner will timeout attempting to connect to the pod it has just created (useful for queueing more builds that the cluster can handle at a time)"`
+ ResourceAvailabilityCheckMaxAttempts int `toml:"resource_availability_check_max_attempts,omitzero" json:"resource_availability_check_max_attempts,omitempty" long:"resource-availability-check-max-attempts" env:"KUBERNETES_RESOURCE_AVAILABILITY_CHECK_MAX_ATTEMPTS" default:"5" description:"The maximum number of attempts to check if a resource (service account and/or pull secret) set is available before giving up. There is 5 seconds interval between each attempt"`
+ PodLabels map[string]string `toml:"pod_labels,omitempty" json:"pod_labels,omitempty" long:"pod-labels" description:"A toml table/json object of key-value. Value is expected to be a string. When set, this will create pods with the given pod labels. Environment variables will be substituted for values here."`
+ PodLabelsOverwriteAllowed string `toml:"pod_labels_overwrite_allowed" json:"pod_labels_overwrite_allowed,omitempty" long:"pod_labels_overwrite_allowed" env:"KUBERNETES_POD_LABELS_OVERWRITE_ALLOWED" description:"Regex to validate 'KUBERNETES_POD_LABELS_*' values"`
+ SchedulerName string `toml:"scheduler_name,omitempty" json:"scheduler_name,omitempty" long:"scheduler-name,omitempty" env:"KUBERNETES_SCHEDULER_NAME" description:"Pods will be scheduled using this scheduler, if it exists"`
+ ServiceAccount string `toml:"service_account,omitempty" json:"service_account,omitempty" long:"service-account" env:"KUBERNETES_SERVICE_ACCOUNT" description:"Executor pods will use this Service Account to talk to kubernetes API"`
+ ServiceAccountOverwriteAllowed string `toml:"service_account_overwrite_allowed,omitempty" json:"service_account_overwrite_allowed,omitempty" long:"service_account_overwrite_allowed" env:"KUBERNETES_SERVICE_ACCOUNT_OVERWRITE_ALLOWED" description:"Regex to validate 'KUBERNETES_SERVICE_ACCOUNT' value"`
+ PodAnnotations map[string]string `toml:"pod_annotations,omitempty" json:"pod_annotations,omitempty" long:"pod-annotations" description:"A toml table/json object of key-value. Value is expected to be a string. When set, this will create pods with the given annotations. Can be overwritten in build with KUBERNETES_POD_ANNOTATION_* variables"`
+ PodAnnotationsOverwriteAllowed string `toml:"pod_annotations_overwrite_allowed,omitempty" json:"pod_annotations_overwrite_allowed,omitempty" long:"pod_annotations_overwrite_allowed" env:"KUBERNETES_POD_ANNOTATIONS_OVERWRITE_ALLOWED" description:"Regex to validate 'KUBERNETES_POD_ANNOTATIONS_*' values"`
+ PodSecurityContext *KubernetesPodSecurityContext `toml:"pod_security_context,omitempty" json:"pod_security_context,omitempty" namespace:"pod-security-context" description:"A security context attached to each build pod"`
+ InitPermissionsContainerSecurityContext *KubernetesContainerSecurityContext `toml:"init_permissions_container_security_context,omitempty" json:"init_permissions_container_security_context,omitempty" namespace:"init_permissions_container_security_context" description:"A security context attached to the init-permissions container inside the build pod"`
+ BuildContainerSecurityContext *KubernetesContainerSecurityContext `toml:"build_container_security_context,omitempty" json:"build_container_security_context,omitempty" namespace:"build_container_security_context" description:"A security context attached to the build container inside the build pod"`
+ HelperContainerSecurityContext *KubernetesContainerSecurityContext `toml:"helper_container_security_context,omitempty" json:"helper_container_security_context,omitempty" namespace:"helper_container_security_context" description:"A security context attached to the helper container inside the build pod"`
+ ServiceContainerSecurityContext *KubernetesContainerSecurityContext `toml:"service_container_security_context,omitempty" json:"service_container_security_context,omitempty" namespace:"service_container_security_context" description:"A security context attached to the service containers inside the build pod"`
+ Volumes *KubernetesVolumes `toml:"volumes,omitempty" json:"volumes,omitempty" json:"volumes,omitempty"`
+ HostAliases []KubernetesHostAliases `toml:"host_aliases,omitempty" json:"host_aliases,omitempty" long:"host_aliases" description:"Add a custom host-to-IP mapping"`
+ Services []Service `toml:"services,omitempty" json:"services,omitempty" description:"Add service that is started with container"`
+ CapAdd []string `toml:"cap_add,omitempty" json:"cap_add,omitempty" long:"cap-add" env:"KUBERNETES_CAP_ADD" description:"Add Linux capabilities"`
+ CapDrop []string `toml:"cap_drop,omitempty" json:"cap_drop,omitempty" long:"cap-drop" env:"KUBERNETES_CAP_DROP" description:"Drop Linux capabilities"`
+ DNSPolicy string `toml:"dns_policy,omitempty" json:"dns_policy,omitempty" long:"dns-policy" env:"KUBERNETES_DNS_POLICY" description:"How Kubernetes should try to resolve DNS from the created pods. If unset, Kubernetes will use the default 'ClusterFirst'. Valid values are: none, default, cluster-first, cluster-first-with-host-net"`
+ DNSConfig *KubernetesDNSConfig `toml:"dns_config" json:"dns_config,omitempty" description:"Pod DNS config"`
+ ContainerLifecycle *KubernetesContainerLifecyle `toml:"container_lifecycle,omitempty" json:"container_lifecycle,omitempty" description:"Actions that the management system should take in response to container lifecycle events"`
+ PriorityClassName string `toml:"priority_class_name,omitempty" json:"priority_class_name,omitempty" long:"priority_class_name" env:"KUBERNETES_PRIORITY_CLASS_NAME" description:"If set, the Kubernetes Priority Class to be set to the Pods"`
+}
+
+type KubernetesDNSConfig struct {
+ Nameservers []string `toml:"nameservers" json:"nameservers,omitempty" description:"A list of IP addresses that will be used as DNS servers for the Pod."`
+ Options []KubernetesDNSConfigOption `toml:"options" json:"options,omitempty" description:"An optional list of objects where each object may have a name property (required) and a value property (optional)."`
+ Searches []string `toml:"searches" json:"searches,omitempty" description:"A list of DNS search domains for hostname lookup in the Pod."`
+}
+
+type KubernetesDNSConfigOption struct {
+ Name string `toml:"name" json:"name" json:"name"`
+ Value *string `toml:"value,omitempty" json:"value,omitempty" json:"value,omitempty"`
+}
+
+type KubernetesVolumes struct {
+ HostPaths []KubernetesHostPath `toml:"host_path" json:"host_path,omitempty" description:"The host paths which will be mounted"`
+ PVCs []KubernetesPVC `toml:"pvc" json:"pvc,omitempty" description:"The persistent volume claims that will be mounted"`
+ ConfigMaps []KubernetesConfigMap `toml:"config_map" json:"config_map,omitempty" description:"The config maps which will be mounted as volumes"`
+ Secrets []KubernetesSecret `toml:"secret" json:"secret,omitempty" description:"The secret maps which will be mounted"`
+ EmptyDirs []KubernetesEmptyDir `toml:"empty_dir" json:"empty_dir,omitempty" description:"The empty dirs which will be mounted"`
+ CSIs []KubernetesCSI `toml:"csi" json:"csi,omitempty" description:"The CSI volumes which will be mounted"`
+}
+
+type KubernetesConfigMap struct {
+ Name string `toml:"name" json:"name" description:"The name of the volume and ConfigMap to use"`
+ MountPath string `toml:"mount_path" json:"mount_path" description:"Path where volume should be mounted inside of container"`
+ SubPath string `toml:"sub_path,omitempty" json:"sub_path,omitempty" description:"The sub-path of the volume to mount (defaults to volume root)"`
+ ReadOnly bool `toml:"read_only,omitempty" json:"read_only,omitempty" description:"If this volume should be mounted read only"`
+ Items map[string]string `toml:"items,omitempty" json:"items,omitempty" description:"Key-to-path mapping for keys from the config map that should be used."`
+}
+
+type KubernetesHostPath struct {
+ Name string `toml:"name" json:"name" description:"The name of the volume"`
+ MountPath string `toml:"mount_path" json:"mount_path" description:"Path where volume should be mounted inside of container"`
+ SubPath string `toml:"sub_path,omitempty" json:"sub_path,omitempty" description:"The sub-path of the volume to mount (defaults to volume root)"`
+ ReadOnly bool `toml:"read_only,omitempty" json:"read_only,omitempty" description:"If this volume should be mounted read only"`
+ HostPath string `toml:"host_path,omitempty" json:"host_path,omitempty" description:"Path from the host that should be mounted as a volume"`
+}
+
+type KubernetesPVC struct {
+ Name string `toml:"name" json:"name" description:"The name of the volume and PVC to use"`
+ MountPath string `toml:"mount_path" json:"mount_path" description:"Path where volume should be mounted inside of container"`
+ SubPath string `toml:"sub_path,omitempty" json:"sub_path,omitempty" description:"The sub-path of the volume to mount (defaults to volume root)"`
+ ReadOnly bool `toml:"read_only,omitempty" json:"read_only,omitempty" description:"If this volume should be mounted read only"`
+}
+
+type KubernetesSecret struct {
+ Name string `toml:"name" json:"name" description:"The name of the volume and Secret to use"`
+ MountPath string `toml:"mount_path" json:"mount_path" description:"Path where volume should be mounted inside of container"`
+ SubPath string `toml:"sub_path,omitempty" json:"sub_path,omitempty" description:"The sub-path of the volume to mount (defaults to volume root)"`
+ ReadOnly bool `toml:"read_only,omitempty" json:"read_only,omitempty" description:"If this volume should be mounted read only"`
+ Items map[string]string `toml:"items,omitempty" json:"items,omitempty" description:"Key-to-path mapping for keys from the secret that should be used."`
+}
+
+type KubernetesEmptyDir struct {
+ Name string `toml:"name" json:"name" description:"The name of the volume and EmptyDir to use"`
+ MountPath string `toml:"mount_path" json:"mount_path" description:"Path where volume should be mounted inside of container"`
+ SubPath string `toml:"sub_path,omitempty" json:"sub_path,omitempty" description:"The sub-path of the volume to mount (defaults to volume root)"`
+ Medium string `toml:"medium,omitempty" json:"medium,omitempty" description:"Set to 'Memory' to have a tmpfs"`
+}
+
+type KubernetesCSI struct {
+ Name string `toml:"name" json:"name" description:"The name of the CSI volume and volumeMount to use"`
+ MountPath string `toml:"mount_path" json:"mount_path" description:"Path where volume should be mounted inside of container"`
+ SubPath string `toml:"sub_path,omitempty" json:"sub_path,omitempty" description:"The sub-path of the volume to mount (defaults to volume root)"`
+ Driver string `toml:"driver" json:"driver" description:"A string value that specifies the name of the volume driver to use."`
+ FSType string `toml:"fs_type" json:"fs_type" description:"Filesystem type to mount. If not provided, the empty value is passed to the associated CSI driver which will determine the default filesystem to apply."`
+ ReadOnly bool `toml:"read_only,omitempty" json:"read_only,omitempty" description:"If this volume should be mounted read only"`
+ VolumeAttributes map[string]string `toml:"volume_attributes,omitempty" json:"volume_attributes,omitempty" description:"Key-value pair mapping for attributes of the CSI volume."`
+}
+
+type KubernetesPodSecurityContext struct {
+ FSGroup *int64 `toml:"fs_group,omitempty" json:"fs_group,omitempty" long:"fs-group" env:"KUBERNETES_POD_SECURITY_CONTEXT_FS_GROUP" description:"A special supplemental group that applies to all containers in a pod"`
+ RunAsGroup *int64 `toml:"run_as_group,omitempty" json:"run_as_group,omitempty" long:"run-as-group" env:"KUBERNETES_POD_SECURITY_CONTEXT_RUN_AS_GROUP" description:"The GID to run the entrypoint of the container process"`
+ RunAsNonRoot *bool `toml:"run_as_non_root,omitempty" json:"run_as_non_root,omitempty" long:"run-as-non-root" env:"KUBERNETES_POD_SECURITY_CONTEXT_RUN_AS_NON_ROOT" description:"Indicates that the container must run as a non-root user"`
+ RunAsUser *int64 `toml:"run_as_user,omitempty" json:"run_as_user,omitempty" long:"run-as-user" env:"KUBERNETES_POD_SECURITY_CONTEXT_RUN_AS_USER" description:"The UID to run the entrypoint of the container process"`
+ SupplementalGroups []int64 `toml:"supplemental_groups,omitempty" json:"supplemental_groups,omitempty" long:"supplemental-groups" description:"A list of groups applied to the first process run in each container, in addition to the container's primary GID"`
+}
+
+type KubernetesContainerCapabilities struct {
+ Add []api.Capability `toml:"add" json:"add" long:"add" env:"@ADD" description:"List of capabilities to add to the build container"`
+ Drop []api.Capability `toml:"drop" json:"drop" long:"drop" env:"@DROP" description:"List of capabilities to drop from the build container"`
+}
+
+type KubernetesContainerSecurityContext struct {
+ Capabilities *KubernetesContainerCapabilities `toml:"capabilities,omitempty" json:"capabilities,omitempty" namespace:"capabilities" description:"The capabilities to add/drop when running the container"`
+ Privileged *bool `toml:"privileged" json:"privileged,omitempty" long:"privileged" env:"@PRIVILEGED" description:"Run container in privileged mode"`
+ RunAsUser *int64 `toml:"run_as_user,omitempty" json:"run_as_user,omitempty" long:"run-as-user" env:"@RUN_AS_USER" description:"The UID to run the entrypoint of the container process"`
+ RunAsGroup *int64 `toml:"run_as_group,omitempty" json:"run_as_group,omitempty" long:"run-as-group" env:"@RUN_AS_GROUP" description:"The GID to run the entrypoint of the container process"`
+ RunAsNonRoot *bool `toml:"run_as_non_root,omitempty" json:"run_as_non_root,omitempty" long:"run-as-non-root" env:"@RUN_AS_NON_ROOT" description:"Indicates that the container must run as a non-root user"`
+ ReadOnlyRootFilesystem *bool `toml:"read_only_root_filesystem" json:"read_only_root_filesystem,omitempty" long:"read-only-root-filesystem" env:"@READ_ONLY_ROOT_FILESYSTEM" description:" Whether this container has a read-only root filesystem."`
+ AllowPrivilegeEscalation *bool `toml:"allow_privilege_escalation" json:"allow_privilege_escalation,omitempty" long:"allow-privilege-escalation" env:"@ALLOW_PRIVILEGE_ESCALATION" description:"AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process"`
+}
+
+type KubernetesAffinity struct {
+ NodeAffinity *KubernetesNodeAffinity `toml:"node_affinity,omitempty" json:"node_affinity,omitempty" long:"node-affinity" description:"Node affinity is conceptually similar to nodeSelector -- it allows you to constrain which nodes your pod is eligible to be scheduled on, based on labels on the node."`
+ PodAffinity *KubernetesPodAffinity `toml:"pod_affinity,omitempty" json:"pod_affinity,omitempty" description:"Pod affinity allows to constrain which nodes your pod is eligible to be scheduled on based on the labels on other pods."`
+ PodAntiAffinity *KubernetesPodAntiAffinity `toml:"pod_anti_affinity,omitempty" json:"pod_anti_affinity,omitempty" description:"Pod anti-affinity allows to constrain which nodes your pod is eligible to be scheduled on based on the labels on other pods."`
+}
+
+type KubernetesNodeAffinity struct {
+ RequiredDuringSchedulingIgnoredDuringExecution *NodeSelector `toml:"required_during_scheduling_ignored_during_execution,omitempty" json:"required_during_scheduling_ignored_during_execution,omitempty" json:"required_during_scheduling_ignored_during_execution"`
+ PreferredDuringSchedulingIgnoredDuringExecution []PreferredSchedulingTerm `toml:"preferred_during_scheduling_ignored_during_execution,omitempty" json:"preferred_during_scheduling_ignored_during_execution,omitempty" json:"preferred_during_scheduling_ignored_during_execution"`
+}
+
+type KubernetesPodAffinity struct {
+ RequiredDuringSchedulingIgnoredDuringExecution []PodAffinityTerm `toml:"required_during_scheduling_ignored_during_execution,omitempty" json:"required_during_scheduling_ignored_during_execution,omitempty" json:"required_during_scheduling_ignored_during_execution"`
+ PreferredDuringSchedulingIgnoredDuringExecution []WeightedPodAffinityTerm `toml:"preferred_during_scheduling_ignored_during_execution,omitempty" json:"preferred_during_scheduling_ignored_during_execution,omitempty" json:"preferred_during_scheduling_ignored_during_execution"`
+}
+
+type KubernetesPodAntiAffinity struct {
+ RequiredDuringSchedulingIgnoredDuringExecution []PodAffinityTerm `toml:"required_during_scheduling_ignored_during_execution,omitempty" json:"required_during_scheduling_ignored_during_execution,omitempty" json:"required_during_scheduling_ignored_during_execution"`
+ PreferredDuringSchedulingIgnoredDuringExecution []WeightedPodAffinityTerm `toml:"preferred_during_scheduling_ignored_during_execution,omitempty" json:"preferred_during_scheduling_ignored_during_execution,omitempty" json:"preferred_during_scheduling_ignored_during_execution"`
+}
+
+type KubernetesHostAliases struct {
+ IP string `toml:"ip" json:"ip" long:"ip" description:"The IP address you want to attach hosts to"`
+ Hostnames []string `toml:"hostnames" json:"hostnames" long:"hostnames" description:"A list of hostnames that will be attached to the IP"`
+}
+
+// https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.21/#lifecycle-v1-core
+type KubernetesContainerLifecyle struct {
+ PostStart *KubernetesLifecycleHandler `toml:"post_start,omitempty" json:"post_start,omitempty" description:"PostStart is called immediately after a container is created. If the handler fails, the container is terminated and restarted according to its restart policy. Other management of the container blocks until the hook completes"`
+ PreStop *KubernetesLifecycleHandler `toml:"pre_stop,omitempty" json:"pre_stop,omitempty" description:"PreStop is called immediately before a container is terminated due to an API request or management event such as liveness/startup probe failure, preemption, resource contention, etc. The handler is not called if the container crashes or exits. The reason for termination is passed to the handler. The Pod's termination grace period countdown begins before the PreStop hooked is executed. Regardless of the outcome of the handler, the container will eventually terminate within the Pod's termination grace period. Other management of the container blocks until the hook completes or until the termination grace period is reached"`
+}
+
+type KubernetesLifecycleHandler struct {
+ Exec *KubernetesLifecycleExecAction `toml:"exec" json:"exec" description:"Exec specifies the action to take"`
+ HTTPGet *KubernetesLifecycleHTTPGet `toml:"http_get" json:"http_get" description:"HTTPGet specifies the http request to perform."`
+ TCPSocket *KubernetesLifecycleTCPSocket `toml:"tcp_socket" json:"tcp_socket" description:"TCPSocket specifies an action involving a TCP port"`
+}
+
+type KubernetesLifecycleExecAction struct {
+ Command []string `toml:"command" json:"command" description:"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy"`
+}
+
+type KubernetesLifecycleHTTPGet struct {
+ Host string `toml:"host" json:"host" description:"Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead"`
+ HTTPHeaders []KubernetesLifecycleHTTPGetHeader `toml:"http_headers" json:"http_headers" description:"Custom headers to set in the request. HTTP allows repeated headers"`
+ Path string `toml:"path" json:"path" description:"Path to access on the HTTP server"`
+ Port int `toml:"port" json:"port" description:"Number of the port to access on the container. Number must be in the range 1 to 65535"`
+ Scheme string `toml:"scheme" json:"scheme" description:"Scheme to use for connecting to the host. Defaults to HTTP"`
+}
+
+type KubernetesLifecycleHTTPGetHeader struct {
+ Name string `toml:"name" json:"name" description:"The header field name"`
+ Value string `toml:"value" json:"value" description:"The header field value"`
+}
+
+type KubernetesLifecycleTCPSocket struct {
+ Host string `toml:"host" json:"host" description:"Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead"`
+ Port int `toml:"port" json:"port" description:"Number of the port to access on the container. Number must be in the range 1 to 65535"`
+}
+
+type NodeSelector struct {
+ NodeSelectorTerms []NodeSelectorTerm `toml:"node_selector_terms" json:"node_selector_terms" json:"node_selector_terms"`
+}
+
+type PreferredSchedulingTerm struct {
+ Weight int32 `toml:"weight" json:"weight" json:"weight"`
+ Preference NodeSelectorTerm `toml:"preference" json:"preference" json:"preference"`
+}
+
+type WeightedPodAffinityTerm struct {
+ Weight int32 `toml:"weight" json:"weight" json:"weight"`
+ PodAffinityTerm PodAffinityTerm `toml:"pod_affinity_term" json:"pod_affinity_term" json:"pod_affinity_term"`
+}
+
+type NodeSelectorTerm struct {
+ MatchExpressions []NodeSelectorRequirement `toml:"match_expressions,omitempty" json:"match_expressions,omitempty" json:"match_expressions"`
+ MatchFields []NodeSelectorRequirement `toml:"match_fields,omitempty" json:"match_fields,omitempty" json:"match_fields"`
+}
+
+type NodeSelectorRequirement struct {
+ Key string `toml:"key,omitempty" json:"key,omitempty" json:"key"`
+ Operator string `toml:"operator,omitempty" json:"operator,omitempty" json:"operator"`
+ Values []string `toml:"values,omitempty" json:"values,omitempty" json:"values"`
+}
+
+type PodAffinityTerm struct {
+ LabelSelector *LabelSelector `toml:"label_selector,omitempty" json:"label_selector,omitempty" json:"label_selector"`
+ Namespaces []string `toml:"namespaces,omitempty" json:"namespaces,omitempty" json:"namespaces"`
+ TopologyKey string `toml:"topology_key,omitempty" json:"topology_key,omitempty" json:"topology_key"`
+ NamespaceSelector *LabelSelector `toml:"namespace_selector,omitempty" json:"namespace_selector,omitempty" json:"namespace_selector"`
+}
+
+type LabelSelector struct {
+ MatchLabels map[string]string `toml:"match_labels,omitempty" json:"match_labels,omitempty" json:"match_labels"`
+ MatchExpressions []NodeSelectorRequirement `toml:"match_expressions,omitempty" json:"match_expressions,omitempty" json:"match_expressions"`
+}
+
+type Service struct {
+ Name string `toml:"name" json:"name" long:"name" description:"The image path for the service"`
+ Alias string `toml:"alias,omitempty" json:"alias,omitempty" long:"alias" description:"The alias of the service"`
+ Command []string `toml:"command" json:"command" long:"command" description:"Command or script that should be used as the container’s command. Syntax is similar to https://docs.docker.com/engine/reference/builder/#cmd"`
+ Entrypoint []string `toml:"entrypoint" json:"entrypoint" long:"entrypoint" description:"Command or script that should be executed as the container’s entrypoint. syntax is similar to https://docs.docker.com/engine/reference/builder/#entrypoint"`
+}
+
+// RegisterNewRunnerOptions represents the available RegisterNewRunner()
+// options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/runners.html#register-a-new-runner
+type RegisterNewRunnerOptions struct {
+ Token *string `url:"token" json:"token,omitempty"`
+ Description *string `url:"description,omitempty" json:"description,omitempty"`
+ Info *RegisterNewRunnerInfoOptions `url:"info,omitempty" json:"info,omitempty"`
+ // Active is deprecated. use paused instead
+ Active *bool `url:"active,omitempty" json:"active,omitempty"`
+ Paused *bool `url:"paused,omitempty" json:"paused,omitempty"`
+ Locked *bool `url:"locked,omitempty" json:"locked,omitempty"`
+ RunUntagged *bool `url:"run_untagged,omitempty" json:"run_untagged,omitempty"`
+ TagList []string `url:"tag_list[],omitempty" json:"tag_list,omitempty"`
+ AccessLevel *string `url:"accessLevel,omitempty" json:"accessLevel,omitempty"`
+ MaximumTimeout *int `url:"maximum_timeout,omitempty" json:"maximum_timeout,omitempty"`
+ MaintenanceNote *string `url:"maintenance_note,omitempty" json:"maintenance_note,omitempty"`
+}
+
+// RegisterNewRunnerInfoOptions represents the info hashmap parameter in
+// RegisterNewRunnerOptions.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/runners.html#register-a-new-runner
+type RegisterNewRunnerInfoOptions struct {
+ Name *string `url:"name,omitempty" json:"name,omitempty"`
+ Version *string `url:"version,omitempty" json:"version,omitempty"`
+ Revision *string `url:"revision,omitempty" json:"revision,omitempty"`
+ Platform *string `url:"platform,omitempty" json:"platform,omitempty"`
+ Architecture *string `url:"architecture,omitempty" json:"architecture,omitempty"`
+}
diff --git a/api/v1beta1/groupversion_info.go b/api/v1beta1/groupversion_info.go
index e9f3f28..f259a35 100644
--- a/api/v1beta1/groupversion_info.go
+++ b/api/v1beta1/groupversion_info.go
@@ -15,8 +15,8 @@ limitations under the License.
*/
// Package v1beta1 contains API Schema definitions for the gitlab v1beta1 API group
-//+kubebuilder:object:generate=true
-//+groupName=gitlab.k8s.alekc.dev
+// +kubebuilder:object:generate=true
+// +groupName=gitlab.k8s.alekc.dev
package v1beta1
import (
diff --git a/api/v1beta1/multirunner_types.go b/api/v1beta1/multirunner_types.go
new file mode 100644
index 0000000..618bd8b
--- /dev/null
+++ b/api/v1beta1/multirunner_types.go
@@ -0,0 +1,204 @@
+/*
+Copyright 2021.
+
+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 v1beta1
+
+import (
+ "context"
+ "fmt"
+ "reflect"
+
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/utils/pointer"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
+)
+
+// MultiRunnerSpec defines the desired state of MultiRunner
+type MultiRunnerSpec struct {
+ // +kubebuilder:validation:Minimum=1
+ Concurrent int `json:"concurrent,omitempty"`
+
+ // +kubebuilder:validation:Enum=panic;fatal;error;warning;info;debug
+ LogLevel string `json:"log_level,omitempty"`
+
+ // +kubebuilder:validation:Enum=runner;text;json
+ LogFormat string `json:"log_format,omitempty"`
+
+ // +kubebuilder:validation:Minimum=3
+ // +kubebuilder:default:3
+ CheckInterval int `json:"check_interval,omitempty"`
+
+ // SentryDsn Enables tracking of all system level errors to Sentry.
+ SentryDsn string `json:"sentry_dsn,omitempty"`
+
+ GitlabInstanceURL string `json:"gitlab_instance_url,omitempty"`
+
+ Entries []MultiRunnerEntry `json:"entries"`
+}
+
+type MultiRunnerEntry struct {
+ Name string `json:"name"`
+ RegistrationConfig RegisterNewRunnerOptions `json:"registration_config"`
+ ExecutorConfig KubernetesConfig `json:"executor_config,omitempty"`
+ Environment []string `json:"environment,omitempty"`
+}
+
+// MultiRunnerStatus defines the observed state of MultiRunner
+type MultiRunnerStatus struct {
+ Error string `json:"error"`
+ AuthTokens map[string]string `json:"auth_tokens"`
+ LastRegistrationTags map[string][]string `json:"last_registration_tags"`
+ Ready bool `json:"ready"`
+ ConfigMapVersion string `json:"config_map_version"`
+}
+
+// +kubebuilder:object:root=true
+// +kubebuilder:subresource:status
+
+// MultiRunner is the Schema for the multirunners API
+type MultiRunner struct {
+ metav1.TypeMeta `json:",inline"`
+ metav1.ObjectMeta `json:"metadata,omitempty"`
+
+ Spec MultiRunnerSpec `json:"spec,omitempty"`
+ Status MultiRunnerStatus `json:"status,omitempty"`
+}
+
+func (r *MultiRunner) GetStatus() any {
+ return r.Status
+}
+
+func (r *MultiRunner) IsAuthenticated() bool {
+ // every entry r our runner book must have its auth
+ for _, entry := range r.Spec.Entries {
+ if _, found := r.Status.AuthTokens[*entry.RegistrationConfig.Token]; !found {
+ return false
+ }
+ }
+ return true
+}
+func (r *MultiRunner) finalizer() string {
+ return "gitlab.k8s.alekc.dev/mr-finalizer"
+}
+
+func (r *MultiRunner) HasFinalizer() bool {
+ return controllerutil.ContainsFinalizer(r, r.finalizer())
+}
+
+func (r *MultiRunner) RemoveFinalizer() {
+ controllerutil.RemoveFinalizer(r, r.finalizer())
+}
+
+func (r *MultiRunner) AddFinalizer() (finalizerUpdated bool) {
+ return controllerutil.AddFinalizer(r, r.finalizer())
+}
+
+func (r *MultiRunner) Update(ctx context.Context, writer client.Writer) error {
+ return writer.Update(ctx, r)
+}
+
+func (r *MultiRunner) SetConfigMapVersion(versionHash string) {
+ r.Status.ConfigMapVersion = versionHash
+}
+
+func (r *MultiRunner) SetStatus(newStatus any) {
+ r.Status = newStatus.(MultiRunnerStatus)
+}
+
+func (r *MultiRunner) UpdateStatus(ctx context.Context, writer client.StatusWriter) error {
+ return writer.Update(ctx, r)
+}
+
+func (r *MultiRunner) SetStatusReady(ready bool) {
+ r.Status.Ready = ready
+}
+
+// HasValidAuth verify if one or more entries from registration has to be registered again
+func (r *MultiRunner) HasValidAuth() bool {
+ // every entry r our runner book must have its auth
+ for _, entry := range r.Spec.Entries {
+ // do we have a valid token?
+ token := *entry.RegistrationConfig.Token
+ if _, found := r.Status.AuthTokens[token]; !found {
+ return false
+ }
+ // old tags must be stored and they need to match the present one
+ if oldTags, found := r.Status.LastRegistrationTags[token]; !found || !reflect.DeepEqual(oldTags, entry.RegistrationConfig.TagList) {
+ return false
+ }
+ }
+ return true
+}
+
+func (r *MultiRunner) ConfigMapVersion() string {
+ return r.Status.ConfigMapVersion
+}
+
+func (r *MultiRunner) RegistrationConfig() []GitlabRegInfo {
+ var res []GitlabRegInfo
+ for _, entry := range r.Spec.Entries {
+ token := *entry.RegistrationConfig.Token
+ res = append(res, GitlabRegInfo{
+ RegisterNewRunnerOptions: entry.RegistrationConfig,
+ AuthToken: r.Status.AuthTokens[token],
+ GitlabUrl: r.Spec.GitlabInstanceURL,
+ })
+ }
+ return res
+}
+
+func (r *MultiRunner) StoreRunnerRegistration(info GitlabRegInfo) {
+ token := *info.RegisterNewRunnerOptions.Token
+ r.Status.AuthTokens[token] = info.AuthToken
+ r.Status.LastRegistrationTags[token] = info.TagList
+}
+
+func (r *MultiRunner) ChildName() string {
+ return fmt.Sprintf("gitlab-mrunner-%s", r.Name)
+}
+
+func (r *MultiRunner) GenerateOwnerReference() []metav1.OwnerReference {
+ return []metav1.OwnerReference{{
+ APIVersion: GroupVersion.String(), // due to https://github.com/kubernetes/client-go/issues/541 type meta is empty
+ Kind: "MultirRunner",
+ Name: r.Name,
+ UID: r.UID,
+ Controller: pointer.Bool(true),
+ BlockOwnerDeletion: nil,
+ }}
+}
+
+func (r *MultiRunner) SetStatusError(errorMessage string) {
+ r.Status.Error = errorMessage
+}
+
+// +kubebuilder:object:root=true
+
+// MultiRunnerList contains a list of MultiRunner
+type MultiRunnerList struct {
+ metav1.TypeMeta `json:",inline"`
+ metav1.ListMeta `json:"metadata,omitempty"`
+ Items []MultiRunner `json:"items"`
+}
+
+func init() {
+ SchemeBuilder.Register(&MultiRunner{}, &MultiRunnerList{})
+}
+
+func (r *MultiRunner) IsBeingDeleted() bool {
+ return !r.ObjectMeta.DeletionTimestamp.IsZero()
+}
diff --git a/api/v1beta1/reg.go b/api/v1beta1/reg.go
new file mode 100644
index 0000000..411b126
--- /dev/null
+++ b/api/v1beta1/reg.go
@@ -0,0 +1,7 @@
+package v1beta1
+
+type GitlabRegInfo struct {
+ RegisterNewRunnerOptions
+ AuthToken string `json:"auth_token"`
+ GitlabUrl string
+}
diff --git a/api/v1beta1/runner_types.go b/api/v1beta1/runner_types.go
index 6792376..6529226 100644
--- a/api/v1beta1/runner_types.go
+++ b/api/v1beta1/runner_types.go
@@ -17,11 +17,14 @@ limitations under the License.
package v1beta1
import (
+ "context"
"fmt"
+ "reflect"
- api "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/utils/pointer"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
@@ -44,6 +47,9 @@ type RunnerSpec struct {
// +kubebuilder:default:3
CheckInterval int `json:"check_interval,omitempty"`
+ // +kubebuilder:validation:Enum=runner;text;json
+ LogFormat string `json:"log_format,omitempty"`
+
ExecutorConfig KubernetesConfig `json:"executor_config,omitempty"`
// +kubebuilder:validation:Optional
@@ -80,327 +86,43 @@ type Runner struct {
Status RunnerStatus `json:"status,omitempty"`
}
-// +kubebuilder:object:root=true
-
-// RunnerList contains a list of Runner
-type RunnerList struct {
- metav1.TypeMeta `json:",inline"`
- metav1.ListMeta `json:"metadata,omitempty"`
- Items []Runner `json:"items"`
-}
-
-func init() {
- SchemeBuilder.Register(&Runner{}, &RunnerList{})
-}
-
-type KubernetesConfig struct {
- Host string `toml:"host" json:"host,omitempty" long:"host" env:"KUBERNETES_HOST" description:"Optional Kubernetes master host URL (auto-discovery attempted if not specified)"`
- CertFile string `toml:"cert_file,omitempty" json:"cert_file,omitempty" long:"cert-file" env:"KUBERNETES_CERT_FILE" description:"Optional Kubernetes master auth certificate"`
- KeyFile string `toml:"key_file,omitempty" json:"key_file,omitempty" long:"key-file" env:"KUBERNETES_KEY_FILE" description:"Optional Kubernetes master auth private key"`
- CAFile string `toml:"ca_file,omitempty" json:"ca_file,omitempty" long:"ca-file" env:"KUBERNETES_CA_FILE" description:"Optional Kubernetes master auth ca certificate"`
- BearerTokenOverwriteAllowed *bool `toml:"bearer_token_overwrite_allowed,omitempty" json:"bearer_token_overwrite_allowed,omitempty" long:"bearer_token_overwrite_allowed" env:"KUBERNETES_BEARER_TOKEN_OVERWRITE_ALLOWED" description:"Bool to authorize builds to specify their own bearer token for creation."`
- BearerToken string `toml:"bearer_token,omitempty" json:"bearer_token,omitempty" long:"bearer_token" env:"KUBERNETES_BEARER_TOKEN" description:"Optional Kubernetes service account token used to start build pods."`
- Image string `toml:"image,omitempty" json:"image,omitempty" long:"image" env:"KUBERNETES_IMAGE" description:"Default docker image to use for builds when none is specified"`
- Namespace string `toml:"namespace,omitempty" json:"namespace,omitempty" long:"namespace" env:"KUBERNETES_NAMESPACE" description:"Namespace to run Kubernetes jobs in"`
- NamespaceOverwriteAllowed string `toml:"namespace_overwrite_allowed,omitempty" json:"namespace_overwrite_allowed,omitempty" long:"namespace_overwrite_allowed" env:"KUBERNETES_NAMESPACE_OVERWRITE_ALLOWED" description:"Regex to validate 'KUBERNETES_NAMESPACE_OVERWRITE' value"`
- Privileged *bool `toml:"privileged,omitzero" json:"privileged,omitempty" long:"privileged" env:"KUBERNETES_PRIVILEGED" description:"Run all containers with the privileged flag enabled"`
- AllowPrivilegeEscalation *bool `toml:"allow_privilege_escalation,omitzero" json:"allow_privilege_escalation,omitempty" long:"allow-privilege-escalation" env:"KUBERNETES_ALLOW_PRIVILEGE_ESCALATION" description:"Run all containers with the security context allowPrivilegeEscalation flag enabled. When empty, it does not define the allowPrivilegeEscalation flag in the container SecurityContext and allows Kubernetes to use the default privilege escalation behavior."`
- CPULimit string `toml:"cpu_limit,omitempty" json:"cpu_limit,omitempty" long:"cpu-limit" env:"KUBERNETES_CPU_LIMIT" description:"The CPU allocation given to build containers"`
- CPULimitOverwriteMaxAllowed string `toml:"cpu_limit_overwrite_max_allowed,omitempty" json:"cpu_limit_overwrite_max_allowed,omitempty" long:"cpu-limit-overwrite-max-allowed" env:"KUBERNETES_CPU_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the cpu limit can be set to. Used with the KUBERNETES_CPU_LIMIT variable in the build."`
- CPURequest string `toml:"cpu_request,omitempty" json:"cpu_request,omitempty" long:"cpu-request" env:"KUBERNETES_CPU_REQUEST" description:"The CPU allocation requested for build containers"`
- CPURequestOverwriteMaxAllowed string `toml:"cpu_request_overwrite_max_allowed,omitempty" json:"cpu_request_overwrite_max_allowed,omitempty" long:"cpu-request-overwrite-max-allowed" env:"KUBERNETES_CPU_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the cpu request can be set to. Used with the KUBERNETES_CPU_REQUEST variable in the build."`
- MemoryLimit string `toml:"memory_limit,omitempty" json:"memory_limit,omitempty" long:"memory-limit" env:"KUBERNETES_MEMORY_LIMIT" description:"The amount of memory allocated to build containers"`
- MemoryLimitOverwriteMaxAllowed string `toml:"memory_limit_overwrite_max_allowed,omitempty" json:"memory_limit_overwrite_max_allowed,omitempty" long:"memory-limit-overwrite-max-allowed" env:"KUBERNETES_MEMORY_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the memory limit can be set to. Used with the KUBERNETES_MEMORY_LIMIT variable in the build."`
- MemoryRequest string `toml:"memory_request,omitempty" json:"memory_request,omitempty" long:"memory-request" env:"KUBERNETES_MEMORY_REQUEST" description:"The amount of memory requested from build containers"`
- MemoryRequestOverwriteMaxAllowed string `toml:"memory_request_overwrite_max_allowed,omitempty" json:"memory_request_overwrite_max_allowed,omitempty" long:"memory-request-overwrite-max-allowed" env:"KUBERNETES_MEMORY_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the memory request can be set to. Used with the KUBERNETES_MEMORY_REQUEST variable in the build."`
- EphemeralStorageLimit string `toml:"ephemeral_storage_limit,omitempty" json:"ephemeral_storage_limit,omitempty" long:"ephemeral-storage-limit" env:"KUBERNETES_EPHEMERAL_STORAGE_LIMIT" description:"The amount of ephemeral storage allocated to build containers"`
- EphemeralStorageLimitOverwriteMaxAllowed string `toml:"ephemeral_storage_limit_overwrite_max_allowed,omitempty" json:"ephemeral_storage_limit_overwrite_max_allowed,omitempty" long:"ephemeral-storage-limit-overwrite-max-allowed" env:"KUBERNETES_EPHEMERAL_STORAGE_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the ephemeral limit can be set to. Used with the KUBERNETES_EPHEMERAL_STORAGE_LIMIT variable in the build."`
- EphemeralStorageRequest string `toml:"ephemeral_storage_request,omitempty" json:"ephemeral_storage_request,omitempty" long:"ephemeral-storage-request" env:"KUBERNETES_EPHEMERAL_STORAGE_REQUEST" description:"The amount of ephemeral storage requested from build containers"`
- EphemeralStorageRequestOverwriteMaxAllowed string `toml:"ephemeral_storage_request_overwrite_max_allowed,omitempty" json:"ephemeral_storage_request_overwrite_max_allowed,omitempty" long:"ephemeral-storage-request-overwrite-max-allowed" env:"KUBERNETES_EPHEMERAL_STORAGE_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the ephemeral storage request can be set to. Used with the KUBERNETES_EPHEMERAL_STORAGE_REQUEST variable in the build."`
- ServiceCPULimit string `toml:"service_cpu_limit,omitempty" json:"service_cpu_limit,omitempty" long:"service-cpu-limit" env:"KUBERNETES_SERVICE_CPU_LIMIT" description:"The CPU allocation given to build service containers"`
- ServiceCPULimitOverwriteMaxAllowed string `toml:"service_cpu_limit_overwrite_max_allowed,omitempty" json:"service_cpu_limit_overwrite_max_allowed,omitempty" long:"service-cpu-limit-overwrite-max-allowed" env:"KUBERNETES_SERVICE_CPU_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the service cpu limit can be set to. Used with the KUBERNETES_SERVICE_CPU_LIMIT variable in the build."`
- ServiceCPURequest string `toml:"service_cpu_request,omitempty" json:"service_cpu_request,omitempty" long:"service-cpu-request" env:"KUBERNETES_SERVICE_CPU_REQUEST" description:"The CPU allocation requested for build service containers"`
- ServiceCPURequestOverwriteMaxAllowed string `toml:"service_cpu_request_overwrite_max_allowed,omitempty" json:"service_cpu_request_overwrite_max_allowed,omitempty" long:"service-cpu-request-overwrite-max-allowed" env:"KUBERNETES_SERVICE_CPU_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the service cpu request can be set to. Used with the KUBERNETES_SERVICE_CPU_REQUEST variable in the build."`
- ServiceMemoryLimit string `toml:"service_memory_limit,omitempty" json:"service_memory_limit,omitempty" long:"service-memory-limit" env:"KUBERNETES_SERVICE_MEMORY_LIMIT" description:"The amount of memory allocated to build service containers"`
- ServiceMemoryLimitOverwriteMaxAllowed string `toml:"service_memory_limit_overwrite_max_allowed,omitempty" json:"service_memory_limit_overwrite_max_allowed,omitempty" long:"service-memory-limit-overwrite-max-allowed" env:"KUBERNETES_SERVICE_MEMORY_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the service memory limit can be set to. Used with the KUBERNETES_SERVICE_MEMORY_LIMIT variable in the build."`
- ServiceMemoryRequest string `toml:"service_memory_request,omitempty" json:"service_memory_request,omitempty" long:"service-memory-request" env:"KUBERNETES_SERVICE_MEMORY_REQUEST" description:"The amount of memory requested for build service containers"`
- ServiceMemoryRequestOverwriteMaxAllowed string `toml:"service_memory_request_overwrite_max_allowed,omitempty" json:"service_memory_request_overwrite_max_allowed,omitempty" long:"service-memory-request-overwrite-max-allowed" env:"KUBERNETES_SERVICE_MEMORY_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the service memory request can be set to. Used with the KUBERNETES_SERVICE_MEMORY_REQUEST variable in the build."`
- ServiceEphemeralStorageLimit string `toml:"service_ephemeral_storage_limit,omitempty" json:"service_ephemeral_storage_limit,omitempty" long:"service-ephemeral_storage-limit" env:"KUBERNETES_SERVICE_EPHEMERAL_STORAGE_LIMIT" description:"The amount of ephemeral storage allocated to build service containers"`
- ServiceEphemeralStorageLimitOverwriteMaxAllowed string `toml:"service_ephemeral_storage_limit_overwrite_max_allowed,omitempty" json:"service_ephemeral_storage_limit_overwrite_max_allowed,omitempty" long:"service-ephemeral_storage-limit-overwrite-max-allowed" env:"KUBERNETES_SERVICE_EPHEMERAL_STORAGE_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the service ephemeral storage limit can be set to. Used with the KUBERNETES_SERVICE_EPHEMERAL_STORAGE_LIMIT variable in the build."`
- ServiceEphemeralStorageRequest string `toml:"service_ephemeral_storage_request,omitempty" json:"service_ephemeral_storage_request,omitempty" long:"service-ephemeral_storage-request" env:"KUBERNETES_SERVICE_EPHEMERAL_STORAGE_REQUEST" description:"The amount of ephemeral storage requested for build service containers"`
- ServiceEphemeralStorageRequestOverwriteMaxAllowed string `toml:"service_ephemeral_storage_request_overwrite_max_allowed,omitempty" json:"service_ephemeral_storage_request_overwrite_max_allowed,omitempty" long:"service-ephemeral_storage-request-overwrite-max-allowed" env:"KUBERNETES_SERVICE_EPHEMERAL_STORAGE_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the service ephemeral storage request can be set to. Used with the KUBERNETES_SERVICE_EPHEMERAL_STORAGE_REQUEST variable in the build."`
- HelperCPULimit string `toml:"helper_cpu_limit,omitempty" json:"helper_cpu_limit,omitempty" long:"helper-cpu-limit" env:"KUBERNETES_HELPER_CPU_LIMIT" description:"The CPU allocation given to build helper containers"`
- HelperCPULimitOverwriteMaxAllowed string `toml:"helper_cpu_limit_overwrite_max_allowed,omitempty" json:"helper_cpu_limit_overwrite_max_allowed,omitempty" long:"helper-cpu-limit-overwrite-max-allowed" env:"KUBERNETES_HELPER_CPU_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the helper cpu limit can be set to. Used with the KUBERNETES_HELPER_CPU_LIMIT variable in the build."`
- HelperCPURequest string `toml:"helper_cpu_request,omitempty" json:"helper_cpu_request,omitempty" long:"helper-cpu-request" env:"KUBERNETES_HELPER_CPU_REQUEST" description:"The CPU allocation requested for build helper containers"`
- HelperCPURequestOverwriteMaxAllowed string `toml:"helper_cpu_request_overwrite_max_allowed,omitempty" json:"helper_cpu_request_overwrite_max_allowed,omitempty" long:"helper-cpu-request-overwrite-max-allowed" env:"KUBERNETES_HELPER_CPU_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the helper cpu request can be set to. Used with the KUBERNETES_HELPER_CPU_REQUEST variable in the build."`
- HelperMemoryLimit string `toml:"helper_memory_limit,omitempty" json:"helper_memory_limit,omitempty" long:"helper-memory-limit" env:"KUBERNETES_HELPER_MEMORY_LIMIT" description:"The amount of memory allocated to build helper containers"`
- HelperMemoryLimitOverwriteMaxAllowed string `toml:"helper_memory_limit_overwrite_max_allowed,omitempty" json:"helper_memory_limit_overwrite_max_allowed,omitempty" long:"helper-memory-limit-overwrite-max-allowed" env:"KUBERNETES_HELPER_MEMORY_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the helper memory limit can be set to. Used with the KUBERNETES_HELPER_MEMORY_LIMIT variable in the build."`
- HelperMemoryRequest string `toml:"helper_memory_request,omitempty" json:"helper_memory_request,omitempty" long:"helper-memory-request" env:"KUBERNETES_HELPER_MEMORY_REQUEST" description:"The amount of memory requested for build helper containers"`
- HelperMemoryRequestOverwriteMaxAllowed string `toml:"helper_memory_request_overwrite_max_allowed,omitempty" json:"helper_memory_request_overwrite_max_allowed,omitempty" long:"helper-memory-request-overwrite-max-allowed" env:"KUBERNETES_HELPER_MEMORY_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the helper memory request can be set to. Used with the KUBERNETES_HELPER_MEMORY_REQUEST variable in the build."`
- HelperEphemeralStorageLimit string `toml:"helper_ephemeral_storage_limit,omitempty" json:"helper_ephemeral_storage_limit,omitempty" long:"helper-ephemeral_storage-limit" env:"KUBERNETES_HELPER_EPHEMERAL_STORAGE_LIMIT" description:"The amount of ephemeral storage allocated to build helper containers"`
- HelperEphemeralStorageLimitOverwriteMaxAllowed string `toml:"helper_ephemeral_storage_limit_overwrite_max_allowed,omitempty" json:"helper_ephemeral_storage_limit_overwrite_max_allowed,omitempty" long:"helper-ephemeral_storage-limit-overwrite-max-allowed" env:"KUBERNETES_HELPER_EPHEMERAL_STORAGE_LIMIT_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the helper ephemeral storage limit can be set to. Used with the KUBERNETES_HELPER_EPHEMERAL_STORAGE_LIMIT variable in the build."`
- HelperEphemeralStorageRequest string `toml:"helper_ephemeral_storage_request,omitempty" json:"helper_ephemeral_storage_request,omitempty" long:"helper-ephemeral_storage-request" env:"KUBERNETES_HELPER_EPHEMERAL_STORAGE_REQUEST" description:"The amount of ephemeral storage requested for build helper containers"`
- HelperEphemeralStorageRequestOverwriteMaxAllowed string `toml:"helper_ephemeral_storage_request_overwrite_max_allowed,omitempty" json:"helper_ephemeral_storage_request_overwrite_max_allowed,omitempty" long:"helper-ephemeral_storage-request-overwrite-max-allowed" env:"KUBERNETES_HELPER_EPHEMERAL_STORAGE_REQUEST_OVERWRITE_MAX_ALLOWED" description:"If set, the max amount the helper ephemeral storage request can be set to. Used with the KUBERNETES_HELPER_EPHEMERAL_STORAGE_REQUEST variable in the build."`
- AllowedImages []string `toml:"allowed_images,omitempty" json:"allowed_images,omitempty" long:"allowed-images" env:"KUBERNETES_ALLOWED_IMAGES" description:"Image allowlist"`
- AllowedServices []string `toml:"allowed_services,omitempty" json:"allowed_services,omitempty" long:"allowed-services" env:"KUBERNETES_ALLOWED_SERVICES" description:"Service allowlist"`
- PullPolicy []string `toml:"pull_policy,omitempty" json:"pull_policy,omitempty" long:"pull-policy" env:"KUBERNETES_PULL_POLICY" description:"Policy for if/when to pull a container image (never, if-not-present, always). The cluster default will be used if not set"`
- NodeSelector map[string]string `toml:"node_selector,omitempty" json:"node_selector,omitempty" long:"node-selector" env:"KUBERNETES_NODE_SELECTOR" description:"A toml table/json object of key:value. Value is expected to be a string. When set this will create pods on k8s nodes that match all the key:value pairs. Only one selector is supported through environment variable configuration."`
- NodeTolerations map[string]string `toml:"node_tolerations,omitempty" json:"node_tolerations,omitempty" long:"node-tolerations" env:"KUBERNETES_NODE_TOLERATIONS" description:"A toml table/json object of key=value:effect. Value and effect are expected to be strings. When set, pods will tolerate the given taints. Only one toleration is supported through environment variable configuration."`
- Affinity *KubernetesAffinity `toml:"affinity,omitempty" json:"affinity,omitempty" long:"affinity" description:"Kubernetes Affinity setting that is used to select the node that spawns a pod"`
- ImagePullSecrets []string `toml:"image_pull_secrets,omitempty" json:"image_pull_secrets,omitempty" long:"image-pull-secrets" env:"KUBERNETES_IMAGE_PULL_SECRETS" description:"A list of image pull secrets that are used for pulling docker image"`
- HelperImage string `toml:"helper_image,omitempty" json:"helper_image,omitempty" long:"helper-image" env:"KUBERNETES_HELPER_IMAGE" description:"[ADVANCED] Override the default helper image used to clone repos and upload artifacts"`
- HelperImageFlavor string `toml:"helper_image_flavor,omitempty" json:"helper_image_flavor,omitempty" long:"helper-image-flavor" env:"KUBERNETES_HELPER_IMAGE_FLAVOR" description:"Set helper image flavor (alpine, ubuntu), defaults to alpine"`
- TerminationGracePeriodSeconds *int64 `toml:"terminationGracePeriodSeconds,omitzero" json:"terminationGracePeriodSeconds,omitempty" long:"terminationGracePeriodSeconds" env:"KUBERNETES_TERMINATIONGRACEPERIODSECONDS" description:"Duration after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal.DEPRECATED: use KUBERNETES_POD_TERMINATION_GRACE_PERIOD_SECONDS and KUBERNETES_CLEANUP_GRACE_PERIOD_SECONDS instead."`
- PodTerminationGracePeriodSeconds *int64 `toml:"pod_termination_grace_period_seconds,omitzero" json:"pod_termination_grace_period_seconds,omitempty" long:"pod_termination_grace_period_seconds" env:"KUBERNETES_POD_TERMINATION_GRACE_PERIOD_SECONDS" description:"Pod-level setting which determines the duration in seconds which the pod has to terminate gracefully. After this, the processes are forcibly halted with a kill signal. Ignored if KUBERNETES_TERMINATIONGRACEPERIODSECONDS is specified."`
- CleanupGracePeriodSeconds *int64 `toml:"cleanup_grace_period_seconds,omitzero" json:"cleanup_grace_period_seconds,omitempty" long:"cleanup_grace_period_seconds" env:"KUBERNETES_CLEANUP_GRACE_PERIOD_SECONDS" description:"When cleaning up a pod on completion of a job, the duration in seconds which the pod has to terminate gracefully. After this, the processes are forcibly halted with a kill signal. Ignored if KUBERNETES_TERMINATIONGRACEPERIODSECONDS is specified."`
- PollInterval int `toml:"poll_interval,omitzero" json:"poll_interval,omitempty" long:"poll-interval" env:"KUBERNETES_POLL_INTERVAL" description:"How frequently, in seconds, the runner will poll the Kubernetes pod it has just created to check its status"`
- PollTimeout int `toml:"poll_timeout,omitzero" json:"poll_timeout,omitempty" long:"poll-timeout" env:"KUBERNETES_POLL_TIMEOUT" description:"The total amount of time, in seconds, that needs to pass before the runner will timeout attempting to connect to the pod it has just created (useful for queueing more builds that the cluster can handle at a time)"`
- PodLabels map[string]string `toml:"pod_labels,omitempty" json:"pod_labels,omitempty" long:"pod-labels" description:"A toml table/json object of key-value. Value is expected to be a string. When set, this will create pods with the given pod labels. Environment variables will be substituted for values here."`
- ServiceAccount string `toml:"service_account,omitempty" json:"service_account,omitempty" long:"service-account" env:"KUBERNETES_SERVICE_ACCOUNT" description:"Executor pods will use this Service Account to talk to kubernetes API"`
- ServiceAccountOverwriteAllowed string `toml:"service_account_overwrite_allowed,omitempty" json:"service_account_overwrite_allowed,omitempty" long:"service_account_overwrite_allowed" env:"KUBERNETES_SERVICE_ACCOUNT_OVERWRITE_ALLOWED" description:"Regex to validate 'KUBERNETES_SERVICE_ACCOUNT' value"`
- PodAnnotations map[string]string `toml:"pod_annotations,omitempty" json:"pod_annotations,omitempty" long:"pod-annotations" description:"A toml table/json object of key-value. Value is expected to be a string. When set, this will create pods with the given annotations. Can be overwritten in build with KUBERNETES_POD_ANNOTATION_* variables"`
- PodAnnotationsOverwriteAllowed string `toml:"pod_annotations_overwrite_allowed,omitempty" json:"pod_annotations_overwrite_allowed,omitempty" long:"pod_annotations_overwrite_allowed" env:"KUBERNETES_POD_ANNOTATIONS_OVERWRITE_ALLOWED" description:"Regex to validate 'KUBERNETES_POD_ANNOTATIONS_*' values"`
- PodSecurityContext *KubernetesPodSecurityContext `toml:"pod_security_context,omitempty" json:"pod_security_context,omitempty" namespace:"pod-security-context" description:"A security context attached to each build pod"`
- BuildContainerSecurityContext *KubernetesContainerSecurityContext `toml:"build_container_security_context,omitempty" json:"build_container_security_context,omitempty" namespace:"build_container_security_context" description:"A security context attached to the build container inside the build pod"`
- HelperContainerSecurityContext *KubernetesContainerSecurityContext `toml:"helper_container_security_context,omitempty" json:"helper_container_security_context,omitempty" namespace:"helper_container_security_context" description:"A security context attached to the helper container inside the build pod"`
- ServiceContainerSecurityContext *KubernetesContainerSecurityContext `toml:"service_container_security_context,omitempty" json:"service_container_security_context,omitempty" namespace:"service_container_security_context" description:"A security context attached to the service containers inside the build pod"`
- Volumes *KubernetesVolumes `toml:"volumes,omitempty" json:"volumes,omitempty" json:"volumes,omitempty"`
- HostAliases []KubernetesHostAliases `toml:"host_aliases,omitempty" json:"host_aliases,omitempty" long:"host_aliases" description:"Add a custom host-to-IP mapping"`
- Services []Service `toml:"services,omitempty" json:"services,omitempty" description:"Add service that is started with container"`
- CapAdd []string `toml:"cap_add,omitempty" json:"cap_add,omitempty" long:"cap-add" env:"KUBERNETES_CAP_ADD" description:"Add Linux capabilities"`
- CapDrop []string `toml:"cap_drop,omitempty" json:"cap_drop,omitempty" long:"cap-drop" env:"KUBERNETES_CAP_DROP" description:"Drop Linux capabilities"`
- DNSPolicy string `toml:"dns_policy,omitempty" json:"dns_policy,omitempty" long:"dns-policy" env:"KUBERNETES_DNS_POLICY" description:"How Kubernetes should try to resolve DNS from the created pods. If unset, Kubernetes will use the default 'ClusterFirst'. Valid values are: none, default, cluster-first, cluster-first-with-host-net"`
- DNSConfig *KubernetesDNSConfig `toml:"dns_config" json:"dns_config,omitempty" description:"Pod DNS config"`
- ContainerLifecycle *KubernetesContainerLifecyle `toml:"container_lifecycle,omitempty" json:"container_lifecycle,omitempty" description:"Actions that the management system should take in response to container lifecycle events"`
-}
-
-type KubernetesDNSConfig struct {
- Nameservers []string `toml:"nameservers" json:"nameservers" description:"A list of IP addresses that will be used as DNS servers for the Pod."`
- Options []KubernetesDNSConfigOption `toml:"options" json:"options" description:"An optional list of objects where each object may have a name property (required) and a value property (optional)."`
- Searches []string `toml:"searches" json:"searches" description:"A list of DNS search domains for hostname lookup in the Pod."`
-}
-
-type KubernetesDNSConfigOption struct {
- Name string `toml:"name" json:"name" json:"name"`
- Value *string `toml:"value,omitempty" json:"value,omitempty" json:"value,omitempty"`
-}
-
-type KubernetesVolumes struct {
- HostPaths []KubernetesHostPath `toml:"host_path" json:"host_path" description:"The host paths which will be mounted"`
- PVCs []KubernetesPVC `toml:"pvc" json:"pvc" description:"The persistent volume claims that will be mounted"`
- ConfigMaps []KubernetesConfigMap `toml:"config_map" json:"config_map" description:"The config maps which will be mounted as volumes"`
- Secrets []KubernetesSecret `toml:"secret" json:"secret" description:"The secret maps which will be mounted"`
- EmptyDirs []KubernetesEmptyDir `toml:"empty_dir" json:"empty_dir" description:"The empty dirs which will be mounted"`
- CSIs []KubernetesCSI `toml:"csi" json:"csi" description:"The CSI volumes which will be mounted"`
-}
-
-type KubernetesConfigMap struct {
- Name string `toml:"name" json:"name" description:"The name of the volume and ConfigMap to use"`
- MountPath string `toml:"mount_path" json:"mount_path" description:"Path where volume should be mounted inside of container"`
- SubPath string `toml:"sub_path,omitempty" json:"sub_path,omitempty" description:"The sub-path of the volume to mount (defaults to volume root)"`
- ReadOnly bool `toml:"read_only,omitempty" json:"read_only,omitempty" description:"If this volume should be mounted read only"`
- Items map[string]string `toml:"items,omitempty" json:"items,omitempty" description:"Key-to-path mapping for keys from the config map that should be used."`
-}
-
-type KubernetesHostPath struct {
- Name string `toml:"name" json:"name" description:"The name of the volume"`
- MountPath string `toml:"mount_path" json:"mount_path" description:"Path where volume should be mounted inside of container"`
- SubPath string `toml:"sub_path,omitempty" json:"sub_path,omitempty" description:"The sub-path of the volume to mount (defaults to volume root)"`
- ReadOnly bool `toml:"read_only,omitempty" json:"read_only,omitempty" description:"If this volume should be mounted read only"`
- HostPath string `toml:"host_path,omitempty" json:"host_path,omitempty" description:"Path from the host that should be mounted as a volume"`
-}
-
-type KubernetesPVC struct {
- Name string `toml:"name" json:"name" description:"The name of the volume and PVC to use"`
- MountPath string `toml:"mount_path" json:"mount_path" description:"Path where volume should be mounted inside of container"`
- SubPath string `toml:"sub_path,omitempty" json:"sub_path,omitempty" description:"The sub-path of the volume to mount (defaults to volume root)"`
- ReadOnly bool `toml:"read_only,omitempty" json:"read_only,omitempty" description:"If this volume should be mounted read only"`
-}
-
-type KubernetesSecret struct {
- Name string `toml:"name" json:"name" description:"The name of the volume and Secret to use"`
- MountPath string `toml:"mount_path" json:"mount_path" description:"Path where volume should be mounted inside of container"`
- SubPath string `toml:"sub_path,omitempty" json:"sub_path,omitempty" description:"The sub-path of the volume to mount (defaults to volume root)"`
- ReadOnly bool `toml:"read_only,omitempty" json:"read_only,omitempty" description:"If this volume should be mounted read only"`
- Items map[string]string `toml:"items,omitempty" json:"items,omitempty" description:"Key-to-path mapping for keys from the secret that should be used."`
-}
-
-type KubernetesEmptyDir struct {
- Name string `toml:"name" json:"name" description:"The name of the volume and EmptyDir to use"`
- MountPath string `toml:"mount_path" json:"mount_path" description:"Path where volume should be mounted inside of container"`
- SubPath string `toml:"sub_path,omitempty" json:"sub_path,omitempty" description:"The sub-path of the volume to mount (defaults to volume root)"`
- Medium string `toml:"medium,omitempty" json:"medium,omitempty" description:"Set to 'Memory' to have a tmpfs"`
-}
-
-type KubernetesCSI struct {
- Name string `toml:"name" json:"name" description:"The name of the CSI volume and volumeMount to use"`
- MountPath string `toml:"mount_path" json:"mount_path" description:"Path where volume should be mounted inside of container"`
- SubPath string `toml:"sub_path,omitempty" json:"sub_path,omitempty" description:"The sub-path of the volume to mount (defaults to volume root)"`
- Driver string `toml:"driver" json:"driver" description:"A string value that specifies the name of the volume driver to use."`
- FSType string `toml:"fs_type" json:"fs_type" description:"Filesystem type to mount. If not provided, the empty value is passed to the associated CSI driver which will determine the default filesystem to apply."`
- ReadOnly bool `toml:"read_only,omitempty" json:"read_only,omitempty" description:"If this volume should be mounted read only"`
- VolumeAttributes map[string]string `toml:"volume_attributes,omitempty" json:"volume_attributes,omitempty" description:"Key-value pair mapping for attributes of the CSI volume."`
-}
-
-type KubernetesPodSecurityContext struct {
- FSGroup *int64 `toml:"fs_group,omitempty" json:"fs_group,omitempty" long:"fs-group" env:"KUBERNETES_POD_SECURITY_CONTEXT_FS_GROUP" description:"A special supplemental group that applies to all containers in a pod"`
- RunAsGroup *int64 `toml:"run_as_group,omitempty" json:"run_as_group,omitempty" long:"run-as-group" env:"KUBERNETES_POD_SECURITY_CONTEXT_RUN_AS_GROUP" description:"The GID to run the entrypoint of the container process"`
- RunAsNonRoot *bool `toml:"run_as_non_root,omitempty" json:"run_as_non_root,omitempty" long:"run-as-non-root" env:"KUBERNETES_POD_SECURITY_CONTEXT_RUN_AS_NON_ROOT" description:"Indicates that the container must run as a non-root user"`
- RunAsUser *int64 `toml:"run_as_user,omitempty" json:"run_as_user,omitempty" long:"run-as-user" env:"KUBERNETES_POD_SECURITY_CONTEXT_RUN_AS_USER" description:"The UID to run the entrypoint of the container process"`
- SupplementalGroups []int64 `toml:"supplemental_groups,omitempty" json:"supplemental_groups,omitempty" long:"supplemental-groups" description:"A list of groups applied to the first process run in each container, in addition to the container's primary GID"`
-}
-
-type KubernetesContainerCapabilities struct {
- Add []api.Capability `toml:"add" json:"add" long:"add" env:"@ADD" description:"List of capabilities to add to the build container"`
- Drop []api.Capability `toml:"drop" json:"drop" long:"drop" env:"@DROP" description:"List of capabilities to drop from the build container"`
-}
-
-type KubernetesContainerSecurityContext struct {
- Capabilities *KubernetesContainerCapabilities `toml:"capabilities,omitempty" json:"capabilities,omitempty" namespace:"capabilities" description:"The capabilities to add/drop when running the container"`
- Privileged *bool `toml:"privileged" json:"privileged" long:"privileged" env:"@PRIVILEGED" description:"Run container in privileged mode"`
- RunAsUser *int64 `toml:"run_as_user,omitempty" json:"run_as_user,omitempty" long:"run-as-user" env:"@RUN_AS_USER" description:"The UID to run the entrypoint of the container process"`
- RunAsGroup *int64 `toml:"run_as_group,omitempty" json:"run_as_group,omitempty" long:"run-as-group" env:"@RUN_AS_GROUP" description:"The GID to run the entrypoint of the container process"`
- RunAsNonRoot *bool `toml:"run_as_non_root,omitempty" json:"run_as_non_root,omitempty" long:"run-as-non-root" env:"@RUN_AS_NON_ROOT" description:"Indicates that the container must run as a non-root user"`
- ReadOnlyRootFilesystem *bool `toml:"read_only_root_filesystem" json:"read_only_root_filesystem" long:"read-only-root-filesystem" env:"@READ_ONLY_ROOT_FILESYSTEM" description:" Whether this container has a read-only root filesystem."`
- AllowPrivilegeEscalation *bool `toml:"allow_privilege_escalation" json:"allow_privilege_escalation" long:"allow-privilege-escalation" env:"@ALLOW_PRIVILEGE_ESCALATION" description:"AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process"`
-}
-
-type KubernetesAffinity struct {
- NodeAffinity *KubernetesNodeAffinity `toml:"node_affinity,omitempty" json:"node_affinity,omitempty" long:"node-affinity" description:"Node affinity is conceptually similar to nodeSelector -- it allows you to constrain which nodes your pod is eligible to be scheduled on, based on labels on the node."`
- PodAffinity *KubernetesPodAffinity `toml:"pod_affinity,omitempty" json:"pod_affinity,omitempty" description:"Pod affinity allows to constrain which nodes your pod is eligible to be scheduled on based on the labels on other pods."`
- PodAntiAffinity *KubernetesPodAntiAffinity `toml:"pod_anti_affinity,omitempty" json:"pod_anti_affinity,omitempty" description:"Pod anti-affinity allows to constrain which nodes your pod is eligible to be scheduled on based on the labels on other pods."`
-}
-
-type KubernetesNodeAffinity struct {
- RequiredDuringSchedulingIgnoredDuringExecution *NodeSelector `toml:"required_during_scheduling_ignored_during_execution,omitempty" json:"required_during_scheduling_ignored_during_execution,omitempty" json:"required_during_scheduling_ignored_during_execution"`
- PreferredDuringSchedulingIgnoredDuringExecution []PreferredSchedulingTerm `toml:"preferred_during_scheduling_ignored_during_execution,omitempty" json:"preferred_during_scheduling_ignored_during_execution,omitempty" json:"preferred_during_scheduling_ignored_during_execution"`
-}
-
-type KubernetesPodAffinity struct {
- RequiredDuringSchedulingIgnoredDuringExecution []PodAffinityTerm `toml:"required_during_scheduling_ignored_during_execution,omitempty" json:"required_during_scheduling_ignored_during_execution,omitempty" json:"required_during_scheduling_ignored_during_execution"`
- PreferredDuringSchedulingIgnoredDuringExecution []WeightedPodAffinityTerm `toml:"preferred_during_scheduling_ignored_during_execution,omitempty" json:"preferred_during_scheduling_ignored_during_execution,omitempty" json:"preferred_during_scheduling_ignored_during_execution"`
-}
-
-type KubernetesPodAntiAffinity struct {
- RequiredDuringSchedulingIgnoredDuringExecution []PodAffinityTerm `toml:"required_during_scheduling_ignored_during_execution,omitempty" json:"required_during_scheduling_ignored_during_execution,omitempty" json:"required_during_scheduling_ignored_during_execution"`
- PreferredDuringSchedulingIgnoredDuringExecution []WeightedPodAffinityTerm `toml:"preferred_during_scheduling_ignored_during_execution,omitempty" json:"preferred_during_scheduling_ignored_during_execution,omitempty" json:"preferred_during_scheduling_ignored_during_execution"`
-}
-
-type KubernetesHostAliases struct {
- IP string `toml:"ip" json:"ip" long:"ip" description:"The IP address you want to attach hosts to"`
- Hostnames []string `toml:"hostnames" json:"hostnames" long:"hostnames" description:"A list of hostnames that will be attached to the IP"`
-}
-
-// https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.21/#lifecycle-v1-core
-type KubernetesContainerLifecyle struct {
- PostStart *KubernetesLifecycleHandler `toml:"post_start,omitempty" json:"post_start,omitempty" description:"PostStart is called immediately after a container is created. If the handler fails, the container is terminated and restarted according to its restart policy. Other management of the container blocks until the hook completes"`
- PreStop *KubernetesLifecycleHandler `toml:"pre_stop,omitempty" json:"pre_stop,omitempty" description:"PreStop is called immediately before a container is terminated due to an API request or management event such as liveness/startup probe failure, preemption, resource contention, etc. The handler is not called if the container crashes or exits. The reason for termination is passed to the handler. The Pod's termination grace period countdown begins before the PreStop hooked is executed. Regardless of the outcome of the handler, the container will eventually terminate within the Pod's termination grace period. Other management of the container blocks until the hook completes or until the termination grace period is reached"`
-}
-
-type KubernetesLifecycleHandler struct {
- Exec *KubernetesLifecycleExecAction `toml:"exec" json:"exec" description:"Exec specifies the action to take"`
- HTTPGet *KubernetesLifecycleHTTPGet `toml:"http_get" json:"http_get" description:"HTTPGet specifies the http request to perform."`
- TCPSocket *KubernetesLifecycleTCPSocket `toml:"tcp_socket" json:"tcp_socket" description:"TCPSocket specifies an action involving a TCP port"`
-}
-
-type KubernetesLifecycleExecAction struct {
- Command []string `toml:"command" json:"command" description:"Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy"`
-}
-
-type KubernetesLifecycleHTTPGet struct {
- Host string `toml:"host" json:"host" description:"Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead"`
- HTTPHeaders []KubernetesLifecycleHTTPGetHeader `toml:"http_headers" json:"http_headers" description:"Custom headers to set in the request. HTTP allows repeated headers"`
- Path string `toml:"path" json:"path" description:"Path to access on the HTTP server"`
- Port int `toml:"port" json:"port" description:"Number of the port to access on the container. Number must be in the range 1 to 65535"`
- Scheme string `toml:"scheme" json:"scheme" description:"Scheme to use for connecting to the host. Defaults to HTTP"`
-}
-
-type KubernetesLifecycleHTTPGetHeader struct {
- Name string `toml:"name" json:"name" description:"The header field name"`
- Value string `toml:"value" json:"value" description:"The header field value"`
-}
-
-type KubernetesLifecycleTCPSocket struct {
- Host string `toml:"host" json:"host" description:"Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead"`
- Port int `toml:"port" json:"port" description:"Number of the port to access on the container. Number must be in the range 1 to 65535"`
-}
-
-type NodeSelector struct {
- NodeSelectorTerms []NodeSelectorTerm `toml:"node_selector_terms" json:"node_selector_terms" json:"node_selector_terms"`
-}
-
-type PreferredSchedulingTerm struct {
- Weight int32 `toml:"weight" json:"weight" json:"weight"`
- Preference NodeSelectorTerm `toml:"preference" json:"preference" json:"preference"`
-}
-
-type WeightedPodAffinityTerm struct {
- Weight int32 `toml:"weight" json:"weight" json:"weight"`
- PodAffinityTerm PodAffinityTerm `toml:"pod_affinity_term" json:"pod_affinity_term" json:"pod_affinity_term"`
+func (r *Runner) RegistrationConfig() []GitlabRegInfo {
+ return []GitlabRegInfo{{
+ RegisterNewRunnerOptions: r.Spec.RegistrationConfig,
+ AuthToken: r.Status.AuthenticationToken,
+ GitlabUrl: r.Spec.GitlabInstanceURL,
+ }}
}
-type NodeSelectorTerm struct {
- MatchExpressions []NodeSelectorRequirement `toml:"match_expressions,omitempty" json:"match_expressions,omitempty" json:"match_expressions"`
- MatchFields []NodeSelectorRequirement `toml:"match_fields,omitempty" json:"match_fields,omitempty" json:"match_fields"`
+func (r *Runner) StoreRunnerRegistration(info GitlabRegInfo) {
+ r.Status.AuthenticationToken = info.AuthToken
+ r.Status.LastRegistrationToken = *info.Token
+ r.Status.LastRegistrationTags = info.TagList
}
-type NodeSelectorRequirement struct {
- Key string `toml:"key,omitempty" json:"key,omitempty" json:"key"`
- Operator string `toml:"operator,omitempty" json:"operator,omitempty" json:"operator"`
- Values []string `toml:"values,omitempty" json:"values,omitempty" json:"values"`
+func (r *Runner) GitlabRegTokens() []string {
+ return []string{r.Status.AuthenticationToken}
}
-type PodAffinityTerm struct {
- LabelSelector *LabelSelector `toml:"label_selector,omitempty" json:"label_selector,omitempty" json:"label_selector"`
- Namespaces []string `toml:"namespaces,omitempty" json:"namespaces,omitempty" json:"namespaces"`
- TopologyKey string `toml:"topology_key,omitempty" json:"topology_key,omitempty" json:"topology_key"`
- NamespaceSelector *LabelSelector `toml:"namespace_selector,omitempty" json:"namespace_selector,omitempty" json:"namespace_selector"`
+func (r *Runner) GetStatus() any {
+ return r.Status
}
-type LabelSelector struct {
- MatchLabels map[string]string `toml:"match_labels,omitempty" json:"match_labels,omitempty" json:"match_labels"`
- MatchExpressions []NodeSelectorRequirement `toml:"match_expressions,omitempty" json:"match_expressions,omitempty" json:"match_expressions"`
+func (r *Runner) SetStatus(newStatus any) {
+ r.Status = newStatus.(RunnerStatus)
}
-type Service struct {
- Name string `toml:"name" json:"name" long:"name" description:"The image path for the service"`
- Alias string `toml:"alias,omitempty" json:"alias,omitempty" long:"alias" description:"The alias of the service"`
- Command []string `toml:"command" json:"command" long:"command" description:"Command or script that should be used as the container’s command. Syntax is similar to https://docs.docker.com/engine/reference/builder/#cmd"`
- Entrypoint []string `toml:"entrypoint" json:"entrypoint" long:"entrypoint" description:"Command or script that should be executed as the container’s entrypoint. syntax is similar to https://docs.docker.com/engine/reference/builder/#entrypoint"`
-}
+// +kubebuilder:object:root=true
-// RegisterNewRunnerOptions represents the available RegisterNewRunner()
-// options.
-//
-// GitLab API docs:
-// https://docs.gitlab.com/ce/api/runners.html#register-a-new-runner
-type RegisterNewRunnerOptions struct {
- Token *string `url:"token" json:"token,omitempty"`
- TokenSecret string `json:"token_secret,omitempty"`
- Description *string `url:"description,omitempty" json:"description,omitempty"`
- Info *RegisterNewRunnerInfoOptions `url:"info,omitempty" json:"info,omitempty"`
- // Active is deprecated. use paused instead
- Active *bool `url:"active,omitempty" json:"active,omitempty"`
- Paused *bool `url:"paused,omitempty" json:"paused,omitempty"`
- Locked *bool `url:"locked,omitempty" json:"locked,omitempty"`
- RunUntagged *bool `url:"run_untagged,omitempty" json:"run_untagged,omitempty"`
- TagList []string `url:"tag_list[],omitempty" json:"tag_list,omitempty"`
- AccessLevel *string `url:"accessLevel,omitempty" json:"accessLevel,omitempty"`
- MaximumTimeout *int `url:"maximum_timeout,omitempty" json:"maximum_timeout,omitempty"`
- MaintenanceNote *string `url:"maintenance_note,omitempty" json:"maintenance_note,omitempty"`
+// RunnerList contains a list of Runner
+type RunnerList struct {
+ metav1.TypeMeta `json:",inline"`
+ metav1.ListMeta `json:"metadata,omitempty"`
+ Items []Runner `json:"items"`
}
-// RegisterNewRunnerInfoOptions represents the info hashmap parameter in
-// RegisterNewRunnerOptions.
-//
-// GitLab API docs:
-// https://docs.gitlab.com/ce/api/runners.html#register-a-new-runner
-type RegisterNewRunnerInfoOptions struct {
- Name *string `url:"name,omitempty" json:"name,omitempty"`
- Version *string `url:"version,omitempty" json:"version,omitempty"`
- Revision *string `url:"revision,omitempty" json:"revision,omitempty"`
- Platform *string `url:"platform,omitempty" json:"platform,omitempty"`
- Architecture *string `url:"architecture,omitempty" json:"architecture,omitempty"`
+func init() {
+ SchemeBuilder.Register(&Runner{}, &RunnerList{})
}
// GetAnnotation returns the annotation value for a given key.
@@ -423,7 +145,7 @@ func (r *Runner) GenerateOwnerReference() []metav1.OwnerReference {
Kind: "Runner",
Name: r.Name,
UID: r.UID,
- Controller: pointer.BoolPtr(true),
+ Controller: pointer.Bool(true),
BlockOwnerDeletion: nil,
}}
}
@@ -432,3 +154,89 @@ func (r *Runner) GenerateOwnerReference() []metav1.OwnerReference {
func (r *Runner) ChildName() string {
return fmt.Sprintf("gitlab-runner-%s", r.Name)
}
+
+// Namespace returns namespace of the runner
+func (r *Runner) GetNamespace() string {
+ return r.Namespace
+}
+
+// SetStatusError sets the error on the status
+func (r *Runner) SetStatusError(errorMessage string) {
+ r.Status.Error = errorMessage
+}
+
+// SetStatusConfigMapVersion sets configm map version hash in the runner status
+func (r *Runner) SetConfigMapVersion(versionHash string) {
+ r.Status.ConfigMapVersion = versionHash
+}
+
+func (r *Runner) IsBeingDeleted() bool {
+ return !r.ObjectMeta.DeletionTimestamp.IsZero()
+}
+
+func (r *Runner) IsAuthenticated() bool {
+ return r.Status.AuthenticationToken != ""
+}
+func (r *Runner) RemoveFinalizer() {
+ controllerutil.RemoveFinalizer(r, r.finalizer())
+}
+func (r *Runner) Update(ctx context.Context, writer client.Writer) error {
+ return writer.Update(ctx, r)
+}
+func (r *Runner) AddFinalizer() bool {
+ return controllerutil.AddFinalizer(r, r.finalizer())
+}
+
+func (r *Runner) UpdateStatus(ctx context.Context, writer client.StatusWriter) error {
+ return writer.Update(ctx, r)
+}
+func (r *Runner) finalizer() string {
+ return "gitlab.k8s.alekc.dev/finalizer"
+}
+func (r *Runner) HasFinalizer() bool {
+ return controllerutil.ContainsFinalizer(r, r.finalizer())
+}
+func (r *Runner) SetStatusReady(ready bool) {
+ r.Status.Ready = ready
+}
+func (r *Runner) HasValidAuth() bool {
+ return !(r.Status.AuthenticationToken == "" ||
+ r.Status.LastRegistrationToken != *r.Spec.RegistrationConfig.Token ||
+ !reflect.DeepEqual(r.Status.LastRegistrationTags, r.Spec.RegistrationConfig.TagList))
+}
+
+// func (r *Runner) RegisterOnGitlab(gitlabClient api2.GitlabClient, logger logr.Logger) (ctrl.Result, error) {
+// // since we are doing a new registration, IF the runner already has an authentication token, delete it from gitlab server
+// if r.Status.AuthenticationToken != "" {
+// _, err := gitlabClient.DeleteByToken(r.Status.AuthenticationToken)
+// if err != nil {
+// logger.Error(err, "cannot remove gitlab runner registration")
+// return ctrl.Result{Requeue: true, RequeueAfter: time.Second * 30}, err
+// }
+// }
+// token, err := gitlabClient.Register(r.Spec.RegistrationConfig)
+// if err != nil {
+// logger.Error(err, "cannot register runner on the gitlab")
+// r.Status.Error = fmt.Sprintf("Cannot register the runner on gitlab api. %s", err.Error())
+// return ctrl.Result{Requeue: true, RequeueAfter: 1 * time.Minute}, err
+// }
+// r.Status.AuthenticationToken = token
+// r.Status.LastRegistrationToken = *r.Spec.RegistrationConfig.Token
+// r.Status.LastRegistrationTags = r.Spec.RegistrationConfig.TagList
+//
+// logger.Info("registered a new runner on gitlab")
+// return ctrl.Result{Requeue: true}, nil
+// }
+
+// func (r *Runner) DeleteFromGitlab(apiClient api2.GitlabClient, logger logr.Logger) error {
+// // _, err := apiClient.DeleteByToken(r.Status.AuthenticationToken)
+// // if err != nil {
+// // logger.Error(err, "cannot remove gitlab runner from the gitlab server")
+// // }
+// // return err
+// return nil
+// }
+
+func (r *Runner) ConfigMapVersion() string {
+ return r.Status.ConfigMapVersion
+}
diff --git a/api/v1beta1/runner_webhook.go b/api/v1beta1/runner_webhook.go
index d5c3781..02f9501 100644
--- a/api/v1beta1/runner_webhook.go
+++ b/api/v1beta1/runner_webhook.go
@@ -21,6 +21,7 @@ import (
ctrl "sigs.k8s.io/controller-runtime"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/webhook"
+ "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)
// log is for logging in this package.
@@ -51,26 +52,26 @@ func (r *Runner) Default() {
var _ webhook.Validator = &Runner{}
// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
-func (r *Runner) ValidateCreate() error {
+func (r *Runner) ValidateCreate() (admission.Warnings, error) {
// runnerlog.Info("validate create", "name", r.Name)
- return nil
+ return nil, nil
}
// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
-func (r *Runner) ValidateUpdate(old runtime.Object) error {
+func (r *Runner) ValidateUpdate(old runtime.Object) (admission.Warnings, error) {
// runnerlog.Info("validate update", "name", r.Name)
// we do not want to permit changing of the name of the runner (or better, we'd rather avoid dealing with cleaning)
// TODO(user): fill in your validation logic upon object update.
- return nil
+ return nil, nil
}
// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
-func (r *Runner) ValidateDelete() error {
+func (r *Runner) ValidateDelete() (admission.Warnings, error) {
// runnerlog.Info("validate delete", "name", r.Name)
// TODO(user): fill in your validation logic upon object deletion.
- return nil
+ return nil, nil
}
diff --git a/api/v1beta1/webhook_suite_test.go b/api/v1beta1/webhook_suite_test.go
index 6d6dad2..25720dd 100644
--- a/api/v1beta1/webhook_suite_test.go
+++ b/api/v1beta1/webhook_suite_test.go
@@ -23,6 +23,7 @@ import (
"net"
"os"
"path/filepath"
+ "sigs.k8s.io/controller-runtime/pkg/webhook"
"testing"
"time"
@@ -36,7 +37,6 @@ import (
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/envtest"
- "sigs.k8s.io/controller-runtime/pkg/envtest/printer"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
)
@@ -53,9 +53,7 @@ var cancel context.CancelFunc
func TestAPIs(t *testing.T) {
RegisterFailHandler(Fail)
- RunSpecsWithDefaultAndCustomReporters(t,
- "Webhook Suite",
- []Reporter{printer.NewlineReporter{}})
+ RunSpecs(t, "Webhook Suite")
}
var _ = BeforeSuite(func() {
@@ -95,12 +93,13 @@ var _ = BeforeSuite(func() {
// start webhook server using Manager
webhookInstallOptions := &testEnv.WebhookInstallOptions
mgr, err := ctrl.NewManager(cfg, ctrl.Options{
- Scheme: scheme,
- Host: webhookInstallOptions.LocalServingHost,
- Port: webhookInstallOptions.LocalServingPort,
- CertDir: webhookInstallOptions.LocalServingCertDir,
- LeaderElection: false,
- MetricsBindAddress: "0",
+ Scheme: scheme,
+ WebhookServer: webhook.NewServer(webhook.Options{
+ Host: webhookInstallOptions.LocalServingHost,
+ Port: webhookInstallOptions.LocalServingPort,
+ CertDir: webhookInstallOptions.LocalServingCertDir,
+ }),
+ LeaderElection: false,
})
Expect(err).NotTo(HaveOccurred())
diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go
index b86840a..f99fdf1 100644
--- a/api/v1beta1/zz_generated.deepcopy.go
+++ b/api/v1beta1/zz_generated.deepcopy.go
@@ -1,5 +1,4 @@
//go:build !ignore_autogenerated
-// +build !ignore_autogenerated
/*
Copyright 2021.
@@ -26,6 +25,22 @@ import (
"k8s.io/apimachinery/pkg/runtime"
)
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *GitlabRegInfo) DeepCopyInto(out *GitlabRegInfo) {
+ *out = *in
+ in.RegisterNewRunnerOptions.DeepCopyInto(&out.RegisterNewRunnerOptions)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitlabRegInfo.
+func (in *GitlabRegInfo) DeepCopy() *GitlabRegInfo {
+ if in == nil {
+ return nil
+ }
+ out := new(GitlabRegInfo)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *KubernetesAffinity) DeepCopyInto(out *KubernetesAffinity) {
*out = *in
@@ -91,6 +106,11 @@ func (in *KubernetesConfig) DeepCopyInto(out *KubernetesConfig) {
*out = new(bool)
**out = **in
}
+ if in.RuntimeClassName != nil {
+ in, out := &in.RuntimeClassName, &out.RuntimeClassName
+ *out = new(string)
+ **out = **in
+ }
if in.AllowPrivilegeEscalation != nil {
in, out := &in.AllowPrivilegeEscalation, &out.AllowPrivilegeEscalation
*out = new(bool)
@@ -101,6 +121,11 @@ func (in *KubernetesConfig) DeepCopyInto(out *KubernetesConfig) {
*out = make([]string, len(*in))
copy(*out, *in)
}
+ if in.AllowedPullPolicies != nil {
+ in, out := &in.AllowedPullPolicies, &out.AllowedPullPolicies
+ *out = make([]string, len(*in))
+ copy(*out, *in)
+ }
if in.AllowedServices != nil {
in, out := &in.AllowedServices, &out.AllowedServices
*out = make([]string, len(*in))
@@ -169,6 +194,11 @@ func (in *KubernetesConfig) DeepCopyInto(out *KubernetesConfig) {
*out = new(KubernetesPodSecurityContext)
(*in).DeepCopyInto(*out)
}
+ if in.InitPermissionsContainerSecurityContext != nil {
+ in, out := &in.InitPermissionsContainerSecurityContext, &out.InitPermissionsContainerSecurityContext
+ *out = new(KubernetesContainerSecurityContext)
+ (*in).DeepCopyInto(*out)
+ }
if in.BuildContainerSecurityContext != nil {
in, out := &in.BuildContainerSecurityContext, &out.BuildContainerSecurityContext
*out = new(KubernetesContainerSecurityContext)
@@ -801,6 +831,147 @@ func (in *LabelSelector) DeepCopy() *LabelSelector {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *MultiRunner) DeepCopyInto(out *MultiRunner) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+ in.Spec.DeepCopyInto(&out.Spec)
+ in.Status.DeepCopyInto(&out.Status)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MultiRunner.
+func (in *MultiRunner) DeepCopy() *MultiRunner {
+ if in == nil {
+ return nil
+ }
+ out := new(MultiRunner)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *MultiRunner) DeepCopyObject() runtime.Object {
+ if c := in.DeepCopy(); c != nil {
+ return c
+ }
+ return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *MultiRunnerEntry) DeepCopyInto(out *MultiRunnerEntry) {
+ *out = *in
+ in.RegistrationConfig.DeepCopyInto(&out.RegistrationConfig)
+ in.ExecutorConfig.DeepCopyInto(&out.ExecutorConfig)
+ if in.Environment != nil {
+ in, out := &in.Environment, &out.Environment
+ *out = make([]string, len(*in))
+ copy(*out, *in)
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MultiRunnerEntry.
+func (in *MultiRunnerEntry) DeepCopy() *MultiRunnerEntry {
+ if in == nil {
+ return nil
+ }
+ out := new(MultiRunnerEntry)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *MultiRunnerList) DeepCopyInto(out *MultiRunnerList) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ListMeta.DeepCopyInto(&out.ListMeta)
+ if in.Items != nil {
+ in, out := &in.Items, &out.Items
+ *out = make([]MultiRunner, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MultiRunnerList.
+func (in *MultiRunnerList) DeepCopy() *MultiRunnerList {
+ if in == nil {
+ return nil
+ }
+ out := new(MultiRunnerList)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *MultiRunnerList) DeepCopyObject() runtime.Object {
+ if c := in.DeepCopy(); c != nil {
+ return c
+ }
+ return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *MultiRunnerSpec) DeepCopyInto(out *MultiRunnerSpec) {
+ *out = *in
+ if in.Entries != nil {
+ in, out := &in.Entries, &out.Entries
+ *out = make([]MultiRunnerEntry, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MultiRunnerSpec.
+func (in *MultiRunnerSpec) DeepCopy() *MultiRunnerSpec {
+ if in == nil {
+ return nil
+ }
+ out := new(MultiRunnerSpec)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *MultiRunnerStatus) DeepCopyInto(out *MultiRunnerStatus) {
+ *out = *in
+ if in.AuthTokens != nil {
+ in, out := &in.AuthTokens, &out.AuthTokens
+ *out = make(map[string]string, len(*in))
+ for key, val := range *in {
+ (*out)[key] = val
+ }
+ }
+ if in.LastRegistrationTags != nil {
+ in, out := &in.LastRegistrationTags, &out.LastRegistrationTags
+ *out = make(map[string][]string, len(*in))
+ for key, val := range *in {
+ var outVal []string
+ if val == nil {
+ (*out)[key] = nil
+ } else {
+ inVal := (*in)[key]
+ in, out := &inVal, &outVal
+ *out = make([]string, len(*in))
+ copy(*out, *in)
+ }
+ (*out)[key] = outVal
+ }
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MultiRunnerStatus.
+func (in *MultiRunnerStatus) DeepCopy() *MultiRunnerStatus {
+ if in == nil {
+ return nil
+ }
+ out := new(MultiRunnerStatus)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NodeSelector) DeepCopyInto(out *NodeSelector) {
*out = *in
diff --git a/config/config.go b/config/config.go
index aa1ac87..e890f03 100644
--- a/config/config.go
+++ b/config/config.go
@@ -1,6 +1,8 @@
package config
-import "gitlab.k8s.alekc.dev/api/v1beta1"
+import (
+ "gitlab.k8s.alekc.dev/api/v1beta1"
+)
//nolint:lll
type Config struct {
diff --git a/config/crd/bases/gitlab.k8s.alekc.dev_multirunners.yaml b/config/crd/bases/gitlab.k8s.alekc.dev_multirunners.yaml
new file mode 100644
index 0000000..713602e
--- /dev/null
+++ b/config/crd/bases/gitlab.k8s.alekc.dev_multirunners.yaml
@@ -0,0 +1,1092 @@
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.13.0
+ name: multirunners.gitlab.k8s.alekc.dev
+spec:
+ group: gitlab.k8s.alekc.dev
+ names:
+ kind: MultiRunner
+ listKind: MultiRunnerList
+ plural: multirunners
+ singular: multirunner
+ scope: Namespaced
+ versions:
+ - name: v1beta1
+ schema:
+ openAPIV3Schema:
+ description: MultiRunner is the Schema for the multirunners 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: MultiRunnerSpec defines the desired state of MultiRunner
+ properties:
+ check_interval:
+ minimum: 3
+ type: integer
+ concurrent:
+ minimum: 1
+ type: integer
+ entries:
+ items:
+ properties:
+ environment:
+ items:
+ type: string
+ type: array
+ executor_config:
+ properties:
+ affinity:
+ properties:
+ node_affinity:
+ properties:
+ preferred_during_scheduling_ignored_during_execution:
+ items:
+ properties:
+ preference:
+ properties:
+ match_expressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ type: object
+ type: array
+ match_fields:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ type: object
+ type: array
+ type: object
+ weight:
+ format: int32
+ type: integer
+ required:
+ - preference
+ - weight
+ type: object
+ type: array
+ required_during_scheduling_ignored_during_execution:
+ properties:
+ node_selector_terms:
+ items:
+ properties:
+ match_expressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ type: object
+ type: array
+ match_fields:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ type: object
+ type: array
+ type: object
+ type: array
+ required:
+ - node_selector_terms
+ type: object
+ type: object
+ pod_affinity:
+ properties:
+ preferred_during_scheduling_ignored_during_execution:
+ items:
+ properties:
+ pod_affinity_term:
+ properties:
+ label_selector:
+ properties:
+ match_expressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ type: object
+ type: array
+ match_labels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ namespace_selector:
+ properties:
+ match_expressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ type: object
+ type: array
+ match_labels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ namespaces:
+ items:
+ type: string
+ type: array
+ topology_key:
+ type: string
+ type: object
+ weight:
+ format: int32
+ type: integer
+ required:
+ - pod_affinity_term
+ - weight
+ type: object
+ type: array
+ required_during_scheduling_ignored_during_execution:
+ items:
+ properties:
+ label_selector:
+ properties:
+ match_expressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ type: object
+ type: array
+ match_labels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ namespace_selector:
+ properties:
+ match_expressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ type: object
+ type: array
+ match_labels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ namespaces:
+ items:
+ type: string
+ type: array
+ topology_key:
+ type: string
+ type: object
+ type: array
+ type: object
+ pod_anti_affinity:
+ properties:
+ preferred_during_scheduling_ignored_during_execution:
+ items:
+ properties:
+ pod_affinity_term:
+ properties:
+ label_selector:
+ properties:
+ match_expressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ type: object
+ type: array
+ match_labels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ namespace_selector:
+ properties:
+ match_expressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ type: object
+ type: array
+ match_labels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ namespaces:
+ items:
+ type: string
+ type: array
+ topology_key:
+ type: string
+ type: object
+ weight:
+ format: int32
+ type: integer
+ required:
+ - pod_affinity_term
+ - weight
+ type: object
+ type: array
+ required_during_scheduling_ignored_during_execution:
+ items:
+ properties:
+ label_selector:
+ properties:
+ match_expressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ type: object
+ type: array
+ match_labels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ namespace_selector:
+ properties:
+ match_expressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ type: object
+ type: array
+ match_labels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ namespaces:
+ items:
+ type: string
+ type: array
+ topology_key:
+ type: string
+ type: object
+ type: array
+ type: object
+ type: object
+ allow_privilege_escalation:
+ type: boolean
+ allowed_images:
+ items:
+ type: string
+ type: array
+ allowed_pull_policies:
+ items:
+ type: string
+ type: array
+ allowed_services:
+ items:
+ type: string
+ type: array
+ bearer_token:
+ type: string
+ bearer_token_overwrite_allowed:
+ type: boolean
+ build_container_security_context:
+ properties:
+ allow_privilege_escalation:
+ type: boolean
+ capabilities:
+ properties:
+ add:
+ items:
+ description: Capability represent POSIX capabilities
+ type
+ type: string
+ type: array
+ drop:
+ items:
+ description: Capability represent POSIX capabilities
+ type
+ type: string
+ type: array
+ required:
+ - add
+ - drop
+ type: object
+ privileged:
+ type: boolean
+ read_only_root_filesystem:
+ type: boolean
+ run_as_group:
+ format: int64
+ type: integer
+ run_as_non_root:
+ type: boolean
+ run_as_user:
+ format: int64
+ type: integer
+ type: object
+ ca_file:
+ type: string
+ cap_add:
+ items:
+ type: string
+ type: array
+ cap_drop:
+ items:
+ type: string
+ type: array
+ cert_file:
+ type: string
+ cleanup_grace_period_seconds:
+ format: int64
+ type: integer
+ container_lifecycle:
+ description: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.21/#lifecycle-v1-core
+ properties:
+ post_start:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ required:
+ - command
+ type: object
+ http_get:
+ properties:
+ host:
+ type: string
+ http_headers:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ path:
+ type: string
+ port:
+ type: integer
+ scheme:
+ type: string
+ required:
+ - host
+ - http_headers
+ - path
+ - port
+ - scheme
+ type: object
+ tcp_socket:
+ properties:
+ host:
+ type: string
+ port:
+ type: integer
+ required:
+ - host
+ - port
+ type: object
+ required:
+ - exec
+ - http_get
+ - tcp_socket
+ type: object
+ pre_stop:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ required:
+ - command
+ type: object
+ http_get:
+ properties:
+ host:
+ type: string
+ http_headers:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ path:
+ type: string
+ port:
+ type: integer
+ scheme:
+ type: string
+ required:
+ - host
+ - http_headers
+ - path
+ - port
+ - scheme
+ type: object
+ tcp_socket:
+ properties:
+ host:
+ type: string
+ port:
+ type: integer
+ required:
+ - host
+ - port
+ type: object
+ required:
+ - exec
+ - http_get
+ - tcp_socket
+ type: object
+ type: object
+ cpu_limit:
+ type: string
+ cpu_limit_overwrite_max_allowed:
+ type: string
+ cpu_request:
+ type: string
+ cpu_request_overwrite_max_allowed:
+ type: string
+ dns_config:
+ properties:
+ nameservers:
+ items:
+ type: string
+ type: array
+ options:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ searches:
+ items:
+ type: string
+ type: array
+ type: object
+ dns_policy:
+ type: string
+ ephemeral_storage_limit:
+ type: string
+ ephemeral_storage_limit_overwrite_max_allowed:
+ type: string
+ ephemeral_storage_request:
+ type: string
+ ephemeral_storage_request_overwrite_max_allowed:
+ type: string
+ helper_container_security_context:
+ properties:
+ allow_privilege_escalation:
+ type: boolean
+ capabilities:
+ properties:
+ add:
+ items:
+ description: Capability represent POSIX capabilities
+ type
+ type: string
+ type: array
+ drop:
+ items:
+ description: Capability represent POSIX capabilities
+ type
+ type: string
+ type: array
+ required:
+ - add
+ - drop
+ type: object
+ privileged:
+ type: boolean
+ read_only_root_filesystem:
+ type: boolean
+ run_as_group:
+ format: int64
+ type: integer
+ run_as_non_root:
+ type: boolean
+ run_as_user:
+ format: int64
+ type: integer
+ type: object
+ helper_cpu_limit:
+ type: string
+ helper_cpu_limit_overwrite_max_allowed:
+ type: string
+ helper_cpu_request:
+ type: string
+ helper_cpu_request_overwrite_max_allowed:
+ type: string
+ helper_ephemeral_storage_limit:
+ type: string
+ helper_ephemeral_storage_limit_overwrite_max_allowed:
+ type: string
+ helper_ephemeral_storage_request:
+ type: string
+ helper_ephemeral_storage_request_overwrite_max_allowed:
+ type: string
+ helper_image:
+ type: string
+ helper_image_flavor:
+ type: string
+ helper_memory_limit:
+ type: string
+ helper_memory_limit_overwrite_max_allowed:
+ type: string
+ helper_memory_request:
+ type: string
+ helper_memory_request_overwrite_max_allowed:
+ type: string
+ host:
+ type: string
+ host_aliases:
+ items:
+ properties:
+ hostnames:
+ items:
+ type: string
+ type: array
+ ip:
+ type: string
+ required:
+ - hostnames
+ - ip
+ type: object
+ type: array
+ image:
+ type: string
+ image_pull_secrets:
+ items:
+ type: string
+ type: array
+ init_permissions_container_security_context:
+ properties:
+ allow_privilege_escalation:
+ type: boolean
+ capabilities:
+ properties:
+ add:
+ items:
+ description: Capability represent POSIX capabilities
+ type
+ type: string
+ type: array
+ drop:
+ items:
+ description: Capability represent POSIX capabilities
+ type
+ type: string
+ type: array
+ required:
+ - add
+ - drop
+ type: object
+ privileged:
+ type: boolean
+ read_only_root_filesystem:
+ type: boolean
+ run_as_group:
+ format: int64
+ type: integer
+ run_as_non_root:
+ type: boolean
+ run_as_user:
+ format: int64
+ type: integer
+ type: object
+ key_file:
+ type: string
+ memory_limit:
+ type: string
+ memory_limit_overwrite_max_allowed:
+ type: string
+ memory_request:
+ type: string
+ memory_request_overwrite_max_allowed:
+ type: string
+ namespace:
+ type: string
+ namespace_overwrite_allowed:
+ type: string
+ node_selector:
+ additionalProperties:
+ type: string
+ type: object
+ node_selector_overwrite_allowed:
+ type: string
+ node_tolerations:
+ additionalProperties:
+ type: string
+ type: object
+ pod_annotations:
+ additionalProperties:
+ type: string
+ type: object
+ pod_annotations_overwrite_allowed:
+ type: string
+ pod_labels:
+ additionalProperties:
+ type: string
+ type: object
+ pod_labels_overwrite_allowed:
+ type: string
+ pod_security_context:
+ properties:
+ fs_group:
+ format: int64
+ type: integer
+ run_as_group:
+ format: int64
+ type: integer
+ run_as_non_root:
+ type: boolean
+ run_as_user:
+ format: int64
+ type: integer
+ supplemental_groups:
+ items:
+ format: int64
+ type: integer
+ type: array
+ type: object
+ pod_termination_grace_period_seconds:
+ format: int64
+ type: integer
+ poll_interval:
+ type: integer
+ poll_timeout:
+ type: integer
+ priority_class_name:
+ type: string
+ privileged:
+ type: boolean
+ pull_policy:
+ items:
+ type: string
+ type: array
+ resource_availability_check_max_attempts:
+ type: integer
+ runtime_class_name:
+ type: string
+ scheduler_name:
+ type: string
+ service_account:
+ type: string
+ service_account_overwrite_allowed:
+ type: string
+ service_container_security_context:
+ properties:
+ allow_privilege_escalation:
+ type: boolean
+ capabilities:
+ properties:
+ add:
+ items:
+ description: Capability represent POSIX capabilities
+ type
+ type: string
+ type: array
+ drop:
+ items:
+ description: Capability represent POSIX capabilities
+ type
+ type: string
+ type: array
+ required:
+ - add
+ - drop
+ type: object
+ privileged:
+ type: boolean
+ read_only_root_filesystem:
+ type: boolean
+ run_as_group:
+ format: int64
+ type: integer
+ run_as_non_root:
+ type: boolean
+ run_as_user:
+ format: int64
+ type: integer
+ type: object
+ service_cpu_limit:
+ type: string
+ service_cpu_limit_overwrite_max_allowed:
+ type: string
+ service_cpu_request:
+ type: string
+ service_cpu_request_overwrite_max_allowed:
+ type: string
+ service_ephemeral_storage_limit:
+ type: string
+ service_ephemeral_storage_limit_overwrite_max_allowed:
+ type: string
+ service_ephemeral_storage_request:
+ type: string
+ service_ephemeral_storage_request_overwrite_max_allowed:
+ type: string
+ service_memory_limit:
+ type: string
+ service_memory_limit_overwrite_max_allowed:
+ type: string
+ service_memory_request:
+ type: string
+ service_memory_request_overwrite_max_allowed:
+ type: string
+ services:
+ items:
+ properties:
+ alias:
+ type: string
+ command:
+ items:
+ type: string
+ type: array
+ entrypoint:
+ items:
+ type: string
+ type: array
+ name:
+ type: string
+ required:
+ - command
+ - entrypoint
+ - name
+ type: object
+ type: array
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ volumes:
+ properties:
+ config_map:
+ items:
+ properties:
+ items:
+ additionalProperties:
+ type: string
+ type: object
+ mount_path:
+ type: string
+ name:
+ type: string
+ read_only:
+ type: boolean
+ sub_path:
+ type: string
+ required:
+ - mount_path
+ - name
+ type: object
+ type: array
+ csi:
+ items:
+ properties:
+ driver:
+ type: string
+ fs_type:
+ type: string
+ mount_path:
+ type: string
+ name:
+ type: string
+ read_only:
+ type: boolean
+ sub_path:
+ type: string
+ volume_attributes:
+ additionalProperties:
+ type: string
+ type: object
+ required:
+ - driver
+ - fs_type
+ - mount_path
+ - name
+ type: object
+ type: array
+ empty_dir:
+ items:
+ properties:
+ medium:
+ type: string
+ mount_path:
+ type: string
+ name:
+ type: string
+ sub_path:
+ type: string
+ required:
+ - mount_path
+ - name
+ type: object
+ type: array
+ host_path:
+ items:
+ properties:
+ host_path:
+ type: string
+ mount_path:
+ type: string
+ name:
+ type: string
+ read_only:
+ type: boolean
+ sub_path:
+ type: string
+ required:
+ - mount_path
+ - name
+ type: object
+ type: array
+ pvc:
+ items:
+ properties:
+ mount_path:
+ type: string
+ name:
+ type: string
+ read_only:
+ type: boolean
+ sub_path:
+ type: string
+ required:
+ - mount_path
+ - name
+ type: object
+ type: array
+ secret:
+ items:
+ properties:
+ items:
+ additionalProperties:
+ type: string
+ type: object
+ mount_path:
+ type: string
+ name:
+ type: string
+ read_only:
+ type: boolean
+ sub_path:
+ type: string
+ required:
+ - mount_path
+ - name
+ type: object
+ type: array
+ type: object
+ type: object
+ name:
+ type: string
+ registration_config:
+ description: "RegisterNewRunnerOptions represents the available
+ RegisterNewRunner() options. \n GitLab API docs: https://docs.gitlab.com/ce/api/runners.html#register-a-new-runner"
+ properties:
+ accessLevel:
+ type: string
+ active:
+ description: Active is deprecated. use paused instead
+ type: boolean
+ description:
+ type: string
+ info:
+ description: "RegisterNewRunnerInfoOptions represents the
+ info hashmap parameter in RegisterNewRunnerOptions. \n
+ GitLab API docs: https://docs.gitlab.com/ce/api/runners.html#register-a-new-runner"
+ properties:
+ architecture:
+ type: string
+ name:
+ type: string
+ platform:
+ type: string
+ revision:
+ type: string
+ version:
+ type: string
+ type: object
+ locked:
+ type: boolean
+ maintenance_note:
+ type: string
+ maximum_timeout:
+ type: integer
+ paused:
+ type: boolean
+ run_untagged:
+ type: boolean
+ tag_list:
+ items:
+ type: string
+ type: array
+ token:
+ type: string
+ type: object
+ required:
+ - name
+ - registration_config
+ type: object
+ type: array
+ gitlab_instance_url:
+ type: string
+ log_format:
+ enum:
+ - runner
+ - text
+ - json
+ type: string
+ log_level:
+ enum:
+ - panic
+ - fatal
+ - error
+ - warning
+ - info
+ - debug
+ type: string
+ sentry_dsn:
+ description: SentryDsn Enables tracking of all system level errors
+ to Sentry.
+ type: string
+ required:
+ - entries
+ type: object
+ status:
+ description: MultiRunnerStatus defines the observed state of MultiRunner
+ properties:
+ auth_tokens:
+ additionalProperties:
+ type: string
+ type: object
+ config_map_version:
+ type: string
+ error:
+ type: string
+ last_registration_tags:
+ additionalProperties:
+ items:
+ type: string
+ type: array
+ type: object
+ ready:
+ type: boolean
+ required:
+ - auth_tokens
+ - config_map_version
+ - error
+ - last_registration_tags
+ - ready
+ type: object
+ type: object
+ served: true
+ storage: true
+ subresources:
+ status: {}
diff --git a/config/crd/bases/gitlab.k8s.alekc.dev_runners.yaml b/config/crd/bases/gitlab.k8s.alekc.dev_runners.yaml
index d31ddfa..ed1fd94 100644
--- a/config/crd/bases/gitlab.k8s.alekc.dev_runners.yaml
+++ b/config/crd/bases/gitlab.k8s.alekc.dev_runners.yaml
@@ -1,11 +1,9 @@
-
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
- controller-gen.kubebuilder.io/version: v0.4.1
- creationTimestamp: null
+ controller-gen.kubebuilder.io/version: v0.13.0
name: runners.gitlab.k8s.alekc.dev
spec:
group: gitlab.k8s.alekc.dev
@@ -366,6 +364,10 @@ spec:
items:
type: string
type: array
+ allowed_pull_policies:
+ items:
+ type: string
+ type: array
allowed_services:
items:
type: string
@@ -408,10 +410,6 @@ spec:
run_as_user:
format: int64
type: integer
- required:
- - allow_privilege_escalation
- - privileged
- - read_only_root_filesystem
type: object
ca_file:
type: string
@@ -571,10 +569,6 @@ spec:
items:
type: string
type: array
- required:
- - nameservers
- - options
- - searches
type: object
dns_policy:
type: string
@@ -620,10 +614,6 @@ spec:
run_as_user:
format: int64
type: integer
- required:
- - allow_privilege_escalation
- - privileged
- - read_only_root_filesystem
type: object
helper_cpu_limit:
type: string
@@ -675,6 +665,41 @@ spec:
items:
type: string
type: array
+ init_permissions_container_security_context:
+ properties:
+ allow_privilege_escalation:
+ type: boolean
+ capabilities:
+ properties:
+ add:
+ items:
+ description: Capability represent POSIX capabilities
+ type
+ type: string
+ type: array
+ drop:
+ items:
+ description: Capability represent POSIX capabilities
+ type
+ type: string
+ type: array
+ required:
+ - add
+ - drop
+ type: object
+ privileged:
+ type: boolean
+ read_only_root_filesystem:
+ type: boolean
+ run_as_group:
+ format: int64
+ type: integer
+ run_as_non_root:
+ type: boolean
+ run_as_user:
+ format: int64
+ type: integer
+ type: object
key_file:
type: string
memory_limit:
@@ -693,6 +718,8 @@ spec:
additionalProperties:
type: string
type: object
+ node_selector_overwrite_allowed:
+ type: string
node_tolerations:
additionalProperties:
type: string
@@ -707,6 +734,8 @@ spec:
additionalProperties:
type: string
type: object
+ pod_labels_overwrite_allowed:
+ type: string
pod_security_context:
properties:
fs_group:
@@ -733,12 +762,20 @@ spec:
type: integer
poll_timeout:
type: integer
+ priority_class_name:
+ type: string
privileged:
type: boolean
pull_policy:
items:
type: string
type: array
+ resource_availability_check_max_attempts:
+ type: integer
+ runtime_class_name:
+ type: string
+ scheduler_name:
+ type: string
service_account:
type: string
service_account_overwrite_allowed:
@@ -777,10 +814,6 @@ spec:
run_as_user:
format: int64
type: integer
- required:
- - allow_privilege_escalation
- - privileged
- - read_only_root_filesystem
type: object
service_cpu_limit:
type: string
@@ -948,17 +981,16 @@ spec:
- name
type: object
type: array
- required:
- - config_map
- - csi
- - empty_dir
- - host_path
- - pvc
- - secret
type: object
type: object
gitlab_instance_url:
type: string
+ log_format:
+ enum:
+ - runner
+ - text
+ - json
+ type: string
log_level:
enum:
- panic
@@ -1011,8 +1043,6 @@ spec:
type: array
token:
type: string
- token_secret:
- type: string
type: object
required:
- registration_config
@@ -1055,9 +1085,3 @@ spec:
storage: true
subresources:
status: {}
-status:
- acceptedNames:
- kind: ""
- plural: ""
- conditions: []
- storedVersions: []
diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml
index 5adb7f2..7d071e2 100644
--- a/config/crd/kustomization.yaml
+++ b/config/crd/kustomization.yaml
@@ -3,17 +3,20 @@
# It should be run by config/default
resources:
- bases/gitlab.k8s.alekc.dev_runners.yaml
+- bases/gitlab.k8s.alekc.dev_multirunners.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_runners.yaml
+#- patches/webhook_in_multirunners.yaml
#+kubebuilder:scaffold:crdkustomizewebhookpatch
# [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix.
# patches here are for enabling the CA injection for each CRD
#- patches/cainjection_in_runners.yaml
+#- patches/cainjection_in_multirunners.yaml
#+kubebuilder:scaffold:crdkustomizecainjectionpatch
# the following config is for teaching kustomize how to do kustomization for CRDs.
diff --git a/config/crd/patches/cainjection_in_multirunners.yaml b/config/crd/patches/cainjection_in_multirunners.yaml
new file mode 100644
index 0000000..a406518
--- /dev/null
+++ b/config/crd/patches/cainjection_in_multirunners.yaml
@@ -0,0 +1,7 @@
+# The following patch adds a directive for certmanager to inject CA into the CRD
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
+ name: multirunners.gitlab.k8s.alekc.dev
diff --git a/config/crd/patches/webhook_in_multirunners.yaml b/config/crd/patches/webhook_in_multirunners.yaml
new file mode 100644
index 0000000..8ca2e67
--- /dev/null
+++ b/config/crd/patches/webhook_in_multirunners.yaml
@@ -0,0 +1,16 @@
+# The following patch enables a conversion webhook for the CRD
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ name: multirunners.gitlab.k8s.alekc.dev
+spec:
+ conversion:
+ strategy: Webhook
+ webhook:
+ clientConfig:
+ service:
+ namespace: system
+ name: webhook-service
+ path: /convert
+ conversionReviewVersions:
+ - v1
diff --git a/config/rbac/multirunner_editor_role.yaml b/config/rbac/multirunner_editor_role.yaml
new file mode 100644
index 0000000..c7a537e
--- /dev/null
+++ b/config/rbac/multirunner_editor_role.yaml
@@ -0,0 +1,24 @@
+# permissions for end users to edit multirunners.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: multirunner-editor-role
+rules:
+- apiGroups:
+ - gitlab.k8s.alekc.dev
+ resources:
+ - multirunners
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - update
+ - watch
+- apiGroups:
+ - gitlab.k8s.alekc.dev
+ resources:
+ - multirunners/status
+ verbs:
+ - get
diff --git a/config/rbac/multirunner_viewer_role.yaml b/config/rbac/multirunner_viewer_role.yaml
new file mode 100644
index 0000000..c6e149c
--- /dev/null
+++ b/config/rbac/multirunner_viewer_role.yaml
@@ -0,0 +1,20 @@
+# permissions for end users to view multirunners.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: multirunner-viewer-role
+rules:
+- apiGroups:
+ - gitlab.k8s.alekc.dev
+ resources:
+ - multirunners
+ verbs:
+ - get
+ - list
+ - watch
+- apiGroups:
+ - gitlab.k8s.alekc.dev
+ resources:
+ - multirunners/status
+ verbs:
+ - get
diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml
index b1b597d..e71f211 100644
--- a/config/rbac/role.yaml
+++ b/config/rbac/role.yaml
@@ -1,9 +1,7 @@
-
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
- creationTimestamp: null
name: manager-role
rules:
- apiGroups:
@@ -32,6 +30,32 @@ rules:
- patch
- update
- watch
+- apiGroups:
+ - gitlab.k8s.alekc.dev
+ resources:
+ - multirunners
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - update
+ - watch
+- apiGroups:
+ - gitlab.k8s.alekc.dev
+ resources:
+ - multirunners/finalizers
+ verbs:
+ - update
+- apiGroups:
+ - gitlab.k8s.alekc.dev
+ resources:
+ - multirunners/status
+ verbs:
+ - get
+ - patch
+ - update
- apiGroups:
- gitlab.k8s.alekc.dev
resources:
diff --git a/config/samples/dind.yaml b/config/samples/dind.yaml
new file mode 100644
index 0000000..5cefb86
--- /dev/null
+++ b/config/samples/dind.yaml
@@ -0,0 +1,29 @@
+apiVersion: gitlab.k8s.alekc.dev/v1beta1
+kind: Runner
+metadata:
+ labels:
+ argocd.argoproj.io/instance: gitlab-runner
+ name: dind
+spec:
+ check_interval: 5
+ concurrent: 10
+ executor_config:
+ cpu_request: '0.5'
+ memory_request: 150Mi
+ node_selector:
+ kubernetes.io/arch: amd64
+ privileged: true
+ volumes:
+ empty_dir:
+ - medium: Memory
+ mount_path: /certs/client
+ name: docker-certs
+ gitlab_instance_url: 'https://gitlab.com/'
+ log_level: debug
+ registration_config:
+ tag_list:
+ - dev
+ - k8s
+ - amd64
+ - dind
+ token: xxxxx
diff --git a/config/samples/gitlab_v1beta1_multirunner.yaml b/config/samples/gitlab_v1beta1_multirunner.yaml
new file mode 100644
index 0000000..a3880ba
--- /dev/null
+++ b/config/samples/gitlab_v1beta1_multirunner.yaml
@@ -0,0 +1,27 @@
+apiVersion: gitlab.k8s.alekc.dev/v1beta1
+kind: MultiRunner
+metadata:
+ name: multirunner-simple
+spec:
+ concurrent: 10
+ gitlab_instance_url: https://gitlab.com/
+ entries:
+ - name: runner 1
+ registration_config:
+ tag_list:
+ - dev1
+ - test-gitlab-runner
+ token: xxx
+ description: "Runner 1desc"
+ executor_config:
+ memory_request: "50Mi"
+ - name: runner 2
+ registration_config:
+ tag_list:
+ - dev2
+ - runner2
+ token: xxx
+ description: "Runner 2 desc"
+
+
+
diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml
index 31576c8..aac7617 100644
--- a/config/webhook/manifests.yaml
+++ b/config/webhook/manifests.yaml
@@ -1,9 +1,7 @@
-
---
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
- creationTimestamp: null
name: mutating-webhook-configuration
webhooks:
- admissionReviewVersions:
@@ -27,12 +25,10 @@ webhooks:
resources:
- runners
sideEffects: None
-
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
- creationTimestamp: null
name: validating-webhook-configuration
webhooks:
- admissionReviewVersions:
diff --git a/controllers/multirunner_controller.go b/controllers/multirunner_controller.go
new file mode 100644
index 0000000..4390daa
--- /dev/null
+++ b/controllers/multirunner_controller.go
@@ -0,0 +1,252 @@
+/*
+Copyright 2021.
+
+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 controllers
+
+import (
+ "context"
+ "reflect"
+
+ "gitlab.k8s.alekc.dev/internal/api"
+ "gitlab.k8s.alekc.dev/internal/crud"
+ "gitlab.k8s.alekc.dev/internal/generate"
+ "gitlab.k8s.alekc.dev/internal/result"
+ "gitlab.k8s.alekc.dev/internal/validate"
+ appsv1 "k8s.io/api/apps/v1"
+ corev1 "k8s.io/api/core/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/client-go/util/retry"
+ ctrl "sigs.k8s.io/controller-runtime"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/event"
+ "sigs.k8s.io/controller-runtime/pkg/log"
+ "sigs.k8s.io/controller-runtime/pkg/predicate"
+
+ gitlabv1beta1 "gitlab.k8s.alekc.dev/api/v1beta1"
+)
+
+// MultiRunnerReconciler reconciles a MultiRunner object
+type MultiRunnerReconciler struct {
+ client.Client
+ Scheme *runtime.Scheme
+ GitlabApiClient api.GitlabClient
+}
+
+// +kubebuilder:rbac:groups=gitlab.k8s.alekc.dev,resources=multirunners,verbs=get;list;watch;create;update;patch;delete
+// +kubebuilder:rbac:groups=gitlab.k8s.alekc.dev,resources=multirunners/status,verbs=get;update;patch
+// +kubebuilder:rbac:groups=gitlab.k8s.alekc.dev,resources=multirunners/finalizers,verbs=update
+// +kubebuilder:rbac:groups="apps",resources=deployments,verbs=get;list;watch;create;update;patch;delete
+// +kubebuilder:rbac:groups="core",resources=configmaps;secrets;serviceaccounts,verbs=get;list;watch;create;update;patch;delete
+// +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=roles;rolebindings,verbs=*
+
+func (r *MultiRunnerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
+ logger := log.FromContext(ctx)
+
+ // find the object, in case we cannot find it just return.
+ runnerObj, err := crud.MultiRunner(ctx, r.Client, req.NamespacedName)
+ if err != nil {
+ return *result.DontRequeue(), client.IgnoreNotFound(err)
+ }
+
+ logger.Info("reconciling")
+ if runnerObj.IsBeingDeleted() {
+ logger.Info("runner is being deleted")
+
+ if !runnerObj.IsAuthenticated() {
+ logger.Info("removing runner/s from gitlab")
+ for _, reg := range runnerObj.RegistrationConfig() {
+ cl := r.GitlabApiClient
+ if cl == nil {
+ if cl, err = api.NewGitlabClient(*reg.Token, reg.GitlabUrl); err != nil {
+ logger.Error(err, "cannot get gitlab api client for deletion of the runner")
+ continue
+ }
+ }
+ if _, err = cl.DeleteByToken(reg.AuthToken); err != nil {
+ // do not interrupt execution flow, just report it
+ logger.Error(err, "warning: cannot delete token from gitlab")
+ }
+ }
+ }
+
+ runnerObj.RemoveFinalizer()
+ if err = runnerObj.Update(ctx, r); err != nil {
+ logger.Error(err, "cannot remove finalizer")
+ return resultRequeueAfterDefaultTimeout, err
+ }
+ return resultRequeueNow, nil
+ }
+
+ // update the status when done processing in case there is anything pending
+ defer func() {
+ err := retry.RetryOnConflict(retry.DefaultRetry, func() error {
+ newRunner, err := crud.MultiRunner(
+ context.Background(),
+ r.Client,
+ client.ObjectKey{
+ Namespace: runnerObj.GetNamespace(),
+ Name: runnerObj.GetName()},
+ )
+ switch {
+ case err != nil:
+ logger.Error(err, "cannot get runner")
+ return err
+
+ // no changes in status detected
+ case reflect.DeepEqual(runnerObj.GetStatus(), newRunner.GetStatus()):
+ return nil
+ }
+ newRunner.SetStatus(runnerObj.GetStatus())
+ return newRunner.UpdateStatus(ctx, r.Status())
+ })
+ if err != nil {
+ logger.Error(err, "cannot update runner's status")
+ }
+ }()
+
+ // if finalizer is not registered, do it now
+ if !runnerObj.HasFinalizer() {
+ logger.Info("setting finalizer")
+ runnerObj.AddFinalizer()
+ if err := runnerObj.Update(ctx, r); err != nil {
+ logger.Error(err, "cannot set finalizer")
+ return resultRequeueAfterDefaultTimeout, err
+ }
+ return resultRequeueNow, nil
+ }
+
+ // reset the error. If there is one still present we will get it later on
+ runnerObj.SetStatusError("")
+ runnerObj.SetStatusReady(false)
+
+ // if the runner doesn't have a saved authentication token
+ // or the latest registration token/tags are different from the
+ // current one, we need to redo the registration
+ if !runnerObj.HasValidAuth() {
+ // todo: add reg removal
+ for _, regConfig := range runnerObj.RegistrationConfig() {
+ gitlabClient := r.GitlabApiClient
+ if gitlabClient == nil {
+ if gitlabClient, err = api.NewGitlabClient(*regConfig.Token, regConfig.GitlabUrl); err != nil {
+ logger.Error(err, "cannot get gitlab api client")
+ return resultRequeueAfterDefaultTimeout, err
+ }
+ }
+ regConfig.AuthToken, err = gitlabClient.Register(regConfig.RegisterNewRunnerOptions)
+ if err != nil {
+ logger.Error(err, "cannot register on gitlab")
+ return resultRequeueAfterDefaultTimeout, err
+ }
+ logger.Info("registered new runner on the gitlab", "token", regConfig.Token)
+ runnerObj.StoreRunnerRegistration(regConfig)
+ }
+ return resultRequeueNow, nil
+ }
+
+ // create required rbac credentials if they are missing
+ if err = crud.CreateRBACIfMissing(ctx, r.Client, runnerObj, logger); err != nil {
+ runnerObj.SetStatusError("Cannot create the rbac objects")
+ logger.Error(err, "cannot create rbac objects")
+ return resultRequeueAfterDefaultTimeout, err
+ }
+
+ // generate a new config map based on the runner spec
+ generatedTomlConfig, configHashKey, err := generate.TomlConfig(runnerObj)
+ if err != nil {
+ logger.Error(err, "cannot generate config map")
+ return resultRequeueAfterDefaultTimeout, err
+ }
+
+ // if the config version differs, perform the upgrade
+ // set the status with a config map hash
+ if result, err := validate.ConfigMap(ctx, r.Client, runnerObj, logger, generatedTomlConfig, configHashKey); result != nil || err != nil {
+ return *result, err
+ }
+
+ // validate deployment data
+ if result, err := validate.Deployment(ctx, r.Client, runnerObj, logger); result != nil || err != nil {
+ return *result, err
+ }
+
+ runnerObj.SetStatusReady(true)
+ return *result.DontRequeue(), nil
+}
+
+// SetupWithManager sets up the controller with the Manager.
+func (r *MultiRunnerReconciler) SetupWithManager(mgr ctrl.Manager) error {
+ const runnerOwnerCmKey = ".metadata.cmmrcontroller"
+ const runnerOwnerDpKey = ".metadata.dpmrcontroller"
+
+ ctx := context.Background()
+ if err := mgr.GetFieldIndexer().IndexField(ctx, &corev1.ConfigMap{}, runnerOwnerCmKey, func(object client.Object) []string {
+ // grab the configMap object, extract the owner...
+ configMap := object.(*corev1.ConfigMap)
+ owner := metav1.GetControllerOf(configMap)
+ if owner == nil {
+ return nil
+ }
+
+ // ensure that we're dealing with a proper object
+ if owner.APIVersion != gitlabv1beta1.GroupVersion.String() || owner.Kind != "MultiRunner" {
+ return nil
+ }
+
+ return []string{string(owner.UID)}
+ }); err != nil {
+ return err
+ }
+
+ // deployments
+ if err := mgr.GetFieldIndexer().IndexField(ctx, &appsv1.Deployment{}, runnerOwnerDpKey, func(rawObj client.Object) []string {
+ // grab the deployment object, extract the owner...
+ deployment := rawObj.(*appsv1.Deployment)
+ owner := metav1.GetControllerOf(deployment)
+ if owner == nil {
+ return nil
+ }
+
+ // ensure that we're dealing with a proper object
+ if owner.APIVersion != gitlabv1beta1.GroupVersion.String() || owner.Kind != "MultiRunner" {
+ return nil
+ }
+
+ return []string{string(owner.UID)}
+ }); err != nil {
+ return err
+ }
+
+ return ctrl.NewControllerManagedBy(mgr).
+ For(&gitlabv1beta1.MultiRunner{}).
+ WithEventFilter(predicate.Funcs{
+ UpdateFunc: func(e event.UpdateEvent) bool {
+ if e.ObjectOld == nil || e.ObjectNew == nil {
+ return false
+ }
+ return e.ObjectNew.GetGeneration() != e.ObjectOld.GetGeneration()
+ },
+ DeleteFunc: func(event event.DeleteEvent) bool {
+ // The reconciler adds a finalizer when the delete timestamp is added.
+ // Avoid reconciling in case it's a runner, we still want to reconcile if it's a dependent object
+ if _, ok := event.Object.(*gitlabv1beta1.MultiRunner); ok {
+ return false
+ }
+ return true
+ }}).
+ Owns(&corev1.ConfigMap{}).
+ Owns(&appsv1.Deployment{}).
+ Complete(r)
+}
diff --git a/controllers/multirunner_controller_test.go b/controllers/multirunner_controller_test.go
new file mode 100644
index 0000000..e4998c9
--- /dev/null
+++ b/controllers/multirunner_controller_test.go
@@ -0,0 +1,141 @@
+package controllers
+
+//
+// import (
+// "context"
+// "fmt"
+// "os"
+// "strconv"
+// "time"
+//
+// "gitlab.k8s.alekc.dev/api/v1beta1"
+// "gitlab.k8s.alekc.dev/internal/generate"
+// corev1 "k8s.io/api/core/v1"
+// metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+// "k8s.io/apimachinery/pkg/types"
+// "k8s.io/utils/pointer"
+// "sigs.k8s.io/controller-runtime/pkg/client"
+//
+// . "github.com/onsi/ginkgo"
+// "github.com/onsi/ginkgo/extensions/table"
+// . "github.com/onsi/gomega"
+// )
+//
+// // +kubebuilder:docs-gen:collapse=Imports
+//
+// type multiRunnerTestCase struct {
+// MutliRunner *v1beta1.MultiRunner
+// CheckCondition func(*v1beta1.MultiRunner) bool
+// CheckRunner func(*v1beta1.MultiRunner)
+// }
+//
+// type multiRunnerTestCaseTweak func(runnerTestCase *multiRunnerTestCase)
+//
+// var _ = Describe("Runner controller", func() {
+//
+// // Define utility constants for object names and testing timeouts/durations and intervals.
+// var RunnerNamespace string
+// var RunnerName string
+//
+// // if we are running a debugger, then increase timeout to 10 minutes to prevent killed debug sessions
+// if val, ok := os.LookupEnv("TEST_TIMEOUT"); ok {
+// newTimeout, err := strconv.Atoi(val)
+// if err == nil {
+// timeout = time.Second * time.Duration(newTimeout)
+// }
+// }
+//
+// // before every test
+// BeforeEach(func() {
+// var err error
+// // create a runner namespace
+// RunnerNamespace, err = CreateNamespace(k8sClient)
+// Expect(err).ToNot(HaveOccurred())
+//
+// // generate a random name for the runner
+// RunnerName = fmt.Sprintf("test-runner-%s", generate.RandomString(5))
+// })
+//
+// // after each test perform some cleaning actions
+// AfterEach(func() {
+// // delete the runner
+// Expect(k8sClient.Delete(context.Background(), &v1beta1.MultiRunner{
+// ObjectMeta: metav1.ObjectMeta{
+// Name: RunnerName,
+// Namespace: RunnerNamespace,
+// },
+// }, client.PropagationPolicy(metav1.DeletePropagationBackground)), client.GracePeriodSeconds(0)).To(Succeed())
+//
+// // delete namespace
+// Expect(k8sClient.Delete(context.Background(), &corev1.Namespace{
+// ObjectMeta: metav1.ObjectMeta{
+// Name: RunnerNamespace,
+// },
+// }, client.PropagationPolicy(metav1.DeletePropagationBackground)), client.GracePeriodSeconds(0)).To(Succeed())
+// })
+//
+// table.DescribeTable(
+// "When reconciling Gitlab Runner",
+// func(tweaks ...multiRunnerTestCaseTweak) {
+// tc := &multiRunnerTestCase{
+// MutliRunner: defaultMultiRunner(RunnerName, RunnerNamespace),
+// CheckCondition: func(runner *v1beta1.MultiRunner) bool {
+// return runner.UID != "" && runner.Status.Ready
+// },
+// }
+// for _, tweak := range tweaks {
+// tweak(tc)
+// }
+//
+// ctx := context.Background()
+// By("creating a runner")
+// Expect(Expect(k8sClient.Create(ctx, tc.MutliRunner)).To(Succeed()))
+//
+// //
+// esKey := types.NamespacedName{Name: RunnerName, Namespace: RunnerNamespace}
+// createdRunner := &v1beta1.MultiRunner{}
+// By("checking the runner condition")
+// Eventually(func() bool {
+// err := k8sClient.Get(ctx, esKey, createdRunner)
+// if err != nil {
+// return false
+// }
+// return tc.CheckCondition(createdRunner)
+// }, timeout, interval).Should(BeTrue())
+//
+// //
+// tc.CheckRunner(createdRunner)
+// },
+// // table.Entry("Should register", caseEnvironmentIsSpecified),
+// // table.Entry("Should have created a different registration on tag update", caseTagsChanged),
+// // table.Entry("Should have created a different registration on registration token update", caseRegistrationTokenChanged),
+// // table.Entry("Should have updated runner status with auth token", caseTestAuthToken),
+// // table.Entry("Should have created required RBAC", caseRBACCheck),
+// // table.Entry("Should have generated config map", caseGeneratedConfigMap),
+// // table.Entry("Should have generated deployment", caseCheckDeployment),
+// // table.Entry("On spec change, config map should be updated", caseSpecChanged),
+// )
+// })
+//
+// func defaultMultiRunner(name string, nameSpace string) *v1beta1.MultiRunner {
+// return &v1beta1.MultiRunner{
+// TypeMeta: metav1.TypeMeta{
+// APIVersion: v1beta1.GroupVersion.String(),
+// Kind: "MultiRunner",
+// },
+// ObjectMeta: metav1.ObjectMeta{
+// Name: name,
+// Namespace: nameSpace,
+// },
+// Spec: v1beta1.MultiRunnerSpec{
+// Entries: []v1beta1.MultiRunnerEntry{
+// {
+// RegistrationConfig: v1beta1.RegisterNewRunnerOptions{
+// Token: pointer.String("zTS6g2Q8bp8y13_ynfpN"),
+// TagList: []string{"default-tag"},
+// },
+// },
+// },
+// },
+// }
+// }
diff --git a/controllers/runner_controller.go b/controllers/runner_controller.go
index 757895d..763d73f 100644
--- a/controllers/runner_controller.go
+++ b/controllers/runner_controller.go
@@ -18,25 +18,21 @@ package controllers
import (
"context"
- coreErrors "errors"
- "fmt"
- "gitlab.k8s.alekc.dev/internal/result"
- "k8s.io/client-go/util/retry"
+ "gitlab.k8s.alekc.dev/internal/types"
"reflect"
- "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"time"
"github.com/go-logr/logr"
"gitlab.k8s.alekc.dev/internal/api"
+ "gitlab.k8s.alekc.dev/internal/crud"
"gitlab.k8s.alekc.dev/internal/generate"
+ "gitlab.k8s.alekc.dev/internal/result"
"gitlab.k8s.alekc.dev/internal/validate"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
- v1 "k8s.io/api/rbac/v1"
- "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
- "k8s.io/apimachinery/pkg/types"
+ "k8s.io/client-go/util/retry"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/event"
@@ -46,9 +42,6 @@ import (
gitlabv1beta1 "gitlab.k8s.alekc.dev/api/v1beta1"
)
-const ownerCmKey = ".metadata.cmcontroller"
-const ownerDpKey = ".metadata.dpcontroller"
-
const defaultTimeout = 15 * time.Second
const configMapKeyName = "config.toml"
const configVersionAnnotationKey = "config-version"
@@ -71,59 +64,41 @@ var resultRequeueNow = ctrl.Result{Requeue: true}
// +kubebuilder:rbac:groups="core",resources=configmaps;secrets;serviceaccounts,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=roles;rolebindings,verbs=*
-// Reconcile is part of the main kubernetes reconciliation loop which aims to
-// move the current state of the cluster closer to the desired state.
-//
-// For more details, check Reconcile and its Result here:
-// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.8.3/pkg/reconcile
func (r *RunnerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
- const finalizer = "gitlab.k8s.alekc.dev/finalizer"
logger := log.FromContext(ctx)
// find the object, in case we cannot find it just return.
- runnerObj := &gitlabv1beta1.Runner{}
- err := r.Client.Get(ctx, req.NamespacedName, runnerObj)
+ runnerObj, err := crud.SingleRunner(ctx, r.Client, req.NamespacedName)
if err != nil {
return *result.DontRequeue(), client.IgnoreNotFound(err)
}
logger.Info("reconciling")
- if !runnerObj.ObjectMeta.DeletionTimestamp.IsZero() {
- logger.Info("runner is being deleted")
-
- if runnerObj.Status.AuthenticationToken != "" {
- logger.Info("removing runner from gitlab")
- if res, err := r.RemoveRunnerFromGitlab(ctx, runnerObj, logger); res != nil {
- return *res, err
- }
- }
- controllerutil.RemoveFinalizer(runnerObj, finalizer)
- if err := r.Update(ctx, runnerObj); err != nil {
- logger.Error(err, "cannot remove finalizer")
- return resultRequeueAfterDefaultTimeout, err
- }
- return resultRequeueNow, nil
+ if runnerObj.IsBeingDeleted() {
+ return r.manageDeletion(ctx, runnerObj, logger)
}
// update the status when done processing in case there is anything pending
defer func() {
err := retry.RetryOnConflict(retry.DefaultRetry, func() error {
- var newRunner gitlabv1beta1.Runner
- err = r.Client.Get(
- ctx,
- client.ObjectKey{Namespace: runnerObj.Namespace, Name: runnerObj.GetName()},
- &newRunner)
+ newRunner, err := crud.SingleRunner(
+ context.Background(),
+ r.Client,
+ client.ObjectKey{
+ Namespace: runnerObj.GetNamespace(),
+ Name: runnerObj.GetName()},
+ )
switch {
case err != nil:
logger.Error(err, "cannot get runner")
return err
// no changes in status detected
- case reflect.DeepEqual(runnerObj.Status, newRunner.Status):
+ case reflect.DeepEqual(runnerObj.GetStatus(), newRunner.GetStatus()):
return nil
}
- newRunner.Status = runnerObj.Status
- return r.Status().Update(ctx, &newRunner)
+ newRunner.SetStatus(runnerObj.GetStatus())
+ return newRunner.UpdateStatus(ctx, r.Status())
})
if err != nil {
logger.Error(err, "cannot update runner's status")
@@ -131,10 +106,10 @@ func (r *RunnerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr
}()
// if finalizer is not registered, do it now
- if !controllerutil.ContainsFinalizer(runnerObj, finalizer) {
+ if !runnerObj.HasFinalizer() {
logger.Info("setting finalizer")
- controllerutil.AddFinalizer(runnerObj, finalizer)
- if err := r.Update(ctx, runnerObj); err != nil {
+ runnerObj.AddFinalizer()
+ if err := runnerObj.Update(ctx, r); err != nil {
logger.Error(err, "cannot set finalizer")
return resultRequeueAfterDefaultTimeout, err
}
@@ -142,35 +117,42 @@ func (r *RunnerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr
}
// reset the error. If there is one still present we will get it later on
- runnerObj.Status.Error = ""
- runnerObj.Status.Ready = false
+ runnerObj.SetStatusError("")
+ runnerObj.SetStatusReady(false)
// if the runner doesn't have a saved authentication token
// or the latest registration token/tags are different from the
// current one, we need to redo the registration
- if runnerObj.Status.AuthenticationToken == "" ||
- runnerObj.Status.LastRegistrationToken != *runnerObj.Spec.RegistrationConfig.Token ||
- !reflect.DeepEqual(runnerObj.Status.LastRegistrationTags, runnerObj.Spec.RegistrationConfig.TagList) {
-
- // since we are doing a new registration, IF the runner already has an authentication token, delete it from gitlab server
- if runnerObj.Status.AuthenticationToken != "" {
- if res, err := r.RemoveRunnerFromGitlab(ctx, runnerObj, logger); res != nil {
- return *res, err
+ if !runnerObj.HasValidAuth() {
+ // todo: add reg removal
+ for _, regConfig := range runnerObj.RegistrationConfig() {
+ gitlabClient := r.GitlabApiClient
+ if gitlabClient == nil {
+ if gitlabClient, err = api.NewGitlabClient(*regConfig.Token, regConfig.GitlabUrl); err != nil {
+ logger.Error(err, "cannot get gitlab api client")
+ return resultRequeueAfterDefaultTimeout, err
+ }
+ }
+ regConfig.AuthToken, err = gitlabClient.Register(regConfig.RegisterNewRunnerOptions)
+ if err != nil {
+ logger.Error(err, "cannot register on gitlab")
+ return resultRequeueAfterDefaultTimeout, err
}
+ logger.Info("registered new runner on the gitlab", "token", regConfig.Token)
+ runnerObj.StoreRunnerRegistration(regConfig)
}
-
- return r.RegisterNewRunnerOnGitlab(ctx, runnerObj, logger)
+ return resultRequeueNow, nil
}
// create required rbac credentials if they are missing
- if err = r.CreateRBACIfMissing(ctx, runnerObj, logger); err != nil {
- runnerObj.Status.Error = "Cannot create the rbac objects"
+ if err = crud.CreateRBACIfMissing(ctx, r.Client, runnerObj, logger); err != nil {
+ runnerObj.SetStatusError("Cannot create the rbac objects")
logger.Error(err, "cannot create rbac objects")
return resultRequeueAfterDefaultTimeout, err
}
// generate a new config map based on the runner spec
- generatedTomlConfig, configHashKey, err := generate.ConfigText(runnerObj)
+ generatedTomlConfig, configHashKey, err := generate.TomlConfig(runnerObj)
if err != nil {
logger.Error(err, "cannot generate config map")
return resultRequeueAfterDefaultTimeout, err
@@ -187,211 +169,16 @@ func (r *RunnerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr
return *result, err
}
- runnerObj.Status.Ready = true
+ runnerObj.SetStatusReady(true)
return *result.DontRequeue(), nil
}
-func (r *RunnerReconciler) createSAIfMissing(ctx context.Context, runnerObject *gitlabv1beta1.Runner, log logr.Logger) error {
- namespacedKey := client.ObjectKey{Namespace: runnerObject.Namespace, Name: runnerObject.ChildName()}
- err := r.Client.Get(ctx, namespacedKey, &corev1.ServiceAccount{})
- switch {
- case err == nil: // service account exists
- return nil
- case !errors.IsNotFound(err):
- log.Error(err, "cannot get the service account")
- return err
- }
- // sa doesn't exists, create it
- log.Info("creating missing s")
- sa := &corev1.ServiceAccount{
- ObjectMeta: metav1.ObjectMeta{
- Name: runnerObject.ChildName(),
- Namespace: runnerObject.Namespace,
- OwnerReferences: runnerObject.GenerateOwnerReference(),
- },
- }
- if err = r.Client.Create(ctx, sa); err != nil {
- log.Error(err, "cannot create service-account")
- return err
- }
- return nil
-}
-
-func (r *RunnerReconciler) createRoleIfMissing(ctx context.Context, runnerObject *gitlabv1beta1.Runner,
- log logr.Logger) error {
- namespacedKey := client.ObjectKey{Namespace: runnerObject.Namespace, Name: runnerObject.ChildName()}
- err := r.Client.Get(ctx, namespacedKey, &v1.Role{})
- switch {
- case err == nil:
- return nil
- case !errors.IsNotFound(err):
- log.Error(err, "cannot get the role")
- return err
- }
- // sa doesn't exists, create it
- log.Info("creating missing role")
- role := &v1.Role{
- ObjectMeta: metav1.ObjectMeta{
- Name: runnerObject.ChildName(),
- Namespace: runnerObject.Namespace,
- OwnerReferences: runnerObject.GenerateOwnerReference(),
- },
- Rules: []v1.PolicyRule{{
- Verbs: []string{"get", "list", "watch", "create", "patch", "delete", "update"},
- APIGroups: []string{"*"},
- Resources: []string{"pods", "pods/exec", "pods/attach", "secrets", "configmaps"},
- }},
- }
- err = r.Client.Create(ctx, role)
- if err != nil {
- log.Error(err, "cannot create role")
- return err
- }
- return nil
-}
-
-func (r *RunnerReconciler) createRoleBindingIfMissing(ctx context.Context, runnerObject *gitlabv1beta1.Runner,
- log logr.Logger) error {
- namespacedKey := client.ObjectKey{Namespace: runnerObject.Namespace, Name: runnerObject.ChildName()}
- err := r.Client.Get(ctx, namespacedKey, &v1.RoleBinding{})
- switch {
- case err == nil: // service account exists
- return nil
- case !errors.IsNotFound(err):
- log.Error(err, "cannot get the Role binding")
- return err
- }
- // sa doesn't exists, create it
- log.Info("creating missing rolebinding")
- role := &v1.RoleBinding{
- ObjectMeta: metav1.ObjectMeta{
- Name: runnerObject.ChildName(),
- Namespace: runnerObject.Namespace,
- OwnerReferences: runnerObject.GenerateOwnerReference(),
- },
- Subjects: []v1.Subject{{
- Kind: "ServiceAccount",
- Name: runnerObject.ChildName(),
- Namespace: runnerObject.Namespace,
- }},
- RoleRef: v1.RoleRef{
- APIGroup: "rbac.authorization.k8s.io",
- Kind: "Role",
- Name: runnerObject.ChildName(),
- },
- }
- err = r.Client.Create(ctx, role)
- if err != nil {
- log.Error(err, "cannot create rolebindings")
- return err
- }
- return nil
-}
-
-// CreateRBACIfMissing creates missing rbacs if needed
-func (r *RunnerReconciler) CreateRBACIfMissing(ctx context.Context, runnerObject *gitlabv1beta1.Runner, log logr.Logger) error {
- if err := r.createSAIfMissing(ctx, runnerObject, log); err != nil {
- return err
- }
- if err := r.createRoleIfMissing(ctx, runnerObject, log); err != nil {
- return err
- }
- if err := r.createRoleBindingIfMissing(ctx, runnerObject, log); err != nil {
- return err
- }
- return nil
-}
-
-func (r *RunnerReconciler) getGitlabApiClient(ctx context.Context, runnerObject *gitlabv1beta1.Runner) (api.GitlabClient, error) {
- // if the client is already defined, return that one instead of trying to obtain a new one.
- if r.GitlabApiClient != nil {
- return r.GitlabApiClient, nil
- }
-
- // if we have defined token in the config, then use that one.
- if runnerObject.Spec.RegistrationConfig.Token != nil && *runnerObject.Spec.RegistrationConfig.Token != "" {
- return api.NewGitlabClient(*runnerObject.Spec.RegistrationConfig.Token, runnerObject.Spec.GitlabInstanceURL)
- }
-
- // we did not store the registration token in clear view. Hopefully we have defined and created a secret holding it
- if runnerObject.Spec.RegistrationConfig.TokenSecret == "" {
- return nil, coreErrors.New("you need to either define a token or a secret pointing to it")
- }
-
- // let's try to fetch the secret with our token
- var gitlabSecret corev1.Secret
- if err := r.Client.Get(ctx, types.NamespacedName{
- Namespace: runnerObject.Namespace,
- Name: runnerObject.Spec.RegistrationConfig.TokenSecret,
- }, &gitlabSecret); err != nil {
- return nil, err
- }
-
- //
- token, ok := gitlabSecret.Data["token"]
- if !ok || string(token) == "" {
- return nil, coreErrors.New("secret doesn't contain field token or it's empty")
- }
-
- // and finally
- decryptedToken := string(token)
- runnerObject.Spec.RegistrationConfig.Token = &decryptedToken
- return api.NewGitlabClient(*runnerObject.Spec.RegistrationConfig.Token, runnerObject.Spec.GitlabInstanceURL)
-}
-
-// RegisterNewRunnerOnGitlab registers runner against gitlab server and saves the value inside the status
-func (r *RunnerReconciler) RegisterNewRunnerOnGitlab(ctx context.Context, runner *gitlabv1beta1.Runner, logger logr.Logger) (ctrl.Result, error) {
- logger.Info("Registering new runner on gitlab")
-
- // get the gitlab api client
- gitlabApiClient, err := r.getGitlabApiClient(ctx, runner)
- if err != nil {
- return resultRequeueAfterDefaultTimeout, err
- }
-
- // obtain the registration token from gitlab
- token, err := gitlabApiClient.Register(runner.Spec.RegistrationConfig)
- if err != nil {
- logger.Error(err, "cannot register the runner against gitlab api")
- runner.Status.Error = fmt.Sprintf("Cannot register the runner on gitlab api. %s", err.Error())
- return ctrl.Result{Requeue: true, RequeueAfter: 1 * time.Minute}, err
- }
-
- // set the new auth token and record the reg details used for the operation (token and tags)
- runner.Status.AuthenticationToken = token
- runner.Status.LastRegistrationToken = *runner.Spec.RegistrationConfig.Token
- runner.Status.LastRegistrationTags = runner.Spec.RegistrationConfig.TagList
-
- logger.Info("registered a new runner on gitlab server")
- return ctrl.Result{Requeue: true}, nil
-}
-
-// RemoveRunnerFromGitlab removes the runner from giltab server. Usually should happen when we delete our runner
-// or we are forced to redo the registration.
-func (r *RunnerReconciler) RemoveRunnerFromGitlab(ctx context.Context, runner *gitlabv1beta1.Runner, logger logr.Logger) (*ctrl.Result, error) {
- // get the gitlab api client
- gitlabApiClient, err := r.getGitlabApiClient(ctx, runner)
- if err != nil {
- return &resultRequeueAfterDefaultTimeout, err
- }
-
- // obtain the registration token from gitlab
- res, err := gitlabApiClient.DeleteByToken(runner.Status.AuthenticationToken)
- if err != nil {
- logger.Error(err, "cannot remove runner from gitlab server", "response", res.Body)
- runner.Status.Error = fmt.Sprintf("Cannot remove the runner on gitlab api. %s", err.Error())
- return nil, err // even if we can't remove the runner, we do not want to interrupt the flow
- }
-
- logger.Info("removed runner from gitlab server")
- return nil, nil
-}
-
// SetupWithManager sets up the controller with the Manager.
func (r *RunnerReconciler) SetupWithManager(mgr ctrl.Manager) error {
- // setup indexers on configmap created by x
+ const runnerOwnerCmKey = ".metadata.cmcontroller"
+ const runnerOwnerDpKey = ".metadata.dpcontroller"
ctx := context.Background()
- if err := mgr.GetFieldIndexer().IndexField(ctx, &corev1.ConfigMap{}, ownerCmKey, func(object client.Object) []string {
+ if err := mgr.GetFieldIndexer().IndexField(ctx, &corev1.ConfigMap{}, runnerOwnerCmKey, func(object client.Object) []string {
// grab the configMap object, extract the owner...
configMap := object.(*corev1.ConfigMap)
owner := metav1.GetControllerOf(configMap)
@@ -399,7 +186,7 @@ func (r *RunnerReconciler) SetupWithManager(mgr ctrl.Manager) error {
return nil
}
- // ensure that we dealing with a proper object
+ // ensure that we're dealing with a proper object
if owner.APIVersion != gitlabv1beta1.GroupVersion.String() || owner.Kind != "Runner" {
return nil
}
@@ -410,7 +197,7 @@ func (r *RunnerReconciler) SetupWithManager(mgr ctrl.Manager) error {
}
// deployments
// todo : unify with configmap above
- if err := mgr.GetFieldIndexer().IndexField(ctx, &appsv1.Deployment{}, ownerDpKey, func(rawObj client.Object) []string {
+ if err := mgr.GetFieldIndexer().IndexField(ctx, &appsv1.Deployment{}, runnerOwnerDpKey, func(rawObj client.Object) []string {
// grab the deployment object, extract the owner...
deployment := rawObj.(*appsv1.Deployment)
owner := metav1.GetControllerOf(deployment)
@@ -418,7 +205,7 @@ func (r *RunnerReconciler) SetupWithManager(mgr ctrl.Manager) error {
return nil
}
- // ensure that we dealing with a proper object
+ // ensure that we're dealing with a proper object
if owner.APIVersion != gitlabv1beta1.GroupVersion.String() || owner.Kind != "Runner" {
return nil
}
@@ -448,3 +235,32 @@ func (r *RunnerReconciler) SetupWithManager(mgr ctrl.Manager) error {
Owns(&appsv1.Deployment{}).
Complete(r)
}
+
+func (r *RunnerReconciler) manageDeletion(ctx context.Context, runnerObj types.RunnerInfo, logger logr.Logger) (ctrl.Result, error) {
+ logger.Info("runner is being deleted")
+ var err error
+
+ if !runnerObj.IsAuthenticated() {
+ logger.Info("removing runner/s from gitlab")
+ for _, reg := range runnerObj.RegistrationConfig() {
+ cl := r.GitlabApiClient
+ if cl == nil {
+ if cl, err = api.NewGitlabClient(*reg.Token, reg.GitlabUrl); err != nil {
+ logger.Error(err, "cannot get gitlab api client for deletion of the runner")
+ continue
+ }
+ }
+ if _, err = cl.DeleteByToken(reg.AuthToken); err != nil {
+ // do not interrupt execution flow, just report it
+ logger.Error(err, "warning: cannot delete token from gitlab")
+ }
+ }
+ }
+
+ runnerObj.RemoveFinalizer()
+ if err = runnerObj.Update(ctx, r); err != nil {
+ logger.Error(err, "cannot remove finalizer")
+ return resultRequeueAfterDefaultTimeout, err
+ }
+ return resultRequeueNow, nil
+}
diff --git a/controllers/runner_controller_test.go b/controllers/runner_controller_test.go
index 7adc480..24610b5 100644
--- a/controllers/runner_controller_test.go
+++ b/controllers/runner_controller_test.go
@@ -24,11 +24,12 @@ package controllers
import (
"context"
"fmt"
- v1 "k8s.io/api/rbac/v1"
"os"
"strconv"
"time"
+ v1 "k8s.io/api/rbac/v1"
+
. "github.com/onsi/ginkgo"
"github.com/onsi/ginkgo/extensions/table"
. "github.com/onsi/gomega"
@@ -325,10 +326,10 @@ func getChangedConfigMap(ctx context.Context, name types.NamespacedName, resourc
}
func nameSpacedRunnerName(runner *v1beta1.Runner) types.NamespacedName {
- return types.NamespacedName{Name: runner.Name, Namespace: runner.Namespace}
+ return types.NamespacedName{Name: runner.Name, Namespace: runner.GetNamespace()}
}
func nameSpacedDependencyName(runner *v1beta1.Runner) types.NamespacedName {
- return types.NamespacedName{Name: runner.ChildName(), Namespace: runner.Namespace}
+ return types.NamespacedName{Name: runner.ChildName(), Namespace: runner.GetNamespace()}
}
func CreateNamespace(c client.Client) (string, error) {
ns := &corev1.Namespace{
@@ -360,7 +361,7 @@ func defaultRunner(name string, nameSpace string) *v1beta1.Runner {
},
Spec: v1beta1.RunnerSpec{
RegistrationConfig: v1beta1.RegisterNewRunnerOptions{
- Token: pointer.StringPtr("zTS6g2Q8bp8y13_ynfpN"),
+ Token: pointer.String("zTS6g2Q8bp8y13_ynfpN"),
TagList: []string{"default-tag"},
},
},
diff --git a/controllers/suite_test.go b/controllers/suite_test.go
index b81fe38..e1d1610 100644
--- a/controllers/suite_test.go
+++ b/controllers/suite_test.go
@@ -19,23 +19,24 @@ package controllers
import (
"crypto/md5"
"encoding/hex"
- "github.com/xanzy/go-gitlab"
"path/filepath"
"strings"
"testing"
+ "github.com/xanzy/go-gitlab"
+
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
- api2 "gitlab.k8s.alekc.dev/internal/api"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
controllerruntime "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/envtest"
- "sigs.k8s.io/controller-runtime/pkg/envtest/printer"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
+ api2 "gitlab.k8s.alekc.dev/internal/api"
+
gitlabv1beta1 "gitlab.k8s.alekc.dev/api/v1beta1"
// +kubebuilder:scaffold:imports
)
@@ -49,10 +50,7 @@ var testEnv *envtest.Environment
func TestAPIs(t *testing.T) {
RegisterFailHandler(Fail)
-
- RunSpecsWithDefaultAndCustomReporters(t,
- "Controller Suite",
- []Reporter{printer.NewlineReporter{}})
+ RunSpecs(t, "Controller Suite")
}
var _ = BeforeSuite(func() {
@@ -77,6 +75,9 @@ var _ = BeforeSuite(func() {
err = gitlabv1beta1.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
+ err = gitlabv1beta1.AddToScheme(scheme.Scheme)
+ Expect(err).NotTo(HaveOccurred())
+
//+kubebuilder:scaffold:scheme
// create the test client
diff --git a/go.mod b/go.mod
index b072212..5723d7c 100644
--- a/go.mod
+++ b/go.mod
@@ -1,79 +1,79 @@
module gitlab.k8s.alekc.dev
-go 1.17
+go 1.22.0
+
+toolchain go1.22.1
require (
- github.com/BurntSushi/toml v0.3.1
- github.com/go-logr/logr v1.2.0
+ github.com/BurntSushi/toml v1.4.0
+ github.com/go-logr/logr v1.4.2
github.com/onsi/ginkgo v1.16.5
- github.com/onsi/gomega v1.17.0
- github.com/xanzy/go-gitlab v0.50.0
- k8s.io/api v0.23.0
- k8s.io/apimachinery v0.23.0
- k8s.io/client-go v0.23.0
- k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b
- sigs.k8s.io/controller-runtime v0.11.1
+ github.com/onsi/gomega v1.33.1
+ github.com/xanzy/go-gitlab v0.106.0
+ k8s.io/api v0.30.2
+ k8s.io/apimachinery v0.30.2
+ k8s.io/client-go v0.30.2
+ k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0
+ sigs.k8s.io/controller-runtime v0.18.4
)
require (
- cloud.google.com/go v0.81.0 // indirect
- github.com/Azure/go-autorest v14.2.0+incompatible // indirect
- github.com/Azure/go-autorest/autorest v0.11.18 // indirect
- github.com/Azure/go-autorest/autorest/adal v0.9.13 // indirect
- github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
- github.com/Azure/go-autorest/logger v0.2.1 // indirect
- github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
- github.com/cespare/xxhash/v2 v2.1.1 // indirect
+ github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
- github.com/evanphx/json-patch v4.12.0+incompatible // indirect
- github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect
- github.com/fsnotify/fsnotify v1.5.1 // indirect
- github.com/go-logr/zapr v1.2.0 // indirect
+ github.com/emicklei/go-restful/v3 v3.12.1 // indirect
+ github.com/evanphx/json-patch/v5 v5.9.0 // indirect
+ github.com/fsnotify/fsnotify v1.7.0 // 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/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
- github.com/golang/protobuf v1.5.2 // indirect
- github.com/google/go-cmp v0.5.5 // indirect
- github.com/google/go-querystring v1.0.0 // indirect
- github.com/google/gofuzz v1.1.0 // indirect
- github.com/google/uuid v1.1.2 // indirect
- github.com/googleapis/gnostic v0.5.5 // indirect
- github.com/hashicorp/go-cleanhttp v0.5.1 // indirect
- github.com/hashicorp/go-retryablehttp v0.6.8 // indirect
- github.com/imdario/mergo v0.3.12 // indirect
+ github.com/golang/protobuf v1.5.4 // indirect
+ github.com/google/gnostic-models v0.6.8 // indirect
+ github.com/google/go-cmp v0.6.0 // indirect
+ github.com/google/go-querystring v1.1.0 // indirect
+ github.com/google/gofuzz v1.2.0 // indirect
+ github.com/google/uuid v1.6.0 // indirect
+ github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
+ github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
+ github.com/imdario/mergo v0.3.16 // indirect
+ github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
- github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
+ github.com/mailru/easyjson v0.7.7 // indirect
+ github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
+ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/nxadm/tail v1.4.8 // indirect
github.com/pkg/errors v0.9.1 // indirect
- github.com/prometheus/client_golang v1.11.0 // indirect
- github.com/prometheus/client_model v0.2.0 // indirect
- github.com/prometheus/common v0.28.0 // indirect
- github.com/prometheus/procfs v0.6.0 // indirect
+ github.com/prometheus/client_golang v1.19.1 // indirect
+ github.com/prometheus/client_model v0.6.1 // indirect
+ github.com/prometheus/common v0.55.0 // indirect
+ github.com/prometheus/procfs v0.15.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
- go.uber.org/atomic v1.7.0 // indirect
- go.uber.org/multierr v1.6.0 // indirect
- go.uber.org/zap v1.19.1 // indirect
- golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
- golang.org/x/net v0.0.0-20210825183410-e898025ed96a // indirect
- golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f // indirect
- golang.org/x/sys v0.0.0-20211029165221-6e7872819dc8 // indirect
- golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b // indirect
- golang.org/x/text v0.3.7 // indirect
- golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
- gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
- google.golang.org/appengine v1.6.7 // indirect
- google.golang.org/protobuf v1.27.1 // indirect
+ go.uber.org/multierr v1.11.0 // indirect
+ go.uber.org/zap v1.27.0 // indirect
+ golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect
+ golang.org/x/net v0.26.0 // indirect
+ golang.org/x/oauth2 v0.21.0 // indirect
+ golang.org/x/sys v0.21.0 // indirect
+ golang.org/x/term v0.21.0 // indirect
+ golang.org/x/text v0.16.0 // indirect
+ golang.org/x/time v0.5.0 // indirect
+ gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
+ google.golang.org/appengine v1.6.8 // indirect
+ google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
- gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
- k8s.io/apiextensions-apiserver v0.23.0 // indirect
- k8s.io/component-base v0.23.0 // indirect
- k8s.io/klog/v2 v2.30.0 // indirect
- k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 // indirect
- sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 // indirect
- sigs.k8s.io/structured-merge-diff/v4 v4.2.0 // indirect
- sigs.k8s.io/yaml v1.3.0 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
+ k8s.io/apiextensions-apiserver v0.30.2 // indirect
+ k8s.io/component-base v0.30.2 // indirect
+ k8s.io/klog/v2 v2.130.1 // indirect
+ k8s.io/kube-openapi v0.0.0-20240620174524-b456828f718b // indirect
+ sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
+ sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
+ sigs.k8s.io/yaml v1.4.0 // indirect
)
diff --git a/go.sum b/go.sum
index ecc4cad..0b56e92 100644
--- a/go.sum
+++ b/go.sum
@@ -1,972 +1,377 @@
-cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
-cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
-cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
-cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
-cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
-cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
-cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
-cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
-cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
-cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
-cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
-cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
-cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
-cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
-cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
-cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
-cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
-cloud.google.com/go v0.81.0 h1:at8Tk2zUz63cLPR0JPWm5vp77pEZmzxEQBEfRKn1VV8=
-cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
-cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
-cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
-cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
-cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
-cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
-cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
-cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
-cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
-cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
-cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
-cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
-cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
-cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
-cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
-cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
-cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
-cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
-cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
-dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
-github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
-github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
-github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
-github.com/Azure/go-autorest/autorest v0.11.18 h1:90Y4srNYrwOtAgVo3ndrQkTYn6kf1Eg/AjTFJ8Is2aM=
-github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA=
-github.com/Azure/go-autorest/autorest/adal v0.9.13 h1:Mp5hbtOePIzM8pJVRa3YLrWWmZtoxRXqUEzCfJt3+/Q=
-github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
-github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
-github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
-github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk=
-github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
-github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg=
-github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
-github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
-github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
-github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
-github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
-github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
-github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
-github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
-github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
-github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
-github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
-github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
-github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
-github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
-github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210826220005-b48c857c3a0e/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY=
-github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
-github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
-github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
-github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
-github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
-github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
+github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
+github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
-github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
-github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
-github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
-github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
-github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
-github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
-github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
-github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
-github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
-github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
-github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
-github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
-github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
-github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
-github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
-github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
-github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
-github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo=
-github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA=
-github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
-github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
-github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
-github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
-github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
-github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
-github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
-github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
-github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
+github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
+github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
-github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
-github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
-github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
-github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
-github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
-github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
-github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
-github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
-github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
-github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
-github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
-github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
-github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
-github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
-github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ=
-github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84=
-github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
-github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
-github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
-github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
-github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c=
-github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
+github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
+github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
+github.com/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/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U=
+github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww=
+github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4=
+github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg=
+github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
-github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
-github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
-github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg=
-github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
-github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
-github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
-github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
-github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
-github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
-github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
-github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
-github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
-github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
-github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
-github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
-github.com/go-logr/logr v1.2.0 h1:QK40JKJyMdUDz+h+xvCsru/bJhvG0UxvePV0ufL/AcE=
+github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
+github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
+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/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
-github.com/go-logr/zapr v1.2.0 h1:n4JnPI1T3Qq1SFEi/F8rwLrZERp2bso19PJZDB9dayk=
-github.com/go-logr/zapr v1.2.0/go.mod h1:Qa4Bsj2Vb+FAVeAKsLD8RLQ+YRJB8YDmOAKxaBQf7Ro=
-github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
-github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
-github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
-github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
-github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
-github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
-github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
+github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
+github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo=
+github.com/go-logr/zapr v1.2.4/go.mod h1:FyHWQIzQORZ0QVE1BtVHv3cKtNLuXsbNLtpuhNapBOA=
+github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
+github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg=
+github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
+github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
+github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
+github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
+github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
+github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
+github.com/go-openapi/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.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
+github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
+github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
+github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
-github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
-github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
-github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
-github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
+github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
-github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
-github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
-github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
-github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
-github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
-github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
-github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
-github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
-github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
-github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
-github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
-github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
-github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
-github.com/google/cel-go v0.9.0/go.mod h1:U7ayypeSkw23szu4GaQTPJGx66c20mx8JklMSxrmI1w=
-github.com/google/cel-spec v0.6.0/go.mod h1:Nwjgxy5CbjlPrtCWjeDjUyKMl8w41YBYGjsyDdqk0xA=
-github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
+github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
+github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
+github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
-github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
+github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
+github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
+github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
-github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
-github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
-github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
-github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
-github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
-github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
-github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
-github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
-github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
-github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=
-github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw=
-github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA=
-github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
-github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
-github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
-github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
-github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
-github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
-github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
-github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
-github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
-github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
-github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
-github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
-github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
-github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
+github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
+github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec=
+github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg=
+github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
+github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
+github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
-github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
-github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
-github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
-github.com/hashicorp/go-retryablehttp v0.6.8 h1:92lWxgpa+fF3FozM4B3UZtHZMJX8T5XT+TFdCxsPyWs=
-github.com/hashicorp/go-retryablehttp v0.6.8/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
-github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
-github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
-github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
-github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
-github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
-github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
-github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
-github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
-github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
-github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
-github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
+github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
+github.com/hashicorp/go-retryablehttp v0.7.2 h1:AcYqCvkpalPnPF2pn0KamgwamS42TqUDDYFRKq/RAd0=
+github.com/hashicorp/go-retryablehttp v0.7.2/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
+github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
+github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
-github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
-github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
-github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
-github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
+github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
-github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
-github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
+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/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
-github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
-github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
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/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
-github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
-github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
-github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
-github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
-github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
-github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
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/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
-github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
-github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
-github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
-github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
-github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
-github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
-github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
-github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
-github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
-github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
-github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
-github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
-github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
-github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
-github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
-github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
-github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
-github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
-github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
-github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
-github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A=
+github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
+github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
+github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
+github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
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 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
-github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
-github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
+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/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
-github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
-github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
-github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
-github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
-github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
-github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
-github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
-github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
-github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
+github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU=
+github.com/onsi/ginkgo/v2 v2.17.2 h1:7eMhcy3GimbsA3hEnVKdw/PQM9XN9krpKVXsZdph0/g=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
-github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE=
-github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
-github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
-github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
-github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
-github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
-github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
-github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
+github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M=
+github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
+github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
-github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
-github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
-github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
-github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
-github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
-github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ=
-github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
-github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
-github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
-github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
-github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
-github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
-github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
-github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
-github.com/prometheus/common v0.28.0 h1:vGVfV9KrDTvWt5boZO0I19g2E3CsWfpPPKZM9dt3mEw=
-github.com/prometheus/common v0.28.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
-github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
-github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
-github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
-github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
-github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
-github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
-github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
-github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
-github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
-github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
-github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
-github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
-github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
-github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
-github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
-github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
-github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
-github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
-github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
-github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
-github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
-github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
-github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
-github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
-github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
-github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
-github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
-github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
-github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
-github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
-github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
-github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8=
+github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc=
+github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
+github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
+github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY=
+github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
+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.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
+github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
+github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
+github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
+github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg=
+github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
+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.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
+github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
-github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
-github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
-github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
-github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
-github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
-github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
-github.com/xanzy/go-gitlab v0.50.0 h1:t7IoYTrnLSbdEZN7d8X/5zcr+ZM4TZQ2mXa8MqWlAZQ=
-github.com/xanzy/go-gitlab v0.50.0/go.mod h1:Q+hQhV508bDPoBijv7YjK/Lvlb4PhVhJdKqXVQrUoAE=
-github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
-github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
+github.com/xanzy/go-gitlab v0.95.1 h1:rQjcmX5Au2Lz9bc3QLTdtSK5ZHdTXLnmhz3CAB/G5So=
+github.com/xanzy/go-gitlab v0.95.1/go.mod h1:ETg8tcj4OhrB84UEgeE8dSuV/0h4BBL1uOV/qK0vlyI=
+github.com/xanzy/go-gitlab v0.106.0 h1:EDfD03K74cIlQo2EducfiupVrip+Oj02bq9ofw5F8sA=
+github.com/xanzy/go-gitlab v0.106.0/go.mod h1:ETg8tcj4OhrB84UEgeE8dSuV/0h4BBL1uOV/qK0vlyI=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
-github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
-go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
-go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
-go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
-go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
-go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
-go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0=
-go.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE=
-go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc=
-go.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4=
-go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
-go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
-go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
-go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
-go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
-go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
-go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
-go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E=
-go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4=
-go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo=
-go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM=
-go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU=
-go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw=
-go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc=
-go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE=
-go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE=
-go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw=
-go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
-go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
-go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
-go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
-go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
-go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=
-go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
-go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
-go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
+go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
+go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
-go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
-go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
-go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
-go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI=
-go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
-golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
-golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+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.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
+go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c=
+go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk=
+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-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
-golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
-golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
-golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
-golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
-golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
-golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
-golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
-golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
-golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
-golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
-golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
-golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA=
+golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
+golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY=
+golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
-golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
-golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
-golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
-golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
-golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
-golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
-golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
-golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
-golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
-golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
-golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/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-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
-golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
-golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
-golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
-golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20210825183410-e898025ed96a h1:bRuuGXV8wwSdGTB+CtJf+FjgO1APK1CoO39T4BN/XBw=
-golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
-golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
-golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f h1:Qmd2pbz05z7z6lm0DrgQVVPuBm92jqujBKMHMOlOQEw=
-golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
+golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
+golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
+golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
+golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8=
+golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE=
+golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
+golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211029165221-6e7872819dc8 h1:M69LAlWZCshgp0QSzyDcSsSIejIEeuaCVpmwcKwyLMk=
-golang.org/x/sys v0.0.0-20211029165221-6e7872819dc8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
+golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
+golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE=
-golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
+golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
+golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
+golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs=
-golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
+golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
+golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
+golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
+golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
+golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
+golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
+golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
-golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
-golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
-golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
-golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
-golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
-golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
-golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
-golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.6-0.20210820212750-d4cc65f0b2ff/go.mod h1:YD9qOF0M9xpSpdWTBbzEl5e/RnCefISl8E5Noe10jFM=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM=
+golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
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 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY=
-gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY=
-google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
-google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
-google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
-google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
-google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
-google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
-google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
-google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
-google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
-google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
-google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
-google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
-google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
-google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
-google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
-google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
-google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
-google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
-google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
-google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
-google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
-google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8=
-google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
-google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
-google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw=
+gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
-google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
-google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
-google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
-google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
-google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
-google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
-google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
-google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20201102152239-715cce707fb0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
-google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
-google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
-google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
-google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
-google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
-google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
-google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
-google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
-google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
-google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
-google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
-google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
-google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
-google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
-google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
-google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
-google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
-google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
-google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
-google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
+google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
+google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
-google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
-google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
-google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
+google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
+google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
+google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
-gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+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/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
-gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
-gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
-gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
-gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
-gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
-gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
-gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
-gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
-gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
-honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
-honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
-honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
-k8s.io/api v0.23.0 h1:WrL1gb73VSC8obi8cuYETJGXEoFNEh3LU0Pt+Sokgro=
-k8s.io/api v0.23.0/go.mod h1:8wmDdLBHBNxtOIytwLstXt5E9PddnZb0GaMcqsvDBpg=
-k8s.io/apiextensions-apiserver v0.23.0 h1:uii8BYmHYiT2ZTAJxmvc3X8UhNYMxl2A0z0Xq3Pm+WY=
-k8s.io/apiextensions-apiserver v0.23.0/go.mod h1:xIFAEEDlAZgpVBl/1VSjGDmLoXAWRG40+GsWhKhAxY4=
-k8s.io/apimachinery v0.23.0 h1:mIfWRMjBuMdolAWJ3Fd+aPTMv3X9z+waiARMpvvb0HQ=
-k8s.io/apimachinery v0.23.0/go.mod h1:fFCTTBKvKcwTPFzjlcxp91uPFZr+JA0FubU4fLzzFYc=
-k8s.io/apiserver v0.23.0/go.mod h1:Cec35u/9zAepDPPFyT+UMrgqOCjgJ5qtfVJDxjZYmt4=
-k8s.io/client-go v0.23.0 h1:vcsOqyPq7XV3QmQRCBH/t9BICJM9Q1M18qahjv+rebY=
-k8s.io/client-go v0.23.0/go.mod h1:hrDnpnK1mSr65lHHcUuIZIXDgEbzc7/683c6hyG4jTA=
-k8s.io/code-generator v0.23.0/go.mod h1:vQvOhDXhuzqiVfM/YHp+dmg10WDZCchJVObc9MvowsE=
-k8s.io/component-base v0.23.0 h1:UAnyzjvVZ2ZR1lF35YwtNY6VMN94WtOnArcXBu34es8=
-k8s.io/component-base v0.23.0/go.mod h1:DHH5uiFvLC1edCpvcTDV++NKULdYYU6pR9Tt3HIKMKI=
-k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
-k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
-k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
-k8s.io/klog/v2 v2.30.0 h1:bUO6drIvCIsvZ/XFgfxoGFQU/a4Qkh0iAlvUR7vlHJw=
-k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
-k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 h1:E3J9oCLlaobFUqsjG9DfKbP2BmgwBL2p7pn0A3dG9W4=
-k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk=
-k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
-k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b h1:wxEMGetGMur3J1xuGLQY7GEQYg9bZxKn3tKo5k/eYcs=
-k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
-rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
-rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
-rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
-sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.25/go.mod h1:Mlj9PNLmG9bZ6BHFwFKDo5afkpWyUISkb9Me0GnK66I=
-sigs.k8s.io/controller-runtime v0.11.1 h1:7YIHT2QnHJArj/dk9aUkYhfqfK5cIxPOX5gPECfdZLU=
-sigs.k8s.io/controller-runtime v0.11.1/go.mod h1:KKwLiTooNGu+JmLZGn9Sl3Gjmfj66eMbCQznLP5zcqA=
-sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 h1:fD1pz4yfdADVNfFmcP2aBEtudwUQ1AlLnRBALr33v3s=
-sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs=
-sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
-sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
-sigs.k8s.io/structured-merge-diff/v4 v4.2.0 h1:kDvPBbnPk+qYmkHmSo8vKGp438IASWofnbbUKDE/bv0=
-sigs.k8s.io/structured-merge-diff/v4 v4.2.0/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
-sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
+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.28.4 h1:8ZBrLjwosLl/NYgv1P7EQLqoO8MGQApnbgH8tu3BMzY=
+k8s.io/api v0.28.4/go.mod h1:axWTGrY88s/5YE+JSt4uUi6NMM+gur1en2REMR7IRj0=
+k8s.io/api v0.30.2 h1:+ZhRj+28QT4UOH+BKznu4CBgPWgkXO7XAvMcMl0qKvI=
+k8s.io/api v0.30.2/go.mod h1:ULg5g9JvOev2dG0u2hig4Z7tQ2hHIuS+m8MNZ+X6EmI=
+k8s.io/apiextensions-apiserver v0.28.3 h1:Od7DEnhXHnHPZG+W9I97/fSQkVpVPQx2diy+2EtmY08=
+k8s.io/apiextensions-apiserver v0.28.3/go.mod h1:NE1XJZ4On0hS11aWWJUTNkmVB03j9LM7gJSisbRt8Lc=
+k8s.io/apiextensions-apiserver v0.30.2 h1:l7Eue2t6QiLHErfn2vwK4KgF4NeDgjQkCXtEbOocKIE=
+k8s.io/apiextensions-apiserver v0.30.2/go.mod h1:lsJFLYyK40iguuinsb3nt+Sj6CmodSI4ACDLep1rgjw=
+k8s.io/apimachinery v0.28.4 h1:zOSJe1mc+GxuMnFzD4Z/U1wst50X28ZNsn5bhgIIao8=
+k8s.io/apimachinery v0.28.4/go.mod h1:wI37ncBvfAoswfq626yPTe6Bz1c22L7uaJ8dho83mgg=
+k8s.io/apimachinery v0.30.2 h1:fEMcnBj6qkzzPGSVsAZtQThU62SmQ4ZymlXRC5yFSCg=
+k8s.io/apimachinery v0.30.2/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
+k8s.io/client-go v0.28.4 h1:Np5ocjlZcTrkyRJ3+T3PkXDpe4UpatQxj85+xjaD2wY=
+k8s.io/client-go v0.28.4/go.mod h1:0VDZFpgoZfelyP5Wqu0/r/TRYcLYuJ2U1KEeoaPa1N4=
+k8s.io/client-go v0.30.2 h1:sBIVJdojUNPDU/jObC+18tXWcTJVcwyqS9diGdWHk50=
+k8s.io/client-go v0.30.2/go.mod h1:JglKSWULm9xlJLx4KCkfLLQ7XwtlbflV6uFFSHTMgVs=
+k8s.io/component-base v0.28.3 h1:rDy68eHKxq/80RiMb2Ld/tbH8uAE75JdCqJyi6lXMzI=
+k8s.io/component-base v0.28.3/go.mod h1:fDJ6vpVNSk6cRo5wmDa6eKIG7UlIQkaFmZN2fYgIUD8=
+k8s.io/component-base v0.30.2 h1:pqGBczYoW1sno8q9ObExUqrYSKhtE5rW3y6gX88GZII=
+k8s.io/component-base v0.30.2/go.mod h1:yQLkQDrkK8J6NtP+MGJOws+/PPeEXNpwFixsUI7h/OE=
+k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg=
+k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
+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-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5OhxCKlKJy0sHc+PcDwFB24dQ=
+k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM=
+k8s.io/kube-openapi v0.0.0-20240620174524-b456828f718b h1:Q9xmGWBvOGd8UJyccgpYlLosk/JlfP3xQLNkQlHJeXw=
+k8s.io/kube-openapi v0.0.0-20240620174524-b456828f718b/go.mod h1:UxDHUPsUwTOOxSU+oXURfFBcAS6JwiRXTYqYwfuGowc=
+k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk=
+k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
+k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 h1:jgGTlFYnhF1PM1Ax/lAlxUPE+KfCIXHaathvJg1C3ak=
+k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
+sigs.k8s.io/controller-runtime v0.16.3 h1:2TuvuokmfXvDUamSx1SuAOO3eTyye+47mJCigwG62c4=
+sigs.k8s.io/controller-runtime v0.16.3/go.mod h1:j7bialYoSn142nv9sCOJmQgDXQXxnroFU4VnX/brVJ0=
+sigs.k8s.io/controller-runtime v0.18.4 h1:87+guW1zhvuPLh1PHybKdYFLU0YJp4FhJRmiHvm5BZw=
+sigs.k8s.io/controller-runtime v0.18.4/go.mod h1:TVoGrfdpbA9VRFaRnKgk9P5/atA0pMwq+f+msb9M8Sg=
+sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
+sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
+sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE=
+sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E=
+sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
+sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
+sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
+sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
diff --git a/internal/api/gitlab.go b/internal/api/gitlab.go
index 1a77558..ddf9ac1 100644
--- a/internal/api/gitlab.go
+++ b/internal/api/gitlab.go
@@ -1,8 +1,8 @@
package api
import (
+ "gitlab.k8s.alekc.dev/internal/data/pointer"
"io"
- "k8s.io/utils/pointer"
"github.com/xanzy/go-gitlab"
"gitlab.k8s.alekc.dev/api/v1beta1"
@@ -31,7 +31,7 @@ func (g *gitlabApi) Register(config v1beta1.RegisterNewRunnerOptions) (string, e
Active: config.Active,
Locked: config.Locked,
RunUntagged: config.RunUntagged,
- TagList: config.TagList,
+ TagList: pointer.StringSlice(config.TagList),
MaximumTimeout: config.MaximumTimeout,
}
runner, resp, err := g.gitlabApiClient.Runners.RegisterNewRunner(&convertedConfig)
@@ -44,6 +44,7 @@ func (g *gitlabApi) Register(config v1beta1.RegisterNewRunnerOptions) (string, e
return runner.Token, nil
}
+
func NewGitlabClient(token, url string) (GitlabClient, error) {
var err error
// if we have not passed any private gitlab url, then use a default one.
diff --git a/internal/api/gitlab_test.go b/internal/api/gitlab_test.go
index 3424901..3867248 100644
--- a/internal/api/gitlab_test.go
+++ b/internal/api/gitlab_test.go
@@ -10,7 +10,7 @@ import (
func TestGitlabApi_Register(t *testing.T) {
cl, _ := NewGitlabClient("9Bo36Uxwx6ay-cR-bCLh", "")
_, _ = cl.Register(v1beta1.RegisterNewRunnerOptions{
- Token: pointer.StringPtr("9Bo36Uxwx6ay-cR-bCLh"),
+ Token: pointer.String("9Bo36Uxwx6ay-cR-bCLh"),
Description: nil,
Info: nil,
Active: nil,
diff --git a/internal/api/mocked_client.go b/internal/api/mocked_client.go
index 512aee3..7de6dd8 100644
--- a/internal/api/mocked_client.go
+++ b/internal/api/mocked_client.go
@@ -2,8 +2,8 @@ package api
import (
"errors"
- "github.com/xanzy/go-gitlab"
+ "github.com/xanzy/go-gitlab"
"gitlab.k8s.alekc.dev/api/v1beta1"
)
diff --git a/internal/crud/crud.go b/internal/crud/crud.go
new file mode 100644
index 0000000..b59aca4
--- /dev/null
+++ b/internal/crud/crud.go
@@ -0,0 +1,143 @@
+package crud
+
+import (
+ "context"
+ "gitlab.k8s.alekc.dev/internal/data/maps"
+
+ "github.com/go-logr/logr"
+ gitlabv1beta1 "gitlab.k8s.alekc.dev/api/v1beta1"
+ internalTypes "gitlab.k8s.alekc.dev/internal/types"
+ corev1 "k8s.io/api/core/v1"
+ v1 "k8s.io/api/rbac/v1"
+ "k8s.io/apimachinery/pkg/api/errors"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/types"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+// SingleRunner init single runner from k8s
+func SingleRunner(ctx context.Context, client client.Client, nsName types.NamespacedName) (internalTypes.RunnerInfo, error) {
+ runnerObj := &gitlabv1beta1.Runner{}
+ err := client.Get(ctx, nsName, runnerObj)
+ return runnerObj, err
+}
+
+// MultiRunner fetches multirunner from k8s
+// todo: generics?
+func MultiRunner(ctx context.Context, client client.Client, nsName types.NamespacedName) (internalTypes.RunnerInfo, error) {
+ runnerObj := &gitlabv1beta1.MultiRunner{}
+ err := client.Get(ctx, nsName, runnerObj)
+
+ maps.InitIfNil(&runnerObj.Status.AuthTokens)
+ maps.InitSliceIfNil(&runnerObj.Status.LastRegistrationTags)
+
+ return runnerObj, err
+}
+
+func CreateRBACIfMissing(ctx context.Context, cl client.Client, runnerObject internalTypes.RunnerInfo, log logr.Logger) error {
+ if err := CreateSaIfMissing(ctx, cl, runnerObject, log); err != nil {
+ return err
+ }
+ if err := CreateRoleIfMissing(ctx, cl, runnerObject, log); err != nil {
+ return err
+ }
+ if err := CreateRoleBindingIfMissing(ctx, cl, runnerObject, log); err != nil {
+ return err
+ }
+ return nil
+}
+
+func CreateSaIfMissing(ctx context.Context, cl client.Client, runnerObject internalTypes.RunnerInfo, log logr.Logger) error {
+ namespacedKey := client.ObjectKey{Namespace: runnerObject.GetNamespace(), Name: runnerObject.ChildName()}
+ err := cl.Get(ctx, namespacedKey, &corev1.ServiceAccount{})
+ switch {
+ case err == nil: // service account exists
+ return nil
+ case !errors.IsNotFound(err):
+ log.Error(err, "cannot get the service account")
+ return err
+ }
+ // sa doesn't exists, create it
+ log.Info("creating missing sa")
+ sa := &corev1.ServiceAccount{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: runnerObject.ChildName(),
+ Namespace: runnerObject.GetNamespace(),
+ OwnerReferences: runnerObject.GenerateOwnerReference(),
+ },
+ }
+ if err = cl.Create(ctx, sa); err != nil {
+ log.Error(err, "cannot create service-account")
+ return err
+ }
+ return nil
+}
+
+func CreateRoleIfMissing(ctx context.Context, cl client.Client, runnerObject internalTypes.RunnerInfo, log logr.Logger) error {
+ namespacedKey := client.ObjectKey{Namespace: runnerObject.GetNamespace(), Name: runnerObject.ChildName()}
+ err := cl.Get(ctx, namespacedKey, &v1.Role{})
+ switch {
+ case err == nil:
+ return nil
+ case !errors.IsNotFound(err):
+ log.Error(err, "cannot get the role")
+ return err
+ }
+ // sa doesn't exists, create it
+ log.Info("creating missing role")
+ role := &v1.Role{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: runnerObject.ChildName(),
+ Namespace: runnerObject.GetNamespace(),
+ OwnerReferences: runnerObject.GenerateOwnerReference(),
+ },
+ Rules: []v1.PolicyRule{{
+ Verbs: []string{"get", "list", "watch", "create", "patch", "delete", "update"},
+ APIGroups: []string{"*"},
+ Resources: []string{"pods", "pods/exec", "pods/attach", "secrets", "configmaps"},
+ }},
+ }
+ err = cl.Create(ctx, role)
+ if err != nil {
+ log.Error(err, "cannot create role")
+ return err
+ }
+ return nil
+}
+
+func CreateRoleBindingIfMissing(ctx context.Context, cl client.Client, runnerObject internalTypes.RunnerInfo, log logr.Logger) error {
+ namespacedKey := client.ObjectKey{Namespace: runnerObject.GetNamespace(), Name: runnerObject.ChildName()}
+ err := cl.Get(ctx, namespacedKey, &v1.RoleBinding{})
+ switch {
+ case err == nil: // service account exists
+ return nil
+ case !errors.IsNotFound(err):
+ log.Error(err, "cannot get the Role binding")
+ return err
+ }
+ // sa doesn't exists, create it
+ log.Info("creating missing rolebinding")
+ role := &v1.RoleBinding{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: runnerObject.ChildName(),
+ Namespace: runnerObject.GetNamespace(),
+ OwnerReferences: runnerObject.GenerateOwnerReference(),
+ },
+ Subjects: []v1.Subject{{
+ Kind: "ServiceAccount",
+ Name: runnerObject.ChildName(),
+ Namespace: runnerObject.GetNamespace(),
+ }},
+ RoleRef: v1.RoleRef{
+ APIGroup: "rbac.authorization.k8s.io",
+ Kind: "Role",
+ Name: runnerObject.ChildName(),
+ },
+ }
+ err = cl.Create(ctx, role)
+ if err != nil {
+ log.Error(err, "cannot create rolebindings")
+ return err
+ }
+ return nil
+}
diff --git a/internal/data/maps/map.go b/internal/data/maps/map.go
new file mode 100644
index 0000000..177e410
--- /dev/null
+++ b/internal/data/maps/map.go
@@ -0,0 +1,13 @@
+package maps
+
+func InitIfNil(mapVar *map[string]string) {
+ if *mapVar == nil {
+ *mapVar = make(map[string]string)
+ }
+}
+
+func InitSliceIfNil(mapVar *map[string][]string) {
+ if *mapVar == nil {
+ *mapVar = make(map[string][]string)
+ }
+}
diff --git a/internal/data/pointer/pointer.go b/internal/data/pointer/pointer.go
new file mode 100644
index 0000000..3f23fa5
--- /dev/null
+++ b/internal/data/pointer/pointer.go
@@ -0,0 +1,11 @@
+package pointer
+
+// StringSlice returns a pointer to a slice of strings
+func StringSlice(input []string) *[]string {
+ return &input
+}
+
+// String returns a pointer to a string.
+func String(s string) *string {
+ return &s
+}
diff --git a/internal/generate/config.go b/internal/generate/config.go
index 1f7ef97..aaa42bf 100644
--- a/internal/generate/config.go
+++ b/internal/generate/config.go
@@ -8,34 +8,98 @@ import (
"gitlab.k8s.alekc.dev/api/v1beta1"
"gitlab.k8s.alekc.dev/config"
"gitlab.k8s.alekc.dev/internal/crypto"
+ "gitlab.k8s.alekc.dev/internal/types"
)
-// ConfigText initialize default config object and returns it as a text
-func ConfigText(runnerObject *v1beta1.Runner) (gitlabConfig, configHashKey string, err error) {
+func TomlConfig(runner types.RunnerInfo) (gitlabConfig, configHashKey string, err error) {
+ // ugly as hell, but its the best I can do for now to avoid the import loop.
+ // Blame the kubebuilder which cannot generate deepCopy for external workspace
+ switch r := runner.(type) {
+ case *v1beta1.Runner:
+ return SingleRunnerConfig(r)
+ case *v1beta1.MultiRunner:
+ return MultiRunnerConfig(r)
+ }
+ panic("unknown runner type")
+}
+func SingleRunnerConfig(r *v1beta1.Runner) (gitlabConfig, configHashKey string, err error) {
// define sensible config for some configuration values
runnerConfig := &config.RunnerConfig{
- Name: runnerObject.Name,
+ Name: r.Name,
Limit: 10,
RunnerCredentials: config.RunnerCredentials{
- Token: runnerObject.Status.AuthenticationToken,
- URL: runnerObject.Spec.GitlabInstanceURL,
+ Token: r.Status.AuthenticationToken,
+ URL: r.Spec.GitlabInstanceURL,
},
RunnerSettings: config.RunnerSettings{
- Environment: runnerObject.Spec.Environment,
+ Environment: r.Spec.Environment,
Executor: "kubernetes",
- Kubernetes: &runnerObject.Spec.ExecutorConfig,
+ Kubernetes: &r.Spec.ExecutorConfig,
},
}
// set the namespace to the same one as the runner object if not declared otherwise
if runnerConfig.RunnerSettings.Kubernetes.Namespace == "" {
- runnerConfig.RunnerSettings.Kubernetes.Namespace = runnerObject.Namespace
+ runnerConfig.RunnerSettings.Kubernetes.Namespace = r.Namespace
}
+ rootConfig := &config.Config{
+ ListenAddress: ":9090",
+ Concurrent: int(math.Max(1, float64(r.Spec.Concurrent))),
+ CheckInterval: int(math.Max(3, float64(r.Spec.CheckInterval))),
+ LogLevel: r.Spec.LogLevel,
+ Runners: []*config.RunnerConfig{runnerConfig},
+ }
+
+ // if not explicit, define the log level
+ if rootConfig.LogLevel == "" {
+ rootConfig.LogLevel = "info"
+ }
+
+ var buff bytes.Buffer
+ tomlEncoder := toml.NewEncoder(&buff)
+ err = tomlEncoder.Encode(rootConfig)
+ if err != nil {
+ return "", "", err
+ }
+
+ gitlabConfig = buff.String()
+ configHashKey = crypto.StringToSHA1(gitlabConfig)
+ return buff.String(), configHashKey, nil
+}
+
+// MultiRunnerConfig initialize config for multiple runners object
+func MultiRunnerConfig(runnerObject *v1beta1.MultiRunner) (gitlabConfig, configHashKey string, err error) {
+ // create configuration for the runners
+ var runners []*config.RunnerConfig
+ for _, entry := range runnerObject.Spec.Entries {
+ // executorConfig is a separate variable due to go's loop bug
+ executorConfig := entry.ExecutorConfig
+ runnerConfig := config.RunnerConfig{
+ Name: entry.Name,
+ Limit: 10,
+ RunnerCredentials: config.RunnerCredentials{
+ Token: runnerObject.Status.AuthTokens[*entry.RegistrationConfig.Token],
+ URL: runnerObject.Spec.GitlabInstanceURL,
+ },
+ RunnerSettings: config.RunnerSettings{
+ Environment: entry.Environment,
+ Executor: "kubernetes",
+ Kubernetes: &executorConfig,
+ },
+ }
+ // set the namespace to the same one as the runner object if not declared otherwise
+ if runnerConfig.RunnerSettings.Kubernetes.Namespace == "" {
+ runnerConfig.RunnerSettings.Kubernetes.Namespace = runnerObject.Namespace
+ }
+ runners = append(runners, &runnerConfig)
+ }
+
+ // begin to construct the default value
rootConfig := &config.Config{
ListenAddress: ":9090",
Concurrent: int(math.Max(1, float64(runnerObject.Spec.Concurrent))),
CheckInterval: int(math.Max(3, float64(runnerObject.Spec.CheckInterval))),
LogLevel: runnerObject.Spec.LogLevel,
- Runners: []*config.RunnerConfig{runnerConfig},
+ Runners: runners,
}
// if not explicit, define the log level
diff --git a/internal/types/runner_info.go b/internal/types/runner_info.go
new file mode 100644
index 0000000..3096fa9
--- /dev/null
+++ b/internal/types/runner_info.go
@@ -0,0 +1,35 @@
+package types
+
+import (
+ "context"
+
+ "gitlab.k8s.alekc.dev/api/v1beta1"
+ v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+type RunnerInfo interface {
+ GetNamespace() string
+ ChildName() string
+ GetName() string
+ GetStatus() any
+
+ GenerateOwnerReference() []v1.OwnerReference
+ IsBeingDeleted() bool
+ IsAuthenticated() bool
+ HasFinalizer() bool
+ RemoveFinalizer()
+ AddFinalizer() (finalizerUpdated bool)
+ Update(ctx context.Context, writer client.Writer) error
+ SetStatusError(errorMessage string)
+ SetConfigMapVersion(versionHash string)
+ SetStatus(newStatus any)
+ UpdateStatus(ctx context.Context, writer client.StatusWriter) error
+ SetStatusReady(ready bool)
+ HasValidAuth() bool
+ // RegisterOnGitlab(api.GitlabClient, logr.Logger) (ctrl.Result, error)
+ // DeleteFromGitlab(apiClient api.GitlabClient, logger logr.Logger) error
+ ConfigMapVersion() string
+ RegistrationConfig() []v1beta1.GitlabRegInfo
+ StoreRunnerRegistration(v1beta1.GitlabRegInfo)
+}
diff --git a/internal/validate/validate.go b/internal/validate/validate.go
index ed065e2..59fce28 100644
--- a/internal/validate/validate.go
+++ b/internal/validate/validate.go
@@ -4,7 +4,6 @@ import (
"context"
"github.com/go-logr/logr"
- gitlabv1beta1 "gitlab.k8s.alekc.dev/api/v1beta1"
"gitlab.k8s.alekc.dev/internal/result"
"gitlab.k8s.alekc.dev/internal/types"
appsv1 "k8s.io/api/apps/v1"
@@ -17,19 +16,19 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
)
-func Deployment(ctx context.Context, cl client.Client, runnerObj *gitlabv1beta1.Runner, logger logr.Logger) (*ctrl.Result, error) {
- labels := map[string]string{"deployment": runnerObj.Name}
+func Deployment(ctx context.Context, cl client.Client, runnerObj types.RunnerInfo, logger logr.Logger) (*ctrl.Result, error) {
+ labels := map[string]string{"deployment": runnerObj.GetName()}
wantedDeployment := appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: runnerObj.ChildName(),
- Namespace: runnerObj.Namespace,
+ Namespace: runnerObj.GetNamespace(),
Annotations: map[string]string{
- types.ConfigVersionAnnotationKey: runnerObj.Status.ConfigMapVersion,
+ types.ConfigVersionAnnotationKey: runnerObj.ConfigMapVersion(),
},
OwnerReferences: runnerObj.GenerateOwnerReference(),
},
Spec: appsv1.DeploymentSpec{
- Replicas: pointer.Int32Ptr(1),
+ Replicas: pointer.Int32(1),
Selector: &metav1.LabelSelector{
MatchLabels: labels,
},
@@ -37,7 +36,7 @@ func Deployment(ctx context.Context, cl client.Client, runnerObj *gitlabv1beta1.
ObjectMeta: metav1.ObjectMeta{
Labels: labels,
Annotations: map[string]string{
- types.ConfigVersionAnnotationKey: runnerObj.Status.ConfigMapVersion,
+ types.ConfigVersionAnnotationKey: runnerObj.ConfigMapVersion(),
},
},
Spec: corev1.PodSpec{
@@ -51,7 +50,7 @@ func Deployment(ctx context.Context, cl client.Client, runnerObj *gitlabv1beta1.
}},
Containers: []corev1.Container{{
Name: "runner",
- Image: "gitlab/gitlab-runner:alpine-v14.8.2",
+ Image: "gitlab/gitlab-runner:alpine-v15.9.1",
Resources: corev1.ResourceRequirements{}, // todo:
ImagePullPolicy: "IfNotPresent", // todo
VolumeMounts: []corev1.VolumeMount{{
@@ -67,7 +66,7 @@ func Deployment(ctx context.Context, cl client.Client, runnerObj *gitlabv1beta1.
var existingDeployment appsv1.Deployment
err := cl.Get(ctx, client.ObjectKey{
- Namespace: runnerObj.Namespace,
+ Namespace: runnerObj.GetNamespace(),
Name: runnerObj.ChildName(),
}, &existingDeployment)
@@ -86,8 +85,7 @@ func Deployment(ctx context.Context, cl client.Client, runnerObj *gitlabv1beta1.
}
// deployment exists. Check the configMap annotation
- if existingDeployment.GetAnnotations()[types.ConfigVersionAnnotationKey] != runnerObj.Status.
- ConfigMapVersion || !equality.Semantic.DeepDerivative(wantedDeployment.Spec, existingDeployment.DeepCopy().Spec) {
+ if existingDeployment.GetAnnotations()[types.ConfigVersionAnnotationKey] != runnerObj.ConfigMapVersion() || !equality.Semantic.DeepDerivative(wantedDeployment.Spec, existingDeployment.DeepCopy().Spec) {
logger.Info("deployment is different from our version, updating", "deployment_name", existingDeployment.Name)
err = cl.Update(ctx, &wantedDeployment)
if err != nil {
@@ -99,11 +97,11 @@ func Deployment(ctx context.Context, cl client.Client, runnerObj *gitlabv1beta1.
return nil, nil
}
-func ConfigMap(ctx context.Context, cl client.Client, runnerObj *gitlabv1beta1.Runner, logger logr.Logger, gitlabRunnerTomlConfig string, configHashKey string) (*ctrl.Result, error) {
+func ConfigMap(ctx context.Context, cl client.Client, runnerObj types.RunnerInfo, logger logr.Logger, gitlabRunnerTomlConfig string, configHashKey string) (*ctrl.Result, error) {
// fetch current config map
var configMap corev1.ConfigMap
err := cl.Get(ctx, client.ObjectKey{
- Namespace: runnerObj.Namespace,
+ Namespace: runnerObj.GetNamespace(),
Name: runnerObj.ChildName(),
}, &configMap)
@@ -118,17 +116,17 @@ func ConfigMap(ctx context.Context, cl client.Client, runnerObj *gitlabv1beta1.R
configMap = corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: runnerObj.ChildName(),
- Namespace: runnerObj.Namespace,
+ Namespace: runnerObj.GetNamespace(),
OwnerReferences: runnerObj.GenerateOwnerReference(),
},
Data: map[string]string{types.ConfigMapKeyName: gitlabRunnerTomlConfig},
}
if err = cl.Create(ctx, &configMap); err != nil {
- runnerObj.Status.Error = "cannot create config map"
+ runnerObj.SetStatusError("cannot create config map")
logger.Error(err, "cannot create a config map", "configMapName", configMap.Name)
return result.RequeueWithDefaultTimeout(), err
}
- runnerObj.Status.ConfigMapVersion = configHashKey
+ runnerObj.SetConfigMapVersion(configHashKey)
return result.RequeueNow(), nil
}
@@ -139,11 +137,11 @@ func ConfigMap(ctx context.Context, cl client.Client, runnerObj *gitlabv1beta1.R
newObj.Data[types.ConfigMapKeyName] = gitlabRunnerTomlConfig
if err = cl.Update(ctx, newObj); err != nil {
const errMsg = "cannot update config map with the new configuration"
- runnerObj.Status.Error = errMsg
+ runnerObj.SetStatusError(errMsg)
logger.Error(err, errMsg)
return &ctrl.Result{Requeue: true}, err
}
- runnerObj.Status.ConfigMapVersion = configHashKey
+ runnerObj.SetConfigMapVersion(configHashKey)
return result.RequeueNow(), nil
}
return nil, nil
diff --git a/main.go b/main.go
index 47ae8ba..8ca3b60 100644
--- a/main.go
+++ b/main.go
@@ -19,6 +19,7 @@ package main
import (
"flag"
"os"
+
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
// to ensure that exec-entrypoint and run can make use of them.
_ "k8s.io/client-go/plugin/pkg/client/auth"
@@ -29,6 +30,7 @@ import (
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/healthz"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
+ metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
gitlabv1beta1 "gitlab.k8s.alekc.dev/api/v1beta1"
"gitlab.k8s.alekc.dev/controllers"
@@ -67,9 +69,10 @@ func main() {
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
- Scheme: scheme,
- MetricsBindAddress: metricsAddr,
- Port: 9443,
+ Scheme: scheme,
+ Metrics: metricsserver.Options{
+ BindAddress: metricsAddr,
+ },
HealthProbeBindAddress: probeAddr,
LeaderElection: enableLeaderElection,
LeaderElectionID: "e47991d1.k8s.alekc.dev",
@@ -93,6 +96,13 @@ func main() {
os.Exit(1)
}
}
+ if err = (&controllers.MultiRunnerReconciler{
+ Client: mgr.GetClient(),
+ Scheme: mgr.GetScheme(),
+ }).SetupWithManager(mgr); err != nil {
+ setupLog.Error(err, "unable to create controller", "controller", "MultiRunner")
+ os.Exit(1)
+ }
// +kubebuilder:scaffold:builder
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
diff --git a/runner.yaml b/runner.yaml
new file mode 100644
index 0000000..8dca37e
--- /dev/null
+++ b/runner.yaml
@@ -0,0 +1,15 @@
+apiVersion: gitlab.k8s.alekc.dev/v1beta1
+kind: MultiRunner
+metadata:
+ name: runner-sample
+spec:
+ concurrent: 1
+ log_level: info
+ gitlab_instance_url: https://gitlab.com
+ entries:
+ - registration_config:
+ token: "GR1348941WJWZbWjDB5D9Yb_ZzHiV"
+ tag_list:
+ - test
+ - asd
+ - fgh
diff --git a/source/gitlab-runner b/source/gitlab-runner
new file mode 160000
index 0000000..d540b51
--- /dev/null
+++ b/source/gitlab-runner
@@ -0,0 +1 @@
+Subproject commit d540b510a2100dd1d17e75e89af1c921ce107fb7