diff --git a/.github/workflows/lint-config-checks.yaml b/.github/workflows/lint-config-checks.yaml new file mode 100644 index 000000000..94534e266 --- /dev/null +++ b/.github/workflows/lint-config-checks.yaml @@ -0,0 +1,27 @@ +name: Lint Config Checks + +on: + push: + paths: + - '.golangci.yml' + - '.github/workflows/lint-config-checks.yaml' + branches: [ main ] + pull_request: + paths: + - '.golangci.yml' + - '.github/workflows/lint-config-checks.yaml' + branches: [ main ] + +jobs: + lint-config-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 + + - name: Setup Go + uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6 + with: + go-version-file: 'go.mod' + + - name: Verify linter configuration + run: make lint-config diff --git a/.golangci.yml b/.golangci.yml index 8826e9246..aecf26549 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,14 +1,58 @@ -# Configuration for the golangci-lint tool - ensuring that dot imports do not trigger an ST1001 warning. - version: "2" +run: + allow-parallel-runners: true linters: + default: none enable: + - copyloopvar + - dupl + - errcheck + - ginkgolinter + - goconst + - gocyclo + - govet + - ineffassign + - lll + - misspell + - nakedret + - prealloc + - revive - staticcheck + - unconvert + - unparam + - unused + settings: + revive: + rules: + - name: comment-spacings + - name: import-shadowing exclusions: + generated: lax rules: - path: '^integration_tests/|^tests/|[^/]+_test\.go' linters: - staticcheck # exclude dot-imports checks on test-related files - text: "ST1001:" \ No newline at end of file + text: "ST1001:" + - linters: + - lll + path: api/* + - linters: + - dupl + - lll + path: internal/* + paths: + - third_party$ + - builtin$ + - examples$ +formatters: + enable: + - gofmt + - goimports + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ diff --git a/Makefile b/Makefile index 216640118..588c7cf12 100644 --- a/Makefile +++ b/Makefile @@ -92,12 +92,10 @@ endif # Set the Operator SDK version to use. By default, what is installed on the system is used. # This is useful for CI or a project to utilize a specific version of the operator-sdk toolkit. -OPERATOR_SDK_VERSION ?= v1.37.0 -OPM_VERSION ?= v1.23.0 +OPERATOR_SDK_VERSION ?= v1.42.0 +OPM_VERSION ?= v1.55.0 # Image URL to use all building/pushing image targets IMG ?= $(IMAGE_TAG_BASE):$(IMAGE_TAG_VERSION) -# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. -ENVTEST_K8S_VERSION = 1.28.0 # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) ifeq (,$(shell go env GOBIN)) @@ -127,7 +125,7 @@ SHELL = /usr/bin/env bash -o pipefail .SHELLFLAGS = -ec # Source packages outside of tests -PKGS := $(shell go list ./... | grep -vE 'github.com/redhat-developer/rhdh-operator/(tests/|api/v1alpha([1-2]))') +PKGS := $(shell go list ./... | grep -vE 'github.com/redhat-developer/rhdh-operator/(tests/|api/v1alpha([1-9]+))') .PHONY: all all: build @@ -164,7 +162,7 @@ fmt: goimports ## Format the code using goimports find . -not -path '*/\.*' -name '*.go' -exec $(GOIMPORTS) -w {} \; .PHONY: test -test: manifests generate fmt vet envtest $(LOCALBIN) ## Run tests. We need LOCALBIN=$(LOCALBIN) to get correct default-config path +test: manifests generate fmt vet setup-envtest $(LOCALBIN) ## Run tests. We need LOCALBIN=$(LOCALBIN) to get correct default-config path mkdir -p $(LOCALBIN)/default-config && rm -fr $(LOCALBIN)/default-config/* && cp -r config/profile/$(PROFILE)/default-config/* $(LOCALBIN)/default-config mkdir -p $(LOCALBIN)/plugin-deps && rm -fr $(LOCALBIN)/plugin-deps/* && cp -r config/profile/$(PROFILE)/plugin-deps/* $(LOCALBIN)/plugin-deps 2>/dev/null || : LOCALBIN=$(LOCALBIN) KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $(PKGS) -coverprofile cover.out @@ -200,7 +198,7 @@ test-e2e-upgrade: ginkgo ## Run end-to-end tests dedicated to the operator upgra .PHONY: gosec gosec: addgosec ## run the gosec scanner for non-test files in this repo # we let the report content trigger a failure using the GitHub Security features. - $(GOSEC) -no-fail -fmt $(GOSEC_FMT) -out $(GOSEC_OUTPUT_FILE) ./... + $(GOSEC) -no-fail -exclude-generated -fmt $(GOSEC_FMT) -out $(GOSEC_OUTPUT_FILE) ./... .PHONY: lint lint: golangci-lint ## Run golangci-lint linter & yamllint @@ -210,6 +208,10 @@ lint: golangci-lint ## Run golangci-lint linter & yamllint lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes $(GOLANGCI_LINT) run --fix --timeout 15m +.PHONY: lint-config +lint-config: golangci-lint ## Verify golangci-lint linter configuration + $(GOLANGCI_LINT) config verify + .PHONY: vet vet: ## Run go vet against code. go vet ./... @@ -463,22 +465,23 @@ $(LOCALBIN): ## Tool Binaries KUBECTL ?= kubectl -KUSTOMIZE ?= $(LOCALBIN)/kustomize-$(KUSTOMIZE_VERSION) -CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen-$(CONTROLLER_TOOLS_VERSION) -ENVTEST ?= $(LOCALBIN)/setup-envtest-$(ENVTEST_VERSION) -GOLANGCI_LINT = $(LOCALBIN)/golangci-lint-$(GOLANGCI_LINT_VERSION) -GOIMPORTS ?= $(LOCALBIN)/goimports-$(GOIMPORTS_VERSION) -GOSEC ?= $(LOCALBIN)/gosec-$(GOSEC_VERSION) -GINKGO ?= $(LOCALBIN)/ginkgo-$(GINKGO_VERSION) +KUSTOMIZE ?= $(LOCALBIN)/kustomize +CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen +ENVTEST ?= $(LOCALBIN)/setup-envtest +GOLANGCI_LINT = $(LOCALBIN)/golangci-lint +GOIMPORTS ?= $(LOCALBIN)/goimports +GOSEC ?= $(LOCALBIN)/gosec +GINKGO ?= $(LOCALBIN)/ginkgo ## Tool Versions -KUSTOMIZE_VERSION ?= v5.4.2 -CONTROLLER_TOOLS_VERSION ?= v0.14.0 -ENVTEST_VERSION ?= release-0.17 +KUSTOMIZE_VERSION ?= v5.4.3 +CONTROLLER_TOOLS_VERSION ?= v0.18.0 +ENVTEST_VERSION := $(shell go list -m -f "{{ .Version }}" sigs.k8s.io/controller-runtime | awk -F'[v.]' '{printf "release-%d.%d", $$2, $$3}') +ENVTEST_K8S_VERSION := $(shell go list -m -f "{{ .Version }}" k8s.io/api | awk -F'[v.]' '{printf "1.%d", $$3}') GOLANGCI_LINT_VERSION ?= v2.6.2 -GOIMPORTS_VERSION ?= v0.16.1 -GOSEC_VERSION ?= v2.22.8 -GINKGO_VERSION ?= v2.22.2 +GOIMPORTS_VERSION ?= v0.40.0 +GOSEC_VERSION ?= v2.22.11 +GINKGO_VERSION ?= v2.27.3 ## Gosec options - default format is sarif so we can integrate with Github code scanning GOSEC_FMT ?= sarif # for other options, see https://github.com/securego/gosec#output-formats @@ -494,6 +497,14 @@ controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessar $(CONTROLLER_GEN): $(LOCALBIN) $(call go-install-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen,$(CONTROLLER_TOOLS_VERSION)) +.PHONY: setup-envtest +setup-envtest: envtest ## Download the binaries required for ENVTEST in the local bin directory. + @echo "Setting up envtest binaries for Kubernetes version $(ENVTEST_K8S_VERSION)..." + @$(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path || { \ + echo "Error: Failed to set up envtest binaries for version $(ENVTEST_K8S_VERSION)."; \ + exit 1; \ + } + .PHONY: envtest envtest: $(ENVTEST) ## Download setup-envtest locally if necessary. $(ENVTEST): $(LOCALBIN) @@ -502,62 +513,68 @@ $(ENVTEST): $(LOCALBIN) .PHONY: golangci-lint golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary. $(GOLANGCI_LINT): $(LOCALBIN) - $(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/v2/cmd/golangci-lint,${GOLANGCI_LINT_VERSION}) + $(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/v2/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION)) .PHONY: goimports goimports: $(GOIMPORTS) ## Download goimports locally if necessary. $(GOIMPORTS): $(LOCALBIN) - $(call go-install-tool,$(GOIMPORTS),golang.org/x/tools/cmd/goimports,${GOIMPORTS_VERSION}) + $(call go-install-tool,$(GOIMPORTS),golang.org/x/tools/cmd/goimports,$(GOIMPORTS_VERSION)) .PHONY: addgosec addgosec: $(GOSEC) ## Download gosec locally if necessary. $(GOSEC): $(LOCALBIN) - $(call go-install-tool,$(GOSEC),github.com/securego/gosec/v2/cmd/gosec,${GOSEC_VERSION}) + $(call go-install-tool,$(GOSEC),github.com/securego/gosec/v2/cmd/gosec,$(GOSEC_VERSION)) .PHONY: ginkgo ginkgo: $(GINKGO) ## Download Ginkgo locally if necessary. $(GINKGO): $(LOCALBIN) - $(call go-install-tool,$(GINKGO),github.com/onsi/ginkgo/v2/ginkgo,${GINKGO_VERSION}) + $(call go-install-tool,$(GINKGO),github.com/onsi/ginkgo/v2/ginkgo,$(GINKGO_VERSION)) # go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist # $1 - target path with name of binary (ideally with version) # $2 - package url which can be installed # $3 - specific version of package define go-install-tool -@[ -f $(1) ] || { \ +@[ -f "$(1)-$(3)" ] || { \ set -e; \ package=$(2)@$(3) ;\ echo "Downloading $${package}" ;\ +rm -f $(1) || true ;\ GOBIN=$(LOCALBIN) go install $${package} ;\ -mv "$$(echo "$(1)" | sed "s/-$(3)$$//")" $(1) ;\ -} +mv $(1) $(1)-$(3) ;\ +} ;\ +ln -sf $(1)-$(3) $(1) endef .PHONY: operator-sdk -OPERATOR_SDK ?= $(LOCALBIN)/operator-sdk-$(OPERATOR_SDK_VERSION) +OPERATOR_SDK_WITH_VER ?= $(LOCALBIN)/operator-sdk-$(OPERATOR_SDK_VERSION) +OPERATOR_SDK ?= $(LOCALBIN)/operator-sdk operator-sdk: ## Download operator-sdk locally if necessary. -ifeq (,$(wildcard $(OPERATOR_SDK))) +ifeq (,$(wildcard $(OPERATOR_SDK_WITH_VER))) @{ \ set -e ;\ - mkdir -p $(dir $(OPERATOR_SDK)) ;\ + mkdir -p $(dir $(OPERATOR_SDK_WITH_VER)) ;\ OS=$(shell go env GOOS) && ARCH=$(shell go env GOARCH) && \ - curl -sSLo $(OPERATOR_SDK) https://github.com/operator-framework/operator-sdk/releases/download/$(OPERATOR_SDK_VERSION)/operator-sdk_$${OS}_$${ARCH} ;\ - chmod +x $(OPERATOR_SDK) ;\ + curl -sSLo $(OPERATOR_SDK_WITH_VER) https://github.com/operator-framework/operator-sdk/releases/download/$(OPERATOR_SDK_VERSION)/operator-sdk_$${OS}_$${ARCH} ;\ + chmod +x $(OPERATOR_SDK_WITH_VER) ;\ } endif + ln -sf $(OPERATOR_SDK_WITH_VER) $(OPERATOR_SDK) .PHONY: opm -OPM ?= $(LOCALBIN)/opm-$(OPM_VERSION) +OPM_WITH_VER ?= $(LOCALBIN)/opm-$(OPM_VERSION) +OPM ?= $(LOCALBIN)/opm opm: ## Download opm locally if necessary. -ifeq (,$(wildcard $(OPM))) +ifeq (,$(wildcard $(OPM_WITH_VER))) @{ \ set -e ;\ - mkdir -p $(dir $(OPM)) ;\ + mkdir -p $(dir $(OPM_WITH_VER)) ;\ OS=$(shell go env GOOS) && ARCH=$(shell go env GOARCH) && \ - curl -sSLo $(OPM) https://github.com/operator-framework/operator-registry/releases/download/$(OPM_VERSION)/$${OS}-$${ARCH}-opm ;\ - chmod +x $(OPM) ;\ + curl -sSLo $(OPM_WITH_VER) https://github.com/operator-framework/operator-registry/releases/download/$(OPM_VERSION)/$${OS}-$${ARCH}-opm ;\ + chmod +x $(OPM_WITH_VER) ;\ } endif + ln -sf $(OPM_WITH_VER) $(OPM) ##@ Misc. diff --git a/api/v1alpha3/backstage_types.go b/api/v1alpha3/backstage_types.go index a5d766622..73238fc3b 100644 --- a/api/v1alpha3/backstage_types.go +++ b/api/v1alpha3/backstage_types.go @@ -53,7 +53,7 @@ type RuntimeConfig struct { type Database struct { // Control the creation of a local PostgreSQL DB. Set to false if using for example an external Database for Backstage. // +optional - //+kubebuilder:default=true + // +kubebuilder:default=true EnableLocalDb *bool `json:"enableLocalDb,omitempty"` // Name of the secret for database authentication. Optional. @@ -99,7 +99,7 @@ type Application struct { // Number of desired replicas to set in the Backstage Deployment. // Defaults to 1. // +optional - //+kubebuilder:default=1 + // +kubebuilder:default=1 Replicas *int32 `json:"replicas,omitempty"` // Custom image to use in all containers (including Init Containers). @@ -175,7 +175,7 @@ type ExtraEnvs struct { type EnvObjectRef struct { // Name of the object // We support only ConfigMaps and Secrets. - //+kubebuilder:validation:Required + // +kubebuilder:validation:Required Name string `json:"name"` // Key in the object @@ -186,7 +186,7 @@ type EnvObjectRef struct { type FileObjectRef struct { // Name of the object // Supported ConfigMaps and Secrets - //+kubebuilder:validation:Required + // +kubebuilder:validation:Required Name string `json:"name"` // Key in the object @@ -200,7 +200,7 @@ type FileObjectRef struct { type PvcRef struct { // Name of the object - //+kubebuilder:validation:Required + // +kubebuilder:validation:Required Name string `json:"name"` // Path to mount PVC. If not specified default-path/Name will be used @@ -210,11 +210,11 @@ type PvcRef struct { type Env struct { // Name of the environment variable - //+kubebuilder:validation:Required + // +kubebuilder:validation:Required Name string `json:"name"` // Value of the environment variable - //+kubebuilder:validation:Required + // +kubebuilder:validation:Required Value string `json:"value"` } @@ -225,8 +225,8 @@ type BackstageStatus struct { Conditions []metav1.Condition `json:"conditions,omitempty"` } -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status // +operator-sdk:csv:customresourcedefinitions:displayName="Red Hat Developer Hub" // Backstage is the Schema for the Red Hat Developer Hub backstages API. @@ -241,7 +241,7 @@ type Backstage struct { Status BackstageStatus `json:"status,omitempty"` } -//+kubebuilder:object:root=true +// +kubebuilder:object:root=true // BackstageList contains a list of Backstage type BackstageList struct { @@ -255,7 +255,7 @@ type BackstageList struct { type Route struct { // Control the creation of a Route on OpenShift. // +optional - //+kubebuilder:default=true + // +kubebuilder:default=true Enabled *bool `json:"enabled,omitempty"` // Host is an alias/DNS that points to the service. Optional. diff --git a/api/v1alpha3/zz_generated.deepcopy.go b/api/v1alpha3/zz_generated.deepcopy.go index b315b789e..6613a8896 100644 --- a/api/v1alpha3/zz_generated.deepcopy.go +++ b/api/v1alpha3/zz_generated.deepcopy.go @@ -5,7 +5,7 @@ package v1alpha3 import ( - "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) diff --git a/api/v1alpha4/backstage_types.go b/api/v1alpha4/backstage_types.go index aae700453..4ee0aff55 100644 --- a/api/v1alpha4/backstage_types.go +++ b/api/v1alpha4/backstage_types.go @@ -58,7 +58,7 @@ type RuntimeConfig struct { type Database struct { // Control the creation of a local PostgreSQL DB. Set to false if using for example an external Database for Backstage. // +optional - //+kubebuilder:default=true + // +kubebuilder:default=true EnableLocalDb *bool `json:"enableLocalDb,omitempty"` // Name of the secret for database authentication. Optional. @@ -111,7 +111,7 @@ type Application struct { // Number of desired replicas to set in the Backstage Deployment. // Defaults to 1. // +optional - //+kubebuilder:default=1 + // +kubebuilder:default=1 Replicas *int32 `json:"replicas,omitempty"` // Custom image to use in all containers (including Init Containers). @@ -187,7 +187,7 @@ type ExtraEnvs struct { type EnvObjectRef struct { // Name of the object // We support only ConfigMaps and Secrets. - //+kubebuilder:validation:Required + // +kubebuilder:validation:Required Name string `json:"name"` // Key in the object @@ -205,7 +205,7 @@ type EnvObjectRef struct { type FileObjectRef struct { // Name of the object // Supported ConfigMaps and Secrets - //+kubebuilder:validation:Required + // +kubebuilder:validation:Required Name string `json:"name"` // Key in the object @@ -226,7 +226,7 @@ type FileObjectRef struct { type PvcRef struct { // Name of the object - //+kubebuilder:validation:Required + // +kubebuilder:validation:Required Name string `json:"name"` // Path to mount PVC. If not specified default-path/Name will be used @@ -243,11 +243,11 @@ type PvcRef struct { type Env struct { // Name of the environment variable - //+kubebuilder:validation:Required + // +kubebuilder:validation:Required Name string `json:"name"` // Value of the environment variable - //+kubebuilder:validation:Required + // +kubebuilder:validation:Required Value string `json:"value"` // If set, the env variable will be injected only in the specified containers, otherwise in backstage container only. @@ -265,8 +265,8 @@ type BackstageStatus struct { Conditions []metav1.Condition `json:"conditions,omitempty"` } -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status // +operator-sdk:csv:customresourcedefinitions:displayName="Red Hat Developer Hub" // Backstage is the Schema for the Red Hat Developer Hub backstages API. @@ -281,7 +281,7 @@ type Backstage struct { Status BackstageStatus `json:"status,omitempty"` } -//+kubebuilder:object:root=true +// +kubebuilder:object:root=true // BackstageList contains a list of Backstage type BackstageList struct { @@ -295,7 +295,7 @@ type BackstageList struct { type Route struct { // Control the creation of a Route on OpenShift. // +optional - //+kubebuilder:default=true + // +kubebuilder:default=true Enabled *bool `json:"enabled,omitempty"` // Host is an alias/DNS that points to the service. Optional. diff --git a/api/v1alpha4/zz_generated.deepcopy.go b/api/v1alpha4/zz_generated.deepcopy.go index c9c126a0f..9c86c46ee 100644 --- a/api/v1alpha4/zz_generated.deepcopy.go +++ b/api/v1alpha4/zz_generated.deepcopy.go @@ -5,7 +5,7 @@ package v1alpha4 import ( - "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) diff --git a/api/v1alpha5/backstage_types.go b/api/v1alpha5/backstage_types.go index cdd14bec7..90c9d510c 100644 --- a/api/v1alpha5/backstage_types.go +++ b/api/v1alpha5/backstage_types.go @@ -63,7 +63,7 @@ type RuntimeConfig struct { type Database struct { // Control the creation of a local PostgreSQL DB. Set to false if using for example an external Database for Backstage. // +optional - //+kubebuilder:default=true + // +kubebuilder:default=true EnableLocalDb *bool `json:"enableLocalDb,omitempty"` // Name of the secret for database authentication. Optional. @@ -177,7 +177,7 @@ type ExtraEnvs struct { type EnvObjectRef struct { // Name of the object // We support only ConfigMaps and Secrets. - //+kubebuilder:validation:Required + // +kubebuilder:validation:Required Name string `json:"name"` // Key in the object @@ -195,7 +195,7 @@ type EnvObjectRef struct { type FileObjectRef struct { // Name of the object // Supported ConfigMaps and Secrets - //+kubebuilder:validation:Required + // +kubebuilder:validation:Required Name string `json:"name"` // Key in the object @@ -216,7 +216,7 @@ type FileObjectRef struct { type PvcRef struct { // Name of the object - //+kubebuilder:validation:Required + // +kubebuilder:validation:Required Name string `json:"name"` // Path to mount PVC. If not specified default-path/Name will be used @@ -233,11 +233,11 @@ type PvcRef struct { type Env struct { // Name of the environment variable - //+kubebuilder:validation:Required + // +kubebuilder:validation:Required Name string `json:"name"` // Value of the environment variable - //+kubebuilder:validation:Required + // +kubebuilder:validation:Required Value string `json:"value"` // If set, the env variable will be injected only in the specified containers, otherwise in backstage container only. @@ -255,9 +255,9 @@ type BackstageStatus struct { Conditions []metav1.Condition `json:"conditions,omitempty"` } -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status -//+kubebuilder:storageversion +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:storageversion // +operator-sdk:csv:customresourcedefinitions:displayName="Red Hat Developer Hub" // Backstage is the Schema for the Red Hat Developer Hub backstages API. @@ -272,7 +272,7 @@ type Backstage struct { Status BackstageStatus `json:"status,omitempty"` } -//+kubebuilder:object:root=true +// +kubebuilder:object:root=true // BackstageList contains a list of Backstage type BackstageList struct { @@ -286,7 +286,7 @@ type BackstageList struct { type Route struct { // Control the creation of a Route on OpenShift. // +optional - //+kubebuilder:default=true + // +kubebuilder:default=true Enabled *bool `json:"enabled,omitempty"` // Host is an alias/DNS that points to the service. Optional. diff --git a/api/v1alpha5/zz_generated.deepcopy.go b/api/v1alpha5/zz_generated.deepcopy.go index 9e8b769c9..bd2d60544 100644 --- a/api/v1alpha5/zz_generated.deepcopy.go +++ b/api/v1alpha5/zz_generated.deepcopy.go @@ -5,7 +5,7 @@ package v1alpha5 import ( - "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) diff --git a/bundle/backstage.io/bundle.Dockerfile b/bundle/backstage.io/bundle.Dockerfile index 7910aa207..327282a1b 100644 --- a/bundle/backstage.io/bundle.Dockerfile +++ b/bundle/backstage.io/bundle.Dockerfile @@ -7,7 +7,7 @@ LABEL operators.operatorframework.io.bundle.metadata.v1=metadata/ LABEL operators.operatorframework.io.bundle.package.v1=backstage-operator LABEL operators.operatorframework.io.bundle.channels.v1=alpha LABEL operators.operatorframework.io.bundle.channel.default.v1=alpha -LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.37.0 +LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.42.0 LABEL operators.operatorframework.io.metrics.mediatype.v1=metrics+v1 LABEL operators.operatorframework.io.metrics.project_layout=go.kubebuilder.io/v4 diff --git a/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml b/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml index 11645437b..0090597e7 100644 --- a/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml +++ b/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml @@ -35,9 +35,9 @@ metadata: } } ] - createdAt: "2025-12-18T16:30:38Z" + createdAt: "2026-01-02T16:04:22Z" description: Backstage Operator - operators.operatorframework.io/builder: operator-sdk-v1.37.0 + operators.operatorframework.io/builder: operator-sdk-v1.42.0 operators.operatorframework.io/project_layout: go.kubebuilder.io/v4 labels: operatorframework.io/arch.amd64: supported @@ -230,9 +230,8 @@ spec: containers: - args: - --health-probe-bind-address=:8081 - - --metrics-bind-address=:8443 - - --metrics-secure=true - --leader-elect + - --metrics-bind-address=:8443 command: - /manager image: quay.io/rhdh-community/operator:0.9.0 diff --git a/bundle/backstage.io/manifests/rhdh.redhat.com_backstages.yaml b/bundle/backstage.io/manifests/rhdh.redhat.com_backstages.yaml index 8d5eb27e5..fbeebf019 100644 --- a/bundle/backstage.io/manifests/rhdh.redhat.com_backstages.yaml +++ b/bundle/backstage.io/manifests/rhdh.redhat.com_backstages.yaml @@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.18.0 creationTimestamp: null name: backstages.rhdh.redhat.com spec: @@ -356,16 +356,8 @@ spec: description: Conditions is the list of conditions describing the state of the runtime items: - description: "Condition contains details for one aspect of the current - state of this API Resource.\n---\nThis struct is intended for - direct use as an array at the field path .status.conditions. For - example,\n\n\n\ttype FooStatus struct{\n\t // Represents the - observations of a foo's current state.\n\t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // - +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t - \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" - patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t - \ // other fields\n\t}" + description: Condition contains details for one aspect of the current + state of this API Resource. properties: lastTransitionTime: description: |- @@ -406,12 +398,7 @@ spec: - Unknown type: string type: - description: |- - type of condition in CamelCase or in foo.example.com/CamelCase. - --- - Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be - useful (see .node.status.conditions), the ability to deconflict is important. - The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -865,16 +852,8 @@ spec: description: Conditions is the list of conditions describing the state of the runtime items: - description: "Condition contains details for one aspect of the current - state of this API Resource.\n---\nThis struct is intended for - direct use as an array at the field path .status.conditions. For - example,\n\n\n\ttype FooStatus struct{\n\t // Represents the - observations of a foo's current state.\n\t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // - +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t - \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" - patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t - \ // other fields\n\t}" + description: Condition contains details for one aspect of the current + state of this API Resource. properties: lastTransitionTime: description: |- @@ -915,12 +894,7 @@ spec: - Unknown type: string type: - description: |- - type of condition in CamelCase or in foo.example.com/CamelCase. - --- - Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be - useful (see .node.status.conditions), the ability to deconflict is important. - The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -1363,16 +1337,8 @@ spec: description: Conditions is the list of conditions describing the state of the runtime items: - description: "Condition contains details for one aspect of the current - state of this API Resource.\n---\nThis struct is intended for - direct use as an array at the field path .status.conditions. For - example,\n\n\n\ttype FooStatus struct{\n\t // Represents the - observations of a foo's current state.\n\t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // - +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t - \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" - patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t - \ // other fields\n\t}" + description: Condition contains details for one aspect of the current + state of this API Resource. properties: lastTransitionTime: description: |- @@ -1413,12 +1379,7 @@ spec: - Unknown type: string type: - description: |- - type of condition in CamelCase or in foo.example.com/CamelCase. - --- - Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be - useful (see .node.status.conditions), the ability to deconflict is important. - The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string diff --git a/bundle/backstage.io/metadata/annotations.yaml b/bundle/backstage.io/metadata/annotations.yaml index afbeaa545..cda3824cd 100644 --- a/bundle/backstage.io/metadata/annotations.yaml +++ b/bundle/backstage.io/metadata/annotations.yaml @@ -6,7 +6,7 @@ annotations: operators.operatorframework.io.bundle.package.v1: backstage-operator operators.operatorframework.io.bundle.channels.v1: alpha operators.operatorframework.io.bundle.channel.default.v1: alpha - operators.operatorframework.io.metrics.builder: operator-sdk-v1.37.0 + operators.operatorframework.io.metrics.builder: operator-sdk-v1.42.0 operators.operatorframework.io.metrics.mediatype.v1: metrics+v1 operators.operatorframework.io.metrics.project_layout: go.kubebuilder.io/v4 diff --git a/bundle/rhdh/bundle.Dockerfile b/bundle/rhdh/bundle.Dockerfile index e695be461..c657c4807 100644 --- a/bundle/rhdh/bundle.Dockerfile +++ b/bundle/rhdh/bundle.Dockerfile @@ -7,7 +7,7 @@ LABEL operators.operatorframework.io.bundle.metadata.v1=metadata/ LABEL operators.operatorframework.io.bundle.package.v1=rhdh LABEL operators.operatorframework.io.bundle.channels.v1=fast,fast-${CI_X_VERSION}.${CI_Y_VERSION} LABEL operators.operatorframework.io.bundle.channel.default.v1=fast -LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.37.0 +LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.42.0 LABEL operators.operatorframework.io.metrics.mediatype.v1=metrics+v1 LABEL operators.operatorframework.io.metrics.project_layout=go.kubebuilder.io/v4 diff --git a/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml b/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml index 610cc193f..4c1236bdb 100644 --- a/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml +++ b/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml @@ -39,7 +39,7 @@ metadata: categories: Developer Tools certified: "true" containerImage: registry.redhat.io/rhdh/rhdh-rhel9-operator:1.9 - createdAt: "2025-12-18T16:30:39Z" + createdAt: "2026-01-02T16:04:23Z" description: Red Hat Developer Hub is a Red Hat supported version of Backstage. It comes with pre-built plug-ins and configuration settings, supports use of an external database, and can help streamline the process of setting up a self-managed @@ -56,7 +56,7 @@ metadata: features.operators.openshift.io/token-auth-gcp: "false" operatorframework.io/suggested-namespace: rhdh-operator operators.openshift.io/valid-subscription: '["Red Hat Developer Hub"]' - operators.operatorframework.io/builder: operator-sdk-v1.37.0 + operators.operatorframework.io/builder: operator-sdk-v1.42.0 operators.operatorframework.io/project_layout: go.kubebuilder.io/v4 repository: https://gitlab.cee.redhat.com/rhidp/rhdh/ skipRange: '>=1.0.0 <1.9.0' @@ -322,9 +322,8 @@ spec: containers: - args: - --health-probe-bind-address=:8081 - - --metrics-bind-address=:8443 - - --metrics-secure=true - --leader-elect + - --metrics-bind-address=:8443 command: - /manager env: diff --git a/bundle/rhdh/manifests/rhdh.redhat.com_backstages.yaml b/bundle/rhdh/manifests/rhdh.redhat.com_backstages.yaml index 8d5eb27e5..fbeebf019 100644 --- a/bundle/rhdh/manifests/rhdh.redhat.com_backstages.yaml +++ b/bundle/rhdh/manifests/rhdh.redhat.com_backstages.yaml @@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.18.0 creationTimestamp: null name: backstages.rhdh.redhat.com spec: @@ -356,16 +356,8 @@ spec: description: Conditions is the list of conditions describing the state of the runtime items: - description: "Condition contains details for one aspect of the current - state of this API Resource.\n---\nThis struct is intended for - direct use as an array at the field path .status.conditions. For - example,\n\n\n\ttype FooStatus struct{\n\t // Represents the - observations of a foo's current state.\n\t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // - +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t - \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" - patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t - \ // other fields\n\t}" + description: Condition contains details for one aspect of the current + state of this API Resource. properties: lastTransitionTime: description: |- @@ -406,12 +398,7 @@ spec: - Unknown type: string type: - description: |- - type of condition in CamelCase or in foo.example.com/CamelCase. - --- - Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be - useful (see .node.status.conditions), the ability to deconflict is important. - The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -865,16 +852,8 @@ spec: description: Conditions is the list of conditions describing the state of the runtime items: - description: "Condition contains details for one aspect of the current - state of this API Resource.\n---\nThis struct is intended for - direct use as an array at the field path .status.conditions. For - example,\n\n\n\ttype FooStatus struct{\n\t // Represents the - observations of a foo's current state.\n\t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // - +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t - \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" - patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t - \ // other fields\n\t}" + description: Condition contains details for one aspect of the current + state of this API Resource. properties: lastTransitionTime: description: |- @@ -915,12 +894,7 @@ spec: - Unknown type: string type: - description: |- - type of condition in CamelCase or in foo.example.com/CamelCase. - --- - Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be - useful (see .node.status.conditions), the ability to deconflict is important. - The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -1363,16 +1337,8 @@ spec: description: Conditions is the list of conditions describing the state of the runtime items: - description: "Condition contains details for one aspect of the current - state of this API Resource.\n---\nThis struct is intended for - direct use as an array at the field path .status.conditions. For - example,\n\n\n\ttype FooStatus struct{\n\t // Represents the - observations of a foo's current state.\n\t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // - +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t - \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" - patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t - \ // other fields\n\t}" + description: Condition contains details for one aspect of the current + state of this API Resource. properties: lastTransitionTime: description: |- @@ -1413,12 +1379,7 @@ spec: - Unknown type: string type: - description: |- - type of condition in CamelCase or in foo.example.com/CamelCase. - --- - Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be - useful (see .node.status.conditions), the ability to deconflict is important. - The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string diff --git a/bundle/rhdh/metadata/annotations.yaml b/bundle/rhdh/metadata/annotations.yaml index d8ebbe608..6034d4d77 100644 --- a/bundle/rhdh/metadata/annotations.yaml +++ b/bundle/rhdh/metadata/annotations.yaml @@ -6,7 +6,7 @@ annotations: operators.operatorframework.io.bundle.package.v1: rhdh operators.operatorframework.io.bundle.channels.v1: fast,fast-${CI_X_VERSION}.${CI_Y_VERSION} operators.operatorframework.io.bundle.channel.default.v1: fast - operators.operatorframework.io.metrics.builder: operator-sdk-v1.37.0 + operators.operatorframework.io.metrics.builder: operator-sdk-v1.42.0 operators.operatorframework.io.metrics.mediatype.v1: metrics+v1 operators.operatorframework.io.metrics.project_layout: go.kubebuilder.io/v4 diff --git a/cmd/main.go b/cmd/main.go index 0d2b21686..4725fb14e 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -4,10 +4,12 @@ import ( "crypto/tls" "flag" "os" + "path/filepath" // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) // to ensure that exec-entrypoint and run can make use of them. _ "k8s.io/client-go/plugin/pkg/client/auth" + "sigs.k8s.io/controller-runtime/pkg/certwatcher" "sigs.k8s.io/controller-runtime/pkg/metrics/filters" "k8s.io/apimachinery/pkg/runtime" @@ -20,12 +22,13 @@ import ( "sigs.k8s.io/controller-runtime/pkg/webhook" monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" + bsv1 "github.com/redhat-developer/rhdh-operator/api/v1alpha5" "github.com/redhat-developer/rhdh-operator/internal/controller" openshift "github.com/openshift/api/route/v1" - //+kubebuilder:scaffold:imports + // +kubebuilder:scaffold:imports ) var ( @@ -41,7 +44,7 @@ func init() { utilruntime.Must(openshift.Install(scheme)) utilruntime.Must(monitoringv1.AddToScheme(scheme)) - //+kubebuilder:scaffold:scheme + // +kubebuilder:scaffold:scheme } func main() { @@ -50,16 +53,28 @@ func main() { var probeAddr string var secureMetrics bool var enableHTTP2 bool - flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", - "The address the metrics endpoint binds to. Use 0 to disable the metrics service.") + var metricsCertPath, metricsCertName, metricsCertKey string + var webhookCertPath, webhookCertName, webhookCertKey string + + flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+ + "Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.") flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") flag.BoolVar(&enableLeaderElection, "leader-elect", false, "Enable leader election for controller manager. "+ "Enabling this will ensure there is only one active controller manager.") - flag.BoolVar(&secureMetrics, "metrics-secure", false, - "If set, the metrics endpoint is served securely over HTTPS and requires authentication and authorization.") + flag.BoolVar(&secureMetrics, "metrics-secure", true, + "If set, the metrics endpoint is served securely via HTTPS and requires authentication and authorization. "+ + "Use --metrics-secure=false to use HTTP instead.") flag.BoolVar(&enableHTTP2, "enable-http2", false, "If set, HTTP/2 will be enabled for the metrics and webhook servers") + flag.StringVar(&webhookCertPath, "webhook-cert-path", "", "The directory that contains the webhook certificate.") + flag.StringVar(&webhookCertName, "webhook-cert-name", "tls.crt", "The name of the webhook certificate file.") + flag.StringVar(&webhookCertKey, "webhook-cert-key", "tls.key", "The name of the webhook key file.") + flag.StringVar(&metricsCertPath, "metrics-cert-path", "", + "The directory that contains the metrics server certificate.") + flag.StringVar(&metricsCertName, "metrics-cert-name", "tls.crt", + "The name of the metrics server certificate file.") + flag.StringVar(&metricsCertKey, "metrics-cert-key", "tls.key", "The name of the metrics server key file.") opts := zap.Options{ Development: true, @@ -89,26 +104,77 @@ func main() { tlsOpts = append(tlsOpts, disableHTTP2) } - metricsServerOpts := metricsserver.Options{ + var metricsCertWatcher, webhookCertWatcher *certwatcher.CertWatcher + webhookTLSOpts := tlsOpts + + if len(webhookCertPath) > 0 { + setupLog.Info("Initializing webhook certificate watcher using provided certificates", + "webhook-cert-path", webhookCertPath, "webhook-cert-name", webhookCertName, "webhook-cert-key", webhookCertKey) + + var err error + webhookCertWatcher, err = certwatcher.New( + filepath.Join(webhookCertPath, webhookCertName), + filepath.Join(webhookCertPath, webhookCertKey), + ) + if err != nil { + setupLog.Error(err, "Failed to initialize webhook certificate watcher") + os.Exit(1) + } + + webhookTLSOpts = append(webhookTLSOpts, func(config *tls.Config) { + config.GetCertificate = webhookCertWatcher.GetCertificate + }) + } + + // Metrics endpoint is enabled in 'config/default/kustomization.yaml'. The Metrics options configure the server. + // More info: + // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.0/pkg/metrics/server + // - https://book.kubebuilder.io/reference/metrics.html + metricsServerOptions := metricsserver.Options{ BindAddress: metricsAddr, SecureServing: secureMetrics, - TLSOpts: tlsOpts, + // TODO(user): TLSOpts is used to allow configuring the TLS config used for the server. If certificates are + // not provided, self-signed certificates will be generated by default. This option is not recommended for + // production environments as self-signed certificates do not offer the same level of trust and security + // as certificates issued by a trusted Certificate Authority (CA). The primary risk is potentially allowing + // unauthorized access to sensitive metrics data. Consider replacing with CertDir, CertName, and KeyName + // to provide certificates, ensuring the server communicates using trusted and secure certificates. + TLSOpts: webhookTLSOpts, } if secureMetrics { // FilterProvider is used to protect the metrics endpoint with authn/authz. // These configurations ensure that only authorized users and service accounts // can access the metrics endpoint. The RBAC are configured in 'config/rbac/kustomization.yaml'. More info: - // https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/metrics/filters#WithAuthenticationAndAuthorization - metricsServerOpts.FilterProvider = filters.WithAuthenticationAndAuthorization + // https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.0/pkg/metrics/filters#WithAuthenticationAndAuthorization + metricsServerOptions.FilterProvider = filters.WithAuthenticationAndAuthorization } webhookServer := webhook.NewServer(webhook.Options{ TLSOpts: tlsOpts, }) + if len(metricsCertPath) > 0 { + setupLog.Info("Initializing metrics certificate watcher using provided certificates", + "metrics-cert-path", metricsCertPath, "metrics-cert-name", metricsCertName, "metrics-cert-key", metricsCertKey) + + var err error + metricsCertWatcher, err = certwatcher.New( + filepath.Join(metricsCertPath, metricsCertName), + filepath.Join(metricsCertPath, metricsCertKey), + ) + if err != nil { + setupLog.Error(err, "Failed to initialize metrics certificate watcher") + os.Exit(1) + } + + metricsServerOptions.TLSOpts = append(metricsServerOptions.TLSOpts, func(config *tls.Config) { + config.GetCertificate = metricsCertWatcher.GetCertificate + }) + } + mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ Scheme: scheme, - Metrics: metricsServerOpts, + Metrics: metricsServerOptions, WebhookServer: webhookServer, HealthProbeBindAddress: probeAddr, LeaderElection: enableLeaderElection, @@ -144,7 +210,23 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "Backstage") os.Exit(1) } - //+kubebuilder:scaffold:builder + // +kubebuilder:scaffold:builder + + if metricsCertWatcher != nil { + setupLog.Info("Adding metrics certificate watcher to manager") + if err := mgr.Add(metricsCertWatcher); err != nil { + setupLog.Error(err, "Unable to add metrics certificate watcher to manager") + os.Exit(1) + } + } + + if webhookCertWatcher != nil { + setupLog.Info("Adding webhook certificate watcher to manager") + if err := mgr.Add(webhookCertWatcher); err != nil { + setupLog.Error(err, "Unable to add webhook certificate watcher to manager") + os.Exit(1) + } + } if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { setupLog.Error(err, "unable to set up health check") diff --git a/config/crd/bases/rhdh.redhat.com_backstages.yaml b/config/crd/bases/rhdh.redhat.com_backstages.yaml index a1e7f8d80..0f8a4a047 100644 --- a/config/crd/bases/rhdh.redhat.com_backstages.yaml +++ b/config/crd/bases/rhdh.redhat.com_backstages.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.18.0 name: backstages.rhdh.redhat.com spec: group: rhdh.redhat.com @@ -356,16 +356,8 @@ spec: description: Conditions is the list of conditions describing the state of the runtime items: - description: "Condition contains details for one aspect of the current - state of this API Resource.\n---\nThis struct is intended for - direct use as an array at the field path .status.conditions. For - example,\n\n\n\ttype FooStatus struct{\n\t // Represents the - observations of a foo's current state.\n\t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // - +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t - \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" - patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t - \ // other fields\n\t}" + description: Condition contains details for one aspect of the current + state of this API Resource. properties: lastTransitionTime: description: |- @@ -406,12 +398,7 @@ spec: - Unknown type: string type: - description: |- - type of condition in CamelCase or in foo.example.com/CamelCase. - --- - Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be - useful (see .node.status.conditions), the ability to deconflict is important. - The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -865,16 +852,8 @@ spec: description: Conditions is the list of conditions describing the state of the runtime items: - description: "Condition contains details for one aspect of the current - state of this API Resource.\n---\nThis struct is intended for - direct use as an array at the field path .status.conditions. For - example,\n\n\n\ttype FooStatus struct{\n\t // Represents the - observations of a foo's current state.\n\t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // - +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t - \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" - patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t - \ // other fields\n\t}" + description: Condition contains details for one aspect of the current + state of this API Resource. properties: lastTransitionTime: description: |- @@ -915,12 +894,7 @@ spec: - Unknown type: string type: - description: |- - type of condition in CamelCase or in foo.example.com/CamelCase. - --- - Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be - useful (see .node.status.conditions), the ability to deconflict is important. - The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -1363,16 +1337,8 @@ spec: description: Conditions is the list of conditions describing the state of the runtime items: - description: "Condition contains details for one aspect of the current - state of this API Resource.\n---\nThis struct is intended for - direct use as an array at the field path .status.conditions. For - example,\n\n\n\ttype FooStatus struct{\n\t // Represents the - observations of a foo's current state.\n\t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // - +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t - \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" - patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t - \ // other fields\n\t}" + description: Condition contains details for one aspect of the current + state of this API Resource. properties: lastTransitionTime: description: |- @@ -1413,12 +1379,7 @@ spec: - Unknown type: string type: - description: |- - type of condition in CamelCase or in foo.example.com/CamelCase. - --- - Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be - useful (see .node.status.conditions), the ability to deconflict is important. - The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 104e55f62..4d391c457 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -10,13 +10,7 @@ patches: # patches here are for enabling the conversion webhook for each CRD #+kubebuilder:scaffold:crdkustomizewebhookpatch -# [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. -# patches here are for enabling the CA injection for each CRD -#- path: patches/cainjection_in_immiches.yaml -#+kubebuilder:scaffold:crdkustomizecainjectionpatch - # [WEBHOOK] To enable webhook, uncomment the following section # the following config is for teaching kustomize how to do kustomization for CRDs. - #configurations: #- kustomizeconfig.yaml diff --git a/config/manager/cert_metrics_manager_patch.yaml b/config/manager/cert_metrics_manager_patch.yaml new file mode 100644 index 000000000..d97501553 --- /dev/null +++ b/config/manager/cert_metrics_manager_patch.yaml @@ -0,0 +1,30 @@ +# This patch adds the args, volumes, and ports to allow the manager to use the metrics-server certs. + +# Add the volumeMount for the metrics-server certs +- op: add + path: /spec/template/spec/containers/0/volumeMounts/- + value: + mountPath: /tmp/k8s-metrics-server/metrics-certs + name: metrics-certs + readOnly: true + +# Add the --metrics-cert-path argument for the metrics server +- op: add + path: /spec/template/spec/containers/0/args/- + value: --metrics-cert-path=/tmp/k8s-metrics-server/metrics-certs + +# Add the metrics-server certs volume configuration +- op: add + path: /spec/template/spec/volumes/- + value: + name: metrics-certs + secret: + secretName: metrics-server-cert + optional: false + items: + - key: ca.crt + path: ca.crt + - key: tls.crt + path: tls.crt + - key: tls.key + path: tls.key diff --git a/config/manager/deployment.yaml b/config/manager/deployment.yaml index 85ec0ec4f..1ca60434c 100644 --- a/config/manager/deployment.yaml +++ b/config/manager/deployment.yaml @@ -52,8 +52,6 @@ spec: - /manager args: - --health-probe-bind-address=:8081 - - --metrics-bind-address=:8443 - - --metrics-secure=true - --leader-elect image: controller:latest name: manager diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 0c00d6fb7..768d9e2df 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -3,3 +3,211 @@ kind: Kustomization resources: - deployment.yaml + # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in + # crd/kustomization.yaml + #- ../webhook + # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required. + #- ../certmanager + # [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. + #- ../prometheus + # [METRICS] Expose the controller manager metrics service. +- metrics_service.yaml +# [NETWORK POLICY] Protect the /metrics endpoint and Webhook Server with NetworkPolicy. +# Only Pod(s) running a namespace labeled with 'metrics: enabled' will be able to gather the metrics. +# Only CR(s) which requires webhooks and are applied on namespaces labeled with 'webhooks: enabled' will +# be able to communicate with the Webhook Server. +# TODO(asoro): NetworkPolicies for Operator disabled for now due to https://issues.redhat.com/browse/RHIDP-8734. +# Support should be added as part of https://issues.redhat.com/browse/RHDHPLAN-351 +#- ../network-policy + +patches: +# [METRICS] The following patch will enable the metrics endpoint using HTTPS and the port :8443. +# More info: https://book.kubebuilder.io/reference/metrics +- path: manager_metrics_patch.yaml + target: + kind: Deployment + +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in +# crd/kustomization.yaml +#- path: manager_webhook_patch.yaml +# target: +# kind: Deployment + +# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. +# Uncomment the following replacements to add the cert-manager CA injection annotations +#replacements: +# - source: # Uncomment the following block to enable certificates for metrics +# kind: Service +# version: v1 +# name: controller-manager-metrics-service +# fieldPath: metadata.name +# targets: +# - select: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: metrics-certs +# fieldPaths: +# - spec.dnsNames.0 +# - spec.dnsNames.1 +# options: +# delimiter: '.' +# index: 0 +# create: true +# - select: # Uncomment the following to set the Service name for TLS config in Prometheus ServiceMonitor +# kind: ServiceMonitor +# group: monitoring.coreos.com +# version: v1 +# name: controller-manager-metrics-monitor +# fieldPaths: +# - spec.endpoints.0.tlsConfig.serverName +# options: +# delimiter: '.' +# index: 0 +# create: true +# +# - source: +# kind: Service +# version: v1 +# name: controller-manager-metrics-service +# fieldPath: metadata.namespace +# targets: +# - select: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: metrics-certs +# fieldPaths: +# - spec.dnsNames.0 +# - spec.dnsNames.1 +# options: +# delimiter: '.' +# index: 1 +# create: true +# - select: # Uncomment the following to set the Service namespace for TLS in Prometheus ServiceMonitor +# kind: ServiceMonitor +# group: monitoring.coreos.com +# version: v1 +# name: controller-manager-metrics-monitor +# fieldPaths: +# - spec.endpoints.0.tlsConfig.serverName +# options: +# delimiter: '.' +# index: 1 +# create: true +# +# - source: # Uncomment the following block if you have any webhook +# kind: Service +# version: v1 +# name: webhook-service +# fieldPath: .metadata.name # Name of the service +# targets: +# - select: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPaths: +# - .spec.dnsNames.0 +# - .spec.dnsNames.1 +# options: +# delimiter: '.' +# index: 0 +# create: true +# - source: +# kind: Service +# version: v1 +# name: webhook-service +# fieldPath: .metadata.namespace # Namespace of the service +# targets: +# - select: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPaths: +# - .spec.dnsNames.0 +# - .spec.dnsNames.1 +# options: +# delimiter: '.' +# index: 1 +# create: true +# +# - source: # Uncomment the following block if you have a ValidatingWebhook (--programmatic-validation) +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert # This name should match the one in certificate.yaml +# fieldPath: .metadata.namespace # Namespace of the certificate CR +# targets: +# - select: +# kind: ValidatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 0 +# create: true +# - source: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPath: .metadata.name +# targets: +# - select: +# kind: ValidatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 1 +# create: true +# +# - source: # Uncomment the following block if you have a DefaultingWebhook (--defaulting ) +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPath: .metadata.namespace # Namespace of the certificate CR +# targets: +# - select: +# kind: MutatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 0 +# create: true +# - source: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPath: .metadata.name +# targets: +# - select: +# kind: MutatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 1 +# create: true +# +# - source: # Uncomment the following block if you have a ConversionWebhook (--conversion) +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPath: .metadata.namespace # Namespace of the certificate CR +# targets: # Do not remove or uncomment the following scaffold marker; required to generate code for target CRD. +# +kubebuilder:scaffold:crdkustomizecainjectionns +# - source: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPath: .metadata.name +# targets: # Do not remove or uncomment the following scaffold marker; required to generate code for target CRD. +# +kubebuilder:scaffold:crdkustomizecainjectionname diff --git a/config/manager/manager_metrics_patch.yaml b/config/manager/manager_metrics_patch.yaml new file mode 100644 index 000000000..8e0cedb9e --- /dev/null +++ b/config/manager/manager_metrics_patch.yaml @@ -0,0 +1,4 @@ +# This patch adds the args to allow exposing the metrics endpoint using HTTPS +- op: add + path: /spec/template/spec/containers/0/args/- + value: --metrics-bind-address=:8443 diff --git a/config/rbac/metrics_service.yaml b/config/manager/metrics_service.yaml similarity index 100% rename from config/rbac/metrics_service.yaml rename to config/manager/metrics_service.yaml diff --git a/config/prometheus/monitor.yaml b/config/prometheus/monitor.yaml index b4feb71f6..a08cee709 100644 --- a/config/prometheus/monitor.yaml +++ b/config/prometheus/monitor.yaml @@ -11,10 +11,19 @@ metadata: spec: endpoints: - path: /metrics - port: https + port: https # Ensure this is the name of the port that exposes HTTPS metrics scheme: https bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token tlsConfig: + # TODO(user): The option insecureSkipVerify: true is not recommended for production since it disables + # certificate verification. This poses a significant security risk by making the system vulnerable to + # man-in-the-middle attacks, where an attacker could intercept and manipulate the communication between + # Prometheus and the monitored services. This could lead to unauthorized access to sensitive metrics data, + # compromising the integrity and confidentiality of the information. + # Please use the following options for secure configurations: + # caFile: /etc/metrics-certs/ca.crt + # certFile: /etc/metrics-certs/tls.crt + # keyFile: /etc/metrics-certs/tls.key insecureSkipVerify: true selector: matchLabels: diff --git a/config/rbac/kustomization.yaml b/config/rbac/kustomization.yaml index d449df95b..d9ddf1e3d 100644 --- a/config/rbac/kustomization.yaml +++ b/config/rbac/kustomization.yaml @@ -9,8 +9,6 @@ resources: - role_binding.yaml - leader_election_role.yaml - leader_election_role_binding.yaml -# Metrics -- metrics_service.yaml # The following RBAC configurations are used to protect # the metrics endpoint with authn/authz. These configurations # ensure that only authorized users and service accounts diff --git a/dist/backstage.io/install.yaml b/dist/backstage.io/install.yaml index 3b44f9047..4f4f32511 100644 --- a/dist/backstage.io/install.yaml +++ b/dist/backstage.io/install.yaml @@ -15,7 +15,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.18.0 name: backstages.rhdh.redhat.com spec: group: rhdh.redhat.com @@ -368,16 +368,8 @@ spec: description: Conditions is the list of conditions describing the state of the runtime items: - description: "Condition contains details for one aspect of the current - state of this API Resource.\n---\nThis struct is intended for - direct use as an array at the field path .status.conditions. For - example,\n\n\n\ttype FooStatus struct{\n\t // Represents the - observations of a foo's current state.\n\t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // - +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t - \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" - patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t - \ // other fields\n\t}" + description: Condition contains details for one aspect of the current + state of this API Resource. properties: lastTransitionTime: description: |- @@ -418,12 +410,7 @@ spec: - Unknown type: string type: - description: |- - type of condition in CamelCase or in foo.example.com/CamelCase. - --- - Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be - useful (see .node.status.conditions), the ability to deconflict is important. - The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -877,16 +864,8 @@ spec: description: Conditions is the list of conditions describing the state of the runtime items: - description: "Condition contains details for one aspect of the current - state of this API Resource.\n---\nThis struct is intended for - direct use as an array at the field path .status.conditions. For - example,\n\n\n\ttype FooStatus struct{\n\t // Represents the - observations of a foo's current state.\n\t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // - +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t - \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" - patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t - \ // other fields\n\t}" + description: Condition contains details for one aspect of the current + state of this API Resource. properties: lastTransitionTime: description: |- @@ -927,12 +906,7 @@ spec: - Unknown type: string type: - description: |- - type of condition in CamelCase or in foo.example.com/CamelCase. - --- - Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be - useful (see .node.status.conditions), the ability to deconflict is important. - The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -1375,16 +1349,8 @@ spec: description: Conditions is the list of conditions describing the state of the runtime items: - description: "Condition contains details for one aspect of the current - state of this API Resource.\n---\nThis struct is intended for - direct use as an array at the field path .status.conditions. For - example,\n\n\n\ttype FooStatus struct{\n\t // Represents the - observations of a foo's current state.\n\t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // - +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t - \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" - patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t - \ // other fields\n\t}" + description: Condition contains details for one aspect of the current + state of this API Resource. properties: lastTransitionTime: description: |- @@ -1425,12 +1391,7 @@ spec: - Unknown type: string type: - description: |- - type of condition in CamelCase or in foo.example.com/CamelCase. - --- - Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be - useful (see .node.status.conditions), the ability to deconflict is important. - The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -2055,9 +2016,8 @@ spec: containers: - args: - --health-probe-bind-address=:8081 - - --metrics-bind-address=:8443 - - --metrics-secure=true - --leader-elect + - --metrics-bind-address=:8443 command: - /manager image: quay.io/rhdh-community/operator:0.9.0 diff --git a/dist/rhdh/install.yaml b/dist/rhdh/install.yaml index 08e45a3f8..b0cf61b93 100644 --- a/dist/rhdh/install.yaml +++ b/dist/rhdh/install.yaml @@ -15,7 +15,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.18.0 name: backstages.rhdh.redhat.com spec: group: rhdh.redhat.com @@ -368,16 +368,8 @@ spec: description: Conditions is the list of conditions describing the state of the runtime items: - description: "Condition contains details for one aspect of the current - state of this API Resource.\n---\nThis struct is intended for - direct use as an array at the field path .status.conditions. For - example,\n\n\n\ttype FooStatus struct{\n\t // Represents the - observations of a foo's current state.\n\t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // - +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t - \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" - patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t - \ // other fields\n\t}" + description: Condition contains details for one aspect of the current + state of this API Resource. properties: lastTransitionTime: description: |- @@ -418,12 +410,7 @@ spec: - Unknown type: string type: - description: |- - type of condition in CamelCase or in foo.example.com/CamelCase. - --- - Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be - useful (see .node.status.conditions), the ability to deconflict is important. - The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -877,16 +864,8 @@ spec: description: Conditions is the list of conditions describing the state of the runtime items: - description: "Condition contains details for one aspect of the current - state of this API Resource.\n---\nThis struct is intended for - direct use as an array at the field path .status.conditions. For - example,\n\n\n\ttype FooStatus struct{\n\t // Represents the - observations of a foo's current state.\n\t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // - +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t - \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" - patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t - \ // other fields\n\t}" + description: Condition contains details for one aspect of the current + state of this API Resource. properties: lastTransitionTime: description: |- @@ -927,12 +906,7 @@ spec: - Unknown type: string type: - description: |- - type of condition in CamelCase or in foo.example.com/CamelCase. - --- - Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be - useful (see .node.status.conditions), the ability to deconflict is important. - The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -1375,16 +1349,8 @@ spec: description: Conditions is the list of conditions describing the state of the runtime items: - description: "Condition contains details for one aspect of the current - state of this API Resource.\n---\nThis struct is intended for - direct use as an array at the field path .status.conditions. For - example,\n\n\n\ttype FooStatus struct{\n\t // Represents the - observations of a foo's current state.\n\t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // - +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t - \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" - patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t - \ // other fields\n\t}" + description: Condition contains details for one aspect of the current + state of this API Resource. properties: lastTransitionTime: description: |- @@ -1425,12 +1391,7 @@ spec: - Unknown type: string type: - description: |- - type of condition in CamelCase or in foo.example.com/CamelCase. - --- - Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be - useful (see .node.status.conditions), the ability to deconflict is important. - The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -2935,9 +2896,8 @@ spec: containers: - args: - --health-probe-bind-address=:8081 - - --metrics-bind-address=:8443 - - --metrics-secure=true - --leader-elect + - --metrics-bind-address=:8443 command: - /manager env: diff --git a/go.mod b/go.mod index 5afa98f2a..5d80871ee 100644 --- a/go.mod +++ b/go.mod @@ -5,8 +5,8 @@ go 1.24.0 toolchain go1.24.6 require ( - github.com/onsi/ginkgo/v2 v2.22.2 - github.com/onsi/gomega v1.36.2 + github.com/onsi/ginkgo/v2 v2.27.3 + github.com/onsi/gomega v1.38.3 github.com/openshift/api v0.0.0-20260102143802-d2ec16864f86 github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.71.2 github.com/stretchr/testify v1.10.0 @@ -19,12 +19,14 @@ require ( k8s.io/client-go v0.34.3 k8s.io/klog/v2 v2.130.1 k8s.io/utils v0.0.0-20251222233032-718f0e51e6d2 - sigs.k8s.io/controller-runtime v0.19.4 + sigs.k8s.io/controller-runtime v0.21.0 sigs.k8s.io/kustomize/kyaml v0.18.1 sigs.k8s.io/yaml v1.6.0 ) require ( + cel.dev/expr v0.19.1 // indirect + github.com/Masterminds/semver/v3 v3.4.0 // indirect github.com/antlr4-go/antlr/v4 v4.13.0 // indirect github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -33,12 +35,12 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect - github.com/evanphx/json-patch/v5 v5.9.0 // indirect + github.com/evanphx/json-patch/v5 v5.9.11 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-errors/errors v1.4.2 // indirect - github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/zapr v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect @@ -48,14 +50,14 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect - github.com/google/cel-go v0.20.1 // indirect + github.com/google/cel-go v0.23.2 // indirect github.com/google/gnostic-models v0.7.0 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect + github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect @@ -66,28 +68,30 @@ require ( github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_golang v1.19.1 // indirect + github.com/prometheus/client_golang v1.22.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/common v0.62.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/spf13/cobra v1.8.1 // indirect github.com/spf13/pflag v1.0.6 // indirect - github.com/stoewer/go-strcase v1.2.0 // indirect + github.com/stoewer/go-strcase v1.3.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/x448/float16 v0.8.4 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect - go.opentelemetry.io/otel v1.28.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 // indirect - go.opentelemetry.io/otel/metric v1.28.0 // indirect - go.opentelemetry.io/otel/sdk v1.28.0 // indirect - go.opentelemetry.io/otel/trace v1.28.0 // indirect - go.opentelemetry.io/proto/otlp v1.3.1 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect + go.opentelemetry.io/otel v1.33.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 // indirect + go.opentelemetry.io/otel/metric v1.33.0 // indirect + go.opentelemetry.io/otel/sdk v1.33.0 // indirect + go.opentelemetry.io/otel/trace v1.33.0 // indirect + go.opentelemetry.io/proto/otlp v1.4.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.26.0 // indirect + go.uber.org/zap v1.27.0 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/mod v0.31.0 // indirect golang.org/x/net v0.48.0 // indirect golang.org/x/oauth2 v0.27.0 // indirect golang.org/x/sync v0.19.0 // indirect @@ -97,18 +101,18 @@ require ( golang.org/x/time v0.9.0 // indirect golang.org/x/tools v0.40.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect - google.golang.org/grpc v1.65.0 // indirect - google.golang.org/protobuf v1.36.5 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect + google.golang.org/grpc v1.68.1 // indirect + google.golang.org/protobuf v1.36.7 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiserver v0.34.1 // indirect k8s.io/component-base v0.34.1 // indirect k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect - sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 // indirect + sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect ) // Replace directives to pin k8s.io dependencies to v0.31.3 @@ -131,5 +135,6 @@ replace ( k8s.io/client-go => k8s.io/client-go v0.31.3 k8s.io/component-base => k8s.io/component-base v0.31.3 k8s.io/kube-openapi => k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 + sigs.k8s.io/controller-runtime => sigs.k8s.io/controller-runtime v0.19.4 sigs.k8s.io/yaml => sigs.k8s.io/yaml v1.4.0 ) diff --git a/go.sum b/go.sum index fea0398d5..98d09fedc 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,7 @@ +cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4= +cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= +github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= +github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= @@ -22,19 +26,25 @@ github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxER github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k= github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= -github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= -github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= +github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= +github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= +github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs= +github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo= +github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M= +github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk= +github.com/gkampitakis/go-snaps v0.5.15 h1:amyJrvM1D33cPHwVrjo9jQxX8g/7E2wYdZ+01KS3zGE= +github.com/gkampitakis/go-snaps v0.5.15/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= @@ -49,14 +59,16 @@ github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+Gr github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= +github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 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/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/google/cel-go v0.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84= -github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg= +github.com/google/cel-go v0.23.2 h1:UdEe3CvQh3Nv+E/j9r1Y//WO0K0cSyD7/y0bzyLIMI4= +github.com/google/cel-go v0.23.2/go.mod h1:52Pb6QsDbC5kvgxvZhiL9QX1oZEkcUF/ZqaPx1J5Wwo= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -65,24 +77,28 @@ github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg= -github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 h1:TmHmbvxPmaegwhDubVz0lICL0J5Ka2vwTzhoePEXsGE= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0/go.mod h1:qztMSjm835F2bXf+5HKAPIS5qsmQDqZna/PgVt4rWtI= github.com/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.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 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/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE= +github.com/joshdk/go-junit v1.0.0/go.mod h1:TiiV0PqkaNfFXjEiyjWM3XXrhVyCa1K4Zfga6W52ung= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -90,8 +106,14 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo= +github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg= +github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE= +github.com/mfridman/tparse v0.18.0/go.mod h1:gEvqZTuCgEhPbYk/2lS3Kcxg1GmTxxU7kTC8DvP0i/A= github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU= github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -106,10 +128,10 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU= -github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk= -github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= -github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= +github.com/onsi/ginkgo/v2 v2.27.3 h1:ICsZJ8JoYafeXFFlFAG75a7CxMsJHwgKwtO+82SE9L8= +github.com/onsi/ginkgo/v2 v2.27.3/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= +github.com/onsi/gomega v1.38.3 h1:eTX+W6dobAYfFeGC2PV6RwXRu/MyT+cQguijutvkpSM= +github.com/onsi/gomega v1.38.3/go.mod h1:ZCU1pkQcXDO5Sl9/VVEGlDyp+zm0m1cmeG5TOzLgdh4= github.com/openshift/api v0.0.0-20260102143802-d2ec16864f86 h1:Vsqg+WqSA91LjrwK5lzkSCjztK/B+T8MPKI3MIALx3w= github.com/openshift/api v0.0.0-20260102143802-d2ec16864f86/go.mod h1:d5uzF0YN2nQQFA0jIEWzzOZ+edmo6wzlGLvx5Fhz4uY= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -119,29 +141,28 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.71.2 h1:HZdPRm0ApWPg7F4sHgbqWkL+ddWfpTZsopm5HM/2g4o= github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.71.2/go.mod h1:3RiUkFmR9kmPZi9r/8a5jw0a9yg+LMmr7qa0wjqvSiI= -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_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= +github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= -github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= +github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= -github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= -github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= +github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= +github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 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= @@ -154,34 +175,40 @@ github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JT github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= +github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= -go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= -go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= -go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= -go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= -go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= -go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= -go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= -go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= -go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= -go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= +go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= +go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 h1:5pojmb1U1AogINhN3SurB+zm/nIcusopeBNp42f45QM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA= +go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ= +go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M= +go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM= +go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM= +go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= +go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= +go.opentelemetry.io/proto/otlp v1.4.0 h1:TA9WRvW6zMwP+Ssb6fLoUIuirti1gGbP28GcKG1jgeg= +go.opentelemetry.io/proto/otlp v1.4.0/go.mod h1:PPBWZIP98o2ElSqI35IHfu7hIhSwvc5N38Jw8pXuGFY= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= -go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -189,6 +216,8 @@ golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1i golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU= 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.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI= +golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -227,14 +256,14 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= -google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 h1:7whR9kGa5LUwFtpLm2ArCEejtnxlGeLbAyjFY8sGNFw= -google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= -google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= -google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= -google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= -google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q= +google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 h1:8ZmaLZE4XWrtU3MyClkYqqtl6Oegr3235h7jxsDyqCY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= +google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0= +google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw= +google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A= +google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= @@ -242,8 +271,6 @@ gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSP gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/yaml.v2 v2.2.2/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= @@ -268,15 +295,18 @@ k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7F k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= k8s.io/utils v0.0.0-20251222233032-718f0e51e6d2 h1:OfgiEo21hGiwx1oJUU5MpEaeOEg6coWndBkZF/lkFuE= k8s.io/utils v0.0.0-20251222233032-718f0e51e6d2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 h1:2770sDpzrjjsAtVhSeUFseziht227YAWYHLGNM8QPwY= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 h1:jpcvIRr3GLoUoEKRkHKSmGjxb6lWwrBlJsXc+eUYQHM= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= sigs.k8s.io/controller-runtime v0.19.4 h1:SUmheabttt0nx8uJtoII4oIP27BVVvAKFvdvGFwV/Qo= sigs.k8s.io/controller-runtime v0.19.4/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/kustomize/kyaml v0.18.1 h1:WvBo56Wzw3fjS+7vBjN6TeivvpbW9GmRaWZ9CIVmt4E= sigs.k8s.io/kustomize/kyaml v0.18.1/go.mod h1:C3L2BFVU1jgcddNBE1TxuVLgS46TjObMwW5FT9FcjYo= -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/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= +sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/structured-merge-diff/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc= +sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/integration_tests/config-refresh_test.go b/integration_tests/config-refresh_test.go index c88c144d5..79ac49faa 100644 --- a/integration_tests/config-refresh_test.go +++ b/integration_tests/config-refresh_test.go @@ -24,10 +24,15 @@ import ( // TODO make it with test matrix to not to repeat code +const conf = ` +organization: + name: "my org" +` + var appConfig = "app-config1" var secretEnv = "secret-env1" -//var secretFile = "secret1" +// var secretFile = "secret1" var bsSpec = bsv1.BackstageSpec{ Application: &bsv1.Application{ @@ -71,16 +76,11 @@ var _ = When("create backstage with external configuration", func() { Skip("Skipped for not real controller") } - //appConfig1 := "app-config1" - //secretEnv1 := "secret-env1" + // appConfig1 := "app-config1" + // secretEnv1 := "secret-env1" backstageName := generateRandName("") - conf := ` -organization: - name: "my org" -` - generateConfigMap(ctx, k8sClient, appConfig, ns, map[string]string{"appconfig11": conf}, nil, nil) generateSecret(ctx, k8sClient, secretEnv, ns, map[string]string{"sec11": "val11"}, nil, nil) @@ -95,20 +95,15 @@ organization: Skip("Skipped for not real cluster") } - //if !useExistingController { - // Skip("Skipped for not real controller") - //} + // if !useExistingController { + // Skip("Skipped for not real controller") + // } appConfig1 := "app-config1" secretFile1 := "secret1" backstageName := generateRandName("") - conf := ` -organization: - name: "my org" -` - generateConfigMap(ctx, k8sClient, appConfig1, ns, map[string]string{"appconfig11": conf}, nil, nil) generateSecret(ctx, k8sClient, secretFile1, ns, map[string]string{"sec11": "val11"}, nil, nil) @@ -136,17 +131,20 @@ organization: g.Expect(err).ShouldNot(HaveOccurred()) podList := &corev1.PodList{} - err = k8sClient.List(ctx, podList, client.InNamespace(ns), client.MatchingLabels{model.BackstageAppLabel: utils.BackstageAppLabelValue(backstageName)}) + err = k8sClient.List(ctx, podList, client.InNamespace(ns), + client.MatchingLabels{model.BackstageAppLabel: utils.BackstageAppLabelValue(backstageName)}) g.Expect(err).ShouldNot(HaveOccurred()) - g.Expect(len(podList.Items)).To(Equal(1)) + g.Expect(podList.Items).To(HaveLen(1)) podName = podList.Items[0].Name - out, _, err := executeRemoteCommand(ctx, ns, podName, backstageContainer(*deploy.PodSpec()).Name, "cat /my/appconfig/appconfig11") - g.Expect(err).ShouldNot(HaveOccurred()) + out, stderr, err := executeRemoteCommand(ctx, ns, podName, backstageContainer(*deploy.PodSpec()).Name, + "cat /my/appconfig/appconfig11") + g.Expect(err).ShouldNot(HaveOccurred(), fmt.Sprintf("stdout: %s\n\n, stderr: %s", out, stderr)) g.Expect(strings.ReplaceAll(out, "\r", "")).To(Equal(conf)) - _, _, err = executeRemoteCommand(ctx, ns, podName, backstageContainer(*deploy.PodSpec()).Name, "cat /my/secret/sec11") + _, _, err = executeRemoteCommand(ctx, ns, podName, backstageContainer(*deploy.PodSpec()).Name, + "cat /my/secret/sec11") g.Expect(err).ShouldNot(HaveOccurred()) }, 4*time.Minute, 10*time.Second).Should(Succeed(), controllerMessage()) @@ -169,7 +167,8 @@ organization: g.Expect(err).ShouldNot(HaveOccurred()) // no need to re-ask pod name, it is not recreated, just use what we've got - out, _, err := executeRemoteCommand(ctx, ns, podName, backstageContainer(*deploy.PodSpec()).Name, "cat /my/appconfig/appconfig11") + out, _, err := executeRemoteCommand(ctx, ns, podName, backstageContainer(*deploy.PodSpec()).Name, + "cat /my/appconfig/appconfig11") g.Expect(err).ShouldNot(HaveOccurred()) // let's checkMountWSubpath, just in case (it is k8s job to refresh it :) g.Expect(strings.ReplaceAll(out, "\r", "")).To(Equal(newData)) @@ -188,16 +187,11 @@ organization: Skip("Skipped for not real controller") } - //appConfig1 := "app-config1" - //secretEnv1 := "secret-env1" + // appConfig1 := "app-config1" + // secretEnv1 := "secret-env1" backstageName := generateRandName("") - conf := ` -organization: - name: "my org" -` - generateConfigMap(ctx, k8sClient, appConfig, ns, map[string]string{"appconfig11": conf}, nil, nil) generateSecret(ctx, k8sClient, secretEnv, ns, map[string]string{"sec11": "val11"}, nil, nil) @@ -211,7 +205,13 @@ organization: }) -func checkMountWSubpath(g Gomega, ctx context.Context, k8sClient client.Client, bs bsv1.BackstageSpec, ns, backstageName, conf string) { +func checkMountWSubpath( + _ Gomega, + ctx context.Context, + k8sClient client.Client, + bs bsv1.BackstageSpec, + ns, backstageName, conf string, +) { createAndReconcileBackstage(ctx, ns, bs, backstageName) @@ -220,17 +220,20 @@ func checkMountWSubpath(g Gomega, ctx context.Context, k8sClient client.Client, g.Expect(err).ShouldNot(HaveOccurred()) podList := &corev1.PodList{} - err = k8sClient.List(ctx, podList, client.InNamespace(ns), client.MatchingLabels{model.BackstageAppLabel: utils.BackstageAppLabelValue(backstageName)}) + err = k8sClient.List(ctx, podList, client.InNamespace(ns), + client.MatchingLabels{model.BackstageAppLabel: utils.BackstageAppLabelValue(backstageName)}) g.Expect(err).ShouldNot(HaveOccurred()) - g.Expect(len(podList.Items)).To(Equal(1)) + g.Expect(podList.Items).To(HaveLen(1)) podName := podList.Items[0].Name - out, _, err := executeRemoteCommand(ctx, ns, podName, backstageContainer(*deploy.PodSpec()).Name, "cat /my/mount/path/appconfig11") + out, _, err := executeRemoteCommand(ctx, ns, podName, backstageContainer(*deploy.PodSpec()).Name, + "cat /my/mount/path/appconfig11") g.Expect(err).ShouldNot(HaveOccurred()) out = strings.ReplaceAll(out, "\r", "") g.Expect(out).To(Equal(conf)) - out, _, err = executeRemoteCommand(ctx, ns, podName, backstageContainer(*deploy.PodSpec()).Name, "echo $sec11") + out, _, err = executeRemoteCommand(ctx, ns, podName, backstageContainer(*deploy.PodSpec()).Name, + "echo $sec11") g.Expect(err).ShouldNot(HaveOccurred()) g.Expect("val11\r\n").To(Equal(out)) @@ -267,11 +270,13 @@ organization: // Pod replaced so have to re-ask podList := &corev1.PodList{} - err = k8sClient.List(ctx, podList, client.InNamespace(ns), client.MatchingLabels{model.BackstageAppLabel: utils.BackstageAppLabelValue(backstageName)}) + err = k8sClient.List(ctx, podList, client.InNamespace(ns), + client.MatchingLabels{model.BackstageAppLabel: utils.BackstageAppLabelValue(backstageName)}) g.Expect(err).ShouldNot(HaveOccurred()) podName := podList.Items[0].Name - out, _, err := executeRemoteCommand(ctx, ns, podName, backstageContainer(*deploy.PodSpec()).Name, "cat /my/mount/path/appconfig11") + out, _, err := executeRemoteCommand(ctx, ns, podName, backstageContainer(*deploy.PodSpec()).Name, + "cat /my/mount/path/appconfig11") g.Expect(err).ShouldNot(HaveOccurred()) // TODO nicer method to compare file content with added '\r' // NOTE: it does not work well on envtest, real controller needed @@ -280,7 +285,8 @@ organization: err = k8sClient.Get(ctx, types.NamespacedName{Namespace: ns, Name: secretEnv}, sec) g.Expect(err).ShouldNot(HaveOccurred()) - out2, _, err := executeRemoteCommand(ctx, ns, podName, backstageContainer(*deploy.PodSpec()).Name, "echo $sec11") + out2, _, err := executeRemoteCommand(ctx, ns, podName, backstageContainer(*deploy.PodSpec()).Name, + "echo $sec11") g.Expect(err).ShouldNot(HaveOccurred()) // NOTE: it does not work well on envtest, real controller needed g.Expect(fmt.Sprintf("%s%s", newEnv, "\r\n")).To(Equal(out2)) diff --git a/integration_tests/cr-config_test.go b/integration_tests/cr-config_test.go index af0d58ef0..794fd2e15 100644 --- a/integration_tests/cr-config_test.go +++ b/integration_tests/cr-config_test.go @@ -40,25 +40,40 @@ var _ = When("create backstage with CR configured", func() { It("creates Backstage with external configuration", func() { - appConfig1 := generateConfigMap(ctx, k8sClient, "app-config1", ns, map[string]string{"key11": "app:", "key12": "app:"}, nil, nil) - appConfig2 := generateConfigMap(ctx, k8sClient, "app-config2", ns, map[string]string{"key21": "app:", "key22": "app:"}, nil, nil) - appConfig3 := generateConfigMap(ctx, k8sClient, "app-config3.dot", ns, map[string]string{"key.31": "app31:"}, nil, nil) - - cmFile1 := generateConfigMap(ctx, k8sClient, "cm-file1", ns, map[string]string{"cm11": "11", "cm12": "12"}, nil, nil) - cmFile2 := generateConfigMap(ctx, k8sClient, "cm-file2", ns, map[string]string{"cm21": "21", "cm22": "22"}, nil, nil) - cmFile3 := generateConfigMap(ctx, k8sClient, "cm-file3.dot", ns, map[string]string{"cm.31": "31"}, nil, nil) - cmFileWithPath := generateConfigMap(ctx, k8sClient, "cm-file-withpath", ns, map[string]string{"cm": "withpath"}, nil, nil) - - secretFile1 := generateSecret(ctx, k8sClient, "secret-file1", ns, map[string]string{"sec11": "val11", "sec12": "val12"}, nil, nil) - secretFile2 := generateSecret(ctx, k8sClient, "secret-file2", ns, map[string]string{"sec21": "val21", "sec22": "val22"}, nil, nil) - secretFile3 := generateSecret(ctx, k8sClient, "secret-file3.dot", ns, map[string]string{"sec.31": "val31", "sec.32": "val22"}, nil, nil) - secretFileWithPath := generateSecret(ctx, k8sClient, "secret-file-withpath", ns, map[string]string{"secret": "withpath"}, nil, nil) - - cmEnv1 := generateConfigMap(ctx, k8sClient, "cm-env1", ns, map[string]string{"cm11": "11", "cm12": "12"}, nil, nil) - cmEnv2 := generateConfigMap(ctx, k8sClient, "cm-env2", ns, map[string]string{"cm21": "21", "cm22": "22"}, nil, nil) - - secretEnv1 := generateSecret(ctx, k8sClient, "secret-env1", ns, map[string]string{"sec11": "val11", "sec12": "val12"}, nil, nil) - _ = generateSecret(ctx, k8sClient, "secret-env2", ns, map[string]string{"sec21": "val21", "sec22": "val22"}, nil, nil) + appConfig1 := generateConfigMap(ctx, k8sClient, "app-config1", ns, + map[string]string{"key11": "app:", "key12": "app:"}, nil, nil) + appConfig2 := generateConfigMap(ctx, k8sClient, "app-config2", ns, + map[string]string{"key21": "app:", "key22": "app:"}, nil, nil) + appConfig3 := generateConfigMap(ctx, k8sClient, "app-config3.dot", ns, + map[string]string{"key.31": "app31:"}, nil, nil) + + cmFile1 := generateConfigMap(ctx, k8sClient, "cm-file1", ns, + map[string]string{"cm11": "11", "cm12": "12"}, nil, nil) + cmFile2 := generateConfigMap(ctx, k8sClient, "cm-file2", ns, + map[string]string{"cm21": "21", "cm22": "22"}, nil, nil) + cmFile3 := generateConfigMap(ctx, k8sClient, "cm-file3.dot", ns, + map[string]string{"cm.31": "31"}, nil, nil) + cmFileWithPath := generateConfigMap(ctx, k8sClient, "cm-file-withpath", ns, + map[string]string{"cm": "withpath"}, nil, nil) + + secretFile1 := generateSecret(ctx, k8sClient, "secret-file1", ns, + map[string]string{"sec11": "val11", "sec12": "val12"}, nil, nil) + secretFile2 := generateSecret(ctx, k8sClient, "secret-file2", ns, + map[string]string{"sec21": "val21", "sec22": "val22"}, nil, nil) + secretFile3 := generateSecret(ctx, k8sClient, "secret-file3.dot", ns, + map[string]string{"sec.31": "val31", "sec.32": "val22"}, nil, nil) + secretFileWithPath := generateSecret(ctx, k8sClient, "secret-file-withpath", ns, + map[string]string{"secret": "withpath"}, nil, nil) + + cmEnv1 := generateConfigMap(ctx, k8sClient, "cm-env1", ns, + map[string]string{"cm11": "11", "cm12": "12"}, nil, nil) + cmEnv2 := generateConfigMap(ctx, k8sClient, "cm-env2", ns, + map[string]string{"cm21": "21", "cm22": "22"}, nil, nil) + + secretEnv1 := generateSecret(ctx, k8sClient, "secret-env1", ns, + map[string]string{"sec11": "val11", "sec12": "val12"}, nil, nil) + _ = generateSecret(ctx, k8sClient, "secret-env2", ns, + map[string]string{"sec21": "val21", "sec22": "val22"}, nil, nil) patch, _ := yaml.YAMLToJSON([]byte(` spec: @@ -189,7 +204,8 @@ spec: It("generates label and annotation", func() { - appConfig := generateConfigMap(ctx, k8sClient, "app-config1", ns, map[string]string{"key11": "app:", "key12": "app:"}, nil, nil) + appConfig := generateConfigMap( + ctx, k8sClient, "app-config1", ns, map[string]string{"key11": "app:", "key12": "app:"}, nil, nil) bs := bsv1.BackstageSpec{ Application: &bsv1.Application{ diff --git a/integration_tests/db_test.go b/integration_tests/db_test.go index e72a4e858..0f6f28aa8 100644 --- a/integration_tests/db_test.go +++ b/integration_tests/db_test.go @@ -45,13 +45,19 @@ var _ = When("create backstage with CR configured", func() { Eventually(func(g Gomega) { By("creating Deployment with database.enableLocalDb=true by default") - err := k8sClient.Get(ctx, types.NamespacedName{Namespace: ns, Name: fmt.Sprintf("backstage-psql-%s", backstageName)}, &appsv1.StatefulSet{}) + ssName := types.NamespacedName{ + Namespace: ns, Name: fmt.Sprintf("backstage-psql-%s", backstageName)} + err := k8sClient.Get(ctx, ssName, &appsv1.StatefulSet{}) g.Expect(err).To(Not(HaveOccurred())) - err = k8sClient.Get(ctx, types.NamespacedName{Namespace: ns, Name: fmt.Sprintf("backstage-psql-%s", backstageName)}, &corev1.Service{}) + svcName := types.NamespacedName{ + Namespace: ns, Name: fmt.Sprintf("backstage-psql-%s", backstageName)} + err = k8sClient.Get(ctx, svcName, &corev1.Service{}) g.Expect(err).To(Not(HaveOccurred())) - err = k8sClient.Get(ctx, types.NamespacedName{Namespace: ns, Name: fmt.Sprintf("backstage-psql-secret-%s", backstageName)}, &corev1.Secret{}) + secretName := types.NamespacedName{ + Namespace: ns, Name: fmt.Sprintf("backstage-psql-secret-%s", backstageName)} + err = k8sClient.Get(ctx, secretName, &corev1.Secret{}) g.Expect(err).To(Not(HaveOccurred())) }, time.Minute, time.Second).Should(Succeed()) @@ -71,17 +77,23 @@ var _ = When("create backstage with CR configured", func() { Eventually(func(g Gomega) { By("deleting Local Db StatefulSet, Service and Secret") - err = k8sClient.Get(ctx, types.NamespacedName{Namespace: ns, Name: fmt.Sprintf("backstage-psql-%s", backstageName)}, &appsv1.StatefulSet{}) + ssName := types.NamespacedName{ + Namespace: ns, Name: fmt.Sprintf("backstage-psql-%s", backstageName)} + err = k8sClient.Get(ctx, ssName, &appsv1.StatefulSet{}) g.Expect(err).To(HaveOccurred()) - g.Expect(errors.IsNotFound(err)) + g.Expect(errors.IsNotFound(err)).To(BeTrue()) - err = k8sClient.Get(ctx, types.NamespacedName{Namespace: ns, Name: fmt.Sprintf("backstage-psql-%s", backstageName)}, &corev1.Service{}) + svcName := types.NamespacedName{ + Namespace: ns, Name: fmt.Sprintf("backstage-psql-%s", backstageName)} + err = k8sClient.Get(ctx, svcName, &corev1.Service{}) g.Expect(err).To(HaveOccurred()) - g.Expect(errors.IsNotFound(err)) + g.Expect(errors.IsNotFound(err)).To(BeTrue()) - err = k8sClient.Get(ctx, types.NamespacedName{Namespace: ns, Name: fmt.Sprintf("backstage-psql-secret-%s", backstageName)}, &corev1.Secret{}) + secretName := types.NamespacedName{ + Namespace: ns, Name: fmt.Sprintf("backstage-psql-secret-%s", backstageName)} + err = k8sClient.Get(ctx, secretName, &corev1.Secret{}) g.Expect(err).To(HaveOccurred()) - g.Expect(errors.IsNotFound(err)) + g.Expect(errors.IsNotFound(err)).To(BeTrue()) }, time.Minute, time.Second).Should(Succeed()) }) @@ -100,7 +112,7 @@ var _ = When("create backstage with CR configured", func() { types.NamespacedName{Namespace: ns, Name: fmt.Sprintf("backstage-psql-%s", backstageName)}, &appsv1.StatefulSet{}) g.Expect(err).Should(HaveOccurred()) - g.Expect(errors.IsNotFound(err)) + g.Expect(errors.IsNotFound(err)).To(BeTrue()) By("Checking if Deployment was successfully created in the reconciliation") _, err = backstageDeployment(ctx, k8sClient, ns, backstageName) @@ -121,7 +133,7 @@ var _ = When("create backstage with CR configured", func() { types.NamespacedName{Namespace: ns, Name: fmt.Sprintf("backstage-psql-%s", backstageName)}, &appsv1.StatefulSet{}) g.Expect(err).Should(HaveOccurred()) - g.Expect(errors.IsNotFound(err)) + g.Expect(errors.IsNotFound(err)).To(BeTrue()) By("Checking if Deployment was successfully created in the reconciliation") _, err = backstageDeployment(ctx, k8sClient, ns, backstageName) diff --git a/integration_tests/default-config_test.go b/integration_tests/default-config_test.go index 7cce05e85..a7c457e78 100644 --- a/integration_tests/default-config_test.go +++ b/integration_tests/default-config_test.go @@ -47,12 +47,14 @@ var _ = When("create default backstage", func() { secretName := fmt.Sprintf("backstage-psql-secret-%s", backstageName) err := k8sClient.Get(ctx, types.NamespacedName{Namespace: ns, Name: secretName}, secret) g.Expect(err).ShouldNot(HaveOccurred(), controllerMessage()) - g.Expect(len(secret.Data)).To(Equal(5)) + g.Expect(secret.Data).To(HaveLen(5)) g.Expect(secret.Data).To(HaveKeyWithValue("POSTGRES_USER", []uint8("postgres"))) By("creating a StatefulSet for the Database") ss := &appsv1.StatefulSet{} - err = k8sClient.Get(ctx, types.NamespacedName{Namespace: ns, Name: fmt.Sprintf("backstage-psql-%s", backstageName)}, ss) + ssName := types.NamespacedName{ + Namespace: ns, Name: fmt.Sprintf("backstage-psql-%s", backstageName)} + err = k8sClient.Get(ctx, ssName, ss) g.Expect(err).ShouldNot(HaveOccurred()) By("injecting default DB Secret as an env var for Db container") @@ -60,7 +62,9 @@ var _ = When("create default backstage", func() { g.Expect(ss.GetOwnerReferences()).To(HaveLen(1)) By("creating a Service for the Database") - err = k8sClient.Get(ctx, types.NamespacedName{Name: fmt.Sprintf("backstage-psql-%s", backstageName), Namespace: ns}, &corev1.Service{}) + svcName := types.NamespacedName{ + Name: fmt.Sprintf("backstage-psql-%s", backstageName), Namespace: ns} + err = k8sClient.Get(ctx, svcName, &corev1.Service{}) g.Expect(err).To(Not(HaveOccurred())) By("creating Deployment") @@ -75,7 +79,9 @@ var _ = When("create default backstage", func() { By("creating default app-config") appConfig := &corev1.ConfigMap{} - err = k8sClient.Get(ctx, types.NamespacedName{Namespace: ns, Name: model.AppConfigDefaultName(backstageName)}, appConfig) + appConfigName := types.NamespacedName{ + Namespace: ns, Name: model.AppConfigDefaultName(backstageName)} + err = k8sClient.Get(ctx, appConfigName, appConfig) g.Expect(err).ShouldNot(HaveOccurred()) By("mounting Volume defined in default app-config") diff --git a/integration_tests/matchers.go b/integration_tests/matchers.go index cfe56c50a..50febc90a 100644 --- a/integration_tests/matchers.go +++ b/integration_tests/matchers.go @@ -210,6 +210,7 @@ func HaveAppConfigBaseUrl(expected any) types.GomegaMatcher { } return true, nil }).WithTemplate( - "Expected the default app-config ConfigMap:\n{{.FormattedActual}}\n{{.To}} have the default baseUrls and CORS origin meet the expectations:\n{{format .Data 1}}", + "Expected the default app-config ConfigMap:\n{{.FormattedActual}}\n{{.To}} "+ + "have the default baseUrls and CORS origin meet the expectations:\n{{format .Data 1}}", expected) } diff --git a/integration_tests/plugin-deps_test.go b/integration_tests/plugin-deps_test.go index d2a168f31..c17159d4c 100644 --- a/integration_tests/plugin-deps_test.go +++ b/integration_tests/plugin-deps_test.go @@ -46,7 +46,7 @@ var _ = When("test plugin deps", func() { dynapluginCm := map[string]string{"dynamic-plugins.yaml": readTestYamlFile("raw-dynaplugins-with-deps.yaml")} - bsRaw := generateConfigMap(ctx, k8sClient, "dynaplugins", ns, dynapluginCm, nil, nil) + bsRaw := generateConfigMap(ctx, k8sClient, "dynaplugins", ns, dynapluginCm, map[string]string{}, map[string]string{}) createAndReconcileBackstage(ctx, ns, bsv1.BackstageSpec{ RawRuntimeConfig: &bsv1.RuntimeConfig{ diff --git a/integration_tests/pvcs_test.go b/integration_tests/pvcs_test.go index 3415f37ad..f047ab3ca 100644 --- a/integration_tests/pvcs_test.go +++ b/integration_tests/pvcs_test.go @@ -100,8 +100,8 @@ var _ = When("create backstage PVCs configured", func() { g.Expect(pv.Status.Phase).To(Equal(corev1.VolumeBound)) // check if added to deployment - //depl := &appsv1.Deployment{} - //err = k8sClient.Get(ctx, types.NamespacedName{Namespace: ns, Name: model.DeploymentName(backstageName)}, depl) + // depl := &appsv1.Deployment{} + // err = k8sClient.Get(ctx, types.NamespacedName{Namespace: ns, Name: model.DeploymentName(backstageName)}, depl) bpod, err := getBackstagePod(ctx, k8sClient, ns, backstageName) g.Expect(err).ShouldNot(HaveOccurred()) @@ -118,7 +118,8 @@ var _ = When("create backstage PVCs configured", func() { To(BeAddedAsVolumeToPodSpec(bpod.Spec)) // check if mounted directory is there - _, _, err = executeRemoteCommand(ctx, ns, bpod.Name, backstageContainer(bpod.Spec).Name, fmt.Sprintf("test -d %s", path)) + _, _, err = executeRemoteCommand( + ctx, ns, bpod.Name, backstageContainer(bpod.Spec).Name, fmt.Sprintf("test -d %s", path)) g.Expect(err).ShouldNot(HaveOccurred()) }, 5*time.Minute, time.Second).Should(Succeed(), controllerMessage()) @@ -130,7 +131,7 @@ var _ = When("create backstage PVCs configured", func() { Skip("Skipped for not real cluster") } - //Precreate StorageClass + // Precreate StorageClass sc := storagev1.StorageClass{ ObjectMeta: metav1.ObjectMeta{ Name: scName, @@ -165,7 +166,7 @@ var _ = When("create backstage PVCs configured", func() { err = k8sClient.Create(ctx, &pv) Expect(err).ShouldNot(HaveOccurred()) - //Add PVC to Backstage CR configuration + // Add PVC to Backstage CR configuration pvcCm := generateConfigMap(ctx, k8sClient, "pvc-conf", ns, map[string]string{"pvcs.yaml": readTestYamlFile("raw-pvcs2.yaml")}, nil, nil) @@ -199,7 +200,8 @@ var _ = When("create backstage PVCs configured", func() { g.Expect(err).ShouldNot(HaveOccurred()) // check if mounted directory is there - _, _, err = executeRemoteCommand(ctx, ns, pod.Name, backstageContainer(*depl.PodSpec()).Name, fmt.Sprintf("test -d %s", path)) + _, _, err = executeRemoteCommand( + ctx, ns, pod.Name, backstageContainer(*depl.PodSpec()).Name, fmt.Sprintf("test -d %s", path)) g.Expect(err).ShouldNot(HaveOccurred()) }, 5*time.Minute, time.Second).Should(Succeed(), controllerMessage()) @@ -300,9 +302,12 @@ var _ = When("create backstage PVCs configured", func() { g.Expect(err).ShouldNot(HaveOccurred()) // check if mounted directory is there - _, _, err = executeRemoteCommand(ctx, ns, pod.Name, backstageContainer(*depl.PodSpec()).Name, fmt.Sprintf("test -d %s", path)) + contName := backstageContainer(*depl.PodSpec()).Name + _, _, err = executeRemoteCommand( + ctx, ns, pod.Name, contName, fmt.Sprintf("test -d %s", path)) g.Expect(err).ShouldNot(HaveOccurred()) - _, _, err = executeRemoteCommand(ctx, ns, pod.Name, backstageContainer(*depl.PodSpec()).Name, fmt.Sprintf("test -d %s", path2)) + _, _, err = executeRemoteCommand( + ctx, ns, pod.Name, contName, fmt.Sprintf("test -d %s", path2)) g.Expect(err).ShouldNot(HaveOccurred()) }, 5*time.Minute, time.Second).Should(Succeed(), controllerMessage()) diff --git a/integration_tests/rhdh-config_test.go b/integration_tests/rhdh-config_test.go index 7e56a3e68..f2575eedf 100644 --- a/integration_tests/rhdh-config_test.go +++ b/integration_tests/rhdh-config_test.go @@ -45,11 +45,15 @@ var _ = When("create default rhdh", func() { g.Expect(err).ShouldNot(HaveOccurred(), controllerMessage()) dpCm := &corev1.ConfigMap{} - err = k8sClient.Get(ctx, types.NamespacedName{Namespace: ns, Name: model.DynamicPluginsDefaultName(backstageName)}, dpCm) + dpCmName := types.NamespacedName{ + Namespace: ns, Name: model.DynamicPluginsDefaultName(backstageName)} + err = k8sClient.Get(ctx, dpCmName, dpCm) g.Expect(err).ShouldNot(HaveOccurred()) var appConfigCm corev1.ConfigMap - err = k8sClient.Get(ctx, types.NamespacedName{Namespace: ns, Name: model.AppConfigDefaultName(backstageName)}, &appConfigCm) + appConfigCmName := types.NamespacedName{ + Namespace: ns, Name: model.AppConfigDefaultName(backstageName)} + err = k8sClient.Get(ctx, appConfigCmName, &appConfigCm) g.Expect(err).ShouldNot(HaveOccurred()) g.Expect(deploy.PodSpec().InitContainers).To(HaveLen(1)) @@ -194,7 +198,7 @@ var _ = When("create default rhdh", func() { Eventually(func(g Gomega) { By("getting the Pod ") deploy, err := backstageDeployment(ctx, k8sClient, ns, backstageName) - //bpod, err := getBackstagePod(ctx, k8sClient, ns, backstageName) + // bpod, err := getBackstagePod(ctx, k8sClient, ns, backstageName) g.Expect(err).To(Not(HaveOccurred())) var bsvolume *corev1.Volume @@ -229,7 +233,9 @@ var _ = When("create default rhdh", func() { ctx := context.Background() ns := createNamespace(ctx) - npmrcSecret := generateSecret(ctx, k8sClient, "my-dynamic-plugins-npmrc", ns, map[string]string{".npmrc": "new-npmrc"}, nil, nil) + npmrcSecret := generateSecret( + ctx, k8sClient, "my-dynamic-plugins-npmrc", ns, + map[string]string{".npmrc": "new-npmrc"}, map[string]string{}, map[string]string{}) bsSpec := bsv1.BackstageSpec{ Application: &bsv1.Application{ @@ -254,7 +260,7 @@ var _ = When("create default rhdh", func() { deploy, err := backstageDeployment(ctx, k8sClient, ns, backstageName) g.Expect(err).To(Not(HaveOccurred())) - g.Expect(len(deploy.PodSpec().InitContainers)).To(Equal(1)) + g.Expect(deploy.PodSpec().InitContainers).To(HaveLen(1)) initCont := deploy.PodSpec().InitContainers[0] g.Expect(initCont.Name).To(Equal("install-dynamic-plugins")) found := 0 diff --git a/integration_tests/route_test.go b/integration_tests/route_test.go index 3b641925f..28806eac0 100644 --- a/integration_tests/route_test.go +++ b/integration_tests/route_test.go @@ -54,8 +54,8 @@ var _ = When("create default backstage", func() { { name: "route with subdomain", desiredRoute: bsv1.Route{ - //Host: "localhost", - //Enabled: ptr.To(true), + // Host: "localhost", + // Enabled: ptr.To(true), Subdomain: "test", }, expectedBaseUrlMatcher: func() any { @@ -73,7 +73,6 @@ var _ = When("create default backstage", func() { }, }, } { - tt := tt It("creates Backstage object (on Openshift) - "+tt.name, func() { if !currentPlatform.IsOpenshift() { @@ -109,7 +108,9 @@ var _ = When("create default backstage", func() { By("updating the baseUrls in the default app-config CM, per the desired route settings (RHIDP-6192)") var appConfigCm corev1.ConfigMap - err = k8sClient.Get(ctx, types.NamespacedName{Namespace: ns, Name: model.AppConfigDefaultName(backstageName)}, &appConfigCm) + appConfigCmName := types.NamespacedName{ + Namespace: ns, Name: model.AppConfigDefaultName(backstageName)} + err = k8sClient.Get(ctx, appConfigCmName, &appConfigCm) g.Expect(err).ShouldNot(HaveOccurred()) g.Expect(appConfigCm).To(HaveAppConfigBaseUrl(tt.expectedBaseUrlMatcher())) }, 5*time.Minute, time.Second).Should(Succeed()) diff --git a/integration_tests/suite_test.go b/integration_tests/suite_test.go index cffdcda99..d125662f0 100644 --- a/integration_tests/suite_test.go +++ b/integration_tests/suite_test.go @@ -45,7 +45,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/envtest" logf "sigs.k8s.io/controller-runtime/pkg/log" - //+kubebuilder:scaffold:imports + // +kubebuilder:scaffold:imports ) // These tests use Ginkgo (BDD-style Go testing framework). Refer to @@ -64,7 +64,7 @@ type TestBackstageReconciler struct { func init() { rand.Seed(time.Now().UnixNano()) - //testOnExistingCluster, _ = strconv.ParseBool(os.Getenv("TEST_ON_EXISTING_CLUSTER")) + // testOnExistingCluster, _ = strconv.ParseBool(os.Getenv("TEST_ON_EXISTING_CLUSTER")) } func TestAPIs(t *testing.T) { @@ -74,13 +74,17 @@ func TestAPIs(t *testing.T) { } var _ = BeforeSuite(func() { - //logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) + // logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) logf.SetLogger(zap.New(zap.UseDevMode(true))) By("bootstrapping test environment") testEnv = &envtest.Environment{ - CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases"), filepath.Join("..", "config", "crd", "external")}, + BinaryAssetsDirectory: getFirstFoundEnvTestBinaryDir(), + CRDDirectoryPaths: []string{ + filepath.Join("..", "config", "crd", "bases"), + filepath.Join("..", "config", "crd", "external"), + }, ErrorIfCRDPathMissing: true, } @@ -121,7 +125,7 @@ var _ = BeforeSuite(func() { Expect(err).NotTo(HaveOccurred()) utilruntime.Must(openshift.Install(scheme.Scheme)) - //+kubebuilder:scaffold:scheme + // +kubebuilder:scaffold:scheme k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) Expect(err).NotTo(HaveOccurred()) @@ -135,6 +139,21 @@ var _ = AfterSuite(func() { Expect(err).NotTo(HaveOccurred()) }) +func getFirstFoundEnvTestBinaryDir() string { + basePath := filepath.Join("..", "bin", "k8s") + entries, err := os.ReadDir(basePath) + if err != nil { + logf.Log.Error(err, "Failed to read directory", "path", basePath) + return "" + } + for _, entry := range entries { + if entry.IsDir() { + return filepath.Join(basePath, entry.Name()) + } + } + return "" +} + var letterRunes = []rune("abcdefghijklmnopqrstuvwxyz0123456789") func randString(n int) string { diff --git a/integration_tests/utils.go b/integration_tests/utils.go index e06955c04..dc05f0e91 100644 --- a/integration_tests/utils.go +++ b/integration_tests/utils.go @@ -7,10 +7,11 @@ import ( "os" "path/filepath" + "k8s.io/apimachinery/pkg/util/yaml" + "github.com/redhat-developer/rhdh-operator/internal/controller" "github.com/redhat-developer/rhdh-operator/pkg/model" "github.com/redhat-developer/rhdh-operator/pkg/utils" - "k8s.io/apimachinery/pkg/util/yaml" "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/scheme" @@ -24,7 +25,10 @@ import ( . "github.com/onsi/gomega" ) -func generateConfigMap(ctx context.Context, k8sClient client.Client, name string, namespace string, data, labels map[string]string, annotations map[string]string) string { +func generateConfigMap( + ctx context.Context, k8sClient client.Client, name, namespace string, + data, labels, annotations map[string]string, +) string { Expect(k8sClient.Create(ctx, &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: name, @@ -38,7 +42,10 @@ func generateConfigMap(ctx context.Context, k8sClient client.Client, name string return name } -func generateSecret(ctx context.Context, k8sClient client.Client, name, namespace string, data, labels, annotations map[string]string) string { +func generateSecret( + ctx context.Context, k8sClient client.Client, name, namespace string, + data, labels, annotations map[string]string, +) string { Expect(k8sClient.Create(ctx, &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: name, @@ -59,7 +66,9 @@ func readTestYamlFile(name string) string { return string(b) } -func executeRemoteCommand(ctx context.Context, podNamespace, podName, container, command string) (string, string, error) { +func executeRemoteCommand( + ctx context.Context, podNamespace, podName, container, command string, +) (string, string, error) { kubeCfg := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( clientcmd.NewDefaultClientConfigLoadingRules(), &clientcmd.ConfigOverrides{}, @@ -132,7 +141,8 @@ func backstageContainer(pod corev1.PodSpec) corev1.Container { func getBackstagePod(ctx context.Context, k8sClient client.Client, ns, backstageName string) (*corev1.Pod, error) { podList := &corev1.PodList{} - err := k8sClient.List(ctx, podList, client.InNamespace(ns), client.MatchingLabels{model.BackstageAppLabel: utils.BackstageAppLabelValue(backstageName)}) + labels := client.MatchingLabels{model.BackstageAppLabel: utils.BackstageAppLabelValue(backstageName)} + err := k8sClient.List(ctx, podList, client.InNamespace(ns), labels) if err != nil { return nil, err } @@ -143,6 +153,8 @@ func getBackstagePod(ctx context.Context, k8sClient client.Client, ns, backstage return &podList.Items[0], nil } -func backstageDeployment(ctx context.Context, k8sClient client.Client, namespace, backstageName string) (model.Deployable, error) { +func backstageDeployment( + ctx context.Context, k8sClient client.Client, namespace, backstageName string, +) (model.Deployable, error) { return controller.FindDeployment(ctx, k8sClient, namespace, backstageName) } diff --git a/internal/controller/backstage_controller.go b/internal/controller/backstage_controller.go index 7206335fa..8b1ad4efa 100644 --- a/internal/controller/backstage_controller.go +++ b/internal/controller/backstage_controller.go @@ -52,16 +52,16 @@ type BackstageReconciler struct { Platform platform.Platform } -//+kubebuilder:rbac:groups=rhdh.redhat.com,resources=backstages,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=rhdh.redhat.com,resources=backstages/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=rhdh.redhat.com,resources=backstages/finalizers,verbs=update -//+kubebuilder:rbac:groups="",resources=configmaps;secrets;services;persistentvolumeclaims,verbs=get;watch;create;update;list;delete;patch -//+kubebuilder:rbac:groups="",resources=persistentvolumes,verbs=get;list;watch -//+kubebuilder:rbac:groups="apps",resources=deployments;statefulsets,verbs=get;watch;create;update;list;delete;patch -//+kubebuilder:rbac:groups="route.openshift.io",resources=routes;routes/custom-host,verbs=get;watch;create;update;list;delete;patch -//+kubebuilder:rbac:groups="config.openshift.io",resources=ingresses,verbs=get -//+kubebuilder:rbac:groups=monitoring.coreos.com,resources=servicemonitors,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=batch,resources=jobs,verbs=create;update;patch;delete +// +kubebuilder:rbac:groups=rhdh.redhat.com,resources=backstages,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=rhdh.redhat.com,resources=backstages/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=rhdh.redhat.com,resources=backstages/finalizers,verbs=update +// +kubebuilder:rbac:groups="",resources=configmaps;secrets;services;persistentvolumeclaims,verbs=get;watch;create;update;list;delete;patch +// +kubebuilder:rbac:groups="",resources=persistentvolumes,verbs=get;list;watch +// +kubebuilder:rbac:groups="apps",resources=deployments;statefulsets,verbs=get;watch;create;update;list;delete;patch +// +kubebuilder:rbac:groups="route.openshift.io",resources=routes;routes/custom-host,verbs=get;watch;create;update;list;delete;patch +// +kubebuilder:rbac:groups="config.openshift.io",resources=ingresses,verbs=get +// +kubebuilder:rbac:groups=monitoring.coreos.com,resources=servicemonitors,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=batch,resources=jobs,verbs=create;update;patch;delete // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. @@ -106,7 +106,7 @@ func (r *BackstageReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( return ctrl.Result{}, errorAndStatus(&backstage, "failed to apply ServiceMonitor", err) } - // This creates array of model objects to be reconsiled + // This creates array of model objects to be reconsider bsModel, err := model.InitObjects(ctx, backstage, externalConfig, r.Platform, r.Scheme) if err != nil { return ctrl.Result{}, errorAndStatus(&backstage, "failed to initialize backstage model", err) @@ -203,7 +203,7 @@ func (r *BackstageReconciler) cleanObjects(ctx context.Context, backstage bs.Bac } } - //// check if route disabled, respective objects have to deleted/unowned + // // check if route disabled, respective objects have to deleted/unowned if r.Platform.IsOpenshift() && !backstage.Spec.IsRouteEnabled() { if err := r.tryToDelete(ctx, &openshift.Route{}, model.RouteName(backstage.Name), backstage.Namespace); err != nil { return fmt.Errorf("%s %w", failedToCleanup, err) @@ -235,7 +235,8 @@ func (r *BackstageReconciler) tryToDelete(ctx context.Context, obj client.Object func (r *BackstageReconciler) SetupWithManager(mgr ctrl.Manager) error { b := ctrl.NewControllerManagedBy(mgr). - For(&bs.Backstage{}) + For(&bs.Backstage{}). + Named("backstage") if err := r.addWatchers(b); err != nil { return err diff --git a/internal/controller/backstage_status.go b/internal/controller/backstage_status.go index d3348b077..c01722b28 100644 --- a/internal/controller/backstage_status.go +++ b/internal/controller/backstage_status.go @@ -4,14 +4,15 @@ import ( "context" "fmt" - bs "github.com/redhat-developer/rhdh-operator/api/v1alpha5" - "github.com/redhat-developer/rhdh-operator/pkg/model" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" + + bs "github.com/redhat-developer/rhdh-operator/api/v1alpha5" + "github.com/redhat-developer/rhdh-operator/pkg/model" ) func (r *BackstageReconciler) setDeploymentStatus(ctx context.Context, backstage *bs.Backstage, backstageModel model.BackstageModel) { @@ -40,6 +41,7 @@ func (r *BackstageReconciler) setDeploymentStatus(ctx context.Context, backstage setStatusCondition(backstage, bs.BackstageConditionTypeDeployed, metav1.ConditionFalse, state, msg) } +// nolint:unparam // The condType parameter might be called with different values in the future func setStatusCondition(backstage *bs.Backstage, condType bs.BackstageConditionType, status metav1.ConditionStatus, reason bs.BackstageConditionReason, msg string) { meta.SetStatusCondition(&backstage.Status.Conditions, metav1.Condition{ Type: string(condType), @@ -84,7 +86,7 @@ func statefulSetState(deploy *appsv1.StatefulSet) (state bs.BackstageConditionRe desired = *deploy.Spec.Replicas } - //if deploy.Status.ReadyReplicas == desired { + // if deploy.Status.ReadyReplicas == desired { if deploy.Status.ReadyReplicas == desired && deploy.Status.CurrentReplicas == deploy.Status.UpdatedReplicas { return bs.BackstageConditionReasonDeployed, "" } diff --git a/internal/controller/deployable_finder.go b/internal/controller/deployable_finder.go index 04cfc08b4..02fe970a7 100644 --- a/internal/controller/deployable_finder.go +++ b/internal/controller/deployable_finder.go @@ -13,14 +13,14 @@ import ( func FindDeployment(ctx context.Context, k8sClient client.Client, namespace, backstageName string) (model.Deployable, error) { nn := client.ObjectKey{Namespace: namespace, Name: model.DeploymentName(backstageName)} deploy := &appsv1.Deployment{} - //lg := log.FromContext(ctx) - //lg.V(1).Info("Looking for Deployment", "namespace", namespace, "name", nn.Name) + // lg := log.FromContext(ctx) + // lg.V(1).Info("Looking for Deployment", "namespace", namespace, "name", nn.Name) err := k8sClient.Get(ctx, nn, deploy) if err == nil { return model.CreateDeployable(deploy) } else if errors.IsNotFound(err) { ss := &appsv1.StatefulSet{} - //lg.V(1).Info("Deployment not found, looking for StatefulSet", "namespace", namespace, "name", nn.Name) + // lg.V(1).Info("Deployment not found, looking for StatefulSet", "namespace", namespace, "name", nn.Name) err = k8sClient.Get(ctx, nn, ss) if err == nil { return model.CreateDeployable(ss) diff --git a/internal/controller/mock_client.go b/internal/controller/mock_client.go index f4acc45be..8f7f43b85 100644 --- a/internal/controller/mock_client.go +++ b/internal/controller/mock_client.go @@ -36,7 +36,7 @@ type NameKind struct { func kind(obj runtime.Object) string { str := reflect.TypeOf(obj).String() return str[strings.LastIndex(str, ".")+1:] - //return reflect.TypeOf(obj).String() + // return reflect.TypeOf(obj).String() } func (m MockClient) Get(_ context.Context, key client.ObjectKey, obj client.Object, _ ...client.GetOption) error { diff --git a/internal/controller/monitor_test.go b/internal/controller/monitor_test.go index 50161493c..d14c55d7d 100644 --- a/internal/controller/monitor_test.go +++ b/internal/controller/monitor_test.go @@ -30,11 +30,11 @@ func setupMonitorTestReconciler() BackstageReconciler { } } -func createTestBackstage(name, namespace string, monitoringEnabled bool) *bs.Backstage { +func createTestBackstage(monitoringEnabled bool) *bs.Backstage { backstage := &bs.Backstage{ ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, + Name: "test-bs", + Namespace: "test-ns", }, Spec: bs.BackstageSpec{ Monitoring: bs.Monitoring{ @@ -50,7 +50,7 @@ func TestApplyServiceMonitor_MonitoringDisabled(t *testing.T) { ctx := context.TODO() r := setupMonitorTestReconciler() - backstage := createTestBackstage("test-bs", "test-ns", false) + backstage := createTestBackstage(false) // Create the backstage object err := r.Create(ctx, backstage) @@ -97,7 +97,7 @@ func TestApplyServiceMonitor_MonitoringEnabled_NoCRD(t *testing.T) { Scheme: scheme, } - backstage := createTestBackstage("test-bs", "test-ns", true) + backstage := createTestBackstage(true) // Create the backstage object err := r.Create(ctx, backstage) @@ -114,7 +114,7 @@ func TestApplyServiceMonitor_MonitoringEnabled_WithCRD(t *testing.T) { ctx := context.TODO() r := setupMonitorTestReconciler() - backstage := createTestBackstage("test-bs", "test-ns", true) + backstage := createTestBackstage(true) // Create the backstage object err := r.Create(ctx, backstage) @@ -169,7 +169,7 @@ func TestApplyServiceMonitor_Update_ExistingServiceMonitor(t *testing.T) { ctx := context.TODO() r := setupMonitorTestReconciler() - backstage := createTestBackstage("test-bs", "test-ns", true) + backstage := createTestBackstage(true) // Create the backstage object err := r.Create(ctx, backstage) diff --git a/internal/controller/spec_preprocessor.go b/internal/controller/spec_preprocessor.go index c0ac91a32..215263ee5 100644 --- a/internal/controller/spec_preprocessor.go +++ b/internal/controller/spec_preprocessor.go @@ -34,8 +34,9 @@ import ( // Add additional details to the Backstage Spec helping in making Backstage RuntimeObjects Model // Validates Backstage Spec and fails fast if something not correct +// nolint:gocyclo // TODO: refactor this function to reduce cyclomatic complexity func (r *BackstageReconciler) preprocessSpec(ctx context.Context, backstage bsv1.Backstage) (model.ExternalConfig, error) { - //lg := log.FromContext(ctx) + // lg := log.FromContext(ctx) bsSpec := backstage.Spec ns := backstage.Namespace @@ -129,7 +130,7 @@ func (r *BackstageReconciler) preprocessSpec(ctx context.Context, backstage bsv1 if hashingData, err = r.addExtConfig(ctx, secret, backstage.Name, ee.Name, ns, true, hashingData); err != nil { return result, err } - //result.ExtraEnvSecrets[secret.Name] = *secret + // result.ExtraEnvSecrets[secret.Name] = *secret result.ExtraEnvSecretKeys[ee.Name] = model.NewDataObjectKeys(secret.StringData, secret.Data) } } @@ -141,7 +142,7 @@ func (r *BackstageReconciler) preprocessSpec(ctx context.Context, backstage bsv1 if err := r.checkExternalObject(ctx, pvc, ep.Name, ns); err != nil { return result, err } - //result.ExtraPvcs[pvc.Name] = *pvc + // result.ExtraPvcs[pvc.Name] = *pvc result.ExtraPvcKeys = append(result.ExtraPvcKeys, pvc.Name) } } diff --git a/internal/controller/watchers.go b/internal/controller/watchers.go index 5cce7873b..c0e0d45bc 100644 --- a/internal/controller/watchers.go +++ b/internal/controller/watchers.go @@ -59,7 +59,7 @@ func (r *BackstageReconciler) addWatchers(b *builder.Builder) error { builder.WithPredicates(pred, predicate.Funcs{ DeleteFunc: func(e event.DeleteEvent) bool { return true }, UpdateFunc: func(e event.UpdateEvent) bool { return true }, - //CreateFunc: func(e event.CreateEvent) bool { return true }, + // CreateFunc: func(e event.CreateEvent) bool { return true }, }), ). WatchesMetadata( @@ -70,7 +70,7 @@ func (r *BackstageReconciler) addWatchers(b *builder.Builder) error { builder.WithPredicates(pred, predicate.Funcs{ DeleteFunc: func(e event.DeleteEvent) bool { return true }, UpdateFunc: func(e event.UpdateEvent) bool { return true }, - //CreateFunc: func(e event.CreateEvent) bool { return true }, + // CreateFunc: func(e event.CreateEvent) bool { return true }, })) } @@ -133,7 +133,7 @@ func (r *BackstageReconciler) requestByExtConfigLabel(ctx context.Context, objec backstageName := object.GetAnnotations()[model.BackstageNameAnnotation] if backstageName == "" { - //lg.V(1).Info(fmt.Sprintf("warning: %s annotation is not defined for %s, Backstage instances will not be reconciled in this loop", model.BackstageNameAnnotation, object.GetName())) + // lg.V(1).Info(fmt.Sprintf("warning: %s annotation is not defined for %s, Backstage instances will not be reconciled in this loop", model.BackstageNameAnnotation, object.GetName())) return []reconcile.Request{} } @@ -150,11 +150,11 @@ func (r *BackstageReconciler) requestByExtConfigLabel(ctx context.Context, objec return []reconcile.Request{} } - //ec, err := r.preprocessSpec(ctx, backstage) - //if err != nil { - // lg.Error(err, "request by label failed, preprocess Backstage ") - // return []reconcile.Request{} - //} + // ec, err := r.preprocessSpec(ctx, backstage) + // if err != nil { + // lg.Error(err, "request by label failed, preprocess Backstage ") + // return []reconcile.Request{} + // } deploy, err := FindDeployment(ctx, r.Client, object.GetNamespace(), backstage.Name) if err != nil { diff --git a/pkg/model/appconfig.go b/pkg/model/appconfig.go index dfe54d740..d7abf8234 100644 --- a/pkg/model/appconfig.go +++ b/pkg/model/appconfig.go @@ -34,6 +34,7 @@ func AppConfigDefaultName(backstageName string) string { return utils.GenerateRuntimeObjectName(backstageName, "backstage-appconfig") } +// nolint:unparam // this is required by the interface func (b *AppConfig) addExternalConfig(spec bsv1.BackstageSpec) error { if spec.Application == nil || spec.Application.AppConfig == nil || spec.Application.AppConfig.ConfigMaps == nil { @@ -41,7 +42,8 @@ func (b *AppConfig) addExternalConfig(spec bsv1.BackstageSpec) error { } for _, specCm := range spec.Application.AppConfig.ConfigMaps { - mp, wSubpath := b.model.backstageDeployment.mountPath(specCm.MountPath, specCm.Key, spec.Application.AppConfig.MountPath) + mp, wSubpath := b.model.backstageDeployment.mountPath( + specCm.MountPath, specCm.Key, spec.Application.AppConfig.MountPath) updatePodWithAppConfig(b.model.backstageDeployment, specCm.Name, mp, specCm.Key, wSubpath, b.model.ExternalConfig.AppConfigKeys[specCm.Name]) } @@ -62,9 +64,9 @@ func (b *AppConfig) setObject(obj runtime.Object) { } // implementation of RuntimeObject interface -//func (b *AppConfig) EmptyObject() client.Object { -// return &corev1.ConfigMap{} -//} +// func (b *AppConfig) EmptyObject() client.Object { +// return &corev1.ConfigMap{} +// } // implementation of RuntimeObject interface func (b *AppConfig) addToModel(model *BackstageModel, _ bsv1.Backstage) (bool, error) { @@ -90,10 +92,11 @@ func (b *AppConfig) setMetaInfo(backstage bsv1.Backstage, scheme *runtime.Scheme } // updatePodWithAppConfig contributes to Volumes, container.VolumeMounts and container.Args -func updatePodWithAppConfig(bsd *BackstageDeployment, cmName, mountPath, key string, withSubPath bool, cmData []string) { +func updatePodWithAppConfig( + bsd *BackstageDeployment, cmName, mountPath, key string, withSubPath bool, cmData []string, +) { - _ = bsd.mountFilesFrom(containersFilter{}, ConfigMapObjectKind, - cmName, mountPath, key, withSubPath, cmData) + _ = bsd.mountFilesFrom(containersFilter{}, ConfigMapObjectKind, cmName, mountPath, key, withSubPath, cmData) container := bsd.container() for _, file := range cmData { diff --git a/pkg/model/appconfig_test.go b/pkg/model/appconfig_test.go index ccd3cbad4..0f7048fdd 100644 --- a/pkg/model/appconfig_test.go +++ b/pkg/model/appconfig_test.go @@ -63,7 +63,7 @@ func TestDefaultAppConfig(t *testing.T) { bs := *appConfigTestBackstage.DeepCopy() - testObj := createBackstageTest(bs).withDefaultConfig(true).addToDefaultConfig("app-config.yaml", "raw-app-config.yaml") + testObj := createBackstageTest(bs).withDefaultConfig().addToDefaultConfig("app-config.yaml", "raw-app-config.yaml") model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Kubernetes, testObj.scheme) @@ -75,7 +75,8 @@ func TestDefaultAppConfig(t *testing.T) { assert.Equal(t, 1, len(deployment.container().VolumeMounts)) assert.Contains(t, deployment.container().VolumeMounts[0].MountPath, deployment.defaultMountPath()) - assert.Equal(t, utils.GenerateVolumeNameFromCmOrSecret(AppConfigDefaultName(bs.Name)), deployment.container().VolumeMounts[0].Name) + assert.Equal(t, utils.GenerateVolumeNameFromCmOrSecret(AppConfigDefaultName(bs.Name)), + deployment.container().VolumeMounts[0].Name) assert.Equal(t, 2, len(deployment.container().Args)) assert.Equal(t, 1, len(deployment.podSpec().Volumes)) @@ -92,7 +93,7 @@ func TestSpecifiedAppConfig(t *testing.T) { bs.Spec.Application.AppConfig.ConfigMaps = append(bs.Spec.Application.AppConfig.ConfigMaps, bsv1.FileObjectRef{Name: appConfigTestCm3.Name, Key: "conf31.yaml"}) - testObj := createBackstageTest(bs).withDefaultConfig(true) + testObj := createBackstageTest(bs).withDefaultConfig() testObj.externalConfig.AppConfigKeys = map[string][]string{appConfigTestCm.Name: maps.Keys(appConfigTestCm.Data), appConfigTestCm2.Name: maps.Keys(appConfigTestCm2.Data), appConfigTestCm3.Name: maps.Keys(appConfigTestCm3.Data)} @@ -127,7 +128,7 @@ func TestDefaultAndSpecifiedAppConfig(t *testing.T) { cms := &bs.Spec.Application.AppConfig.ConfigMaps *cms = append(*cms, bsv1.FileObjectRef{Name: appConfigTestCm.Name}) - testObj := createBackstageTest(bs).withDefaultConfig(true).addToDefaultConfig("app-config.yaml", "raw-app-config.yaml") + testObj := createBackstageTest(bs).withDefaultConfig().addToDefaultConfig("app-config.yaml", "raw-app-config.yaml") testObj.externalConfig.AppConfigKeys = map[string][]string{appConfigTestCm.Name: maps.Keys(appConfigTestCm.Data)} diff --git a/pkg/model/configmapenvs.go b/pkg/model/configmapenvs.go index cda56c080..19013d2ee 100644 --- a/pkg/model/configmapenvs.go +++ b/pkg/model/configmapenvs.go @@ -32,7 +32,8 @@ func (p *ConfigMapEnvs) addExternalConfig(spec bsv1.BackstageSpec) error { } for _, specCm := range spec.Application.ExtraEnvs.ConfigMaps { - err := p.model.backstageDeployment.addEnvVarsFrom(containersFilter{names: specCm.Containers}, ConfigMapObjectKind, specCm.Name, specCm.Key) + err := p.model.backstageDeployment.addEnvVarsFrom( + containersFilter{names: specCm.Containers}, ConfigMapObjectKind, specCm.Name, specCm.Key) if err != nil { return fmt.Errorf("failed to add env vars on config map %s: %w", specCm.Name, err) } @@ -53,9 +54,9 @@ func (p *ConfigMapEnvs) setObject(obj runtime.Object) { } // EmptyObject implements RuntimeObject interface -//func (p *ConfigMapEnvs) EmptyObject() client.Object { -// return &corev1.ConfigMap{} -//} +// func (p *ConfigMapEnvs) EmptyObject() client.Object { +// return &corev1.ConfigMap{} +// } // implementation of RuntimeObject interface func (p *ConfigMapEnvs) addToModel(model *BackstageModel, backstage bsv1.Backstage) (bool, error) { @@ -70,8 +71,9 @@ func (p *ConfigMapEnvs) addToModel(model *BackstageModel, backstage bsv1.Backsta // implementation of RuntimeObject interface func (p *ConfigMapEnvs) updateAndValidate(backstage bsv1.Backstage) error { if p.ConfigMap != nil { - err := p.model.backstageDeployment.addEnvVarsFrom(containersFilter{annotation: p.ConfigMap.GetAnnotations()[ContainersAnnotation]}, ConfigMapObjectKind, - p.ConfigMap.Name, "") + annotation := p.ConfigMap.GetAnnotations()[ContainersAnnotation] + err := p.model.backstageDeployment.addEnvVarsFrom( + containersFilter{annotation: annotation}, ConfigMapObjectKind, p.ConfigMap.Name, "") if err != nil { return fmt.Errorf("failed to add env vars on configmap %s: %w", p.ConfigMap.Name, err) } diff --git a/pkg/model/configmapenvs_test.go b/pkg/model/configmapenvs_test.go index 5f8eb55a1..7282ef929 100644 --- a/pkg/model/configmapenvs_test.go +++ b/pkg/model/configmapenvs_test.go @@ -29,7 +29,7 @@ func TestDefaultConfigMapEnvFrom(t *testing.T) { }, } - testObj := createBackstageTest(bs).withDefaultConfig(true).addToDefaultConfig("configmap-envs.yaml", "raw-cm-envs.yaml") + testObj := createBackstageTest(bs).withDefaultConfig().addToDefaultConfig("configmap-envs.yaml", "raw-cm-envs.yaml") model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) @@ -66,7 +66,7 @@ func TestSpecifiedConfigMapEnvs(t *testing.T) { bs.Spec.Application.ExtraEnvs.ConfigMaps = append(bs.Spec.Application.ExtraEnvs.ConfigMaps, bsv1.EnvObjectRef{Name: "mapName", Key: "ENV1"}) - testObj := createBackstageTest(bs).withDefaultConfig(true) + testObj := createBackstageTest(bs).withDefaultConfig() testObj.externalConfig.ExtraEnvConfigMapKeys = map[string]DataObjectKeys{} testObj.externalConfig.ExtraEnvConfigMapKeys["mapName"] = NewDataObjectKeys(map[string]string{"mapName": "ENV1"}, nil) @@ -106,7 +106,7 @@ func TestDefaultAndSpecifiedConfigMapEnvFrom(t *testing.T) { }, } - testObj := createBackstageTest(bs).withDefaultConfig(true).addToDefaultConfig("configmap-envs.yaml", "raw-cm-envs.yaml") + testObj := createBackstageTest(bs).withDefaultConfig().addToDefaultConfig("configmap-envs.yaml", "raw-cm-envs.yaml") testObj.externalConfig.ExtraEnvConfigMapKeys = map[string]DataObjectKeys{} testObj.externalConfig.ExtraEnvConfigMapKeys["mapName"] = NewDataObjectKeys(map[string]string{"mapName": "ENV1"}, nil) @@ -124,22 +124,25 @@ func TestDefaultAndSpecifiedConfigMapEnvFrom(t *testing.T) { } -func TestSpecifiedCMEnvsWithContainers(t *testing.T) { - +func doCheckSpecifiedEnvsWithContainers(t *testing.T, envType string) { bs := *secretEnvsTestBackstage.DeepCopy() bs.Spec.Application = &bsv1.Application{ - ExtraEnvs: &bsv1.ExtraEnvs{ - ConfigMaps: []bsv1.EnvObjectRef{ - { - Name: "cmName", - Key: "ENV1", - Containers: []string{"install-dynamic-plugins", "another-container"}, - }, - }, - }, + ExtraEnvs: &bsv1.ExtraEnvs{}, + } + ref := bsv1.EnvObjectRef{ + Name: "cmName", + Key: "ENV1", + Containers: []string{"install-dynamic-plugins", "another-container"}, + } + switch envType { + case "cm": + bs.Spec.Application.ExtraEnvs.ConfigMaps = []bsv1.EnvObjectRef{ref} + case "secret": + bs.Spec.Application.ExtraEnvs.Secrets = []bsv1.EnvObjectRef{ref} } - testObj := createBackstageTest(bs).withDefaultConfig(true).addToDefaultConfig("deployment.yaml", "multicontainer-deployment.yaml") + testObj := createBackstageTest(bs).withDefaultConfig(). + addToDefaultConfig("deployment.yaml", "multicontainer-deployment.yaml") testObj.externalConfig.ExtraEnvSecretKeys = map[string]DataObjectKeys{} testObj.externalConfig.ExtraEnvSecretKeys["cmName"] = NewDataObjectKeys(map[string]string{"cmName": "ENV1"}, nil) @@ -165,9 +168,15 @@ func TestSpecifiedCMEnvsWithContainers(t *testing.T) { assert.Equal(t, 0, len(cont.Env)) // check * - bs.Spec.Application.ExtraEnvs.ConfigMaps[0].Containers = []string{"*"} + switch envType { + case "cm": + bs.Spec.Application.ExtraEnvs.ConfigMaps[0].Containers = []string{"*"} + case "secret": + bs.Spec.Application.ExtraEnvs.Secrets[0].Containers = []string{"*"} + } - testObj = createBackstageTest(bs).withDefaultConfig(true).addToDefaultConfig("deployment.yaml", "multicontainer-deployment.yaml") + testObj = createBackstageTest(bs).withDefaultConfig(). + addToDefaultConfig("deployment.yaml", "multicontainer-deployment.yaml") testObj.externalConfig.ExtraEnvSecretKeys = map[string]DataObjectKeys{} testObj.externalConfig.ExtraEnvSecretKeys["cmName"] = NewDataObjectKeys(map[string]string{"cmName": "ENV1"}, nil) @@ -184,6 +193,10 @@ func TestSpecifiedCMEnvsWithContainers(t *testing.T) { } } +func TestSpecifiedCMEnvsWithContainers(t *testing.T) { + doCheckSpecifiedEnvsWithContainers(t, "cm") +} + func TestCMEnvsWithNonExistedContainerFailed(t *testing.T) { bs := *secretEnvsTestBackstage.DeepCopy() bs.Spec.Application = &bsv1.Application{ @@ -198,7 +211,7 @@ func TestCMEnvsWithNonExistedContainerFailed(t *testing.T) { }, } - testObj := createBackstageTest(bs).withDefaultConfig(true) + testObj := createBackstageTest(bs).withDefaultConfig() _, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) diff --git a/pkg/model/configmapfiles.go b/pkg/model/configmapfiles.go index a9a11fe5c..e5a33301b 100644 --- a/pkg/model/configmapfiles.go +++ b/pkg/model/configmapfiles.go @@ -34,7 +34,8 @@ func (p *ConfigMapFiles) addExternalConfig(spec bsv1.BackstageSpec) error { for _, specCm := range spec.Application.ExtraFiles.ConfigMaps { - mp, wSubpath := p.model.backstageDeployment.mountPath(specCm.MountPath, specCm.Key, spec.Application.ExtraFiles.MountPath) + mp, wSubpath := p.model.backstageDeployment.mountPath( + specCm.MountPath, specCm.Key, spec.Application.ExtraFiles.MountPath) keys := p.model.ExternalConfig.ExtraFileConfigMapKeys[specCm.Name].All() err := p.model.backstageDeployment.mountFilesFrom(containersFilter{names: specCm.Containers}, ConfigMapObjectKind, specCm.Name, mp, specCm.Key, wSubpath, keys) @@ -58,9 +59,9 @@ func (p *ConfigMapFiles) setObject(obj runtime.Object) { } // implementation of RuntimeObject interface -//func (p *ConfigMapFiles) EmptyObject() client.Object { -// return &corev1.ConfigMap{} -//} +// func (p *ConfigMapFiles) EmptyObject() client.Object { +// return &corev1.ConfigMap{} +// } // implementation of RuntimeObject interface func (p *ConfigMapFiles) addToModel(model *BackstageModel, _ bsv1.Backstage) (bool, error) { @@ -76,7 +77,9 @@ func (p *ConfigMapFiles) addToModel(model *BackstageModel, _ bsv1.Backstage) (bo func (p *ConfigMapFiles) updateAndValidate(_ bsv1.Backstage) error { keys := append(maps.Keys(p.ConfigMap.Data), maps.Keys(p.ConfigMap.BinaryData)...) - err := p.model.backstageDeployment.mountFilesFrom(containersFilter{annotation: p.ConfigMap.GetAnnotations()[ContainersAnnotation]}, ConfigMapObjectKind, + annotation := p.ConfigMap.GetAnnotations()[ContainersAnnotation] + err := p.model.backstageDeployment.mountFilesFrom( + containersFilter{annotation: annotation}, ConfigMapObjectKind, p.ConfigMap.Name, p.model.backstageDeployment.defaultMountPath(), "", true, keys) if err != nil { diff --git a/pkg/model/configmapfiles_test.go b/pkg/model/configmapfiles_test.go index eafd56274..a050e29db 100644 --- a/pkg/model/configmapfiles_test.go +++ b/pkg/model/configmapfiles_test.go @@ -35,7 +35,7 @@ func TestDefaultConfigMapFiles(t *testing.T) { bs := *configMapFilesTestBackstage.DeepCopy() - testObj := createBackstageTest(bs).withDefaultConfig(true).addToDefaultConfig("configmap-files.yaml", "raw-cm-files.yaml") + testObj := createBackstageTest(bs).withDefaultConfig().addToDefaultConfig("configmap-files.yaml", "raw-cm-files.yaml") model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) @@ -57,7 +57,7 @@ func TestSpecifiedConfigMapFiles(t *testing.T) { *cmf = append(*cmf, bsv1.FileObjectRef{Name: "cm2", MountPath: "/custom/path"}) *cmf = append(*cmf, bsv1.FileObjectRef{Name: "cm3", MountPath: "rel"}) - testObj := createBackstageTest(bs).withDefaultConfig(true) + testObj := createBackstageTest(bs).withDefaultConfig() testObj.externalConfig.ExtraFileConfigMapKeys = map[string]DataObjectKeys{} testObj.externalConfig.ExtraFileConfigMapKeys["cm1"] = NewDataObjectKeys(map[string]string{"conf1.yaml": "data"}, nil) @@ -95,10 +95,12 @@ func TestDefaultAndSpecifiedConfigMapFiles(t *testing.T) { cmf := &bs.Spec.Application.ExtraFiles.ConfigMaps *cmf = append(*cmf, bsv1.FileObjectRef{Name: appConfigTestCm.Name}) - testObj := createBackstageTest(bs).withDefaultConfig(true).addToDefaultConfig("configmap-files.yaml", "raw-cm-files.yaml") + testObj := createBackstageTest(bs).withDefaultConfig(). + addToDefaultConfig("configmap-files.yaml", "raw-cm-files.yaml") testObj.externalConfig.ExtraFileConfigMapKeys = map[string]DataObjectKeys{} - testObj.externalConfig.ExtraFileConfigMapKeys[appConfigTestCm.Name] = NewDataObjectKeys(nil, map[string][]byte{"conf1.yaml": []byte("data")}) + testObj.externalConfig.ExtraFileConfigMapKeys[appConfigTestCm.Name] = NewDataObjectKeys( + nil, map[string][]byte{"conf1.yaml": []byte("data")}) model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) @@ -120,10 +122,11 @@ func TestSpecifiedConfigMapFilesWithBinaryData(t *testing.T) { cmf := &bs.Spec.Application.ExtraFiles.ConfigMaps *cmf = append(*cmf, bsv1.FileObjectRef{Name: "cm1"}) - testObj := createBackstageTest(bs).withDefaultConfig(true) + testObj := createBackstageTest(bs).withDefaultConfig() testObj.externalConfig.ExtraFileConfigMapKeys = map[string]DataObjectKeys{} - testObj.externalConfig.ExtraFileConfigMapKeys["cm1"] = NewDataObjectKeys(nil, map[string][]byte{"conf1.yaml": []byte("data")}) + testObj.externalConfig.ExtraFileConfigMapKeys["cm1"] = NewDataObjectKeys( + nil, map[string][]byte{"conf1.yaml": []byte("data")}) model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) @@ -143,11 +146,14 @@ func TestSpecifiedCMFilesWithContainers(t *testing.T) { bs := *configMapFilesTestBackstage.DeepCopy() cmf := &bs.Spec.Application.ExtraFiles.ConfigMaps - *cmf = append(*cmf, bsv1.FileObjectRef{Name: "cm1", Containers: []string{"install-dynamic-plugins", "another-container"}}) - *cmf = append(*cmf, bsv1.FileObjectRef{Name: "cm2", MountPath: "/custom/path", Containers: []string{"install-dynamic-plugins"}}) + *cmf = append(*cmf, bsv1.FileObjectRef{ + Name: "cm1", Containers: []string{"install-dynamic-plugins", "another-container"}}) + *cmf = append(*cmf, bsv1.FileObjectRef{ + Name: "cm2", MountPath: "/custom/path", Containers: []string{"install-dynamic-plugins"}}) *cmf = append(*cmf, bsv1.FileObjectRef{Name: "cm3", MountPath: "rel", Containers: []string{"*"}}) - testObj := createBackstageTest(bs).withDefaultConfig(true).addToDefaultConfig("deployment.yaml", "multicontainer-deployment.yaml") + testObj := createBackstageTest(bs).withDefaultConfig(). + addToDefaultConfig("deployment.yaml", "multicontainer-deployment.yaml") testObj.externalConfig.ExtraFileConfigMapKeys = map[string]DataObjectKeys{} testObj.externalConfig.ExtraFileConfigMapKeys["cm1"] = NewDataObjectKeys(map[string]string{"conf1.yaml": "data"}, nil) @@ -182,7 +188,7 @@ func TestCMFilesWithNonExistedContainerFailed(t *testing.T) { }, } - testObj := createBackstageTest(bs).withDefaultConfig(true) + testObj := createBackstageTest(bs).withDefaultConfig() _, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) @@ -194,12 +200,15 @@ func TestReplaceFiles(t *testing.T) { bs := *configMapFilesTestBackstage.DeepCopy() cmf := &bs.Spec.Application.ExtraFiles.ConfigMaps - *cmf = append(*cmf, bsv1.FileObjectRef{Name: appConfigTestCm.Name, MountPath: DefaultMountDir, Key: "dynamic-plugins123.yaml"}) + *cmf = append(*cmf, bsv1.FileObjectRef{ + Name: appConfigTestCm.Name, MountPath: DefaultMountDir, Key: "dynamic-plugins123.yaml"}) - testObj := createBackstageTest(bs).withDefaultConfig(true).addToDefaultConfig("configmap-files.yaml", "raw-cm-files.yaml") + testObj := createBackstageTest(bs).withDefaultConfig(). + addToDefaultConfig("configmap-files.yaml", "raw-cm-files.yaml") testObj.externalConfig.ExtraFileConfigMapKeys = map[string]DataObjectKeys{} - testObj.externalConfig.ExtraFileConfigMapKeys[appConfigTestCm.Name] = NewDataObjectKeys(nil, map[string][]byte{"dynamic-plugins123.yaml": []byte("data")}) + testObj.externalConfig.ExtraFileConfigMapKeys[appConfigTestCm.Name] = NewDataObjectKeys( + nil, map[string][]byte{"dynamic-plugins123.yaml": []byte("data")}) model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) diff --git a/pkg/model/containers-filter.go b/pkg/model/containers-filter.go index 9fd7da4f0..796016dbc 100644 --- a/pkg/model/containers-filter.go +++ b/pkg/model/containers-filter.go @@ -12,7 +12,8 @@ type containersFilter struct { annotation string } -// getContainers returns a list of containers filtered by the names provided in the names array or by a string annotation containing comma-separated names +// getContainers returns a list of containers filtered by the names provided in the names array +// or by a string annotation containing comma-separated names. // NOTE: If the annotation is not empty, it overrides the names in the struct, so, DO NOT set both. // Empty annotation or names returns the main container. // If the annotation or names[0] is "*" it returns all containers. diff --git a/pkg/model/db-secret.go b/pkg/model/db-secret.go index 416c9066b..d626c0f59 100644 --- a/pkg/model/db-secret.go +++ b/pkg/model/db-secret.go @@ -62,9 +62,9 @@ func (b *DbSecret) addToModel(model *BackstageModel, backstage bsv1.Backstage) ( } // implementation of RuntimeObject interface -//func (b *DbSecret) EmptyObject() client.Object { -// return &corev1.Secret{} -//} +// func (b *DbSecret) EmptyObject() client.Object { +// return &corev1.Secret{} +// } // implementation of RuntimeObject interface func (b *DbSecret) updateAndValidate(_ bsv1.Backstage) error { diff --git a/pkg/model/db-secret_test.go b/pkg/model/db-secret_test.go index 8058e278e..645aeef3c 100644 --- a/pkg/model/db-secret_test.go +++ b/pkg/model/db-secret_test.go @@ -33,7 +33,8 @@ func TestEmptyDbSecret(t *testing.T) { bs := *dbSecretBackstage.DeepCopy() // expected generatePassword = false (default db-secret defined) will come from preprocess - testObj := createBackstageTest(bs).withDefaultConfig(true).withLocalDb().addToDefaultConfig("db-secret.yaml", "db-empty-secret.yaml") + testObj := createBackstageTest(bs).withDefaultConfig().withLocalDb(). + addToDefaultConfig("db-secret.yaml", "db-empty-secret.yaml") model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) @@ -52,15 +53,16 @@ func TestDefaultWithGeneratedSecrets(t *testing.T) { bs := *dbSecretBackstage.DeepCopy() // expected generatePassword = true (no db-secret defined) will come from preprocess - testObj := createBackstageTest(bs).withDefaultConfig(true).withLocalDb().addToDefaultConfig("db-secret.yaml", "db-generated-secret.yaml") + testObj := createBackstageTest(bs).withDefaultConfig().withLocalDb(). + addToDefaultConfig("db-secret.yaml", "db-generated-secret.yaml") model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) assert.NoError(t, err) assert.Equal(t, fmt.Sprintf("backstage-psql-secret-%s", bs.Name), model.LocalDbSecret.secret.Name) - //should be generated - // assert.NotEmpty(t, model.LocalDbSecret.secret.StringData["POSTGRES_USER"]) - // assert.NotEmpty(t, model.LocalDbSecret.secret.StringData["POSTGRES_PASSWORD"]) + // should be generated + // assert.NotEmpty(t, model.LocalDbSecret.secret.StringData["POSTGRES_USER"]) + // assert.NotEmpty(t, model.LocalDbSecret.secret.StringData["POSTGRES_PASSWORD"]) dbss := model.localDbStatefulSet assert.NotNil(t, dbss) @@ -73,7 +75,8 @@ func TestSpecifiedSecret(t *testing.T) { bs.Spec.Database.AuthSecretName = "custom-db-secret" // expected generatePassword = false (db-secret defined in the spec) will come from preprocess - testObj := createBackstageTest(bs).withDefaultConfig(true).withLocalDb().addToDefaultConfig("db-secret.yaml", "db-generated-secret.yaml") + testObj := createBackstageTest(bs).withDefaultConfig().withLocalDb(). + addToDefaultConfig("db-secret.yaml", "db-generated-secret.yaml") model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) diff --git a/pkg/model/db-service.go b/pkg/model/db-service.go index 012f9705c..8b8f4a259 100644 --- a/pkg/model/db-service.go +++ b/pkg/model/db-service.go @@ -47,7 +47,8 @@ func (b *DbService) addToModel(model *BackstageModel, _ bsv1.Backstage) (bool, e b.model = model if b.service == nil { if model.localDbEnabled { - return false, fmt.Errorf("LocalDb Service not initialized, make sure there is db-service.yaml in default or raw configuration") + return false, fmt.Errorf( + "LocalDb Service not initialized, make sure there is db-service.yaml in default or raw configuration") } return false, nil } else { @@ -66,9 +67,9 @@ func (b *DbService) addToModel(model *BackstageModel, _ bsv1.Backstage) (bool, e } // implementation of RuntimeObject interface -//func (b *DbService) EmptyObject() client.Object { -// return &corev1.Service{} -//} +// func (b *DbService) EmptyObject() client.Object { +// return &corev1.Service{} +// } // implementation of RuntimeObject interface func (b *DbService) updateAndValidate(_ bsv1.Backstage) error { diff --git a/pkg/model/db-statefulset.go b/pkg/model/db-statefulset.go index e3461dc9d..1306a3f3d 100644 --- a/pkg/model/db-statefulset.go +++ b/pkg/model/db-statefulset.go @@ -52,7 +52,8 @@ func (b *DbStatefulSet) addToModel(model *BackstageModel, _ bsv1.Backstage) (boo b.model = model if b.statefulSet == nil { if model.localDbEnabled { - return false, fmt.Errorf("LocalDb StatefulSet not configured, make sure there is db-statefulset.yaml in default or raw configuration") + return false, fmt.Errorf( + "LocalDb StatefulSet not configured, make sure there is db-statefulset.yaml in default or raw configuration") } return false, nil } else { @@ -73,20 +74,15 @@ func (b *DbStatefulSet) addToModel(model *BackstageModel, _ bsv1.Backstage) (boo return true, nil } -// implementation of RuntimeObject interface -//func (b *DbStatefulSet) EmptyObject() client.Object { -// return &appsv1.StatefulSet{} -//} - // implementation of RuntimeObject interface func (b *DbStatefulSet) updateAndValidate(backstage bsv1.Backstage) error { // point ServiceName to localDb b.statefulSet.Spec.ServiceName = b.model.LocalDbService.service.Name - //if backstage.Spec.Application != nil && backstage.Spec.Application.ImagePullSecrets != nil { - // utils.SetImagePullSecrets(b.podSpec(), backstage.Spec.Application.ImagePullSecrets) - //} + // if backstage.Spec.Application != nil && backstage.Spec.Application.ImagePullSecrets != nil { + // utils.SetImagePullSecrets(b.podSpec(), backstage.Spec.Application.ImagePullSecrets) + // } if backstage.Spec.IsAuthSecretSpecified() { b.setDbSecretEnvVar(b.container(), backstage.Spec.Database.AuthSecretName) @@ -98,8 +94,10 @@ func (b *DbStatefulSet) updateAndValidate(backstage bsv1.Backstage) error { func (b *DbStatefulSet) setMetaInfo(backstage bsv1.Backstage, scheme *runtime.Scheme) { b.statefulSet.SetName(DbStatefulSetName(backstage.Name)) - utils.GenerateLabel(&b.statefulSet.Spec.Template.Labels, BackstageAppLabel, utils.BackstageDbAppLabelValue(backstage.Name)) - utils.GenerateLabel(&b.statefulSet.Spec.Selector.MatchLabels, BackstageAppLabel, utils.BackstageDbAppLabelValue(backstage.Name)) + utils.GenerateLabel( + &b.statefulSet.Spec.Template.Labels, BackstageAppLabel, utils.BackstageDbAppLabelValue(backstage.Name)) + utils.GenerateLabel( + &b.statefulSet.Spec.Selector.MatchLabels, BackstageAppLabel, utils.BackstageDbAppLabelValue(backstage.Name)) setMetaInfo(b.statefulSet, backstage, scheme) } @@ -114,7 +112,7 @@ func (b *DbStatefulSet) podSpec() *corev1.PodSpec { } func (b *DbStatefulSet) setDbSecretEnvVar(container *corev1.Container, secretName string) { - //AddEnvVarsFrom(container, SecretObjectKind, secretName, "") + // AddEnvVarsFrom(container, SecretObjectKind, secretName, "") envFromSrc := corev1.EnvFromSource{} envFromSrc.SecretRef = &corev1.SecretEnvSource{ LocalObjectReference: corev1.LocalObjectReference{Name: secretName}} diff --git a/pkg/model/db-statefulset_test.go b/pkg/model/db-statefulset_test.go index ceaaf17d8..f5f48695a 100644 --- a/pkg/model/db-statefulset_test.go +++ b/pkg/model/db-statefulset_test.go @@ -32,7 +32,7 @@ var dbStatefulSetBackstage = &bsv1.Backstage{ // test default StatefulSet func TestDefault(t *testing.T) { bs := *dbStatefulSetBackstage.DeepCopy() - testObj := createBackstageTest(bs).withDefaultConfig(true) + testObj := createBackstageTest(bs).withDefaultConfig() model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) assert.NoError(t, err) @@ -47,7 +47,7 @@ func TestOverrideDbImage(t *testing.T) { bs.Spec.Database.EnableLocalDb = ptr.To(false) - testObj := createBackstageTest(bs).withDefaultConfig(true). + testObj := createBackstageTest(bs).withDefaultConfig(). addToDefaultConfig("db-statefulset.yaml", "janus-db-statefulset.yaml").withLocalDb() _ = os.Setenv(LocalDbImageEnvVar, "dummy") @@ -59,40 +59,40 @@ func TestOverrideDbImage(t *testing.T) { } // test bs.Spec.Application.ImagePullSecrets shared with StatefulSet -//func TestImagePullSecretSpec(t *testing.T) { -// //bs := *dbStatefulSetBackstage.DeepCopy() -// //bs.Spec.Application.ImagePullSecrets = []string{"my-secret1", "my-secret2"} -// // -// //testObj := createBackstageTest(bs).withDefaultConfig(true) -// //model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) -// //assert.NoError(t, err) -// // -// //assert.Equal(t, 2, len(model.localDbStatefulSet.statefulSet.Spec.Template.Spec.ImagePullSecrets)) -// //assert.Equal(t, "my-secret1", model.localDbStatefulSet.statefulSet.Spec.Template.Spec.ImagePullSecrets[0].Name) -// //assert.Equal(t, "my-secret2", model.localDbStatefulSet.statefulSet.Spec.Template.Spec.ImagePullSecrets[1].Name) +// func TestImagePullSecretSpec(t *testing.T) { +// //bs := *dbStatefulSetBackstage.DeepCopy() +// //bs.Spec.Application.ImagePullSecrets = []string{"my-secret1", "my-secret2"} +// // +// //testObj := createBackstageTest(bs).withDefaultConfig() +// //model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) +// //assert.NoError(t, err) +// // +// //assert.Equal(t, 2, len(model.localDbStatefulSet.statefulSet.Spec.Template.Spec.ImagePullSecrets)) +// //assert.Equal(t, "my-secret1", model.localDbStatefulSet.statefulSet.Spec.Template.Spec.ImagePullSecrets[0].Name) +// //assert.Equal(t, "my-secret2", model.localDbStatefulSet.statefulSet.Spec.Template.Spec.ImagePullSecrets[1].Name) // -// // no image pull secrets specified -// bs := *dbStatefulSetBackstage.DeepCopy() -// testObj := createBackstageTest(bs).withDefaultConfig(true). -// addToDefaultConfig("db-statefulset.yaml", "ips-db-statefulset.yaml") +// // no image pull secrets specified +// bs := *dbStatefulSetBackstage.DeepCopy() +// testObj := createBackstageTest(bs).withDefaultConfig(). +// addToDefaultConfig("db-statefulset.yaml", "ips-db-statefulset.yaml") // -// model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.OpenShift, testObj.scheme) -// if assert.NoError(t, err) { -// // if imagepullsecrets not defined - default used -// assert.Equal(t, 2, len(model.localDbStatefulSet.statefulSet.Spec.Template.Spec.ImagePullSecrets)) -// assert.Equal(t, "ips-db1", model.localDbStatefulSet.statefulSet.Spec.Template.Spec.ImagePullSecrets[0].Name) -// assert.Equal(t, "ips-db2", model.localDbStatefulSet.statefulSet.Spec.Template.Spec.ImagePullSecrets[1].Name) -// } +// model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.OpenShift, testObj.scheme) +// if assert.NoError(t, err) { +// // if imagepullsecrets not defined - default used +// assert.Equal(t, 2, len(model.localDbStatefulSet.statefulSet.Spec.Template.Spec.ImagePullSecrets)) +// assert.Equal(t, "ips-db1", model.localDbStatefulSet.statefulSet.Spec.Template.Spec.ImagePullSecrets[0].Name) +// assert.Equal(t, "ips-db2", model.localDbStatefulSet.statefulSet.Spec.Template.Spec.ImagePullSecrets[1].Name) +// } // -// // empty list of image pull secrets -// //bs = *dbStatefulSetBackstage.DeepCopy() -// //bs.Spec.Application.ImagePullSecrets = []string{} -// // -// //testObj = createBackstageTest(bs).withDefaultConfig(true). -// // addToDefaultConfig("db-statefulset.yaml", "ips-db-statefulset.yaml") -// // -// //model, err = InitObjects(context.TODO(), bs, testObj.externalConfig, platform.OpenShift, testObj.scheme) -// //if assert.NoError(t, err) { -// // assert.Equal(t, 0, len(model.localDbStatefulSet.statefulSet.Spec.Template.Spec.ImagePullSecrets)) -// //} -//} +// // empty list of image pull secrets +// //bs = *dbStatefulSetBackstage.DeepCopy() +// //bs.Spec.Application.ImagePullSecrets = []string{} +// // +// //testObj = createBackstageTest(bs).withDefaultConfig(). +// // addToDefaultConfig("db-statefulset.yaml", "ips-db-statefulset.yaml") +// // +// //model, err = InitObjects(context.TODO(), bs, testObj.externalConfig, platform.OpenShift, testObj.scheme) +// //if assert.NoError(t, err) { +// // assert.Equal(t, 0, len(model.localDbStatefulSet.statefulSet.Spec.Template.Spec.ImagePullSecrets)) +// //} +// } diff --git a/pkg/model/deployable.go b/pkg/model/deployable.go index 83cac103b..cccc5793b 100644 --- a/pkg/model/deployable.go +++ b/pkg/model/deployable.go @@ -30,7 +30,7 @@ type Deployable interface { SetEmpty() } -//const unsupportedType = "unsupported deployable type: " +// const unsupportedType = "unsupported deployable type: " // CreateDeployable creates a new Deployable object func CreateDeployable(obj runtime.Object) (Deployable, error) { diff --git a/pkg/model/deployment.go b/pkg/model/deployment.go index e4e6d747f..0f791f8da 100644 --- a/pkg/model/deployment.go +++ b/pkg/model/deployment.go @@ -54,7 +54,8 @@ func DeploymentName(backstageName string) string { return utils.GenerateRuntimeObjectName(backstageName, "backstage") } -// BackstageContainerIndex returns the index of backstage container in from deployment.spec.template.spec.containers array +// BackstageContainerIndex returns the index of backstage container +// in from deployment.spec.template.spec.containers array func BackstageContainerIndex(bsdPod *corev1.PodSpec) int { for i, c := range bsdPod.Containers { if c.Name == BackstageContainerName() { @@ -76,11 +77,11 @@ func (b *BackstageDeployment) Object() runtime.Object { // implementation of RuntimeObject interface func (b *BackstageDeployment) setObject(obj runtime.Object) { - //b.deployable = DeploymentObj{} + // b.deployable = DeploymentObj{} - //if obj != nil { - // b.deployable.setObject(obj) - //} + // if obj != nil { + // b.deployable.setObject(obj) + // } var err error b.deployable, err = CreateDeployable(obj) if err != nil { @@ -91,7 +92,8 @@ func (b *BackstageDeployment) setObject(obj runtime.Object) { // implementation of RuntimeObject interface func (b *BackstageDeployment) addToModel(model *BackstageModel, backstage bsv1.Backstage) (bool, error) { if b.deployable.GetObject() == nil { - return false, fmt.Errorf("backstage Deployment is not initialized, make sure there is deployment.yaml in default or raw configuration") + return false, fmt.Errorf( + "backstage Deployment is not initialized, make sure there is deployment.yaml in default or raw configuration") } if BackstageContainerIndex(b.podSpec()) < 0 { @@ -112,7 +114,8 @@ func (b *BackstageDeployment) addToModel(model *BackstageModel, backstage bsv1.B b.setImage(ptr.To(os.Getenv(BackstageImageEnvVar))) } - // Set CATALOG_INDEX_IMAGE from operator env var BEFORE extraEnvs are applied, so user-specified extraEnvs can still override this value + // Set CATALOG_INDEX_IMAGE from operator env var BEFORE extraEnvs are applied, + // so user-specified extraEnvs can still override this value if catalogIndexImage := os.Getenv(CatalogIndexImageEnvVar); catalogIndexImage != "" { if i, _ := DynamicPluginsInitContainer(b.podSpec().InitContainers); i >= 0 { b.setOrAppendEnvVar(&b.podSpec().InitContainers[i], "CATALOG_INDEX_IMAGE", catalogIndexImage) @@ -129,7 +132,7 @@ func (b *BackstageDeployment) addToModel(model *BackstageModel, backstage bsv1.B // implementation of RuntimeObject interface func (b *BackstageDeployment) updateAndValidate(backstage bsv1.Backstage) error { - //DbSecret + // DbSecret var err error if backstage.Spec.IsAuthSecretSpecified() { err = b.addEnvVarsFrom(containersFilter{}, SecretObjectKind, backstage.Spec.Database.AuthSecretName, "") @@ -146,7 +149,8 @@ func (b *BackstageDeployment) updateAndValidate(backstage bsv1.Backstage) error func (b *BackstageDeployment) setMetaInfo(backstage bsv1.Backstage, scheme *runtime.Scheme) { b.deployable.GetObject().SetName(DeploymentName(backstage.Name)) - utils.GenerateLabel(&b.deployable.PodObjectMeta().Labels, BackstageAppLabel, utils.BackstageAppLabelValue(backstage.Name)) + utils.GenerateLabel( + &b.deployable.PodObjectMeta().Labels, BackstageAppLabel, utils.BackstageAppLabelValue(backstage.Name)) b.deployable.SpecSelector().MatchLabels[BackstageAppLabel] = utils.BackstageAppLabelValue(backstage.Name) setMetaInfo(b.deployable.GetObject(), backstage, scheme) @@ -322,16 +326,19 @@ func (b *BackstageDeployment) setOrAppendEnvVar(container *corev1.Container, nam container.Env = append(container.Env, corev1.EnvVar{Name: name, Value: value}) } -// MountFilesFrom adds Volume to specified podSpec and related VolumeMounts to specified belonging to this podSpec container -// from ConfigMap or Secret volume source +// MountFilesFrom adds Volume to specified podSpec and related VolumeMounts +// to specified belonging to this podSpec container from ConfigMap or Secret volume source // containers - array of containers to add VolumeMount(s) to // kind - kind of source, can be ConfigMap or Secret // objectName - name of source object // mountPath - mount path, default one or as it specified in BackstageCR.spec.Application.AppConfig|ExtraFiles // fileName - file name which fits one of the object's key, otherwise error will be returned. -// withSubPath - if true will be mounted file-by-file with subpath, otherwise will be mounted as directory to specified path +// withSubPath - if true will be mounted file-by-file with subpath, otherwise will be mounted as directory // dataKeys - keys for ConfigMap/Secret data -func (b *BackstageDeployment) mountFilesFrom(containersFilter containersFilter, kind ObjectKind, objectName, mountPath, fileName string, withSubPath bool, dataKeys []string) error { +func (b *BackstageDeployment) mountFilesFrom( + containersFilter containersFilter, kind ObjectKind, objectName, mountPath, fileName string, + withSubPath bool, dataKeys []string, +) error { containers, err := containersFilter.getContainers(b) if err != nil { @@ -375,7 +382,8 @@ func (b *BackstageDeployment) mountFilesFrom(containersFilter containersFilter, } } } else { - mountsToAdd = []corev1.VolumeMount{{Name: volName, MountPath: filepath.Join(mountPath, fileName), SubPath: fileName, ReadOnly: true}} + mountsToAdd = []corev1.VolumeMount{{ + Name: volName, MountPath: filepath.Join(mountPath, fileName), SubPath: fileName, ReadOnly: true}} } // Replace or append @@ -408,7 +416,11 @@ func (b *BackstageDeployment) mountFilesFrom(containersFilter containersFilter, // objectName - name of source object // varName - name of env variable -func (b *BackstageDeployment) addEnvVarsFrom(containersFilter containersFilter, kind ObjectKind, objectName, varName string) error { +func (b *BackstageDeployment) addEnvVarsFrom( + containersFilter containersFilter, + kind ObjectKind, + objectName, varName string, +) error { containers, err := containersFilter.getContainers(b) if err != nil { diff --git a/pkg/model/deployment_test.go b/pkg/model/deployment_test.go index 6b2ea8266..2c1e4cbb9 100644 --- a/pkg/model/deployment_test.go +++ b/pkg/model/deployment_test.go @@ -18,6 +18,8 @@ import ( "github.com/stretchr/testify/assert" ) +const statefulSet = "StatefulSet" + var deploymentTestBackstage = bsv1.Backstage{ ObjectMeta: metav1.ObjectMeta{ Name: "bs", @@ -34,7 +36,7 @@ var deploymentTestBackstage = bsv1.Backstage{ func TestWorkingDirMount(t *testing.T) { bs := *deploymentTestBackstage.DeepCopy() - testObj := createBackstageTest(bs).withDefaultConfig(true). + testObj := createBackstageTest(bs).withDefaultConfig(). addToDefaultConfig("deployment.yaml", "working-dir-mount.yaml") model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) @@ -56,7 +58,7 @@ func TestOverrideBackstageImage(t *testing.T) { bs := *deploymentTestBackstage.DeepCopy() - testObj := createBackstageTest(bs).withDefaultConfig(true). + testObj := createBackstageTest(bs).withDefaultConfig(). addToDefaultConfig("deployment.yaml", "sidecar-deployment.yaml") t.Setenv(BackstageImageEnvVar, "dummy") @@ -74,7 +76,7 @@ func TestOverrideBackstageImage(t *testing.T) { func TestSpecImagePullSecrets(t *testing.T) { bs := *deploymentTestBackstage.DeepCopy() - testObj := createBackstageTest(bs).withDefaultConfig(true). + testObj := createBackstageTest(bs).withDefaultConfig(). addToDefaultConfig("deployment.yaml", "ips-deployment.yaml") model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.OpenShift, testObj.scheme) @@ -84,16 +86,16 @@ func TestSpecImagePullSecrets(t *testing.T) { assert.Equal(t, 2, len(model.backstageDeployment.podSpec().ImagePullSecrets)) assert.Equal(t, "ips1", model.backstageDeployment.podSpec().ImagePullSecrets[0].Name) - //bs.Spec.Application.ImagePullSecrets = []string{} + // bs.Spec.Application.ImagePullSecrets = []string{} // - //testObj = createBackstageTest(bs).withDefaultConfig(true). - // addToDefaultConfig("deployment.yaml", "ips-deployment.yaml") + // testObj = createBackstageTest(bs).withDefaultConfig(). + // addToDefaultConfig("deployment.yaml", "ips-deployment.yaml") // - //model, err = InitObjects(context.TODO(), bs, testObj.externalConfig, platform.OpenShift, testObj.scheme) - //assert.NoError(t, err) + // model, err = InitObjects(context.TODO(), bs, testObj.externalConfig, platform.OpenShift, testObj.scheme) + // assert.NoError(t, err) // - //// if explicitly set empty slice - they are empty - //assert.Equal(t, 0, len(model.backstageDeployment.deployment.Spec.Template.Spec.ImagePullSecrets)) + // // if explicitly set empty slice - they are empty + // assert.Equal(t, 0, len(model.backstageDeployment.deployment.Spec.Template.Spec.ImagePullSecrets)) } @@ -130,7 +132,7 @@ spec: `), } - testObj := createBackstageTest(bs).withDefaultConfig(true). + testObj := createBackstageTest(bs).withDefaultConfig(). addToDefaultConfig("deployment.yaml", "janus-deployment.yaml") model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.OpenShift, testObj.scheme) @@ -154,7 +156,8 @@ spec: assert.Equal(t, 4, len(model.backstageDeployment.podSpec().Volumes)) assert.Equal(t, "dynamic-plugins-root", model.backstageDeployment.podSpec().Volumes[0].Name) // overrides StorageClassName - assert.Equal(t, "special", *model.backstageDeployment.podSpec().Volumes[0].Ephemeral.VolumeClaimTemplate.Spec.StorageClassName) + storageClassName := model.backstageDeployment.podSpec().Volumes[0].Ephemeral.VolumeClaimTemplate.Spec.StorageClassName + assert.Equal(t, "special", *storageClassName) // adds new volume assert.Equal(t, "my-vol", model.backstageDeployment.podSpec().Volumes[3].Name) } @@ -175,7 +178,7 @@ spec: t.Setenv(BackstageImageEnvVar, "envvar-image") - testObj := createBackstageTest(bs).withDefaultConfig(true) + testObj := createBackstageTest(bs).withDefaultConfig() model, err := InitObjects(context.TODO(), bsv1.Backstage{}, testObj.externalConfig, platform.OpenShift, testObj.scheme) assert.NoError(t, err) @@ -205,7 +208,7 @@ spec: `), } - testObj := createBackstageTest(bs).withDefaultConfig(true) + testObj := createBackstageTest(bs).withDefaultConfig() model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) assert.NoError(t, err) @@ -258,7 +261,7 @@ spec: }, } - testObj := createBackstageTest(bs).withDefaultConfig(true) + testObj := createBackstageTest(bs).withDefaultConfig() model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) assert.NoError(t, err) @@ -275,19 +278,19 @@ func TestDeploymentKind(t *testing.T) { bs := *deploymentTestBackstage.DeepCopy() bs.Spec.Deployment = &bsv1.BackstageDeployment{} - testObj := createBackstageTest(bs).withDefaultConfig(true) + testObj := createBackstageTest(bs).withDefaultConfig() model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) assert.NoError(t, err) depPodSpec := model.backstageDeployment.podSpec() - bs.Spec.Deployment.Kind = "StatefulSet" - testObj = createBackstageTest(bs).withDefaultConfig(true) + bs.Spec.Deployment.Kind = statefulSet + testObj = createBackstageTest(bs).withDefaultConfig() model, err = InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) assert.NoError(t, err) - assert.Equal(t, "StatefulSet", model.backstageDeployment.deployable.GetObject().GetObjectKind().GroupVersionKind().Kind) + assert.Equal(t, statefulSet, model.backstageDeployment.deployable.GetObject().GetObjectKind().GroupVersionKind().Kind) ssPodSpec := model.backstageDeployment.podSpec() assert.Equal(t, depPodSpec, ssPodSpec) @@ -296,19 +299,19 @@ func TestDeploymentKind(t *testing.T) { func TestPatchedStatefulSet(t *testing.T) { bs := *deploymentTestBackstage.DeepCopy() bs.Spec.Deployment = &bsv1.BackstageDeployment{} - bs.Spec.Deployment.Kind = "StatefulSet" + bs.Spec.Deployment.Kind = statefulSet bs.Spec.Deployment.Patch = &apiextensionsv1.JSON{ Raw: []byte(` spec: serviceName: my-service `), } - testObj := createBackstageTest(bs).withDefaultConfig(true) + testObj := createBackstageTest(bs).withDefaultConfig() model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) assert.NoError(t, err) - assert.Equal(t, "StatefulSet", model.backstageDeployment.deployable.GetObject().GetObjectKind().GroupVersionKind().Kind) + assert.Equal(t, statefulSet, model.backstageDeployment.deployable.GetObject().GetObjectKind().GroupVersionKind().Kind) ss, ok := model.backstageDeployment.deployable.GetObject().(*appv1.StatefulSet) assert.True(t, ok) diff --git a/pkg/model/dynamic-plugins.go b/pkg/model/dynamic-plugins.go index ffcb6c9fa..56f8c0b8e 100644 --- a/pkg/model/dynamic-plugins.go +++ b/pkg/model/dynamic-plugins.go @@ -16,16 +16,16 @@ import ( corev1 "k8s.io/api/core/v1" ) -//it relies on implementation where dynamic-plugin initContainer -//uses specified ConfigMap for producing app-config with dynamic-plugins -//For this implementation: -//- backstage contaier and dynamic-plugin initContainer must share a volume -// where initContainer writes and backstage container reads produced app-config -//- app-config path should be set as a --config parameter of backstage container -//in the deployment manifest +// it relies on implementation where dynamic-plugin initContainer +// uses specified ConfigMap for producing app-config with dynamic-plugins +// For this implementation: +// - backstage contaier and dynamic-plugin initContainer must share a volume +// where initContainer writes and backstage container reads produced app-config +// - app-config path should be set as a --config parameter of backstage container +// in the deployment manifest -//it creates a volume with dynamic-plugins ConfigMap (there should be a key named "dynamic-plugins.yaml") -//and mount it to the dynamic-plugin initContainer's WorkingDir (what if not specified?) +// it creates a volume with dynamic-plugins ConfigMap (there should be a key named "dynamic-plugins.yaml") +// and mount it to the dynamic-plugin initContainer's WorkingDir (what if not specified?) const dynamicPluginInitContainerName = "install-dynamic-plugins" const DynamicPluginsFile = "dynamic-plugins.yaml" @@ -81,9 +81,9 @@ func (p *DynamicPlugins) setObject(obj runtime.Object) { } // implementation of RuntimeObject interface -//func (p *DynamicPlugins) EmptyObject() client.Object { -// return &corev1.ConfigMap{} -//} +// func (p *DynamicPlugins) EmptyObject() client.Object { +// return &corev1.ConfigMap{} +// } // implementation of RuntimeObject interface func (p *DynamicPlugins) addToModel(model *BackstageModel, _ bsv1.Backstage) (bool, error) { @@ -103,7 +103,8 @@ func (p *DynamicPlugins) addToModel(model *BackstageModel, _ bsv1.Backstage) (bo } // implementation of RuntimeObject interface -// ConfigMap name must be the same as (deployment.yaml).spec.template.spec.volumes.name.dynamic-plugins-conf.ConfigMap.name +// ConfigMap name must be the same as +// (deployment.yaml).spec.template.spec.volumes.name.dynamic-plugins-conf.ConfigMap.name func (p *DynamicPlugins) updateAndValidate(backstage bsv1.Backstage) error { _, initContainer := p.getInitContainer() @@ -111,8 +112,10 @@ func (p *DynamicPlugins) updateAndValidate(backstage bsv1.Backstage) error { return fmt.Errorf("failed to find initContainer named %s", dynamicPluginInitContainerName) } if backstage.Spec.Application == nil || backstage.Spec.Application.DynamicPluginsConfigMapName == "" { - if err := p.model.backstageDeployment.mountFilesFrom(containersFilter{names: []string{dynamicPluginInitContainerName}}, ConfigMapObjectKind, - p.ConfigMap.Name, initContainer.WorkingDir, DynamicPluginsFile, true, maps.Keys(p.ConfigMap.Data)); err != nil { + if err := p.model.backstageDeployment.mountFilesFrom( + containersFilter{names: []string{dynamicPluginInitContainerName}}, ConfigMapObjectKind, + p.ConfigMap.Name, initContainer.WorkingDir, DynamicPluginsFile, true, + maps.Keys(p.ConfigMap.Data)); err != nil { return fmt.Errorf("failed to mount dynamic plugins configMap: %w", err) } } @@ -147,13 +150,16 @@ func (p *DynamicPlugins) addExternalConfig(spec bsv1.BackstageSpec) error { return fmt.Errorf("failed to merge dynamic plugins config: %w", err) } p.ConfigMap.Data[DynamicPluginsFile] = mergedData - err = p.model.backstageDeployment.mountFilesFrom(containersFilter{names: []string{dynamicPluginInitContainerName}}, ConfigMapObjectKind, - p.ConfigMap.Name, initContainer.WorkingDir, DynamicPluginsFile, true, maps.Keys(p.ConfigMap.Data)) + err = p.model.backstageDeployment.mountFilesFrom( + containersFilter{names: []string{dynamicPluginInitContainerName}}, ConfigMapObjectKind, + p.ConfigMap.Name, initContainer.WorkingDir, DynamicPluginsFile, true, + maps.Keys(p.ConfigMap.Data)) if err != nil { return fmt.Errorf("failed to mount dynamic plugins configMap: %w", err) } } else { - err := p.model.backstageDeployment.mountFilesFrom(containersFilter{names: []string{dynamicPluginInitContainerName}}, ConfigMapObjectKind, + err := p.model.backstageDeployment.mountFilesFrom( + containersFilter{names: []string{dynamicPluginInitContainerName}}, ConfigMapObjectKind, dp.Name, initContainer.WorkingDir, DynamicPluginsFile, true, maps.Keys(dp.Data)) if err != nil { return fmt.Errorf("failed to mount dynamic plugins configMap: %w", err) diff --git a/pkg/model/dynamic-plugins_test.go b/pkg/model/dynamic-plugins_test.go index 22ec4f8c2..5121f1190 100644 --- a/pkg/model/dynamic-plugins_test.go +++ b/pkg/model/dynamic-plugins_test.go @@ -21,6 +21,8 @@ import ( "github.com/stretchr/testify/assert" ) +const dplugin = "dplugin" + var testDynamicPluginsBackstage = bsv1.Backstage{ ObjectMeta: metav1.ObjectMeta{ Name: "bs", @@ -38,12 +40,12 @@ func TestDynamicPluginsValidationFailed(t *testing.T) { bs := testDynamicPluginsBackstage.DeepCopy() - testObj := createBackstageTest(*bs).withDefaultConfig(true). + testObj := createBackstageTest(*bs).withDefaultConfig(). addToDefaultConfig("dynamic-plugins.yaml", "raw-dynamic-plugins.yaml") _, err := InitObjects(context.TODO(), *bs, testObj.externalConfig, platform.Default, testObj.scheme) - //"failed object validation, reason: failed to find initContainer named install-dynamic-plugins") + // "failed object validation, reason: failed to find initContainer named install-dynamic-plugins") assert.Error(t, err) } @@ -51,21 +53,21 @@ func TestDynamicPluginsValidationFailed(t *testing.T) { func TestDynamicPluginsInvalidKeyName(t *testing.T) { bs := testDynamicPluginsBackstage.DeepCopy() - bs.Spec.Application.DynamicPluginsConfigMapName = "dplugin" + bs.Spec.Application.DynamicPluginsConfigMapName = dplugin - testObj := createBackstageTest(*bs).withDefaultConfig(true). + testObj := createBackstageTest(*bs).withDefaultConfig(). addToDefaultConfig("dynamic-plugins.yaml", "raw-dynamic-plugins.yaml"). addToDefaultConfig("deployment.yaml", "janus-deployment.yaml") testObj.externalConfig.DynamicPlugins = corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Name: "dplugin"}, + ObjectMeta: metav1.ObjectMeta{Name: dplugin}, Data: map[string]string{"WrongKeyName.yml": "tt"}, } _, err := InitObjects(context.TODO(), *bs, testObj.externalConfig, platform.Default, testObj.scheme) assert.Error(t, err) - //assert.Contains(t, err.Error(), "expects exactly one Data key named 'dynamic-plugins.yaml'") + // assert.Contains(t, err.Error(), "expects exactly one Data key named 'dynamic-plugins.yaml'") assert.Contains(t, err.Error(), "dynamic plugin configMap expects 'dynamic-plugins.yaml' Data key") } @@ -75,7 +77,7 @@ func TestDefaultDynamicPlugins(t *testing.T) { bs := testDynamicPluginsBackstage.DeepCopy() - testObj := createBackstageTest(*bs).withDefaultConfig(true). + testObj := createBackstageTest(*bs).withDefaultConfig(). addToDefaultConfig("dynamic-plugins.yaml", "raw-dynamic-plugins.yaml"). addToDefaultConfig("deployment.yaml", "janus-deployment.yaml") @@ -83,18 +85,18 @@ func TestDefaultDynamicPlugins(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, model.backstageDeployment) - //dynamic-plugins-root - //dynamic-plugins-npmrc - //dynamic-plugins-auth - //vol-default-dynamic-plugins + // dynamic-plugins-root + // dynamic-plugins-npmrc + // dynamic-plugins-auth + // vol-default-dynamic-plugins assert.Equal(t, 4, len(model.backstageDeployment.podSpec().Volumes)) ic := initContainer(model) assert.NotNil(t, ic) - //dynamic-plugins-root - //dynamic-plugins-npmrc - //dynamic-plugins-auth - //vol-default-dynamic-plugins + // dynamic-plugins-root + // dynamic-plugins-npmrc + // dynamic-plugins-auth + // vol-default-dynamic-plugins assert.Equal(t, 4, len(ic.VolumeMounts)) deps, err := model.DynamicPlugins.Dependencies() @@ -106,14 +108,14 @@ func TestDefaultDynamicPlugins(t *testing.T) { func TestDefaultAndSpecifiedDynamicPlugins(t *testing.T) { bs := testDynamicPluginsBackstage.DeepCopy() - bs.Spec.Application.DynamicPluginsConfigMapName = "dplugin" + bs.Spec.Application.DynamicPluginsConfigMapName = dplugin - testObj := createBackstageTest(*bs).withDefaultConfig(true). + testObj := createBackstageTest(*bs).withDefaultConfig(). addToDefaultConfig("dynamic-plugins.yaml", "raw-dynamic-plugins.yaml"). addToDefaultConfig("deployment.yaml", "janus-deployment.yaml") testObj.externalConfig.DynamicPlugins = corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Name: "dplugin"}, + ObjectMeta: metav1.ObjectMeta{Name: dplugin}, Data: map[string]string{DynamicPluginsFile: "dynamic-plugins.yaml: | \n plugins: []"}, } @@ -124,10 +126,10 @@ func TestDefaultAndSpecifiedDynamicPlugins(t *testing.T) { ic := initContainer(model) assert.NotNil(t, ic) - //dynamic-plugins-root - //dynamic-plugins-npmrc - //dynamic-plugins-auth - //vol-dplugin + // dynamic-plugins-root + // dynamic-plugins-npmrc + // dynamic-plugins-auth + // vol-dplugin assert.Equal(t, 4, len(ic.VolumeMounts)) assert.Equal(t, utils.GenerateVolumeNameFromCmOrSecret(DynamicPluginsDefaultName(bs.Name)), ic.VolumeMounts[3].Name) @@ -139,13 +141,13 @@ func TestDefaultAndSpecifiedDynamicPlugins(t *testing.T) { func TestSpecifiedOnlyDynamicPlugins(t *testing.T) { bs := testDynamicPluginsBackstage.DeepCopy() - bs.Spec.Application.DynamicPluginsConfigMapName = "dplugin" + bs.Spec.Application.DynamicPluginsConfigMapName = dplugin - testObj := createBackstageTest(*bs).withDefaultConfig(true). + testObj := createBackstageTest(*bs).withDefaultConfig(). addToDefaultConfig("deployment.yaml", "janus-deployment.yaml") testObj.externalConfig.DynamicPlugins = corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Name: "dplugin"}, + ObjectMeta: metav1.ObjectMeta{Name: dplugin}, Data: map[string]string{DynamicPluginsFile: "dynamic-plugins.yaml: | \n plugins: []"}, } @@ -156,10 +158,10 @@ func TestSpecifiedOnlyDynamicPlugins(t *testing.T) { ic := initContainer(model) assert.NotNil(t, ic) - //dynamic-plugins-root - //dynamic-plugins-npmrc - //dynamic-plugins-auth - //dplugin + // dynamic-plugins-root + // dynamic-plugins-npmrc + // dynamic-plugins-auth + // dplugin assert.Equal(t, 4, len(ic.VolumeMounts)) assert.Equal(t, bs.Spec.Application.DynamicPluginsConfigMapName, ic.VolumeMounts[3].Name) @@ -171,9 +173,9 @@ func TestSpecifiedOnlyDynamicPlugins(t *testing.T) { func TestDynamicPluginsFailOnArbitraryDepl(t *testing.T) { bs := testDynamicPluginsBackstage.DeepCopy() - //bs.Spec.Application.DynamicPluginsConfigMapName = "dplugin" + // bs.Spec.Application.DynamicPluginsConfigMapName = dplugin - testObj := createBackstageTest(*bs).withDefaultConfig(true). + testObj := createBackstageTest(*bs).withDefaultConfig(). addToDefaultConfig("dynamic-plugins.yaml", "raw-dynamic-plugins.yaml") _, err := InitObjects(context.TODO(), *bs, testObj.externalConfig, platform.Default, testObj.scheme) @@ -186,7 +188,7 @@ func TestNotConfiguredDPsNotInTheModel(t *testing.T) { bs := testDynamicPluginsBackstage.DeepCopy() assert.Empty(t, bs.Spec.Application.DynamicPluginsConfigMapName) - testObj := createBackstageTest(*bs).withDefaultConfig(true) + testObj := createBackstageTest(*bs).withDefaultConfig() m, err := InitObjects(context.TODO(), *bs, testObj.externalConfig, platform.Default, testObj.scheme) @@ -201,9 +203,9 @@ func TestNotConfiguredDPsNotInTheModel(t *testing.T) { func TestWithDynamicPluginsDeps(t *testing.T) { bs := testDynamicPluginsBackstage.DeepCopy() - bs.Spec.Application.DynamicPluginsConfigMapName = "dplugin" + bs.Spec.Application.DynamicPluginsConfigMapName = dplugin - testObj := createBackstageTest(*bs).withDefaultConfig(true). + testObj := createBackstageTest(*bs).withDefaultConfig(). addToDefaultConfig("dynamic-plugins.yaml", "raw-dynamic-plugins.yaml"). addToDefaultConfig("deployment.yaml", "janus-deployment.yaml") @@ -217,7 +219,7 @@ plugins: ` testObj.externalConfig.DynamicPlugins = corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Name: "dplugin"}, + ObjectMeta: metav1.ObjectMeta{Name: dplugin}, Data: map[string]string{DynamicPluginsFile: yamlData}, } @@ -248,13 +250,12 @@ func initContainer(model *BackstageModel) *corev1.Container { return nil } - // TestCatalogIndexImageFromDefaultConfig verifies that the operator sets CATALOG_INDEX_IMAGE // on the install-dynamic-plugins init container from the default config by default func TestCatalogIndexImageFromDefaultConfig(t *testing.T) { bs := testDynamicPluginsBackstage.DeepCopy() - testObj := createBackstageTest(*bs).withDefaultConfig(true). + testObj := createBackstageTest(*bs).withDefaultConfig(). addToDefaultConfig("dynamic-plugins.yaml", "raw-dynamic-plugins.yaml"). addToDefaultConfig("deployment.yaml", "janus-deployment.yaml") @@ -268,7 +269,8 @@ func TestCatalogIndexImageFromDefaultConfig(t *testing.T) { assert.Len(t, ic.Env, 2) assert.Equal(t, "NPM_CONFIG_USERCONFIG", ic.Env[0].Name) assert.Equal(t, "CATALOG_INDEX_IMAGE", ic.Env[1].Name) - assert.Equal(t, "quay.io/rhdh/plugin-catalog-index:1.9", ic.Env[1].Value, "CATALOG_INDEX_IMAGE should be set from the default config") + assert.Equal(t, "quay.io/rhdh/plugin-catalog-index:1.9", ic.Env[1].Value, + "CATALOG_INDEX_IMAGE should be set from the default config") } // TestCatalogIndexImageOverridesDefaultConfig verifies that RELATED_IMAGE_catalog_index @@ -279,7 +281,7 @@ func TestCatalogIndexImageOverridesDefaultConfig(t *testing.T) { bs := testDynamicPluginsBackstage.DeepCopy() // janus-deployment.yaml has CATALOG_INDEX_IMAGE set (like the real default-config) - testObj := createBackstageTest(*bs).withDefaultConfig(true). + testObj := createBackstageTest(*bs).withDefaultConfig(). addToDefaultConfig("dynamic-plugins.yaml", "raw-dynamic-plugins.yaml"). addToDefaultConfig("deployment.yaml", "janus-deployment.yaml") @@ -296,7 +298,8 @@ func TestCatalogIndexImageOverridesDefaultConfig(t *testing.T) { assert.Len(t, ic.Env, 2) assert.Equal(t, "NPM_CONFIG_USERCONFIG", ic.Env[0].Name) assert.Equal(t, "CATALOG_INDEX_IMAGE", ic.Env[1].Name) - assert.Equal(t, "quay.io/fake-reg/img:1.2.3", ic.Env[1].Value, "RELATED_IMAGE_catalog_index should override the default config value") + assert.Equal(t, "quay.io/fake-reg/img:1.2.3", ic.Env[1].Value, + "RELATED_IMAGE_catalog_index should override the default config value") } // TestCatalogIndexImageUserPatchTakesPrecedence verifies that user-specified deployment patch @@ -319,7 +322,7 @@ spec: `), } - testObj := createBackstageTest(*bs).withDefaultConfig(true). + testObj := createBackstageTest(*bs).withDefaultConfig(). addToDefaultConfig("dynamic-plugins.yaml", "raw-dynamic-plugins.yaml"). addToDefaultConfig("deployment.yaml", "janus-deployment.yaml") @@ -335,7 +338,8 @@ spec: assert.Len(t, ic.Env, 2) assert.Equal(t, "NPM_CONFIG_USERCONFIG", ic.Env[0].Name) assert.Equal(t, "CATALOG_INDEX_IMAGE", ic.Env[1].Name) - assert.Equal(t, "quay.io/user-specified/image:2.0", ic.Env[1].Value, "user's deployment patch should override RELATED_IMAGE_catalog_index") + assert.Equal(t, "quay.io/user-specified/image:2.0", ic.Env[1].Value, + "user's deployment patch should override RELATED_IMAGE_catalog_index") } // TestCatalogIndexImageExtraEnvsOverride verifies that user-specified extraEnvs @@ -346,11 +350,15 @@ func TestCatalogIndexImageExtraEnvsOverride(t *testing.T) { // User specifies a different catalog index image via extraEnvs bs.Spec.Application.ExtraEnvs = &bsv1.ExtraEnvs{ Envs: []bsv1.Env{ - {Name: "CATALOG_INDEX_IMAGE", Value: "quay.io/rhdh/plugin-catalog-index:extra-env", Containers: []string{"install-dynamic-plugins"}}, + { + Name: "CATALOG_INDEX_IMAGE", + Value: "quay.io/rhdh/plugin-catalog-index:extra-env", + Containers: []string{"install-dynamic-plugins"}, + }, }, } - testObj := createBackstageTest(*bs).withDefaultConfig(true). + testObj := createBackstageTest(*bs).withDefaultConfig(). addToDefaultConfig("dynamic-plugins.yaml", "raw-dynamic-plugins.yaml"). addToDefaultConfig("deployment.yaml", "janus-deployment.yaml") @@ -367,7 +375,8 @@ func TestCatalogIndexImageExtraEnvsOverride(t *testing.T) { assert.Len(t, ic.Env, 2) assert.Equal(t, "NPM_CONFIG_USERCONFIG", ic.Env[0].Name) assert.Equal(t, "CATALOG_INDEX_IMAGE", ic.Env[1].Name) - assert.Equal(t, "quay.io/rhdh/plugin-catalog-index:extra-env", ic.Env[1].Value, "extraEnvs value should override the operator's RELATED_IMAGE_catalog_index") + assert.Equal(t, "quay.io/rhdh/plugin-catalog-index:extra-env", ic.Env[1].Value, + "extraEnvs value should override the operator's RELATED_IMAGE_catalog_index") } func TestUnmarshalDynaPluginsConfig(t *testing.T) { @@ -548,8 +557,8 @@ includes: assert.Equal(t, 5, len(mergedConfig.Plugins)) // Validate plugin-a (overridden by specData) - //pluginA := mergedConfig.Plugins[0] - //assert.Equal(t, "plugin-a", pluginA.Package) + // pluginA := mergedConfig.Plugins[0] + // assert.Equal(t, "plugin-a", pluginA.Package) pluginA := findPluginByPackage(mergedConfig.Plugins, "plugin-a") assert.NotNil(t, pluginA) assert.Equal(t, "sha256-overridden", pluginA.Integrity) @@ -564,17 +573,17 @@ includes: assert.Equal(t, true, pluginB.Disabled) // Validate plugin-c (from modelDp, as plugin-b is disabled) - //pluginC := mergedConfig.Plugins[1] + // pluginC := mergedConfig.Plugins[1] pluginC := findPluginByPackage(mergedConfig.Plugins, "plugin-c") assert.NotNil(t, pluginC) - //assert.Equal(t, "plugin-c", pluginC.Package) + // assert.Equal(t, "plugin-c", pluginC.Package) assert.Equal(t, "sha256-ghi789", pluginC.Integrity) assert.Equal(t, "value3", pluginC.PluginConfig["key3"]) - //pluginD := mergedConfig.Plugins[2] + // pluginD := mergedConfig.Plugins[2] pluginD := findPluginByPackage(mergedConfig.Plugins, "plugin-d") assert.NotNil(t, pluginD) - //assert.Equal(t, "plugin-d", pluginD.Package) + // assert.Equal(t, "plugin-d", pluginD.Package) assert.Equal(t, "sha256-ddd", pluginD.Integrity) // Validate merged includes diff --git a/pkg/model/model_tests.go b/pkg/model/model_tests.go index a7d497218..87effcea1 100644 --- a/pkg/model/model_tests.go +++ b/pkg/model/model_tests.go @@ -19,7 +19,8 @@ import ( bsv1 "github.com/redhat-developer/rhdh-operator/api/v1alpha5" ) -// testBackstageObject it is a helper object to simplify testing model component allowing to customize and isolate testing configuration +// testBackstageObject it is a helper object to simplify testing model component +// allowing to customize and isolate testing configuration. // usual sequence of creating testBackstageObject contains such a steps: // createBackstageTest(bsv1.Backstage). // withDefaultConfig(useDef bool) @@ -49,18 +50,19 @@ func (b *testBackstageObject) withLocalDb() *testBackstageObject { } // tells if object should use default Backstage Deployment/Service configuration from ./testdata/default-config or not -func (b *testBackstageObject) withDefaultConfig(useDef bool) *testBackstageObject { - if useDef { - // here we have default-config folder - _ = os.Setenv("LOCALBIN", "./testdata") - } else { - _ = os.Setenv("LOCALBIN", ".") - } +func (b *testBackstageObject) withDefaultConfig( /* useDef bool */ ) *testBackstageObject { + // if useDef { + // // here we have default-config folder + _ = os.Setenv("LOCALBIN", "./testdata") + // } else { + // _ = os.Setenv("LOCALBIN", ".") + // } return b } // adds particular part of configuration pointing to configuration key -// where key is configuration key (such as "deployment.yaml" and fileName is a name of additional conf file in ./testdata +// where key is configuration key (such as "deployment.yaml") +// and fileName is a name of additional conf file in ./testdata func (b *testBackstageObject) addToDefaultConfig(key string, fileName string) *testBackstageObject { yaml, err := readTestYamlFile(fileName) diff --git a/pkg/model/plugin_deps.go b/pkg/model/plugin_deps.go index 777a860ab..12e5740a8 100644 --- a/pkg/model/plugin_deps.go +++ b/pkg/model/plugin_deps.go @@ -15,7 +15,9 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) -func GetPluginDeps(backstage bs.Backstage, plugins DynamicPlugins, scheme *runtime.Scheme) ([]*unstructured.Unstructured, error) { +func GetPluginDeps( + backstage bs.Backstage, plugins DynamicPlugins, scheme *runtime.Scheme, +) ([]*unstructured.Unstructured, error) { dir, ok := os.LookupEnv("PLUGIN_DEPS_DIR_backstage") if !ok { @@ -27,7 +29,7 @@ func GetPluginDeps(backstage bs.Backstage, plugins DynamicPlugins, scheme *runti return nil, fmt.Errorf("failed to get plugin dependencies: %w", err) } - //get refs from enabled + // get refs from enabled var refs []string for _, dep := range pdeps { if dep.Ref != "" { diff --git a/pkg/model/pvcs.go b/pkg/model/pvcs.go index 325390546..ce4d66907 100644 --- a/pkg/model/pvcs.go +++ b/pkg/model/pvcs.go @@ -23,21 +23,34 @@ func init() { } func (b *BackstagePvcs) addExternalConfig(spec bsv1.BackstageSpec) error { - if spec.Application == nil || spec.Application.ExtraFiles == nil || spec.Application.ExtraFiles.Pvcs == nil || len(spec.Application.ExtraFiles.Pvcs) == 0 { + if spec.Application == nil || + spec.Application.ExtraFiles == nil || + spec.Application.ExtraFiles.Pvcs == nil || + len(spec.Application.ExtraFiles.Pvcs) == 0 { return nil } for _, pvcSpec := range spec.Application.ExtraFiles.Pvcs { subPath := "" - mountPath, wSubpath := b.model.backstageDeployment.mountPath(pvcSpec.MountPath, "", spec.Application.ExtraFiles.MountPath) + mountPath, wSubpath := b.model.backstageDeployment.mountPath( + pvcSpec.MountPath, + "", + spec.Application.ExtraFiles.MountPath, + ) if wSubpath { mountPath = filepath.Join(mountPath, pvcSpec.Name) subPath = utils.ToRFC1123Label(pvcSpec.Name) } - err := addPvc(b.model.backstageDeployment, pvcSpec.Name, mountPath, subPath, containersFilter{names: pvcSpec.Containers}) + err := addPvc( + b.model.backstageDeployment, + pvcSpec.Name, + mountPath, + subPath, + containersFilter{names: pvcSpec.Containers}, + ) if err != nil { return fmt.Errorf("failed to add pvc %s: %w", pvcSpec.Name, err) } @@ -62,9 +75,9 @@ func (b *BackstagePvcs) setObject(object runtime.Object) { b.pvcs = object.(*multiobject.MultiObject) } -//func (b *BackstagePvcs) EmptyObject() client.Object { +// func (b *BackstagePvcs) EmptyObject() client.Object { // return &corev1.PersistentVolumeClaim{} -//} +// } func (b *BackstagePvcs) addToModel(model *BackstageModel, _ bsv1.Backstage) (bool, error) { b.model = model @@ -82,7 +95,9 @@ func (b *BackstagePvcs) updateAndValidate(_ bsv1.Backstage) error { return fmt.Errorf("payload is not corev1.PersistentVolumeClaim: %T", o) } mountPath, subPath := b.model.backstageDeployment.getDefConfigMountPath(o) - err := addPvc(b.model.backstageDeployment, pvc.Name, mountPath, subPath, containersFilter{annotation: o.GetAnnotations()[ContainersAnnotation]}) + annotation := o.GetAnnotations()[ContainersAnnotation] + err := addPvc( + b.model.backstageDeployment, pvc.Name, mountPath, subPath, containersFilter{annotation: annotation}) if err != nil { return fmt.Errorf("failed to get containers for pvc %s: %w", o.GetName(), err) } @@ -114,7 +129,7 @@ func addPvc(bsd *BackstageDeployment, pvcName, mountPath, subPath string, filter return fmt.Errorf("failed to mount files for pvc %s: %w", pvcName, err) } for _, c := range affectedContainers { - //update := bsd.containerByName(c) + // update := bsd.containerByName(c) c.VolumeMounts = append(c.VolumeMounts, corev1.VolumeMount{Name: volName, MountPath: mountPath, SubPath: subPath}) } diff --git a/pkg/model/pvcs_test.go b/pkg/model/pvcs_test.go index 2d4f5978d..5c444b10f 100644 --- a/pkg/model/pvcs_test.go +++ b/pkg/model/pvcs_test.go @@ -38,7 +38,7 @@ func TestDefaultPvcs(t *testing.T) { }, } - testObj := createBackstageTest(bs).withDefaultConfig(true).addToDefaultConfig("pvcs.yaml", "multi-pvc.yaml") + testObj := createBackstageTest(bs).withDefaultConfig().addToDefaultConfig("pvcs.yaml", "multi-pvc.yaml") model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.OpenShift, testObj.scheme) assert.NoError(t, err) @@ -46,7 +46,8 @@ func TestDefaultPvcs(t *testing.T) { obj := model.getRuntimeObjectByType(&BackstagePvcs{}) assert.NotNil(t, obj) - assert.Equal(t, utils.GetObjectKind(&corev1.PersistentVolumeClaim{}, testObj.scheme).Kind, obj.Object().GetObjectKind().GroupVersionKind().Kind) + expectedKind := utils.GetObjectKind(&corev1.PersistentVolumeClaim{}, testObj.scheme).Kind + assert.Equal(t, expectedKind, obj.Object().GetObjectKind().GroupVersionKind().Kind) mv, ok := obj.Object().(*multiobject.MultiObject) assert.True(t, ok) assert.Equal(t, 2, len(mv.Items)) @@ -59,7 +60,8 @@ func TestDefaultPvcs(t *testing.T) { assert.Equal(t, PvcsName(bs.Name, "myclaim1"), model.backstageDeployment.podSpec().Volumes[0].Name) assert.Equal(t, 2, len(model.backstageDeployment.container().VolumeMounts)) assert.Equal(t, PvcsName(bs.Name, "myclaim1"), model.backstageDeployment.container().VolumeMounts[0].Name) - assert.Equal(t, filepath.Join(DefaultMountDir, PvcsName(bs.Name, "myclaim1")), model.backstageDeployment.container().VolumeMounts[0].MountPath) + expectedMountPath := filepath.Join(DefaultMountDir, PvcsName(bs.Name, "myclaim1")) + assert.Equal(t, expectedMountPath, model.backstageDeployment.container().VolumeMounts[0].MountPath) assert.Equal(t, "/mount/path/from/annotation", model.backstageDeployment.container().VolumeMounts[1].MountPath) } @@ -71,7 +73,9 @@ func TestMultiContainersPvc(t *testing.T) { }, } - testObj := createBackstageTest(bs).withDefaultConfig(true).addToDefaultConfig("deployment.yaml", "multicontainer-deployment.yaml").addToDefaultConfig("pvcs.yaml", "multi-pvc-containers.yaml") + testObj := createBackstageTest(bs).withDefaultConfig(). + addToDefaultConfig("deployment.yaml", "multicontainer-deployment.yaml"). + addToDefaultConfig("pvcs.yaml", "multi-pvc-containers.yaml") model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.OpenShift, testObj.scheme) assert.NoError(t, err) assert.NotNil(t, model) @@ -111,7 +115,7 @@ func TestSpecifiedPvcs(t *testing.T) { }, } - testObj := createBackstageTest(bs).withDefaultConfig(true) + testObj := createBackstageTest(bs).withDefaultConfig() testObj.externalConfig.ExtraPvcKeys = []string{"my-pvc1", "my-pvc2"} @@ -151,7 +155,8 @@ func TestSpecifiedPvcsWithContainers(t *testing.T) { }, } - testObj := createBackstageTest(bs).withDefaultConfig(true).addToDefaultConfig("deployment.yaml", "multicontainer-deployment.yaml") + testObj := createBackstageTest(bs).withDefaultConfig(). + addToDefaultConfig("deployment.yaml", "multicontainer-deployment.yaml") testObj.externalConfig.ExtraPvcKeys = []string{"my-pvc1", "my-pvc2"} @@ -181,7 +186,7 @@ func TestPvcsWithNonExistedContainerFailed(t *testing.T) { }, } - testObj := createBackstageTest(bs).withDefaultConfig(true) + testObj := createBackstageTest(bs).withDefaultConfig() _, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) diff --git a/pkg/model/route.go b/pkg/model/route.go index 2c81ead9c..0bba77596 100644 --- a/pkg/model/route.go +++ b/pkg/model/route.go @@ -90,9 +90,9 @@ func (b *BackstageRoute) setObject(obj runtime.Object) { } // implementation of RuntimeObject interface -//func (b *BackstageRoute) EmptyObject() client.Object { -// return &openshift.Route{} -//} +// func (b *BackstageRoute) EmptyObject() client.Object { +// return &openshift.Route{} +// } // implementation of RuntimeObject interface func (b *BackstageRoute) addToModel(model *BackstageModel, backstage bsv1.Backstage) (bool, error) { @@ -144,12 +144,14 @@ func (b *BackstageRoute) setMetaInfo(backstage bsv1.Backstage, scheme *runtime.S } // updateAppConfigWithBaseUrls tries to set the baseUrl in the default app-config. -// Note that this is purposely done on a best effort basis. So it is not considered an issue if the cluster ingress domain -// could not be determined, since the user can always set it explicitly in their custom app-config. +// Note that this is purposely done on a best effort basis. +// So it is not considered an issue if the cluster ingress domain could not be determined, +// since the user can always set it explicitly in their custom app-config. func (b *BackstageRoute) updateAppConfigWithBaseUrls(m *BackstageModel, backstage bsv1.Backstage) { if m.appConfig == nil || m.appConfig.ConfigMap == nil { klog.V(1).Infof( - "Default app-config ConfigMap not initialized yet - skipping automatic population of base URLS in the default app-config for Backstage %s", + "Default app-config ConfigMap not initialized yet - "+ + "skipping automatic population of base URLS in the default app-config for Backstage %s", backstage.Name) return } diff --git a/pkg/model/route_test.go b/pkg/model/route_test.go index 7081a81b7..603d2e672 100644 --- a/pkg/model/route_test.go +++ b/pkg/model/route_test.go @@ -34,7 +34,7 @@ func TestDefaultRoute(t *testing.T) { } assert.True(t, bs.Spec.IsRouteEnabled()) - testObj := createBackstageTest(bs).withDefaultConfig(true).addToDefaultConfig("route.yaml", "raw-route.yaml") + testObj := createBackstageTest(bs).withDefaultConfig().addToDefaultConfig("route.yaml", "raw-route.yaml") model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.OpenShift, testObj.scheme) @@ -64,7 +64,7 @@ func TestSpecifiedRoute(t *testing.T) { Route: &bsv1.Route{ Enabled: ptr.To(true), Host: "TestSpecifiedRoute", - //TLS: nil, + // TLS: nil, }, }, }, @@ -73,7 +73,7 @@ func TestSpecifiedRoute(t *testing.T) { assert.True(t, bs.Spec.IsRouteEnabled()) // Test w/o default route configured - testObjNoDef := createBackstageTest(bs).withDefaultConfig(true) + testObjNoDef := createBackstageTest(bs).withDefaultConfig() model, err := InitObjects(context.TODO(), bs, testObjNoDef.externalConfig, platform.OpenShift, testObjNoDef.scheme) assert.NoError(t, err) @@ -110,20 +110,20 @@ func TestDisabledRoute(t *testing.T) { Route: &bsv1.Route{ Enabled: ptr.To(false), Host: "TestSpecifiedRoute", - //TLS: nil, + // TLS: nil, }, }, }, } // With def route config - testObj := createBackstageTest(bs).withDefaultConfig(true).addToDefaultConfig("route.yaml", "raw-route.yaml") + testObj := createBackstageTest(bs).withDefaultConfig().addToDefaultConfig("route.yaml", "raw-route.yaml") model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.OpenShift, testObj.scheme) assert.NoError(t, err) assert.Nil(t, model.route) // W/o def route config - testObj = createBackstageTest(bs).withDefaultConfig(true) + testObj = createBackstageTest(bs).withDefaultConfig() model, err = InitObjects(context.TODO(), bs, testObj.externalConfig, platform.OpenShift, testObj.scheme) assert.NoError(t, err) assert.Nil(t, model.route) @@ -137,18 +137,18 @@ func TestExcludedRoute(t *testing.T) { Name: "TestSpecifiedRoute", Namespace: "ns123", }, - //Spec: bsv1.BackstageSpec{ // //Application: &bsv1.Application{}, - //}, + // Spec: bsv1.BackstageSpec{ // //Application: &bsv1.Application{}, + // }, } // With def route config - create default route - testObj := createBackstageTest(bs).withDefaultConfig(true).addToDefaultConfig("route.yaml", "raw-route.yaml") + testObj := createBackstageTest(bs).withDefaultConfig().addToDefaultConfig("route.yaml", "raw-route.yaml") model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.OpenShift, testObj.scheme) assert.NoError(t, err) assert.NotNil(t, model.route) // W/o def route config - do not create route - testObj = createBackstageTest(bs).withDefaultConfig(true) + testObj = createBackstageTest(bs).withDefaultConfig() model, err = InitObjects(context.TODO(), bs, testObj.externalConfig, platform.OpenShift, testObj.scheme) assert.NoError(t, err) assert.Nil(t, model.route) @@ -169,13 +169,13 @@ func TestEnabledRoute(t *testing.T) { } // With def route config - testObj := createBackstageTest(bs).withDefaultConfig(true).addToDefaultConfig("route.yaml", "raw-route.yaml") + testObj := createBackstageTest(bs).withDefaultConfig().addToDefaultConfig("route.yaml", "raw-route.yaml") model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.OpenShift, testObj.scheme) assert.NoError(t, err) assert.NotNil(t, model.route) // W/o def route config - testObj = createBackstageTest(bs).withDefaultConfig(true) + testObj = createBackstageTest(bs).withDefaultConfig() model, err = InitObjects(context.TODO(), bs, testObj.externalConfig, platform.OpenShift, testObj.scheme) assert.NoError(t, err) assert.NotNil(t, model.route) @@ -422,15 +422,15 @@ organization: assert.Equal(t, expected, v["backend"].(map[string]any)["cors"].(map[string]any)["origin"].(string)) } - //The other fields defined in the default app-config should still be present + // The other fields defined in the default app-config should still be present assert.Equal(t, "My Awesome App", res["my-default-app-config-1.yaml"]["app"].(map[string]any)["title"].(string)) assert.NotNil(t, res["my-default-app-config-1.yaml"]["plugin1"].(map[string]any)) assert.Equal(t, "My Company", res["my-default-app-config-2.yaml"]["organization"].(map[string]any)["name"].(string)) - assert.True(t, - res["my-default-app-config-2.yaml"]["backend"].(map[string]any)["auth"].(map[string]any)["dangerouslyDisableDefaultAuthPolicy"].(bool)) + authPolicy := res["my-default-app-config-2.yaml"]["backend"].(map[string]any)["auth"].(map[string]any) + assert.True(t, authPolicy["dangerouslyDisableDefaultAuthPolicy"].(bool)) assert.True(t, res["my-default-app-config-2.yaml"]["backend"].(map[string]any)["cors"].(map[string]any)["credentials"].(bool)) }, diff --git a/pkg/model/runtime.go b/pkg/model/runtime.go index 108bdc952..51dacc1d7 100644 --- a/pkg/model/runtime.go +++ b/pkg/model/runtime.go @@ -8,9 +8,10 @@ import ( "reflect" "slices" - "github.com/redhat-developer/rhdh-operator/pkg/platform" "k8s.io/apimachinery/pkg/runtime/schema" + "github.com/redhat-developer/rhdh-operator/pkg/platform" + "github.com/redhat-developer/rhdh-operator/pkg/model/multiobject" "sigs.k8s.io/controller-runtime/pkg/client" @@ -98,7 +99,13 @@ func registerConfig(key string, factory ObjectFactory, multiple bool) { } // InitObjects performs a main loop for configuring and making the array of objects to reconcile -func InitObjects(ctx context.Context, backstage bsv1.Backstage, externalConfig ExternalConfig, platform platform.Platform, scheme *runtime.Scheme) (*BackstageModel, error) { +func InitObjects( + ctx context.Context, + backstage bsv1.Backstage, + externalConfig ExternalConfig, + plt platform.Platform, + scheme *runtime.Scheme, +) (*BackstageModel, error) { // 3 phases of Backstage configuration: // 1- load from Operator defaults, modify metadata (labels, selectors..) and namespace as needed @@ -109,7 +116,13 @@ func InitObjects(ctx context.Context, backstage bsv1.Backstage, externalConfig E lg := log.FromContext(ctx) lg.V(1) - model := &BackstageModel{RuntimeObjects: make([]RuntimeObject, 0), ExternalConfig: externalConfig, localDbEnabled: backstage.Spec.IsLocalDbEnabled(), isOpenshift: platform.IsOpenshift(), DynamicPlugins: DynamicPlugins{}} + model := &BackstageModel{ + RuntimeObjects: make([]RuntimeObject, 0), + ExternalConfig: externalConfig, + localDbEnabled: backstage.Spec.IsLocalDbEnabled(), + isOpenshift: plt.IsOpenshift(), + DynamicPlugins: DynamicPlugins{}, + } ecs := make([]ExternalConfigContributor, 0) // looping through the registered runtimeConfig objects initializing the model @@ -118,17 +131,17 @@ func InitObjects(ctx context.Context, backstage bsv1.Backstage, externalConfig E // creating the instance of backstageObject backstageObject := conf.ObjectFactory.newBackstageObject() - //var templ = backstageObject.EmptyObject() - if objs, err := utils.ReadYamlFiles(utils.DefFile(conf.Key), *scheme, platform.Extension); err != nil { + // var templ = backstageObject.EmptyObject() + if objs, err := utils.ReadYamlFiles(utils.DefFile(conf.Key), *scheme, plt.Extension); err != nil { if !errors.Is(err, os.ErrNotExist) { return nil, fmt.Errorf("failed to read default value for the key %s, reason: %s", conf.Key, err) } } else { - if obj, err := adjustObject(conf, objs); err != nil { + obj, err := adjustObject(conf, objs) + if err != nil { return nil, fmt.Errorf("failed to initialize object: %w", err) - } else { - backstageObject.setObject(obj) } + backstageObject.setObject(obj) } // read configuration defined in BackstageCR.Spec.RawConfigContent ConfigMap @@ -142,11 +155,11 @@ func InitObjects(ctx context.Context, backstage bsv1.Backstage, externalConfig E return nil, fmt.Errorf("failed to read default value for the key %s, reason: %s", conf.Key, err) } } else { - if obj, err := adjustObject(conf, objs); err != nil { + obj, err := adjustObject(conf, objs) + if err != nil { return nil, fmt.Errorf("failed to initialize object: %w", err) - } else { - backstageObject.setObject(obj) } + backstageObject.setObject(obj) } } @@ -191,9 +204,9 @@ func setMetaInfo(clientObj client.Object, backstage bsv1.Backstage, scheme *runt clientObj.SetLabels(utils.SetKubeLabels(clientObj.GetLabels(), backstage.Name)) if err := controllerutil.SetControllerReference(&backstage, clientObj, scheme); err != nil { - //error should never have happened, - //otherwise the Operator has invalid (not a runtime.Object) or non-registered type. - //In both cases it will fail before this place + // error should never have happened, + // otherwise the Operator has invalid (not a runtime.Object) or non-registered type. + // In both cases it will fail before this place panic(err) } } diff --git a/pkg/model/runtime_test.go b/pkg/model/runtime_test.go index 614677934..7f221a9eb 100644 --- a/pkg/model/runtime_test.go +++ b/pkg/model/runtime_test.go @@ -18,14 +18,14 @@ import ( "github.com/stretchr/testify/assert" ) -//func TestIfEmptyObjectsContainTypeinfo(t *testing.T) { -// for _, cfg := range runtimeConfig { -// cfg.ObjectFactory.newBackstageObject() -// //assert.NotNil(t, Obj.EmptyObject()) -// // TODO uncomment when Kind is available -// //assert.NotEmpty(t, Obj.EmptyObject().GetObjectKind().GroupVersionKind().Kind) -// } -//} +// func TestIfEmptyObjectsContainTypeinfo(t *testing.T) { +// for _, cfg := range runtimeConfig { +// cfg.ObjectFactory.newBackstageObject() +// //assert.NotNil(t, Obj.EmptyObject()) +// // TODO uncomment when Kind is available +// //assert.NotEmpty(t, Obj.EmptyObject().GetObjectKind().GroupVersionKind().Kind) +// } +// } // NOTE: to make it work locally env var LOCALBIN should point to the directory where default-config folder located func TestInitDefaultDeploy(t *testing.T) { @@ -42,7 +42,7 @@ func TestInitDefaultDeploy(t *testing.T) { }, } - testObj := createBackstageTest(bs).withDefaultConfig(true) + testObj := createBackstageTest(bs).withDefaultConfig() model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) @@ -79,7 +79,7 @@ func TestIfEmptyObjectIsValid(t *testing.T) { }, } - testObj := createBackstageTest(bs).withDefaultConfig(true) + testObj := createBackstageTest(bs).withDefaultConfig() assert.False(t, bs.Spec.IsLocalDbEnabled()) @@ -103,7 +103,7 @@ func TestAddToModel(t *testing.T) { }, }, } - testObj := createBackstageTest(bs).withDefaultConfig(true) + testObj := createBackstageTest(bs).withDefaultConfig() model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) assert.NoError(t, err) @@ -141,7 +141,7 @@ func TestRawConfig(t *testing.T) { Namespace: "ns123", }, } - testObj := createBackstageTest(bs).withDefaultConfig(true) + testObj := createBackstageTest(bs).withDefaultConfig() serviceYaml := `apiVersion: v1 kind: Service metadata: @@ -174,7 +174,7 @@ spec: func TestMultiobject(t *testing.T) { bs := bsv1.Backstage{} - testObj := createBackstageTest(bs).withDefaultConfig(true).addToDefaultConfig("pvcs.yaml", "multi-pvc.yaml") + testObj := createBackstageTest(bs).withDefaultConfig().addToDefaultConfig("pvcs.yaml", "multi-pvc.yaml") model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) assert.NoError(t, err) assert.NotNil(t, model) @@ -191,7 +191,7 @@ func TestMultiobject(t *testing.T) { func TestSingleMultiobject(t *testing.T) { bs := bsv1.Backstage{} - testObj := createBackstageTest(bs).withDefaultConfig(true).addToDefaultConfig("pvcs.yaml", "single-pvc.yaml") + testObj := createBackstageTest(bs).withDefaultConfig().addToDefaultConfig("pvcs.yaml", "single-pvc.yaml") model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) assert.NoError(t, err) assert.NotNil(t, model) @@ -208,14 +208,14 @@ func TestSingleMultiobject(t *testing.T) { func TestSingleFailedWithMultiDefinition(t *testing.T) { bs := bsv1.Backstage{} - testObj := createBackstageTest(bs).withDefaultConfig(true).addToDefaultConfig("service.yaml", "multi-service-err.yaml") + testObj := createBackstageTest(bs).withDefaultConfig().addToDefaultConfig("service.yaml", "multi-service-err.yaml") _, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) assert.EqualError(t, err, "failed to initialize object: multiple objects not expected for: service.yaml") } func TestInvalidObjectKind(t *testing.T) { bs := bsv1.Backstage{} - testObj := createBackstageTest(bs).withDefaultConfig(true).addToDefaultConfig("service.yaml", "invalid-service-type.yaml") + testObj := createBackstageTest(bs).withDefaultConfig().addToDefaultConfig("service.yaml", "invalid-service-type.yaml") _, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) assert.ErrorContains(t, err, "failed to read default value for the key service.yaml") diff --git a/pkg/model/secretenvs.go b/pkg/model/secretenvs.go index f7de4c806..4d079236a 100644 --- a/pkg/model/secretenvs.go +++ b/pkg/model/secretenvs.go @@ -35,7 +35,8 @@ func (p *SecretEnvs) addExternalConfig(spec bsv1.BackstageSpec) error { } for _, specSec := range spec.Application.ExtraEnvs.Secrets { - err := p.model.backstageDeployment.addEnvVarsFrom(containersFilter{names: specSec.Containers}, SecretObjectKind, specSec.Name, specSec.Key) + err := p.model.backstageDeployment.addEnvVarsFrom( + containersFilter{names: specSec.Containers}, SecretObjectKind, specSec.Name, specSec.Key) if err != nil { return fmt.Errorf("failed to add env vars on secret %s: %w", specSec.Name, err) } @@ -57,9 +58,9 @@ func (p *SecretEnvs) setObject(obj runtime.Object) { } // implementation of RuntimeObject interface -//func (p *SecretEnvs) EmptyObject() client.Object { -// return &corev1.Secret{} -//} +// func (p *SecretEnvs) EmptyObject() client.Object { +// return &corev1.Secret{} +// } // implementation of RuntimeObject interface func (p *SecretEnvs) addToModel(model *BackstageModel, _ bsv1.Backstage) (bool, error) { @@ -79,7 +80,9 @@ func (p *SecretEnvs) updateAndValidate(_ bsv1.Backstage) error { if !ok { return fmt.Errorf("payload is not Secret kind: %T", item) } - err := p.model.backstageDeployment.addEnvVarsFrom(containersFilter{annotation: item.GetAnnotations()[ContainersAnnotation]}, SecretObjectKind, item.GetName(), "") + annotation := item.GetAnnotations()[ContainersAnnotation] + err := p.model.backstageDeployment.addEnvVarsFrom( + containersFilter{annotation: annotation}, SecretObjectKind, item.GetName(), "") if err != nil { return fmt.Errorf("failed to add env vars from secret %s: %w", item.GetName(), err) } diff --git a/pkg/model/secretenvs_test.go b/pkg/model/secretenvs_test.go index f1456ec37..99ce333ee 100644 --- a/pkg/model/secretenvs_test.go +++ b/pkg/model/secretenvs_test.go @@ -33,7 +33,7 @@ func TestDefaultSecretEnvFrom(t *testing.T) { bs := *secretEnvsTestBackstage.DeepCopy() - testObj := createBackstageTest(bs).withDefaultConfig(true).addToDefaultConfig(SecretEnvsObjectKey, "raw-sec-envs.yaml") + testObj := createBackstageTest(bs).withDefaultConfig().addToDefaultConfig(SecretEnvsObjectKey, "raw-sec-envs.yaml") model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) @@ -52,7 +52,8 @@ func TestDefaultMultiSecretEnv(t *testing.T) { bs := *secretEnvsTestBackstage.DeepCopy() - testObj := createBackstageTest(bs).withDefaultConfig(true).addToDefaultConfig("deployment.yaml", "multicontainer-deployment.yaml"). + testObj := createBackstageTest(bs).withDefaultConfig(). + addToDefaultConfig("deployment.yaml", "multicontainer-deployment.yaml"). addToDefaultConfig(SecretEnvsObjectKey, "raw-multi-secret.yaml") model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) @@ -81,7 +82,7 @@ func TestSpecifiedSecretEnvs(t *testing.T) { bs.Spec.Application.ExtraEnvs.Secrets = append(bs.Spec.Application.ExtraEnvs.Secrets, bsv1.EnvObjectRef{Name: "secName", Key: "ENV1"}) - testObj := createBackstageTest(bs).withDefaultConfig(true) + testObj := createBackstageTest(bs).withDefaultConfig() testObj.externalConfig.ExtraEnvConfigMapKeys = map[string]DataObjectKeys{} testObj.externalConfig.ExtraEnvConfigMapKeys["secName"] = NewDataObjectKeys(map[string]string{"secName": "ENV1"}, nil) @@ -101,62 +102,7 @@ func TestSpecifiedSecretEnvs(t *testing.T) { func TestSpecifiedSecretEnvsWithContainers(t *testing.T) { - bs := *secretEnvsTestBackstage.DeepCopy() - bs.Spec.Application = &bsv1.Application{ - ExtraEnvs: &bsv1.ExtraEnvs{ - Secrets: []bsv1.EnvObjectRef{ - { - Name: "secName", - Key: "ENV1", - Containers: []string{"install-dynamic-plugins", "another-container"}, - }, - }, - }, - } - - testObj := createBackstageTest(bs).withDefaultConfig(true).addToDefaultConfig("deployment.yaml", "multicontainer-deployment.yaml") - testObj.externalConfig.ExtraEnvSecretKeys = map[string]DataObjectKeys{} - testObj.externalConfig.ExtraEnvSecretKeys["secName"] = NewDataObjectKeys(map[string]string{"secName": "ENV1"}, nil) - - model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) - - assert.NoError(t, err) - assert.NotNil(t, model) - - cont := model.backstageDeployment.containerByName("install-dynamic-plugins") - assert.NotNil(t, cont) - assert.Equal(t, 1, len(cont.Env)) - assert.NotNil(t, cont.Env[0]) - assert.Equal(t, "ENV1", cont.Env[0].Name) - - cont = model.backstageDeployment.containerByName("another-container") - assert.NotNil(t, cont) - assert.Equal(t, 1, len(cont.Env)) - assert.NotNil(t, cont.Env[0]) - assert.Equal(t, "ENV1", cont.Env[0].Name) - - cont = model.backstageDeployment.containerByName("backstage-backend") - assert.NotNil(t, cont) - assert.Equal(t, 0, len(cont.Env)) - - // check * - bs.Spec.Application.ExtraEnvs.Secrets[0].Containers = []string{"*"} - - testObj = createBackstageTest(bs).withDefaultConfig(true).addToDefaultConfig("deployment.yaml", "multicontainer-deployment.yaml") - testObj.externalConfig.ExtraEnvSecretKeys = map[string]DataObjectKeys{} - testObj.externalConfig.ExtraEnvSecretKeys["secName"] = NewDataObjectKeys(map[string]string{"secName": "ENV1"}, nil) - - model, err = InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) - - assert.NoError(t, err) - assert.NotNil(t, model) - assert.Equal(t, 4, len(model.backstageDeployment.allContainers())) - for _, cn := range model.backstageDeployment.allContainers() { - c := model.backstageDeployment.containerByName(cn) - assert.Equal(t, 1, len(c.Env)) - assert.NotNil(t, c.Env[0]) - assert.Equal(t, "ENV1", c.Env[0].Name) - } + doCheckSpecifiedEnvsWithContainers(t, "secret") } func TestSecretEnvsWithNonExistedContainerFailed(t *testing.T) { @@ -173,7 +119,7 @@ func TestSecretEnvsWithNonExistedContainerFailed(t *testing.T) { }, } - testObj := createBackstageTest(bs).withDefaultConfig(true) + testObj := createBackstageTest(bs).withDefaultConfig() _, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) diff --git a/pkg/model/secretfiles.go b/pkg/model/secretfiles.go index bc5f19b81..ce75c0503 100644 --- a/pkg/model/secretfiles.go +++ b/pkg/model/secretfiles.go @@ -43,7 +43,8 @@ func (p *SecretFiles) addExternalConfig(spec bsv1.BackstageSpec) error { if specSec.MountPath == "" && specSec.Key == "" { return fmt.Errorf("key or mountPath has to be specified for secret %s", specSec.Name) } - mp, wSubpath := p.model.backstageDeployment.mountPath(specSec.MountPath, specSec.Key, spec.Application.ExtraFiles.MountPath) + mp, wSubpath := p.model.backstageDeployment.mountPath( + specSec.MountPath, specSec.Key, spec.Application.ExtraFiles.MountPath) keys := p.model.ExternalConfig.ExtraFileSecretKeys[specSec.Name].All() err := p.model.backstageDeployment.mountFilesFrom(containersFilter{names: specSec.Containers}, SecretObjectKind, specSec.Name, mp, specSec.Key, wSubpath, keys) @@ -68,9 +69,9 @@ func (p *SecretFiles) setObject(obj runtime.Object) { } // implementation of RuntimeObject interface -//func (p *SecretFiles) EmptyObject() client.Object { -// return &corev1.Secret{} -//} +// func (p *SecretFiles) EmptyObject() client.Object { +// return &corev1.Secret{} +// } // implementation of RuntimeObject interface func (p *SecretFiles) addToModel(model *BackstageModel, _ bsv1.Backstage) (bool, error) { @@ -93,8 +94,9 @@ func (p *SecretFiles) updateAndValidate(_ bsv1.Backstage) error { keys := append(maps.Keys(secret.Data), maps.Keys(secret.StringData)...) mountPath, subPath := p.model.backstageDeployment.getDefConfigMountPath(item) - //containers, err := p.model.backstageDeployment.filterContainerNames(utils.ParseCommaSeparated(item.GetAnnotations()[ContainersAnnotation])) - err := p.model.backstageDeployment.mountFilesFrom(containersFilter{annotation: item.GetAnnotations()[ContainersAnnotation]}, SecretObjectKind, + annotation := item.GetAnnotations()[ContainersAnnotation] + err := p.model.backstageDeployment.mountFilesFrom( + containersFilter{annotation: annotation}, SecretObjectKind, item.GetName(), mountPath, "", subPath != "", keys) if err != nil { return fmt.Errorf("failed to add files from secret %s: %w", item.GetName(), err) diff --git a/pkg/model/secretfiles_test.go b/pkg/model/secretfiles_test.go index b4383b585..b0a57dd12 100644 --- a/pkg/model/secretfiles_test.go +++ b/pkg/model/secretfiles_test.go @@ -39,7 +39,8 @@ func TestDefaultSecretFiles(t *testing.T) { bs := *secretFilesTestBackstage.DeepCopy() - testObj := createBackstageTest(bs).withDefaultConfig(true).addToDefaultConfig(SecretFilesObjectKey, "raw-secret-files.yaml") + testObj := createBackstageTest(bs).withDefaultConfig(). + addToDefaultConfig(SecretFilesObjectKey, "raw-secret-files.yaml") model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) @@ -66,7 +67,8 @@ func TestDefaultMultiSecretFiles(t *testing.T) { }, } - testObj := createBackstageTest(bs).withDefaultConfig(true).addToDefaultConfig("deployment.yaml", "multicontainer-deployment.yaml"). + testObj := createBackstageTest(bs).withDefaultConfig(). + addToDefaultConfig("deployment.yaml", "multicontainer-deployment.yaml"). addToDefaultConfig(SecretFilesObjectKey, "raw-multi-secret.yaml") model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) @@ -95,12 +97,13 @@ func TestSpecifiedSecretFiles(t *testing.T) { // https://issues.redhat.com/browse/RHIDP-2246 - mounting secret/CM with dot in the name *sf = append(*sf, bsv1.FileObjectRef{Name: "secret.dot", Key: "conf3.yaml"}) - testObj := createBackstageTest(bs).withDefaultConfig(true) + testObj := createBackstageTest(bs).withDefaultConfig() testObj.externalConfig.ExtraFileSecretKeys = map[string]DataObjectKeys{} testObj.externalConfig.ExtraFileSecretKeys["secret1"] = NewDataObjectKeys(nil, nil) testObj.externalConfig.ExtraFileSecretKeys["secret2"] = NewDataObjectKeys(map[string]string{"conf.yaml": "data"}, nil) - testObj.externalConfig.ExtraFileSecretKeys["secret.dot"] = NewDataObjectKeys(nil, map[string][]byte{"conf3.yaml": []byte("data")}) + testObj.externalConfig.ExtraFileSecretKeys["secret.dot"] = NewDataObjectKeys( + nil, map[string][]byte{"conf3.yaml": []byte("data")}) model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) @@ -135,7 +138,7 @@ func TestFailedValidation(t *testing.T) { sf := &bs.Spec.Application.ExtraFiles.Secrets *sf = append(*sf, bsv1.FileObjectRef{Name: "secret1"}) - testObj := createBackstageTest(bs).withDefaultConfig(true) + testObj := createBackstageTest(bs).withDefaultConfig() _, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) assert.ErrorContains(t, err, "key or mountPath has to be specified for secret secret1") @@ -146,9 +149,10 @@ func TestDefaultAndSpecifiedSecretFiles(t *testing.T) { bs := *secretFilesTestBackstage.DeepCopy() sf := &bs.Spec.Application.ExtraFiles.Secrets *sf = append(*sf, bsv1.FileObjectRef{Name: "secret1", Key: "conf.yaml"}) - testObj := createBackstageTest(bs).withDefaultConfig(true).addToDefaultConfig("secret-files.yaml", "raw-secret-files.yaml") + testObj := createBackstageTest(bs).withDefaultConfig().addToDefaultConfig("secret-files.yaml", "raw-secret-files.yaml") - testObj.externalConfig.ExtraFileSecretKeys = map[string]DataObjectKeys{"secret1": NewDataObjectKeys(map[string]string{"conf.yaml": ""}, nil)} + testObj.externalConfig.ExtraFileSecretKeys = map[string]DataObjectKeys{ + "secret1": NewDataObjectKeys(map[string]string{"conf.yaml": ""}, nil)} model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) @@ -169,9 +173,10 @@ func TestSpecifiedSecretFilesWithDataAndKey(t *testing.T) { bs := *secretFilesTestBackstage.DeepCopy() sf := &bs.Spec.Application.ExtraFiles.Secrets *sf = append(*sf, bsv1.FileObjectRef{Name: "secret1", Key: "conf.yaml"}) - testObj := createBackstageTest(bs).withDefaultConfig(true).addToDefaultConfig("secret-files.yaml", "raw-secret-files.yaml") + testObj := createBackstageTest(bs).withDefaultConfig().addToDefaultConfig("secret-files.yaml", "raw-secret-files.yaml") - testObj.externalConfig.ExtraFileSecretKeys = map[string]DataObjectKeys{"secret1": NewDataObjectKeys(nil, map[string][]byte{"conf.yaml": []byte("")})} + testObj.externalConfig.ExtraFileSecretKeys = map[string]DataObjectKeys{ + "secret1": NewDataObjectKeys(nil, map[string][]byte{"conf.yaml": []byte("")})} model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) @@ -193,11 +198,15 @@ func TestSpecifiedSecretFilesWithContainers(t *testing.T) { bs := *secretFilesTestBackstage.DeepCopy() sf := &bs.Spec.Application.ExtraFiles.Secrets - *sf = append(*sf, bsv1.FileObjectRef{Name: "secret1", Key: "conf1.yaml", Containers: []string{"install-dynamic-plugins", "another-container"}}) - *sf = append(*sf, bsv1.FileObjectRef{Name: "secret2", MountPath: "/custom/path", Containers: []string{"install-dynamic-plugins"}}) + *sf = append(*sf, bsv1.FileObjectRef{ + Name: "secret1", Key: "conf1.yaml", + Containers: []string{"install-dynamic-plugins", "another-container"}}) + *sf = append(*sf, bsv1.FileObjectRef{ + Name: "secret2", MountPath: "/custom/path", Containers: []string{"install-dynamic-plugins"}}) *sf = append(*sf, bsv1.FileObjectRef{Name: "secret3", MountPath: "rel", Containers: []string{"*"}}) - testObj := createBackstageTest(bs).withDefaultConfig(true).addToDefaultConfig("deployment.yaml", "multicontainer-deployment.yaml") + testObj := createBackstageTest(bs).withDefaultConfig(). + addToDefaultConfig("deployment.yaml", "multicontainer-deployment.yaml") testObj.externalConfig.ExtraFileSecretKeys = map[string]DataObjectKeys{} testObj.externalConfig.ExtraFileSecretKeys["secret1"] = NewDataObjectKeys(map[string]string{"conf1.yaml": "data"}, nil) @@ -232,7 +241,7 @@ func TestSecretFilesWithNonExistedContainerFailed(t *testing.T) { }, } - testObj := createBackstageTest(bs).withDefaultConfig(true) + testObj := createBackstageTest(bs).withDefaultConfig() _, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) @@ -246,9 +255,11 @@ func TestReplaceSecretFiles(t *testing.T) { sf := &bs.Spec.Application.ExtraFiles.Secrets *sf = append(*sf, bsv1.FileObjectRef{Name: "secret1", MountPath: DefaultMountDir}) - testObj := createBackstageTest(bs).withDefaultConfig(true).addToDefaultConfig("secret-files.yaml", "raw-secret-files.yaml") + testObj := createBackstageTest(bs).withDefaultConfig(). + addToDefaultConfig("secret-files.yaml", "raw-secret-files.yaml") - testObj.externalConfig.ExtraFileSecretKeys = map[string]DataObjectKeys{"secret1": NewDataObjectKeys(map[string]string{"dynamic-plugins321.yaml": "new"}, nil)} + testObj.externalConfig.ExtraFileSecretKeys = map[string]DataObjectKeys{ + "secret1": NewDataObjectKeys(map[string]string{"dynamic-plugins321.yaml": "new"}, nil)} model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) @@ -259,6 +270,6 @@ func TestReplaceSecretFiles(t *testing.T) { // secret1 is replacing secret from default assert.Equal(t, 1, len(deployment.container().VolumeMounts)) - //assert.Equal(t, DefaultMountDir+"/dynamic-plugins321.yaml", deployment.container().VolumeMounts[0].MountPath) + // assert.Equal(t, DefaultMountDir+"/dynamic-plugins321.yaml", deployment.container().VolumeMounts[0].MountPath) } diff --git a/pkg/model/service.go b/pkg/model/service.go index 02e4fc99a..125f9eb94 100644 --- a/pkg/model/service.go +++ b/pkg/model/service.go @@ -46,7 +46,8 @@ func (b *BackstageService) setObject(obj runtime.Object) { func (b *BackstageService) addToModel(model *BackstageModel, _ bsv1.Backstage) (bool, error) { b.model = model if b.service == nil { - return false, fmt.Errorf("backstage Service is not initialized, make sure there is service.yaml in default or raw configuration") + return false, fmt.Errorf( + "backstage Service is not initialized, make sure there is service.yaml in default or raw configuration") } model.backstageService = b model.setRuntimeObject(b) @@ -56,9 +57,9 @@ func (b *BackstageService) addToModel(model *BackstageModel, _ bsv1.Backstage) ( } // implementation of RuntimeObject interface -//func (b *BackstageService) EmptyObject() client.Object { -// return &corev1.Service{} -//} +// func (b *BackstageService) EmptyObject() client.Object { +// return &corev1.Service{} +// } // implementation of RuntimeObject interface func (b *BackstageService) updateAndValidate(_ bsv1.Backstage) error { diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index c153cdef7..ca269192c 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -1,17 +1,12 @@ package utils import ( - //"bytes" "crypto/rand" "encoding/base64" "errors" "fmt" - - //"io" "os" "path/filepath" - - //"reflect" "regexp" "strconv" "strings" @@ -62,7 +57,8 @@ func AddAnnotation(object client.Object, name string, value string) { object.GetAnnotations()[name] = value } -// GenerateRuntimeObjectName generates name using BackstageCR name and objectType which is ConfigObject Key without '.yaml' (like 'deployment') +// GenerateRuntimeObjectName generates name using BackstageCR name and objectType +// which is ConfigObject Key without '.yaml' (like 'deployment') func GenerateRuntimeObjectName(backstageCRName string, objectType string) string { return fmt.Sprintf("%s-%s", objectType, backstageCRName) } @@ -90,7 +86,9 @@ func BackstageDbAppLabelValue(backstageName string) string { // platformPatch - yaml content with platform specific patch, to be merged with manifest if exists // templ - template object to create new objects // scheme - runtime.Scheme -//func ReadYamls(manifest []byte, platformPatch []byte, templ runtime.Object, scheme runtime.Scheme) ([]client.Object, error) { +// +//nolint:lll +// func ReadYamls(manifest, platformPatch []byte, templ runtime.Object, scheme runtime.Scheme) ([]client.Object, error) { // // dec := yaml.NewYAMLOrJSONDecoder(bytes.NewReader(manifest), 1000) // @@ -130,7 +128,7 @@ func BackstageDbAppLabelValue(backstageName string) string { // } // // return objects, nil -//} +// } func ReadYamls(manifest []byte, platformPatch []byte, scheme runtime.Scheme) ([]client.Object, error) { sep := regexp.MustCompile(`(?m)^---\s*$`) @@ -199,7 +197,6 @@ func ReadYamlFiles(path string, scheme runtime.Scheme, platformExt string) ([]cl if err != nil { return nil, fmt.Errorf("failed to read platform patch: %w", err) } - //return ReadYamls(conf, pp, templ, scheme) return ReadYamls(conf, pp, scheme) } @@ -227,7 +224,8 @@ func checkObjectKind(object client.Object, scheme *runtime.Scheme) error { } } - return fmt.Errorf("GroupVersionKind not match, found: %v, expected: %v", object.GetObjectKind().GroupVersionKind(), gvks) + return fmt.Errorf( + "GroupVersionKind not match, found: %v, expected: %v", object.GetObjectKind().GroupVersionKind(), gvks) } @@ -303,7 +301,7 @@ func ParseCommaSeparated(input string) []string { return result } -//func FilterContainers(allContainers []string, filter string) []string { +// func FilterContainers(allContainers []string, filter string) []string { // if filter == "*" { // return allContainers // } else if filter == "" { @@ -319,4 +317,4 @@ func ParseCommaSeparated(input string) []string { // } // } // return filtered -//} +// } diff --git a/pkg/utils/utils_test.go b/pkg/utils/utils_test.go index 185a46ff1..d6256c92d 100644 --- a/pkg/utils/utils_test.go +++ b/pkg/utils/utils_test.go @@ -80,7 +80,7 @@ spec: requests: storage: 2Gi` - objects, err := ReadYamls([]byte(y), nil /*&corev1.PersistentVolumeClaim{},*/, *util_test_scheme) + objects, err := ReadYamls([]byte(y), nil /* &corev1.PersistentVolumeClaim{}, */, *util_test_scheme) assert.NoError(t, err) @@ -110,18 +110,20 @@ metadata: name: pvc2 data:` - _, err := ReadYamls([]byte(y), nil /*&corev1.PersistentVolumeClaim{},*/, *util_test_scheme) + _, err := ReadYamls([]byte(y), nil /* &corev1.PersistentVolumeClaim{}, */, *util_test_scheme) // Kind not match for the second item, PersistentVolumeClaim expected - // TODO with unstructured change we do not have this error anymore as we do not have expected kind. Should we test it in another way/place? - //assert.EqualError(t, err, "GroupVersionKind not match, found: /v1, Kind=ConfigMap, expected: [/v1, Kind=PersistentVolumeClaim]") + // TODO with unstructured change we do not have this error anymore as we do not have expected kind. + // Should we test it in another way/place? + // assert.EqualError(t, err, "GroupVersionKind not match, found: /v1, Kind=ConfigMap, + // expected: [/v1, Kind=PersistentVolumeClaim]") assert.NoError(t, err) } func TestPlatformPatchMerge(t *testing.T) { // ocp (no patch, so default) - //t.Setenv(PlatformEnvVar, "ocp") + // t.Setenv(PlatformEnvVar, "ocp") obj, err := ReadYamlFiles("testdata/deployment.yaml", *util_test_scheme, "ocp") assert.NoError(t, err) @@ -131,7 +133,7 @@ func TestPlatformPatchMerge(t *testing.T) { assert.Nil(t, depl.Spec.Template.Spec.SecurityContext) // k8s (patched) - //t.Setenv(PlatformEnvVar, "k8s") + // t.Setenv(PlatformEnvVar, "k8s") obj, err = ReadYamlFiles("testdata/deployment.yaml", *util_test_scheme, "k8s") assert.NoError(t, err) diff --git a/tests/e2e/e2e_suite_test.go b/tests/e2e/e2e_suite_test.go index a3e6a05bb..421e2080c 100644 --- a/tests/e2e/e2e_suite_test.go +++ b/tests/e2e/e2e_suite_test.go @@ -53,7 +53,9 @@ func installRhdhOperatorManifest(operatorManifest string) { func installRhdhOperator(flavor string) (podLabel string) { Expect(helper.IsOpenShift()).Should(BeTrue(), "install RHDH script works only on OpenShift clusters!") - cmd := exec.Command(filepath.Join(".rhdh", "scripts", "install-rhdh-catalog-source.sh"), "--"+flavor, "--install-operator", "rhdh") + cmd := exec.Command( + filepath.Join(".rhdh", "scripts", "install-rhdh-catalog-source.sh"), + "--"+flavor, "--install-operator", "rhdh") _, err := helper.Run(cmd) ExpectWithOffset(1, err).NotTo(HaveOccurred()) podLabel = "app=rhdh-operator" @@ -64,7 +66,7 @@ func installRhdhOperatorAirgapped() (podLabel string) { Expect(helper.IsOpenShift()).Should(BeTrue(), "airgap preparation script for RHDH works only on OpenShift clusters!") indexImg, ok := os.LookupEnv("BACKSTAGE_OPERATOR_TESTS_AIRGAP_INDEX_IMAGE") if !ok { - //TODO(rm3l): find a way to pass the right OCP version and arch + // TODO(rm3l): find a way to pass the right OCP version and arch indexImg = "quay.io/rhdh/iib:latest-v4.14-x86_64" } operatorVersion, ok := os.LookupEnv("BACKSTAGE_OPERATOR_TESTS_AIRGAP_OPERATOR_VERSION") @@ -117,20 +119,24 @@ spec: return podLabel } +func isEnvEnabled(env string) bool { + return os.Getenv(env) == "true" +} + func installOperatorWithMakeDeploy(withOlm bool) { img, err := helper.Run(exec.Command("make", "--no-print-directory", "show-img")) ExpectWithOffset(1, err).NotTo(HaveOccurred()) operatorImage := strings.TrimSpace(string(img)) imgArg := fmt.Sprintf("IMG=%s", operatorImage) - if os.Getenv("BACKSTAGE_OPERATOR_TESTS_BUILD_IMAGES") == "true" { + if isEnvEnabled("BACKSTAGE_OPERATOR_TESTS_BUILD_IMAGES") { By("building the manager(Operator) image") cmd := exec.Command("make", "image-build", imgArg) _, err = helper.Run(cmd) ExpectWithOffset(1, err).NotTo(HaveOccurred()) } - if os.Getenv("BACKSTAGE_OPERATOR_TESTS_PUSH_IMAGES") == "true" { + if isEnvEnabled("BACKSTAGE_OPERATOR_TESTS_PUSH_IMAGES") { By("building the manager(Operator) image") cmd := exec.Command("make", "image-push", imgArg) _, err = helper.Run(cmd) @@ -170,7 +176,7 @@ func installOperatorWithMakeDeploy(withOlm bool) { } var _ = SynchronizedBeforeSuite(func() []byte { - //runs *only* on process #1 + // runs *only* on process #1 _start = time.Now() GinkgoWriter.Println("isOpenshift:", helper.IsOpenShift()) @@ -192,15 +198,16 @@ var _ = SynchronizedBeforeSuite(func() []byte { } By("validating that the controller-manager pod is running as expected") - EventuallyWithOffset(1, verifyControllerUp, 5*time.Minute, time.Second).WithArguments(managerPodLabel).Should(Succeed()) + EventuallyWithOffset(1, verifyControllerUp, 5*time.Minute, time.Second). + WithArguments(managerPodLabel).Should(Succeed()) return nil }, func(_ []byte) { - //runs on *all* processes + // runs on *all* processes }) var _ = SynchronizedAfterSuite(func() { - //runs on *all* processes + // runs on *all* processes }, // the function below *only* on process #1 func() { @@ -318,7 +325,8 @@ func uninstallOperator() { } func uninstallRhdhOperator(withAirgap bool) { - cmd := exec.Command(helper.GetPlatformTool(), "delete", "subscription", "rhdh", "-n", _namespace, "--ignore-not-found=true") + cmd := exec.Command( + helper.GetPlatformTool(), "delete", "subscription", "rhdh", "-n", _namespace, "--ignore-not-found=true") _, err := helper.Run(cmd) ExpectWithOffset(1, err).NotTo(HaveOccurred()) @@ -326,7 +334,9 @@ func uninstallRhdhOperator(withAirgap bool) { if withAirgap { cs = "rhdh-disconnected-install" } - cmd = exec.Command(helper.GetPlatformTool(), "delete", "catalogsource", cs, "-n", "openshift-marketplace", "--ignore-not-found=true") + cmd = exec.Command( + helper.GetPlatformTool(), "delete", "catalogsource", cs, + "-n", "openshift-marketplace", "--ignore-not-found=true") _, err = helper.Run(cmd) ExpectWithOffset(1, err).NotTo(HaveOccurred()) diff --git a/tests/e2e/e2e_test.go b/tests/e2e/e2e_test.go index 67f12420c..7ed5e5872 100644 --- a/tests/e2e/e2e_test.go +++ b/tests/e2e/e2e_test.go @@ -88,7 +88,8 @@ var _ = Describe("Backstage Operator E2E", func() { return false, fmt.Errorf("invalid json: %q", respBody) } return gjson.Get(respBody, "#").Int() > 0, nil - }).WithMessage("be a valid and non-empty JSON array. This is the response from the 'GET /api/extensions/loaded-plugins' endpoint, using the guest user."), + }).WithMessage("be a valid and non-empty JSON array. " + + "This is the response from the 'GET /api/extensions/loaded-plugins' endpoint, using the guest user."), }, }, }, @@ -108,7 +109,6 @@ var _ = Describe("Backstage Operator E2E", func() { crName: "bs-raw-runtime-config", }, } { - tt := tt When(fmt.Sprintf("applying %s (%s)", tt.name, tt.crFilePath), func() { var crPath string var crLabel string @@ -125,7 +125,7 @@ var _ = Describe("Backstage Operator E2E", func() { It("should handle CR as expected", func() { By("validating that the status of the custom resource created is updated or not", func() { - // [{"lastTransitionTime":"2025-04-09T09:02:06Z","message":"","reason":"Deployed","status":"True","type":"Deployed"}] + // [{"lastTransitionTime":"2025-04-09T09:02:06Z",...,"type":"Deployed"}] Eventually(helper.VerifyBackstageCRStatus, time.Minute, 10*time.Second). WithArguments(ns, tt.crName, ContainSubstring(`"type":"Deployed"`)). Should(Succeed(), func() string { @@ -213,13 +213,14 @@ spec: _, err = helper.Run(cmd) Expect(err).ShouldNot(HaveOccurred()) }) - appUrlProvider = func(g Gomega) (context.CancelFunc, string) { - return nil, "http://" + appUrl + appUrlProvider = func(_ Gomega) (context.CancelFunc, string) { + return func() {}, "http://" + appUrl } } else { // Expose the service and reach localhost:7007 appUrlProvider = func(g Gomega) (context.CancelFunc, string) { - localPort, cancelFunc, err := helper.StartPortForward(context.Background(), fmt.Sprintf("backstage-%s", tt.crName), ns, 80) + localPort, cancelFunc, err := helper.StartPortForward( + context.Background(), fmt.Sprintf("backstage-%s", tt.crName), ns, 80) g.Expect(err).ShouldNot(HaveOccurred()) g.Expect(localPort).ShouldNot(BeZero()) return cancelFunc, fmt.Sprintf("http://127.0.0.1:%d", localPort) @@ -292,7 +293,10 @@ spec: }) }) -func ensureRouteIsReachable(reachabilityTimeoutMinutes time.Duration, ns string, crName string, crLabel string, additionalApiEndpointTests []helper.ApiEndpointTest) { +func ensureRouteIsReachable( + reachabilityTimeoutMinutes time.Duration, ns, crName, crLabel string, + additionalApiEndpointTests []helper.ApiEndpointTest, +) { Eventually(helper.VerifyBackstageRoute, reachabilityTimeoutMinutes, 10*time.Second). WithArguments(ns, crName, additionalApiEndpointTests). Should(Succeed(), func() string { diff --git a/tests/e2e/e2e_upgrade_test.go b/tests/e2e/e2e_upgrade_test.go index 620c311cd..95b83247d 100644 --- a/tests/e2e/e2e_upgrade_test.go +++ b/tests/e2e/e2e_upgrade_test.go @@ -47,7 +47,8 @@ var _ = Describe("Operator upgrade with existing instances", func() { cmd := exec.Command(helper.GetPlatformTool(), "apply", "-f", fromDeploymentManifest) _, err := helper.Run(cmd) Expect(err).ShouldNot(HaveOccurred()) - EventuallyWithOffset(1, verifyControllerUp, 5*time.Minute, time.Second).WithArguments("app=rhdh-operator").Should(Succeed()) + EventuallyWithOffset(1, verifyControllerUp, 5*time.Minute, time.Second). + WithArguments("app=rhdh-operator").Should(Succeed()) cmd = exec.Command(helper.GetPlatformTool(), "-n", ns, "apply", "-f", "-") stdin, err := cmd.StdinPipe() @@ -100,15 +101,17 @@ metadata: } else { installOperatorWithMakeDeploy(false) } - EventuallyWithOffset(1, verifyControllerUp, 5*time.Minute, 10*time.Second).WithArguments(managerPodLabel).Should(Succeed()) + EventuallyWithOffset(1, verifyControllerUp, 5*time.Minute, 10*time.Second). + WithArguments(managerPodLabel).Should(Succeed()) }) crLabel := fmt.Sprintf("rhdh.redhat.com/app=backstage-%s", crName) - // TODO(rm3l): this might never work because the Deployment may not necessarily change after an upgrade of the Operator - // It might not result in a different replicas if the newer operator did not change anything. - //By("ensuring the current operator eventually reconciled through the creation of a new ReplicaSet of the application") - //Eventually(func(g Gomega) { + // TODO(rm3l): this might never work because the Deployment may not necessarily + // change after an upgrade of the Operator. It might not result in a different replicas + // if the newer operator did not change anything. + // By("ensuring the current operator eventually reconciled through the new ReplicaSet") + // Eventually(func(g Gomega) { // cmd := exec.Command(helper.GetPlatformTool(), "get", // "replicasets", "-l", crLabel, // "-o", "go-template={{ range .items }}{{ if not .metadata.deletionTimestamp }}{{ .metadata.name }}"+ @@ -120,7 +123,7 @@ metadata: // rsNames := helper.GetNonEmptyLines(string(rsOutput)) // g.Expect(len(rsNames)).Should(BeNumerically(">=", 2), // fmt.Sprintf("expected at least 2 Backstage operand ReplicaSets, but got %d", len(rsNames))) - //}, 3*time.Minute, 3*time.Second).Should(Succeed(), fetchOperatorLogs) + // }, 3*time.Minute, 3*time.Second).Should(Succeed(), fetchOperatorLogs) By("checking the status of the existing CR") // [{"lastTransitionTime":"2025-04-09T09:02:06Z","message":"","reason":"Deployed","status":"True","type":"Deployed"}] @@ -145,7 +148,8 @@ metadata: podOutput, err := helper.Run(cmd) g.Expect(err).ShouldNot(HaveOccurred()) podNames := helper.GetNonEmptyLines(string(podOutput)) - g.Expect(podNames).Should(HaveLen(1), fmt.Sprintf("expected 1 Backstage operand pod(s) running, but got %d", len(podNames))) + g.Expect(podNames).Should( + HaveLen(1), fmt.Sprintf("expected 1 Backstage operand pod(s) running, but got %d", len(podNames))) }, 15*time.Minute, 10*time.Second).Should(Succeed(), func() string { return fmt.Sprintf("%s\n---\n%s", fetchOperatorAndOperandLogs(managerPodLabel, ns, crLabel), diff --git a/tests/helper/helper_backstage.go b/tests/helper/helper_backstage.go index 14da6e4c6..72ff2ff35 100644 --- a/tests/helper/helper_backstage.go +++ b/tests/helper/helper_backstage.go @@ -80,7 +80,8 @@ func GuestAuth(baseUrl string) (string, error) { return "", fmt.Errorf("error while trying to read response body from 'GET %q': %w", url, err) } if resp.StatusCode != 200 { - return "", fmt.Errorf("expected status code 200, but got %d in response to 'GET %q', body: %s", resp.StatusCode, url, string(body)) + return "", fmt.Errorf("expected status code 200, but got %d in response to 'GET %q', body: %s", + resp.StatusCode, url, string(body)) } var authResponse BackstageAuthRefreshResponse err = json.Unmarshal(body, &authResponse) @@ -104,7 +105,8 @@ func VerifyBackstagePodStatus(g Gomega, ns string, crName string, expectedStatus } func VerifyBackstageCRStatus(g Gomega, ns string, crName string, statusMatcher types.GomegaMatcher) { - cmd := exec.Command(GetPlatformTool(), "get", "backstage", crName, "-o", "jsonpath={.status.conditions}", "-n", ns) // #nosec G204 + cmd := exec.Command( + GetPlatformTool(), "get", "backstage", crName, "-o", "jsonpath={.status.conditions}", "-n", ns) // #nosec G204 status, err := Run(cmd) GinkgoWriter.Println(string(status)) g.Expect(err).ShouldNot(HaveOccurred()) @@ -116,7 +118,8 @@ func PatchBackstageCR(ns string, crName string, jsonPatch string, patchType stri if p == "" { p = "strategic" } - _, err := Run(exec.Command(GetPlatformTool(), "-n", ns, "patch", "backstage", crName, "--patch", jsonPatch, "--type="+p)) // #nosec G204 + _, err := Run(exec.Command( + GetPlatformTool(), "-n", ns, "patch", "backstage", crName, "--patch", jsonPatch, "--type="+p)) // #nosec G204 return err } @@ -136,7 +139,8 @@ func GetBackstageRouteHost(ns string, crName string) (string, error) { routeName := model.RouteName(crName) hostBytes, err := Run(exec.Command( - GetPlatformTool(), "get", "route", routeName, "-o", "go-template={{if .spec.host}}{{.spec.host}}{{end}}", "-n", ns)) // #nosec G204 + GetPlatformTool(), "get", "route", routeName, + "-o", "go-template={{if .spec.host}}{{.spec.host}}{{end}}", "-n", ns)) // #nosec G204 if err != nil { return "", fmt.Errorf("unable to determine host for route %s/%s: %w", ns, routeName, err) } @@ -147,7 +151,8 @@ func GetBackstageRouteHost(ns string, crName string) (string, error) { // try with subdomain in case it was set subDomainBytes, err := Run(exec.Command( - GetPlatformTool(), "get", "route", routeName, "-o", "go-template={{if .spec.subdomain}}{{.spec.subdomain}}{{end}}", "-n", ns)) // #nosec G204 + GetPlatformTool(), "get", "route", routeName, + "-o", "go-template={{if .spec.subdomain}}{{.spec.subdomain}}{{end}}", "-n", ns)) // #nosec G204 if err != nil { return "", fmt.Errorf("unable to determine subdomain for route %s/%s: %w", ns, routeName, err) } @@ -155,7 +160,8 @@ func GetBackstageRouteHost(ns string, crName string) (string, error) { if subDomain == "" { return "", nil } - ingressDomainBytes, err := Run(exec.Command(GetPlatformTool(), "get", "ingresses.config/cluster", "-o", "jsonpath={.spec.domain}")) // #nosec G204 + ingressDomainBytes, err := Run(exec.Command( + GetPlatformTool(), "get", "ingresses.config/cluster", "-o", "jsonpath={.spec.domain}")) // #nosec G204 if err != nil { return "", fmt.Errorf("unable to determine ingress sub-domain: %w", err) } @@ -184,7 +190,11 @@ func VerifyBackstageRoute(g Gomega, ns string, crName string, tests []ApiEndpoin VerifyBackstageAppAccess(g, fmt.Sprintf("https://%s", host), tests) } -func VerifyBackstageAppAccessWithUrlProvider(g Gomega, baseUrlProvider func(g Gomega) (context.CancelFunc, string), tests []ApiEndpointTest) { +func VerifyBackstageAppAccessWithUrlProvider( + g Gomega, + baseUrlProvider func(g Gomega) (context.CancelFunc, string), + tests []ApiEndpointTest, +) { cancelFunc, appUrl := baseUrlProvider(g) if cancelFunc != nil { defer cancelFunc() @@ -210,7 +220,8 @@ func VerifyBackstageAppAccess(g Gomega, baseUrl string, tests []ApiEndpointTest) if tt.BearerTokenRetrievalFn != nil { bearerToken, tErr := tt.BearerTokenRetrievalFn(baseUrl) - g.Expect(tErr).ShouldNot(HaveOccurred(), fmt.Sprintf("error while retrieving bearer token, context: %q", tt.Endpoint)) + g.Expect(tErr).ShouldNot(HaveOccurred(), + fmt.Sprintf("error while retrieving bearer token, context: %q", tt.Endpoint)) if bearerToken != "" { req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", bearerToken)) } @@ -227,7 +238,8 @@ func VerifyBackstageAppAccess(g Gomega, baseUrl string, tests []ApiEndpointTest) body, rErr := io.ReadAll(resp.Body) g.Expect(rErr).ShouldNot(HaveOccurred(), fmt.Sprintf("error while trying to read response body from 'GET %q'", url)) bodyStr := string(body) - g.Expect(resp.StatusCode).Should(Equal(tt.ExpectedHttpStatusCode), fmt.Sprintf("context: %s\n===Response body===\n%s", tt.Endpoint, bodyStr)) + g.Expect(resp.StatusCode).Should(Equal(tt.ExpectedHttpStatusCode), + fmt.Sprintf("context: %s\n===Response body===\n%s", tt.Endpoint, bodyStr)) if tt.BodyMatcher != nil { g.Expect(bodyStr).Should(tt.BodyMatcher, "context: "+tt.Endpoint) } diff --git a/tests/helper/utils.go b/tests/helper/utils.go index 7e3955cb3..260a8ea47 100644 --- a/tests/helper/utils.go +++ b/tests/helper/utils.go @@ -285,7 +285,12 @@ func IsOpenShift() bool { return _isOpenShift } -func StartPortForward(ctx context.Context, svc string, ns string, svcPort int) (localPort int, cancelFunc context.CancelFunc, err error) { +func StartPortForward( + ctx context.Context, + svc string, + ns string, + svcPort int, +) (localPort int, cancelFunc context.CancelFunc, err error) { ctx, cancel := context.WithCancel(ctx) cmd := exec.CommandContext( ctx,