From d7de0681ff3c8f96affca0329f0a10603d3c085c Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Fri, 2 Jan 2026 18:06:29 +0100 Subject: [PATCH 01/19] chore: Bump operator-sdk to v1.42.0 and most dependencies ref: https://sdk.operatorframework.io/docs/upgrading-sdk-version/ --- .github/workflows/lint-config-checks.yaml | 27 +++ .golangci.yml | 50 ++++- Makefile | 81 ++++--- bundle/backstage.io/bundle.Dockerfile | 2 +- ...kstage-operator.clusterserviceversion.yaml | 7 +- .../manifests/rhdh.redhat.com_backstages.yaml | 59 +---- bundle/backstage.io/metadata/annotations.yaml | 2 +- bundle/rhdh/bundle.Dockerfile | 2 +- ...kstage-operator.clusterserviceversion.yaml | 7 +- .../manifests/rhdh.redhat.com_backstages.yaml | 59 +---- bundle/rhdh/metadata/annotations.yaml | 2 +- cmd/main.go | 96 +++++++- .../crd/bases/rhdh.redhat.com_backstages.yaml | 59 +---- config/crd/kustomization.yaml | 6 - .../manager/cert_metrics_manager_patch.yaml | 30 +++ config/manager/deployment.yaml | 2 - config/manager/kustomization.yaml | 208 ++++++++++++++++++ config/manager/manager_metrics_patch.yaml | 4 + config/{rbac => manager}/metrics_service.yaml | 0 config/prometheus/monitor.yaml | 11 +- config/rbac/kustomization.yaml | 2 - dist/backstage.io/install.yaml | 62 +----- dist/rhdh/install.yaml | 62 +----- go.mod | 46 ++-- go.sum | 98 +++++---- integration_tests/suite_test.go | 16 ++ internal/controller/backstage_controller.go | 3 +- 27 files changed, 619 insertions(+), 384 deletions(-) create mode 100644 .github/workflows/lint-config-checks.yaml create mode 100644 config/manager/cert_metrics_manager_patch.yaml create mode 100644 config/manager/manager_metrics_patch.yaml rename config/{rbac => manager}/metrics_service.yaml (100%) 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..58771ecec 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)) @@ -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 @@ -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,18 +465,19 @@ $(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 @@ -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/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..5c8c79bc3 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" @@ -50,16 +52,25 @@ 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 +100,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, @@ -146,6 +208,22 @@ func main() { } //+kubebuilder:scaffold:builder + if metricsCertWatcher != nil { + setupLog.Info("Adding metrics certificate watcher to manager") + if err := mgr.Add(metricsCertWatcher); err != nil { + setupLog.Error(err, "Unable to add metrics certificate watcher to manager") + os.Exit(1) + } + } + + if webhookCertWatcher != nil { + setupLog.Info("Adding webhook certificate watcher to manager") + if err := mgr.Add(webhookCertWatcher); err != nil { + setupLog.Error(err, "Unable to add webhook certificate watcher to manager") + os.Exit(1) + } + } + if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { setupLog.Error(err, "unable to set up health check") os.Exit(1) 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..c7e4f3290 100644 --- a/go.mod +++ b/go.mod @@ -19,12 +19,13 @@ 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/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,7 +34,7 @@ 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 @@ -48,14 +49,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/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 +67,28 @@ 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 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 +98,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/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect + google.golang.org/grpc v1.68.1 // indirect google.golang.org/protobuf v1.36.5 // indirect gopkg.in/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 +132,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..4f014e658 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4= +cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= 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,8 +24,8 @@ 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= @@ -55,8 +57,8 @@ github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l 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= @@ -71,8 +73,8 @@ 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= @@ -83,6 +85,8 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr 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,6 +94,8 @@ 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/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU= @@ -119,29 +125,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= @@ -160,28 +165,30 @@ 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= 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= @@ -227,12 +234,12 @@ 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/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.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -242,8 +249,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 +273,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/suite_test.go b/integration_tests/suite_test.go index cffdcda99..a8c4ab545 100644 --- a/integration_tests/suite_test.go +++ b/integration_tests/suite_test.go @@ -80,6 +80,7 @@ var _ = BeforeSuite(func() { By("bootstrapping test environment") testEnv = &envtest.Environment{ + BinaryAssetsDirectory: getFirstFoundEnvTestBinaryDir(), CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases"), filepath.Join("..", "config", "crd", "external")}, ErrorIfCRDPathMissing: true, } @@ -135,6 +136,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/internal/controller/backstage_controller.go b/internal/controller/backstage_controller.go index 7206335fa..2202d1831 100644 --- a/internal/controller/backstage_controller.go +++ b/internal/controller/backstage_controller.go @@ -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 From c82d756fbef72bac3eeebb112839b14fa5c542f3 Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Fri, 2 Jan 2026 22:49:04 +0100 Subject: [PATCH 02/19] chore: Fix issues reported by golangci-lint --- api/v1alpha3/backstage_types.go | 22 ++-- api/v1alpha4/backstage_types.go | 22 ++-- api/v1alpha5/backstage_types.go | 22 ++-- cmd/main.go | 16 +-- integration_tests/config-refresh_test.go | 80 +++++++------- integration_tests/cr-config_test.go | 56 ++++++---- integration_tests/db_test.go | 34 ++++-- integration_tests/default-config_test.go | 14 ++- integration_tests/matchers.go | 3 +- integration_tests/plugin-deps_test.go | 2 +- integration_tests/pvcs_test.go | 21 ++-- integration_tests/rhdh-config_test.go | 16 ++- integration_tests/route_test.go | 9 +- integration_tests/suite_test.go | 13 ++- integration_tests/utils.go | 24 +++-- internal/controller/backstage_controller.go | 24 ++--- internal/controller/backstage_status.go | 8 +- internal/controller/deployable_finder.go | 6 +- internal/controller/mock_client.go | 2 +- internal/controller/monitor_test.go | 14 +-- internal/controller/spec_preprocessor.go | 7 +- internal/controller/watchers.go | 16 +-- pkg/model/appconfig.go | 17 +-- pkg/model/appconfig_test.go | 9 +- pkg/model/configmapenvs.go | 14 +-- pkg/model/configmapenvs_test.go | 42 +++++--- pkg/model/configmapfiles.go | 13 ++- pkg/model/configmapfiles_test.go | 35 +++--- pkg/model/containers-filter.go | 3 +- pkg/model/db-secret.go | 6 +- pkg/model/db-secret_test.go | 15 +-- pkg/model/db-service.go | 9 +- pkg/model/db-statefulset.go | 22 ++-- pkg/model/db-statefulset_test.go | 72 ++++++------- pkg/model/deployable.go | 2 +- pkg/model/deployment.go | 42 +++++--- pkg/model/deployment_test.go | 47 +++++---- pkg/model/dynamic-plugins.go | 42 ++++---- pkg/model/dynamic-plugins_test.go | 111 +++++++++++--------- pkg/model/model_tests.go | 20 ++-- pkg/model/plugin_deps.go | 6 +- pkg/model/pvcs.go | 29 +++-- pkg/model/pvcs_test.go | 19 ++-- pkg/model/route.go | 14 +-- pkg/model/route_test.go | 30 +++--- pkg/model/runtime.go | 41 +++++--- pkg/model/runtime_test.go | 32 +++--- pkg/model/secretenvs.go | 13 ++- pkg/model/secretenvs_test.go | 66 ++---------- pkg/model/secretfiles.go | 14 +-- pkg/model/secretfiles_test.go | 43 +++++--- pkg/model/service.go | 9 +- pkg/utils/utils.go | 22 ++-- pkg/utils/utils_test.go | 14 +-- tests/e2e/e2e_suite_test.go | 30 ++++-- tests/e2e/e2e_test.go | 18 ++-- tests/e2e/e2e_upgrade_test.go | 20 ++-- tests/helper/helper_backstage.go | 30 ++++-- tests/helper/utils.go | 7 +- 59 files changed, 790 insertions(+), 619 deletions(-) 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/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/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/cmd/main.go b/cmd/main.go index 5c8c79bc3..4725fb14e 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -22,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 ( @@ -43,7 +44,7 @@ func init() { utilruntime.Must(openshift.Install(scheme)) utilruntime.Must(monitoringv1.AddToScheme(scheme)) - //+kubebuilder:scaffold:scheme + // +kubebuilder:scaffold:scheme } func main() { @@ -62,14 +63,17 @@ func main() { "Enable leader election for controller manager. "+ "Enabling this will ensure there is only one active controller manager.") flag.BoolVar(&secureMetrics, "metrics-secure", true, - "If set, the metrics endpoint is served securely via HTTPS and requires authentication and authorization. Use --metrics-secure=false to use HTTP instead.") + "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(&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{ @@ -206,7 +210,7 @@ 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") 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 a8c4ab545..cea11058d 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,14 +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{ BinaryAssetsDirectory: getFirstFoundEnvTestBinaryDir(), - CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases"), filepath.Join("..", "config", "crd", "external")}, + CRDDirectoryPaths: []string{ + filepath.Join("..", "config", "crd", "bases"), + filepath.Join("..", "config", "crd", "external"), + }, ErrorIfCRDPathMissing: true, } @@ -122,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()) 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 2202d1831..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) 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..1e1e5eaa3 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) @@ -167,7 +170,8 @@ func TestSpecifiedCMEnvsWithContainers(t *testing.T) { // check * bs.Spec.Application.ExtraEnvs.ConfigMaps[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 +188,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 +206,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, From f092c46c296738988a886a3d14a3753ff75e0387 Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Fri, 2 Jan 2026 23:26:10 +0100 Subject: [PATCH 03/19] fix unit tests --- pkg/model/configmapenvs_test.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pkg/model/configmapenvs_test.go b/pkg/model/configmapenvs_test.go index 1e1e5eaa3..7282ef929 100644 --- a/pkg/model/configmapenvs_test.go +++ b/pkg/model/configmapenvs_test.go @@ -168,7 +168,12 @@ func doCheckSpecifiedEnvsWithContainers(t *testing.T, envType string) { 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(). addToDefaultConfig("deployment.yaml", "multicontainer-deployment.yaml") From 58a568ed341da5af264aee82f6062bfa3ca6865b Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Fri, 2 Jan 2026 23:35:05 +0100 Subject: [PATCH 04/19] Bump more tools --- Makefile | 6 +++--- go.mod | 13 ++++++++----- go.sum | 42 ++++++++++++++++++++++++++++++++---------- 3 files changed, 43 insertions(+), 18 deletions(-) diff --git a/Makefile b/Makefile index 58771ecec..c2343ad80 100644 --- a/Makefile +++ b/Makefile @@ -479,9 +479,9 @@ 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 diff --git a/go.mod b/go.mod index c7e4f3290..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 @@ -26,6 +26,7 @@ require ( 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 @@ -39,7 +40,7 @@ require ( 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 @@ -53,7 +54,7 @@ require ( 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.24.0 // indirect @@ -89,6 +90,8 @@ require ( go.opentelemetry.io/proto/otlp v1.4.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect + 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 @@ -101,7 +104,7 @@ require ( google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect google.golang.org/grpc v1.68.1 // indirect - google.golang.org/protobuf v1.36.5 // indirect + 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 diff --git a/go.sum b/go.sum index 4f014e658..98d09fedc 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +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= @@ -32,11 +34,17 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos 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= @@ -51,6 +59,8 @@ 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= @@ -67,8 +77,8 @@ 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= @@ -81,6 +91,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 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= @@ -98,6 +110,10 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0 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= @@ -112,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= @@ -159,6 +175,8 @@ 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= @@ -189,6 +207,8 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +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= @@ -196,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= @@ -240,8 +262,8 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 h1: 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.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= -google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +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= From c5bb6975212da8d98c9e2cc7504be8968e6e301a Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Fri, 2 Jan 2026 23:45:32 +0100 Subject: [PATCH 05/19] Fix gosec --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index c2343ad80..588c7cf12 100644 --- a/Makefile +++ b/Makefile @@ -125,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 @@ -198,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 From f189c88a05cf1e70f9d8622588ccd20140e972cf Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Fri, 2 Jan 2026 23:47:16 +0100 Subject: [PATCH 06/19] Add generated code --- api/v1alpha3/zz_generated.deepcopy.go | 2 +- api/v1alpha4/zz_generated.deepcopy.go | 2 +- api/v1alpha5/zz_generated.deepcopy.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) 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/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/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" ) From e61a4af5822fedbeb6cb3664f975cbe302026f1b Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Fri, 2 Jan 2026 23:52:14 +0100 Subject: [PATCH 07/19] fixup! chore: Bump operator-sdk to v1.42.0 and most dependencies --- integration_tests/suite_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration_tests/suite_test.go b/integration_tests/suite_test.go index cea11058d..d125662f0 100644 --- a/integration_tests/suite_test.go +++ b/integration_tests/suite_test.go @@ -140,7 +140,7 @@ var _ = AfterSuite(func() { }) func getFirstFoundEnvTestBinaryDir() string { - basePath := filepath.Join("..", "..", "..", "bin", "k8s") + basePath := filepath.Join("..", "bin", "k8s") entries, err := os.ReadDir(basePath) if err != nil { logf.Log.Error(err, "Failed to read directory", "path", basePath) From f210eef15b561123be23162438fb453cbd103b45 Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Fri, 12 Jun 2026 15:02:14 +0200 Subject: [PATCH 08/19] chore: Bump operator-sdk to v1.42.2, controller-runtime to v0.21.0, and test deps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - operator-sdk: v1.42.0 -> v1.42.2 - controller-runtime: v0.19.4 -> v0.21.0 - k8s.io/* replace directives: v0.31.3 -> v0.33.0 (matching CR v0.21.0) - kube-openapi replace: updated to match k8s.io v0.33.0 - ginkgo: v2.27.3 -> v2.28.1 - gomega: v1.38.3 -> v1.39.1 No code changes needed — the project uses no deprecated/removed APIs from controller-runtime v0.20-v0.21 (no webhooks, no SyncPeriod, no Result.Requeue). The cmd/main.go already matched the v1.42.2 scaffold. Assisted-by: Claude --- Makefile | 4 ++-- go.mod | 48 +++++++++++++++++++--------------------- go.sum | 67 +++++++++++++++++++++++++++----------------------------- 3 files changed, 56 insertions(+), 63 deletions(-) diff --git a/Makefile b/Makefile index 514cb5b3c..013bc4dd4 100644 --- a/Makefile +++ b/Makefile @@ -92,7 +92,7 @@ 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.42.0 +OPERATOR_SDK_VERSION ?= v1.42.2 OPM_VERSION ?= v1.55.0 # Image URL to use all building/pushing image targets IMG ?= $(IMAGE_TAG_BASE):$(IMAGE_TAG_VERSION) @@ -482,7 +482,7 @@ ENVTEST_VERSION ?= $(shell go list -m -f '{{if .Replace}}{{.Replace.Version}}{{e GOLANGCI_LINT_VERSION ?= v2.6.2 GOIMPORTS_VERSION ?= v0.40.0 GOSEC_VERSION ?= v2.22.11 -GINKGO_VERSION ?= v2.27.3 +GINKGO_VERSION ?= v2.28.1 ## 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 diff --git a/go.mod b/go.mod index 81c92bd0f..34c2bba61 100644 --- a/go.mod +++ b/go.mod @@ -5,8 +5,8 @@ go 1.25.0 toolchain go1.25.11 require ( - github.com/onsi/ginkgo/v2 v2.27.3 - github.com/onsi/gomega v1.38.3 + github.com/onsi/ginkgo/v2 v2.28.1 + github.com/onsi/gomega v1.39.1 github.com/openshift/api v0.0.0-20260511191110-9b69e5fa27e9 github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.71.2 github.com/stretchr/testify v1.11.1 @@ -19,7 +19,7 @@ require ( k8s.io/client-go v0.35.4 k8s.io/klog/v2 v2.130.1 k8s.io/utils v0.0.0-20260507154919-ff6756f316d2 - 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 ) @@ -28,14 +28,13 @@ require ( cel.dev/expr v0.25.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 github.com/blang/semver/v4 v4.0.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emicklei/go-restful/v3 v3.12.2 // 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.9.0 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect @@ -59,17 +58,15 @@ require ( github.com/go-openapi/swag/yamlutils v0.25.4 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect + github.com/google/btree v1.1.3 // indirect github.com/google/cel-go v0.26.0 // indirect github.com/google/gnostic-models v0.7.1 // 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-20250403155104-27863c87afa6 // indirect + github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/gorilla/websocket v1.5.0 // indirect + github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect - github.com/imdario/mergo v0.3.12 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/moby/spdystream v0.5.1 // indirect @@ -99,7 +96,7 @@ require ( go.opentelemetry.io/otel/trace v1.43.0 // indirect go.opentelemetry.io/proto/otlp v1.5.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/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/mod v0.37.0 // indirect @@ -116,6 +113,7 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect google.golang.org/grpc v1.79.3 // indirect google.golang.org/protobuf v1.36.11 // indirect + gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiserver v0.35.0 // indirect @@ -123,28 +121,26 @@ require ( k8s.io/kube-openapi v0.0.0-20251125145642-4e65d59e963e // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect + sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect ) -// Replace directives to pin k8s.io dependencies to v0.31.3 +// Replace directives to pin k8s.io dependencies to v0.33.0 (matching controller-runtime v0.21.0). // This is necessary because: -// 1. k8s.io/client-go v0.31.3 depends on k8s.io/api v0.31.x -// 2. github.com/openshift/api (our direct dependency) requires k8s.io/api v0.34.x -// 3. In k8s.io/api v0.34.x, the v1alpha1 packages for coordination and networking were removed -// 4. k8s.io/client-go/kubernetes/scheme still imports these v1alpha1 packages, causing build failures -// 5. Newer k8s.io/kube-openapi versions use structured-merge-diff v6 (incompatible with v0.31.3) -// and go.yaml.in/yaml paths (causing duplicate yaml.v3 module conflicts) -// 6. Newer github.com/google/gnostic-models and sigs.k8s.io/yaml versions use go.yaml.in/yaml paths +// 1. github.com/openshift/api (our direct dependency) transitively requires k8s.io/api v0.34.x+ +// 2. In k8s.io/api v0.34.x, the v1alpha1 packages for coordination and networking were removed +// 3. k8s.io/client-go v0.33.0's kubernetes/scheme still imports these v1alpha1 packages, causing build failures +// 4. Newer github.com/google/gnostic-models and sigs.k8s.io/yaml versions use go.yaml.in/yaml paths // Without these replace directives, go mod tidy fails with: // "module k8s.io/api@latest found (v0.34.1), but does not contain package k8s.io/api/coordination/v1alpha1" replace ( github.com/google/gnostic-models => github.com/google/gnostic-models v0.6.8 - k8s.io/api => k8s.io/api v0.31.3 - k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.31.3 - k8s.io/apimachinery => k8s.io/apimachinery v0.31.3 - k8s.io/apiserver => k8s.io/apiserver v0.31.3 - 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 + k8s.io/api => k8s.io/api v0.33.0 + k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.33.0 + k8s.io/apimachinery => k8s.io/apimachinery v0.33.0 + k8s.io/apiserver => k8s.io/apiserver v0.33.0 + k8s.io/client-go => k8s.io/client-go v0.33.0 + k8s.io/component-base => k8s.io/component-base v0.33.0 + k8s.io/kube-openapi => k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff sigs.k8s.io/yaml => sigs.k8s.io/yaml v1.4.0 ) diff --git a/go.sum b/go.sum index 7ee5aef27..a27acd2c3 100644 --- a/go.sum +++ b/go.sum @@ -6,8 +6,6 @@ github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8 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= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= @@ -25,8 +23,8 @@ github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf github.com/emicklei/go-restful/v3 v3.12.2/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.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= @@ -88,10 +86,10 @@ 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/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= +github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/cel-go v0.26.0 h1:DPGjXackMpJWH680oGY4lZhYjIameYmR+/6RBdDGmaI= github.com/google/cel-go v0.26.0/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= @@ -102,16 +100,14 @@ 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-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= -github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= +github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83 h1:z2ogiKUYzX5Is6zr/vP9vJGqPwcdqsWjOt+V8J7+bTc= +github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI= 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/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= +github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo= github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI= -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/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE= @@ -146,10 +142,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.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/onsi/ginkgo/v2 v2.28.1 h1:S4hj+HbZp40fNKuLUQOYLDgZLwNUVn19N3Atb98NCyI= +github.com/onsi/ginkgo/v2 v2.28.1/go.mod h1:CLtbVInNckU3/+gC8LzkGUb9oF+e8W8TdUsxPwvdOgE= +github.com/onsi/gomega v1.39.1 h1:1IJLAad4zjPn2PsnhH70V4DKRFlrCzGBNrNaru+Vf28= +github.com/onsi/gomega v1.39.1/go.mod h1:hL6yVALoTOxeWudERyfppUcZXjMwIMLnuSfruD2lcfg= github.com/openshift/api v0.0.0-20260511191110-9b69e5fa27e9 h1:yb8ul1HPFYhO04yp0D8T/qSySZnKv210f4nE//i/Bdg= github.com/openshift/api v0.0.0-20260511191110-9b69e5fa27e9/go.mod h1:pyVjK0nZ4sRs4fuQVQ4rubsJdahI1PB94LnQ8sGdvxo= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -180,6 +176,8 @@ github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8w github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= @@ -225,8 +223,8 @@ 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/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= @@ -295,34 +293,33 @@ 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.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.31.3 h1:umzm5o8lFbdN/hIXbrK9oRpOproJO62CV1zqxXrLgk8= -k8s.io/api v0.31.3/go.mod h1:UJrkIp9pnMOI9K2nlL6vwpxRzzEX5sWgn8kGQe92kCE= -k8s.io/apiextensions-apiserver v0.31.3 h1:+GFGj2qFiU7rGCsA5o+p/rul1OQIq6oYpQw4+u+nciE= -k8s.io/apiextensions-apiserver v0.31.3/go.mod h1:2DSpFhUZZJmn/cr/RweH1cEVVbzFw9YBu4T+U3mf1e4= -k8s.io/apimachinery v0.31.3 h1:6l0WhcYgasZ/wk9ktLq5vLaoXJJr5ts6lkaQzgeYPq4= -k8s.io/apimachinery v0.31.3/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= -k8s.io/apiserver v0.31.3 h1:+1oHTtCB+OheqFEz375D0IlzHZ5VeQKX1KGXnx+TTuY= -k8s.io/apiserver v0.31.3/go.mod h1:PrxVbebxrxQPFhJk4powDISIROkNMKHibTg9lTRQ0Qg= -k8s.io/client-go v0.31.3 h1:CAlZuM+PH2cm+86LOBemaJI/lQ5linJ6UFxKX/SoG+4= -k8s.io/client-go v0.31.3/go.mod h1:2CgjPUTpv3fE5dNygAr2NcM8nhHzXvxB8KL5gYc3kJs= -k8s.io/component-base v0.31.3 h1:DMCXXVx546Rfvhj+3cOm2EUxhS+EyztH423j+8sOwhQ= -k8s.io/component-base v0.31.3/go.mod h1:xME6BHfUOafRgT0rGVBGl7TuSg8Z9/deT7qq6w7qjIU= +k8s.io/api v0.33.0 h1:yTgZVn1XEe6opVpP1FylmNrIFWuDqe2H0V8CT5gxfIU= +k8s.io/api v0.33.0/go.mod h1:CTO61ECK/KU7haa3qq8sarQ0biLq2ju405IZAd9zsiM= +k8s.io/apiextensions-apiserver v0.33.0 h1:d2qpYL7Mngbsc1taA4IjJPRJ9ilnsXIrndH+r9IimOs= +k8s.io/apiextensions-apiserver v0.33.0/go.mod h1:VeJ8u9dEEN+tbETo+lFkwaaZPg6uFKLGj5vyNEwwSzc= +k8s.io/apimachinery v0.33.0 h1:1a6kHrJxb2hs4t8EE5wuR/WxKDwGN1FKH3JvDtA0CIQ= +k8s.io/apimachinery v0.33.0/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= +k8s.io/apiserver v0.33.0 h1:QqcM6c+qEEjkOODHppFXRiw/cE2zP85704YrQ9YaBbc= +k8s.io/apiserver v0.33.0/go.mod h1:EixYOit0YTxt8zrO2kBU7ixAtxFce9gKGq367nFmqI8= +k8s.io/client-go v0.33.0 h1:UASR0sAYVUzs2kYuKn/ZakZlcs2bEHaizrrHUZg0G98= +k8s.io/client-go v0.33.0/go.mod h1:kGkd+l/gNGg8GYWAPr0xF1rRKvVWvzh9vmZAMXtaKOg= +k8s.io/component-base v0.33.0 h1:Ot4PyJI+0JAD9covDhwLp9UNkUja209OzsJ4FzScBNk= +k8s.io/component-base v0.33.0/go.mod h1:aXYZLbw3kihdkOPMDhWbjGCO6sg+luw554KP51t8qCU= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= -k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= k8s.io/utils v0.0.0-20260507154919-ff6756f316d2 h1:wU4tMEhLGgIbLvXQb1cfN+EcM0wf7zC6CPF+C79jroc= k8s.io/utils v0.0.0-20260507154919-ff6756f316d2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= 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/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8= +sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/kustomize/kyaml v0.18.1 h1:WvBo56Wzw3fjS+7vBjN6TeivvpbW9GmRaWZ9CIVmt4E= From 9954ba75c70e33116c20366caf208097892d2e02 Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Fri, 12 Jun 2026 15:07:36 +0200 Subject: [PATCH 09/19] chore: Bump tool versions in Makefile - OPM: v1.55.0 -> v1.72.0 - Kustomize: v5.4.3 -> v5.8.1 - controller-tools: v0.20.0 -> v0.21.0 - golangci-lint: v2.6.2 -> v2.12.2 - goimports: v0.40.0 -> v0.46.0 - gosec: v2.22.11 -> v2.27.1 Assisted-by: Claude --- Makefile | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 013bc4dd4..1375411e1 100644 --- a/Makefile +++ b/Makefile @@ -93,7 +93,7 @@ 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.42.2 -OPM_VERSION ?= v1.55.0 +OPM_VERSION ?= v1.72.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. @@ -476,12 +476,12 @@ GOSEC ?= $(LOCALBIN)/gosec GINKGO ?= $(LOCALBIN)/ginkgo ## Tool Versions -KUSTOMIZE_VERSION ?= v5.4.3 -CONTROLLER_TOOLS_VERSION ?= v0.20.0 +KUSTOMIZE_VERSION ?= v5.8.1 +CONTROLLER_TOOLS_VERSION ?= v0.21.0 ENVTEST_VERSION ?= $(shell go list -m -f '{{if .Replace}}{{.Replace.Version}}{{else}}{{.Version}}{{end}}' sigs.k8s.io/controller-runtime | awk -F'[v.]' '{printf "release-%d.%d", $$2, $$3}') -GOLANGCI_LINT_VERSION ?= v2.6.2 -GOIMPORTS_VERSION ?= v0.40.0 -GOSEC_VERSION ?= v2.22.11 +GOLANGCI_LINT_VERSION ?= v2.12.2 +GOIMPORTS_VERSION ?= v0.46.0 +GOSEC_VERSION ?= v2.27.1 GINKGO_VERSION ?= v2.28.1 ## Gosec options - default format is sarif so we can integrate with Github code scanning From dd73c56daa99c6d50a6a494e539782f9b5781f5a Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Fri, 12 Jun 2026 15:10:19 +0200 Subject: [PATCH 10/19] chore: Bump tool versions in Makefile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - OPM: v1.55.0 -> v1.72.0 - Kustomize: v5.4.3 -> v5.8.1 - golangci-lint: v2.6.2 -> v2.12.2 - goimports: v0.40.0 -> v0.46.0 - gosec: v2.22.11 -> v2.27.1 controller-tools kept at v0.20.0 — v0.21.0 requires Go 1.26 (RHIDP-12020). Assisted-by: Claude --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 1375411e1..981b1f77d 100644 --- a/Makefile +++ b/Makefile @@ -477,7 +477,8 @@ GINKGO ?= $(LOCALBIN)/ginkgo ## Tool Versions KUSTOMIZE_VERSION ?= v5.8.1 -CONTROLLER_TOOLS_VERSION ?= v0.21.0 +# TODO(asoro): bump to v0.21.0+ once we switch to Go 1.26 (RHIDP-12020) +CONTROLLER_TOOLS_VERSION ?= v0.20.0 ENVTEST_VERSION ?= $(shell go list -m -f '{{if .Replace}}{{.Replace.Version}}{{else}}{{.Version}}{{end}}' sigs.k8s.io/controller-runtime | awk -F'[v.]' '{printf "release-%d.%d", $$2, $$3}') GOLANGCI_LINT_VERSION ?= v2.12.2 GOIMPORTS_VERSION ?= v0.46.0 From a002779d9a8dc5758360c1864ce81c65823450e4 Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Fri, 12 Jun 2026 15:11:57 +0200 Subject: [PATCH 11/19] chore: regen bundle manifests --- api/v1alpha1/zz_generated.deepcopy.go | 2 +- api/v1alpha2/zz_generated.deepcopy.go | 2 +- api/v1alpha3/zz_generated.deepcopy.go | 2 +- api/v1alpha4/zz_generated.deepcopy.go | 2 +- api/v1alpha5/zz_generated.deepcopy.go | 2 +- bundle/backstage.io/bundle.Dockerfile | 2 +- .../backstage-operator.clusterserviceversion.yaml | 7 +++---- bundle/backstage.io/metadata/annotations.yaml | 2 +- bundle/rhdh/bundle.Dockerfile | 2 +- .../backstage-operator.clusterserviceversion.yaml | 7 +++---- bundle/rhdh/metadata/annotations.yaml | 2 +- dist/backstage.io/install.yaml | 3 +-- dist/rhdh/install.yaml | 3 +-- 13 files changed, 17 insertions(+), 21 deletions(-) diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index ca4d7a346..9b7acdd38 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -5,7 +5,7 @@ package v1alpha1 import ( - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) diff --git a/api/v1alpha2/zz_generated.deepcopy.go b/api/v1alpha2/zz_generated.deepcopy.go index 1357c08bd..c3ecd6b25 100644 --- a/api/v1alpha2/zz_generated.deepcopy.go +++ b/api/v1alpha2/zz_generated.deepcopy.go @@ -5,7 +5,7 @@ package v1alpha2 import ( - v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/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/v1alpha3/zz_generated.deepcopy.go b/api/v1alpha3/zz_generated.deepcopy.go index 6613a8896..b315b789e 100644 --- a/api/v1alpha3/zz_generated.deepcopy.go +++ b/api/v1alpha3/zz_generated.deepcopy.go @@ -5,7 +5,7 @@ package v1alpha3 import ( - v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/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/zz_generated.deepcopy.go b/api/v1alpha4/zz_generated.deepcopy.go index 9c86c46ee..c9c126a0f 100644 --- a/api/v1alpha4/zz_generated.deepcopy.go +++ b/api/v1alpha4/zz_generated.deepcopy.go @@ -5,7 +5,7 @@ package v1alpha4 import ( - v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/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/zz_generated.deepcopy.go b/api/v1alpha5/zz_generated.deepcopy.go index 1ee510162..0e0086d44 100644 --- a/api/v1alpha5/zz_generated.deepcopy.go +++ b/api/v1alpha5/zz_generated.deepcopy.go @@ -5,7 +5,7 @@ package v1alpha5 import ( - v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/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 327282a1b..897cb0274 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.42.0 +LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.42.2 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 5a709d967..7429acb1f 100644 --- a/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml +++ b/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml @@ -25,9 +25,9 @@ metadata: } } ] - createdAt: "2026-05-27T12:26:15Z" + createdAt: "2026-06-12T13:10:37Z" description: Backstage Operator - operators.operatorframework.io/builder: operator-sdk-v1.37.0 + operators.operatorframework.io/builder: operator-sdk-v1.42.2 operators.operatorframework.io/project_layout: go.kubebuilder.io/v4 labels: operatorframework.io/arch.amd64: supported @@ -211,9 +211,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.11.0 diff --git a/bundle/backstage.io/metadata/annotations.yaml b/bundle/backstage.io/metadata/annotations.yaml index cda3824cd..4fb3e43b5 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.42.0 + operators.operatorframework.io.metrics.builder: operator-sdk-v1.42.2 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 c657c4807..2ce4cde43 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.42.0 +LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.42.2 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 bc7a41fc1..de743139e 100644 --- a/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml +++ b/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml @@ -29,7 +29,7 @@ metadata: categories: Developer Tools certified: "true" containerImage: registry.redhat.io/rhdh/rhdh-rhel9-operator:1.11 - createdAt: "2026-05-27T12:26:14Z" + createdAt: "2026-06-12T13:10:38Z" 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 @@ -46,7 +46,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.2 operators.operatorframework.io/project_layout: go.kubebuilder.io/v4 repository: https://gitlab.cee.redhat.com/rhidp/rhdh/ skipRange: '>=1.0.0 <1.11.0' @@ -303,9 +303,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/metadata/annotations.yaml b/bundle/rhdh/metadata/annotations.yaml index 6034d4d77..10e554c0f 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.42.0 + operators.operatorframework.io.metrics.builder: operator-sdk-v1.42.2 operators.operatorframework.io.metrics.mediatype.v1: metrics+v1 operators.operatorframework.io.metrics.project_layout: go.kubebuilder.io/v4 diff --git a/dist/backstage.io/install.yaml b/dist/backstage.io/install.yaml index 37ecabc32..f6c29acc7 100644 --- a/dist/backstage.io/install.yaml +++ b/dist/backstage.io/install.yaml @@ -2792,9 +2792,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.11.0 diff --git a/dist/rhdh/install.yaml b/dist/rhdh/install.yaml index 8d0b771fe..d9770af48 100644 --- a/dist/rhdh/install.yaml +++ b/dist/rhdh/install.yaml @@ -4019,9 +4019,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: From 0ef3d1b1976bd3adfe1ae8707ea99a06390efcc9 Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Fri, 12 Jun 2026 15:17:01 +0200 Subject: [PATCH 12/19] style: Normalize kubebuilder marker format to use space after // Consistently use "// +kubebuilder:" across all API versions. Previously v1alpha1 and v1alpha2 used "//+kubebuilder:" (no space) for some markers while v1alpha3-5 and the controller used the space form throughout. Assisted-by: Claude --- api/v1alpha1/backstage_types.go | 22 +++++++++++----------- api/v1alpha2/backstage_types.go | 22 +++++++++++----------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/api/v1alpha1/backstage_types.go b/api/v1alpha1/backstage_types.go index f6c831fb9..e895e41f3 100644 --- a/api/v1alpha1/backstage_types.go +++ b/api/v1alpha1/backstage_types.go @@ -39,7 +39,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. @@ -85,7 +85,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). @@ -156,7 +156,7 @@ type ExtraEnvs struct { type ObjectKeyRef 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 @@ -166,11 +166,11 @@ type ObjectKeyRef 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"` } @@ -181,10 +181,10 @@ type BackstageStatus struct { Conditions []metav1.Condition `json:"conditions,omitempty"` } -//+kubebuilder:unservedversion -//+kubebuilder:deprecatedversion:warning="v1alpha1 is not served" -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status +// +kubebuilder:unservedversion +// +kubebuilder:deprecatedversion:warning="v1alpha1 is not served" +// +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. @@ -199,7 +199,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 { @@ -213,7 +213,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/v1alpha2/backstage_types.go b/api/v1alpha2/backstage_types.go index 20412c3ef..1ac1b5869 100644 --- a/api/v1alpha2/backstage_types.go +++ b/api/v1alpha2/backstage_types.go @@ -52,7 +52,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. @@ -98,7 +98,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). @@ -169,7 +169,7 @@ type ExtraEnvs struct { type ObjectKeyRef 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 @@ -179,11 +179,11 @@ type ObjectKeyRef 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"` } @@ -194,10 +194,10 @@ type BackstageStatus struct { Conditions []metav1.Condition `json:"conditions,omitempty"` } -//+kubebuilder:unservedversion -//+kubebuilder:deprecatedversion:warning="v1alpha2 is not served" -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status +// +kubebuilder:unservedversion +// +kubebuilder:deprecatedversion:warning="v1alpha2 is not served" +// +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. @@ -212,7 +212,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 { @@ -226,7 +226,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. From cbb8d1def12df1caec78f51f1a01e4c7e19432f6 Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Fri, 12 Jun 2026 15:32:31 +0200 Subject: [PATCH 13/19] fix: Align cmd/main.go with operator-sdk v1.42.2 scaffold - Fix TLS options: metrics server uses base tlsOpts, webhook server uses webhookTLSOpts (previously swapped) - Add TODO(user) block for certManager with [METRICS-WITH-CERTS] and [PROMETHEUS-WITH-CERTS] references - Add self-signed certificate warning comment - Add scaffold comments for cert watchers and webhook TLS options - Update controller-runtime doc URLs from @v0.19.0 to @v0.21.0 - Lowercase error messages to match scaffold convention Assisted-by: Claude --- cmd/main.go | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 2a7376fc9..20db65561 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -111,7 +111,10 @@ func main() { tlsOpts = append(tlsOpts, disableHTTP2) } + // Create watchers for metrics and webhooks certificates var metricsCertWatcher, webhookCertWatcher *certwatcher.CertWatcher + + // Initial webhook TLS options webhookTLSOpts := tlsOpts if len(webhookCertPath) > 0 { @@ -135,31 +138,33 @@ func main() { // 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://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.21.0/pkg/metrics/server // - https://book.kubebuilder.io/reference/metrics.html metricsServerOptions := metricsserver.Options{ BindAddress: metricsAddr, SecureServing: secureMetrics, - // 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, + TLSOpts: tlsOpts, } if secureMetrics { // FilterProvider is used to protect the metrics endpoint with authn/authz. // These configurations ensure that only authorized users and service accounts // can access the metrics endpoint. The RBAC are configured in 'config/rbac/kustomization.yaml'. More info: - // https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.0/pkg/metrics/filters#WithAuthenticationAndAuthorization + // https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.21.0/pkg/metrics/filters#WithAuthenticationAndAuthorization metricsServerOptions.FilterProvider = filters.WithAuthenticationAndAuthorization } webhookServer := webhook.NewServer(webhook.Options{ - TLSOpts: tlsOpts, + TLSOpts: webhookTLSOpts, }) + // If the certificate is not specified, controller-runtime will automatically + // generate self-signed certificates for the metrics server. While convenient for development and testing, + // this setup is not recommended for production. + // + // TODO(user): If you enable certManager, uncomment the following lines: + // - [METRICS-WITH-CERTS] at config/default/kustomization.yaml to generate and use certificates + // managed by cert-manager for the metrics server. + // - [PROMETHEUS-WITH-CERTS] at config/prometheus/kustomization.yaml for TLS certification. if len(metricsCertPath) > 0 { setupLog.Info("Initializing metrics certificate watcher using provided certificates", "metrics-cert-path", metricsCertPath, "metrics-cert-name", metricsCertName, "metrics-cert-key", metricsCertKey) @@ -249,7 +254,7 @@ func main() { 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") + setupLog.Error(err, "unable to add metrics certificate watcher to manager") os.Exit(1) } } @@ -257,7 +262,7 @@ func main() { 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") + setupLog.Error(err, "unable to add webhook certificate watcher to manager") os.Exit(1) } } From eb96aaf6fdadcb9a9c9d00d78346b71f963a6ad1 Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Fri, 12 Jun 2026 15:33:05 +0200 Subject: [PATCH 14/19] chore: regen bundle manifests --- .../manifests/backstage-operator.clusterserviceversion.yaml | 2 +- .../manifests/backstage-operator.clusterserviceversion.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml b/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml index 7429acb1f..2e689df6d 100644 --- a/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml +++ b/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml @@ -25,7 +25,7 @@ metadata: } } ] - createdAt: "2026-06-12T13:10:37Z" + createdAt: "2026-06-12T13:32:52Z" description: Backstage Operator operators.operatorframework.io/builder: operator-sdk-v1.42.2 operators.operatorframework.io/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 de743139e..5c59f0386 100644 --- a/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml +++ b/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml @@ -29,7 +29,7 @@ metadata: categories: Developer Tools certified: "true" containerImage: registry.redhat.io/rhdh/rhdh-rhel9-operator:1.11 - createdAt: "2026-06-12T13:10:38Z" + createdAt: "2026-06-12T13:32:53Z" 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 From 489fac486961bddaec570a0acb188d96bb7c70b1 Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Fri, 12 Jun 2026 15:48:57 +0200 Subject: [PATCH 15/19] chore: Align golangci-lint config with operator-sdk v1.42.2 scaffold Adopt the operator-sdk v1.42.2 scaffold golangci-lint v2 config structure while disabling 9 stricter linters (copyloopvar, dupl, ginkgolinter, goconst, gocyclo, lll, prealloc, revive, unparam) that surface pre-existing violations. These are commented out with TODO(asoro) markers for follow-up tech debt cleanup. Assisted-by: Claude --- .golangci.yml | 46 +++++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index aecf26549..4830366b6 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -5,28 +5,31 @@ run: 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 + # TODO(asoro): Enable these linters from the operator-sdk scaffold once the + # existing violations are fixed. Tracked as tech debt. + #- copyloopvar + #- dupl + #- ginkgolinter + #- goconst + #- gocyclo + #- lll + #- prealloc + #- revive + #- unparam + # TODO(asoro): Re-add revive settings when the linter is enabled + #settings: + # revive: + # rules: + # - name: comment-spacings + # - name: import-shadowing exclusions: generated: lax rules: @@ -35,13 +38,14 @@ linters: - staticcheck # exclude dot-imports checks on test-related files text: "ST1001:" - - linters: - - lll - path: api/* - - linters: - - dupl - - lll - path: internal/* + # TODO(asoro): Re-add these exclusions when lll and dupl are enabled + #- linters: + # - lll + # path: api/* + #- linters: + # - dupl + # - lll + # path: internal/* paths: - third_party$ - builtin$ From 4f7c06de30000816f8b66fb2d82919e123e56c35 Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Fri, 12 Jun 2026 15:53:38 +0200 Subject: [PATCH 16/19] fix: Match ServiceMonitor port name to metrics Service The ServiceMonitor referenced port "https" which does not exist on the metrics Service (which uses "metrics"). Prometheus cannot scrape metrics when the port names are mismatched. Assisted-by: Claude --- config/prometheus/monitor.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/prometheus/monitor.yaml b/config/prometheus/monitor.yaml index a08cee709..1b59aac31 100644 --- a/config/prometheus/monitor.yaml +++ b/config/prometheus/monitor.yaml @@ -11,7 +11,7 @@ metadata: spec: endpoints: - path: /metrics - port: https # Ensure this is the name of the port that exposes HTTPS metrics + port: metrics scheme: https bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token tlsConfig: From 5aed70774e94084dc84a23c1fb3dc859a505d35a Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Fri, 12 Jun 2026 15:55:29 +0200 Subject: [PATCH 17/19] fix: Set envtest BinaryAssetsDirectory conditionally Align with operator-sdk v1.42.2 scaffold pattern: only set BinaryAssetsDirectory as a fallback for IDE/direct go test usage. When KUBEBUILDER_ASSETS is set (e.g., via Makefile), envtest uses that instead, avoiding picking the wrong k8s version. Assisted-by: Claude --- integration_tests/suite_test.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/integration_tests/suite_test.go b/integration_tests/suite_test.go index c4209c0d8..b44621aee 100644 --- a/integration_tests/suite_test.go +++ b/integration_tests/suite_test.go @@ -80,7 +80,6 @@ var _ = BeforeSuite(func() { By("bootstrapping test environment") testEnv = &envtest.Environment{ - BinaryAssetsDirectory: getFirstFoundEnvTestBinaryDir(), CRDDirectoryPaths: []string{ filepath.Join("..", "config", "crd", "bases"), filepath.Join("..", "config", "crd", "external"), @@ -88,6 +87,13 @@ var _ = BeforeSuite(func() { ErrorIfCRDPathMissing: true, } + // Optionally set the path to the binary assets directory for envtest. + // This is useful when running tests directly (e.g., via an IDE) without + // using Makefile targets that set the KUBEBUILDER_ASSETS environment variable. + if dir := getFirstFoundEnvTestBinaryDir(); dir != "" { + testEnv.BinaryAssetsDirectory = dir + } + testEnv.UseExistingCluster = ptr.To(false) if val, ok := os.LookupEnv("USE_EXISTING_CLUSTER"); ok { boolValue, err := strconv.ParseBool(val) @@ -139,11 +145,14 @@ var _ = AfterSuite(func() { Expect(err).NotTo(HaveOccurred()) }) +// getFirstFoundEnvTestBinaryDir locates the first subdirectory under +// ../bin/k8s, streamlining the process of finding envtest binaries similar +// to setting the KUBEBUILDER_ASSETS environment variable. 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) + logf.Log.Info("Could not read envtest binary directory", "path", basePath, "error", err) return "" } for _, entry := range entries { From 5cd26b9b1eed94d057ad724f4a0a06010155845b Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Tue, 16 Jun 2026 13:17:41 +0200 Subject: [PATCH 18/19] fix: Tighten PKGS regex to only exclude unserved API versions The regex was changed from [1-2] to [1-9]+ which excluded all v1alpha versions including active ones (v1alpha4-5). Tighten to [1-3] to match only the unserved API versions. Assisted-by: Claude --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 981b1f77d..2d9d5efcf 100644 --- a/Makefile +++ b/Makefile @@ -126,8 +126,8 @@ endif 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-9]+))') +# Source packages outside of tests and unserved API versions (v1alpha1-3) +PKGS := $(shell go list ./... | grep -vE 'github.com/redhat-developer/rhdh-operator/(tests/|api/v1alpha[1-3]$$)') .PHONY: all all: build From 18a364490733408df8a56bea19e87dd84d2bf7db Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Thu, 18 Jun 2026 12:57:28 +0200 Subject: [PATCH 19/19] chore: Bump controller-tools to v0.21.0 and regen manifests Now that Go 1.26 is available, bump controller-tools from v0.20.0 to v0.21.0 and regenerate CRDs, bundle manifests, and dist artifacts. Assisted-by: Claude --- Makefile | 3 +-- .../manifests/backstage-operator.clusterserviceversion.yaml | 2 +- bundle/backstage.io/manifests/rhdh.redhat.com_backstages.yaml | 2 +- .../manifests/backstage-operator.clusterserviceversion.yaml | 2 +- bundle/rhdh/manifests/rhdh.redhat.com_backstages.yaml | 2 +- config/crd/bases/rhdh.redhat.com_backstages.yaml | 2 +- dist/backstage.io/install.yaml | 2 +- dist/rhdh/install.yaml | 2 +- 8 files changed, 8 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 2d9d5efcf..44bcbeaad 100644 --- a/Makefile +++ b/Makefile @@ -477,8 +477,7 @@ GINKGO ?= $(LOCALBIN)/ginkgo ## Tool Versions KUSTOMIZE_VERSION ?= v5.8.1 -# TODO(asoro): bump to v0.21.0+ once we switch to Go 1.26 (RHIDP-12020) -CONTROLLER_TOOLS_VERSION ?= v0.20.0 +CONTROLLER_TOOLS_VERSION ?= v0.21.0 ENVTEST_VERSION ?= $(shell go list -m -f '{{if .Replace}}{{.Replace.Version}}{{else}}{{.Version}}{{end}}' sigs.k8s.io/controller-runtime | awk -F'[v.]' '{printf "release-%d.%d", $$2, $$3}') GOLANGCI_LINT_VERSION ?= v2.12.2 GOIMPORTS_VERSION ?= v0.46.0 diff --git a/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml b/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml index 675c6ba05..a069bc880 100644 --- a/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml +++ b/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml @@ -25,7 +25,7 @@ metadata: } } ] - createdAt: "2026-06-16T11:07:01Z" + createdAt: "2026-06-18T10:56:32Z" description: Backstage Operator operators.operatorframework.io/builder: operator-sdk-v1.42.2 operators.operatorframework.io/project_layout: go.kubebuilder.io/v4 diff --git a/bundle/backstage.io/manifests/rhdh.redhat.com_backstages.yaml b/bundle/backstage.io/manifests/rhdh.redhat.com_backstages.yaml index 271d6e40b..372b4ca38 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.20.0 + controller-gen.kubebuilder.io/version: v0.21.0 creationTimestamp: null name: backstages.rhdh.redhat.com spec: diff --git a/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml b/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml index 417525d13..1165d3338 100644 --- a/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml +++ b/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml @@ -29,7 +29,7 @@ metadata: categories: Developer Tools certified: "true" containerImage: registry.redhat.io/rhdh/rhdh-rhel9-operator:1.11 - createdAt: "2026-06-16T11:07:02Z" + createdAt: "2026-06-18T10:56:33Z" 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 diff --git a/bundle/rhdh/manifests/rhdh.redhat.com_backstages.yaml b/bundle/rhdh/manifests/rhdh.redhat.com_backstages.yaml index 271d6e40b..372b4ca38 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.20.0 + controller-gen.kubebuilder.io/version: v0.21.0 creationTimestamp: null name: backstages.rhdh.redhat.com spec: diff --git a/config/crd/bases/rhdh.redhat.com_backstages.yaml b/config/crd/bases/rhdh.redhat.com_backstages.yaml index 109073c27..1d30468b9 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.20.0 + controller-gen.kubebuilder.io/version: v0.21.0 name: backstages.rhdh.redhat.com spec: group: rhdh.redhat.com diff --git a/dist/backstage.io/install.yaml b/dist/backstage.io/install.yaml index f6c29acc7..801b2148e 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.20.0 + controller-gen.kubebuilder.io/version: v0.21.0 name: backstages.rhdh.redhat.com spec: group: rhdh.redhat.com diff --git a/dist/rhdh/install.yaml b/dist/rhdh/install.yaml index 3d15369bc..dd67cbea5 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.20.0 + controller-gen.kubebuilder.io/version: v0.21.0 name: backstages.rhdh.redhat.com spec: group: rhdh.redhat.com