diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 4dafb3c..3acb421 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,6 +1,6 @@ { "name": "Kubebuilder DevContainer", - "image": "docker.io/golang:1.24", + "image": "docker.io/golang:1.25", "features": { "ghcr.io/devcontainers/features/docker-in-docker:2": {}, "ghcr.io/devcontainers/features/git:1": {} diff --git a/.devcontainer/post-install.sh b/.devcontainer/post-install.sh index 265c43e..67f3e97 100644 --- a/.devcontainer/post-install.sh +++ b/.devcontainer/post-install.sh @@ -1,16 +1,16 @@ #!/bin/bash set -x -curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-amd64 +curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-$(go env GOARCH) chmod +x ./kind mv ./kind /usr/local/bin/kind -curl -L -o kubebuilder https://go.kubebuilder.io/dl/latest/linux/amd64 +curl -L -o kubebuilder https://go.kubebuilder.io/dl/latest/linux/$(go env GOARCH) chmod +x kubebuilder mv kubebuilder /usr/local/bin/ KUBECTL_VERSION=$(curl -L -s https://dl.k8s.io/release/stable.txt) -curl -LO "https://dl.k8s.io/release/$KUBECTL_VERSION/bin/linux/amd64/kubectl" +curl -LO "https://dl.k8s.io/release/$KUBECTL_VERSION/bin/linux/$(go env GOARCH)/kubectl" chmod +x kubectl mv kubectl /usr/local/bin/kubectl diff --git a/.dockerignore b/.dockerignore index a3aab7a..0afd217 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,3 @@ # More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file # Ignore build and test binaries. -bin/ +bin/ \ No newline at end of file diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 0c9f2db..52e66d0 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -20,6 +20,6 @@ jobs: go-version-file: go.mod - name: Run linter - uses: golangci/golangci-lint-action@v6 + uses: golangci/golangci-lint-action@v8 with: - version: v1.64.8 + version: v2.5.0 diff --git a/.github/workflows/test-e2e.yml b/.github/workflows/test-e2e.yml index 0ce9473..a7ca273 100644 --- a/.github/workflows/test-e2e.yml +++ b/.github/workflows/test-e2e.yml @@ -21,16 +21,13 @@ jobs: - name: Install the latest version of kind run: | - curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-amd64 + curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-$(go env GOARCH) chmod +x ./kind sudo mv ./kind /usr/local/bin/kind - name: Verify kind installation run: kind version - - name: Create kind cluster - run: kind create cluster - - name: Running Test e2e run: | go mod tidy diff --git a/.gitignore b/.gitignore index ada68ff..9f0f3a1 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,6 @@ go.work *.swp *.swo *~ + +# Kubeconfig might contain secrets +*.kubeconfig diff --git a/.golangci.bck.yml b/.golangci.bck.yml new file mode 100644 index 0000000..331b6f2 --- /dev/null +++ b/.golangci.bck.yml @@ -0,0 +1,116 @@ +--- +run: + # Timeout for analysis. + timeout: 5m + + # Modules download mode (do not modify go.mod) + modules-download-mode: readonly + + # Include test files (see below to exclude certain linters) + tests: true + +issues: + exclude-rules: + # Exclude certain linters for test code + - path: "_test\\.go" + linters: + - bodyclose + - dupl + - dogsled + - funlen + - gosec + +output: + formats: + - format: colored-line-number + path: stdout + print-issued-lines: true + print-linter-name: true + +linters-settings: + depguard: + rules: + main: + # Packages that are not allowed where the value is a suggestion. + deny: + - pkg: "github.com/pkg/errors" + desc: Should be replaced by standard lib errors package + cyclop: + # The maximal code complexity to report. + max-complexity: 15 + skip-tests: true + funlen: + lines: 100 + nestif: + min-complexity: 6 + forbidigo: + forbid: + - http\.NotFound.* # return RFC 7807 problem details instead + - http\.Error.* # return RFC 7807 problem details instead + gomoddirectives: + replace-allow-list: + - github.com/abbot/go-http-auth # https://github.com/traefik/traefik/issues/6873#issuecomment-637654361 + +linters: + disable-all: true + enable: + # enabled by default by golangci-lint + - errcheck # checking for unchecked errors, these unchecked errors can be critical bugs in some cases + - gosimple # specializes in simplifying a code + - govet # reports suspicious constructs, such as Printf calls whose arguments do not align with the format string + - ineffassign # detects when assignments to existing variables are not used + - staticcheck # is a go vet on steroids, applying a ton of static analysis checks + - typecheck # like the front-end of a Go compiler, parses and type-checks Go code + - unused # checks for unused constants, variables, functions and types + # extra enabled by us + - asasalint # checks for pass []any as any in variadic func(...any) + - asciicheck # checks that your code does not contain non-ASCII identifiers + - bidichk # checks for dangerous unicode character sequences + - bodyclose # checks whether HTTP response body is closed successfully + - cyclop # checks function and package cyclomatic complexity + - dupl # tool for code clone detection + - durationcheck # checks for two durations multiplied together + - dogsled # find assignments/declarations with too many blank identifiers + - errname # checks that sentinel errors are prefixed with the Err and error types are suffixed with the Error + - errorlint # finds code that will cause problems with the error wrapping scheme introduced in Go 1.13 + - exhaustive # checks exhaustiveness of enum switch statements + - exptostd # detects functions from golang.org/x/exp/ that can be replaced by std functions + - copyloopvar # checks for pointers to enclosing loop variables + - fatcontext # detects nested contexts in loops and function literals + - forbidigo # forbids identifiers + - funlen # tool for detection of long functions + - gocheckcompilerdirectives # validates go compiler directive comments (//go:) + - goconst # finds repeated strings that could be replaced by a constant + - gocritic # provides diagnostics that check for bugs, performance and style issues + - gofmt # checks if the code is formatted according to 'gofmt' command + - goimports # in addition to fixing imports, goimports also formats your code in the same style as gofmt + - gomoddirectives # manages the use of 'replace', 'retract', and 'excludes' directives in go.mod + - gomodguard # allow and block lists linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations + - goprintffuncname # checks that printf-like functions are named with f at the end + - gosec # inspects source code for security problems + - loggercheck # checks key value pairs for common logger libraries (kitlog,klog,logr,zap) + - makezero # finds slice declarations with non-zero initial length + - mirror # reports wrong mirror patterns of bytes/strings usage + - misspell # finds commonly misspelled English words + - nakedret # finds naked returns in functions greater than a specified function length + - nestif # reports deeply nested if statements + - nilerr # finds the code that returns nil even if it checks that the error is not nil + - nolintlint # reports ill-formed or insufficient nolint directives + - nosprintfhostport # checks for misuse of Sprintf to construct a host with port in a URL + - perfsprint # Golang linter for performance, aiming at usages of fmt.Sprintf which have faster alternatives + - predeclared # finds code that shadows one of Go's predeclared identifiers + - promlinter # checks Prometheus metrics naming via promlint + - reassign # checks that package variables are not reassigned + - revive # fast, configurable, extensible, flexible, and beautiful linter for Go, drop-in replacement of golint + - rowserrcheck # checks whether Err of rows is checked successfully + - sqlclosecheck # checks that sql.Rows and sql.Stmt are closed + - sloglint # A Go linter that ensures consistent code style when using log/slog + - tagliatelle # checks the struct tags. + - testableexamples # checks if examples are testable (have an expected output) + - tparallel # detects inappropriate usage of t.Parallel() method in your Go test codes + - usetesting # detects using os.Setenv instead of t.Setenv since Go1.17 + - unconvert # removes unnecessary type conversions + - unparam # reports unused function parameters + - usestdlibvars # detects the possibility to use variables/constants from the Go standard library + - wastedassign # finds wasted assignment statements + fast: false \ No newline at end of file diff --git a/.golangci.yml b/.golangci.yml index 331b6f2..0c75149 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,116 +1,118 @@ ---- +version: "2" run: - # Timeout for analysis. - timeout: 5m - - # Modules download mode (do not modify go.mod) modules-download-mode: readonly - - # Include test files (see below to exclude certain linters) tests: true - -issues: - exclude-rules: - # Exclude certain linters for test code - - path: "_test\\.go" - linters: - - bodyclose - - dupl - - dogsled - - funlen - - gosec - output: formats: - - format: colored-line-number + text: path: stdout - print-issued-lines: true - print-linter-name: true - -linters-settings: - depguard: - rules: - main: - # Packages that are not allowed where the value is a suggestion. - deny: - - pkg: "github.com/pkg/errors" - desc: Should be replaced by standard lib errors package - cyclop: - # The maximal code complexity to report. - max-complexity: 15 - skip-tests: true - funlen: - lines: 100 - nestif: - min-complexity: 6 - forbidigo: - forbid: - - http\.NotFound.* # return RFC 7807 problem details instead - - http\.Error.* # return RFC 7807 problem details instead - gomoddirectives: - replace-allow-list: - - github.com/abbot/go-http-auth # https://github.com/traefik/traefik/issues/6873#issuecomment-637654361 - + print-linter-name: true + print-issued-lines: true linters: - disable-all: true + default: none + enable: + - asasalint + - asciicheck + - bidichk + - bodyclose + - copyloopvar + - cyclop + - dogsled + - dupl + - durationcheck + - errcheck + - errname + - errorlint + - exhaustive + - exptostd + - fatcontext + - forbidigo + - funlen + - gocheckcompilerdirectives + - goconst + - gocritic + - gomoddirectives + - gomodguard + - goprintffuncname + - gosec + - govet + - ineffassign + - loggercheck + - makezero + - mirror + - misspell + - nakedret + - nestif + - nilerr + - nolintlint + - nosprintfhostport + - perfsprint + - predeclared + - promlinter + - reassign + - revive + - rowserrcheck + - sloglint + - sqlclosecheck + - staticcheck + - tagliatelle + - testableexamples + - tparallel + - unconvert + - unparam + - unused + - usestdlibvars + - usetesting + - wastedassign + settings: + cyclop: + max-complexity: 15 + depguard: + rules: + main: + deny: + - pkg: github.com/pkg/errors + desc: Should be replaced by standard lib errors package + forbidigo: + forbid: + - pattern: http\.NotFound.* + - pattern: http\.Error.* + funlen: + lines: 100 + gomoddirectives: + replace-allow-list: + - github.com/abbot/go-http-auth + nestif: + min-complexity: 6 + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + rules: + - linters: + - bodyclose + - dogsled + - dupl + - funlen + - gosec + path: _test\.go + - linters: + - cyclop + path: (.+)_test\.go + paths: + - third_party$ + - builtin$ + - examples$ +formatters: enable: - # enabled by default by golangci-lint - - errcheck # checking for unchecked errors, these unchecked errors can be critical bugs in some cases - - gosimple # specializes in simplifying a code - - govet # reports suspicious constructs, such as Printf calls whose arguments do not align with the format string - - ineffassign # detects when assignments to existing variables are not used - - staticcheck # is a go vet on steroids, applying a ton of static analysis checks - - typecheck # like the front-end of a Go compiler, parses and type-checks Go code - - unused # checks for unused constants, variables, functions and types - # extra enabled by us - - asasalint # checks for pass []any as any in variadic func(...any) - - asciicheck # checks that your code does not contain non-ASCII identifiers - - bidichk # checks for dangerous unicode character sequences - - bodyclose # checks whether HTTP response body is closed successfully - - cyclop # checks function and package cyclomatic complexity - - dupl # tool for code clone detection - - durationcheck # checks for two durations multiplied together - - dogsled # find assignments/declarations with too many blank identifiers - - errname # checks that sentinel errors are prefixed with the Err and error types are suffixed with the Error - - errorlint # finds code that will cause problems with the error wrapping scheme introduced in Go 1.13 - - exhaustive # checks exhaustiveness of enum switch statements - - exptostd # detects functions from golang.org/x/exp/ that can be replaced by std functions - - copyloopvar # checks for pointers to enclosing loop variables - - fatcontext # detects nested contexts in loops and function literals - - forbidigo # forbids identifiers - - funlen # tool for detection of long functions - - gocheckcompilerdirectives # validates go compiler directive comments (//go:) - - goconst # finds repeated strings that could be replaced by a constant - - gocritic # provides diagnostics that check for bugs, performance and style issues - - gofmt # checks if the code is formatted according to 'gofmt' command - - goimports # in addition to fixing imports, goimports also formats your code in the same style as gofmt - - gomoddirectives # manages the use of 'replace', 'retract', and 'excludes' directives in go.mod - - gomodguard # allow and block lists linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations - - goprintffuncname # checks that printf-like functions are named with f at the end - - gosec # inspects source code for security problems - - loggercheck # checks key value pairs for common logger libraries (kitlog,klog,logr,zap) - - makezero # finds slice declarations with non-zero initial length - - mirror # reports wrong mirror patterns of bytes/strings usage - - misspell # finds commonly misspelled English words - - nakedret # finds naked returns in functions greater than a specified function length - - nestif # reports deeply nested if statements - - nilerr # finds the code that returns nil even if it checks that the error is not nil - - nolintlint # reports ill-formed or insufficient nolint directives - - nosprintfhostport # checks for misuse of Sprintf to construct a host with port in a URL - - perfsprint # Golang linter for performance, aiming at usages of fmt.Sprintf which have faster alternatives - - predeclared # finds code that shadows one of Go's predeclared identifiers - - promlinter # checks Prometheus metrics naming via promlint - - reassign # checks that package variables are not reassigned - - revive # fast, configurable, extensible, flexible, and beautiful linter for Go, drop-in replacement of golint - - rowserrcheck # checks whether Err of rows is checked successfully - - sqlclosecheck # checks that sql.Rows and sql.Stmt are closed - - sloglint # A Go linter that ensures consistent code style when using log/slog - - tagliatelle # checks the struct tags. - - testableexamples # checks if examples are testable (have an expected output) - - tparallel # detects inappropriate usage of t.Parallel() method in your Go test codes - - usetesting # detects using os.Setenv instead of t.Setenv since Go1.17 - - unconvert # removes unnecessary type conversions - - unparam # reports unused function parameters - - usestdlibvars # detects the possibility to use variables/constants from the Go standard library - - wastedassign # finds wasted assignment statements - fast: false \ No newline at end of file + - gofmt + - goimports + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ diff --git a/Dockerfile b/Dockerfile index 735a9dc..2485647 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Build the manager binary -FROM docker.io/golang:1.24 AS builder +FROM docker.io/golang:1.25 AS builder ARG TARGETOS ARG TARGETARCH diff --git a/Makefile b/Makefile index bdc20dd..fad5ea9 100644 --- a/Makefile +++ b/Makefile @@ -43,13 +43,13 @@ help: ## Display this help. .PHONY: manifests manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. - $(CONTROLLER_GEN) rbac:roleName=manager-role crd:allowDangerousTypes=true webhook paths="./..." output:crd:artifacts:config=config/crd/bases + "$(CONTROLLER_GEN)" rbac:roleName=manager-role crd:allowDangerousTypes=true webhook paths="./..." output:crd:artifacts:config=config/crd/bases go run config/crd/update_openapi.go config/crd/bases ## allowDangerousTypes=true for v2beta structs .PHONY: generate generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. - $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." + "$(CONTROLLER_GEN)" object:headerFile="hack/boilerplate.go.txt" paths="./..." .PHONY: fmt fmt: ## Run go fmt against code. @@ -65,32 +65,44 @@ test: manifests generate fmt vet setup-envtest ## Run tests. # TODO(user): To use a different vendor for e2e tests, modify the setup under 'tests/e2e'. # The default setup assumes Kind is pre-installed and builds/loads the Manager Docker image locally. -# Prometheus and CertManager are installed by default; skip with: -# - PROMETHEUS_INSTALL_SKIP=true +# CertManager is installed by default; skip with: # - CERT_MANAGER_INSTALL_SKIP=true -.PHONY: test-e2e -test-e2e: manifests generate fmt vet ## Run the e2e tests. Expected an isolated environment using Kind. - @command -v kind >/dev/null 2>&1 || { \ +KIND_CLUSTER ?= mapserver-operator-test-e2e + +.PHONY: setup-test-e2e +setup-test-e2e: ## Set up a Kind cluster for e2e tests if it does not exist + @command -v $(KIND) >/dev/null 2>&1 || { \ echo "Kind is not installed. Please install Kind manually."; \ exit 1; \ } - @kind get clusters | grep -q 'kind' || { \ - echo "No Kind cluster is running. Please start a Kind cluster before running the e2e tests."; \ - exit 1; \ - } - go test ./test/e2e/ -v -ginkgo.v + @case "$$($(KIND) get clusters)" in \ + *"$(KIND_CLUSTER)"*) \ + echo "Kind cluster '$(KIND_CLUSTER)' already exists. Skipping creation." ;; \ + *) \ + echo "Creating Kind cluster '$(KIND_CLUSTER)'..."; \ + $(KIND) create cluster --name $(KIND_CLUSTER) ;; \ + esac + +.PHONY: test-e2e +test-e2e: setup-test-e2e manifests generate fmt vet ## Run the e2e tests. Expected an isolated environment using Kind. + KIND=$(KIND) KIND_CLUSTER=$(KIND_CLUSTER) go test -tags=e2e ./test/e2e/ -v -ginkgo.v + $(MAKE) cleanup-test-e2e + +.PHONY: cleanup-test-e2e +cleanup-test-e2e: ## Tear down the Kind cluster used for e2e tests + @$(KIND) delete cluster --name $(KIND_CLUSTER) .PHONY: lint lint: golangci-lint ## Run golangci-lint linter - $(GOLANGCI_LINT) run + "$(GOLANGCI_LINT)" run .PHONY: lint-fix lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes - $(GOLANGCI_LINT) run --fix + "$(GOLANGCI_LINT)" run --fix .PHONY: lint-config lint-config: golangci-lint ## Verify golangci-lint linter configuration - $(GOLANGCI_LINT) config verify + "$(GOLANGCI_LINT)" config verify ##@ Build @@ -133,8 +145,8 @@ docker-buildx: ## Build and push docker image for the manager for cross-platform .PHONY: build-installer build-installer: manifests generate kustomize ## Generate a consolidated YAML with CRDs and deployment. mkdir -p dist - cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} - $(KUSTOMIZE) build config/default > dist/install.yaml + cd config/manager && "$(KUSTOMIZE)" edit set image controller=${IMG} + "$(KUSTOMIZE)" build config/default > dist/install.yaml ##@ Deployment @@ -144,44 +156,53 @@ endif .PHONY: install install: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config. - $(KUSTOMIZE) build config/crd | $(KUBECTL) apply -f - + @out="$$( "$(KUSTOMIZE)" build config/crd 2>/dev/null || true )"; \ + if [ -n "$$out" ]; then echo "$$out" | "$(KUBECTL)" apply -f -; else echo "No CRDs to install; skipping."; fi .PHONY: uninstall uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. - $(KUSTOMIZE) build config/crd | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f - + @out="$$( "$(KUSTOMIZE)" build config/crd 2>/dev/null || true )"; \ + if [ -n "$$out" ]; then echo "$$out" | "$(KUBECTL)" delete --ignore-not-found=$(ignore-not-found) -f -; else echo "No CRDs to delete; skipping."; fi .PHONY: deploy deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config. - cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} - $(KUSTOMIZE) build config/default | $(KUBECTL) apply -f - + cd config/manager && "$(KUSTOMIZE)" edit set image controller=${IMG} + "$(KUSTOMIZE)" build config/default | "$(KUBECTL)" apply -f - .PHONY: undeploy undeploy: kustomize ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. - $(KUSTOMIZE) build config/default | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f - + "$(KUSTOMIZE)" build config/default | "$(KUBECTL)" delete --ignore-not-found=$(ignore-not-found) -f - ##@ Dependencies ## Location to install dependencies to LOCALBIN ?= $(shell pwd)/bin $(LOCALBIN): - mkdir -p $(LOCALBIN) + mkdir -p "$(LOCALBIN)" ## Tool Binaries KUBECTL ?= kubectl +KIND ?= kind KUSTOMIZE ?= $(LOCALBIN)/kustomize CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen ENVTEST ?= $(LOCALBIN)/setup-envtest GOLANGCI_LINT = $(LOCALBIN)/golangci-lint ## Tool Versions -KUSTOMIZE_VERSION ?= v5.5.0 -CONTROLLER_TOOLS_VERSION ?= v0.17.1 +KUSTOMIZE_VERSION ?= v5.7.1 +CONTROLLER_TOOLS_VERSION ?= v0.19.0 + #ENVTEST_VERSION is the version of controller-runtime release branch to fetch the envtest setup script (i.e. release-0.20) -ENVTEST_VERSION ?= $(shell go list -m -f "{{ .Version }}" sigs.k8s.io/controller-runtime | awk -F'[v.]' '{printf "release-%d.%d", $$2, $$3}') +ENVTEST_VERSION ?= $(shell v='$(call gomodver,sigs.k8s.io/controller-runtime)'; \ + [ -n "$$v" ] || { echo "Set ENVTEST_VERSION manually (controller-runtime replace has no tag)" >&2; exit 1; }; \ + printf '%s\n' "$$v" | sed -E 's/^v?([0-9]+)\.([0-9]+).*/release-\1.\2/') + #ENVTEST_K8S_VERSION is the version of Kubernetes to use for setting up ENVTEST binaries (i.e. 1.31) -ENVTEST_K8S_VERSION ?= $(shell go list -m -f "{{ .Version }}" k8s.io/api | awk -F'[v.]' '{printf "1.%d", $$3}') -GOLANGCI_LINT_VERSION ?= v1.64.8 +ENVTEST_K8S_VERSION ?= $(shell v='$(call gomodver,k8s.io/api)'; \ + [ -n "$$v" ] || { echo "Set ENVTEST_K8S_VERSION manually (k8s.io/api replace has no tag)" >&2; exit 1; }; \ + printf '%s\n' "$$v" | sed -E 's/^v?[0-9]+\.([0-9]+).*/1.\1/') +GOLANGCI_LINT_VERSION ?= v2.5.0 .PHONY: kustomize kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. $(KUSTOMIZE): $(LOCALBIN) @@ -195,7 +216,7 @@ $(CONTROLLER_GEN): $(LOCALBIN) .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 || { \ + @"$(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; \ } @@ -208,20 +229,24 @@ $(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/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION)) + $(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/v2/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION)) # go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist # $1 - target path with name of binary # $2 - package url which can be installed # $3 - specific version of package define go-install-tool -@[ -f "$(1)-$(3)" ] || { \ +@[ -f "$(1)-$(3)" ] && [ "$$(readlink -- "$(1)" 2>/dev/null)" = "$(1)-$(3)" ] || { \ set -e; \ package=$(2)@$(3) ;\ echo "Downloading $${package}" ;\ -rm -f $(1) || true ;\ -GOBIN=$(LOCALBIN) go install $${package} ;\ -mv $(1) $(1)-$(3) ;\ +rm -f "$(1)" ;\ +GOBIN="$(LOCALBIN)" go install $${package} ;\ +mv "$(LOCALBIN)/$$(basename "$(1)")" "$(1)-$(3)" ;\ } ;\ -ln -sf $(1)-$(3) $(1) +ln -sf "$$(realpath "$(1)-$(3)")" "$(1)" +endef + +define gomodver +$(shell go list -m -f '{{if .Replace}}{{.Replace.Version}}{{else}}{{.Version}}{{end}}' $(1) 2>/dev/null) endef diff --git a/PROJECT b/PROJECT index 55b9919..2c555d1 100644 --- a/PROJECT +++ b/PROJECT @@ -2,6 +2,7 @@ # This file is used to track the info used to scaffold your project # and allow the plugins properly work. # More info: https://book.kubebuilder.io/reference/project-config.html +cliVersion: 4.10.1 domain: pdok.nl layout: - go.kubebuilder.io/v4 diff --git a/README.md b/README.md index afabaa5..ce7844f 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ This operator uses two Custom Resources(CR) called _WMS_ and _WFS_ as the input ## Getting Started ### Prerequisites -- go version v1.24.0+ +- go version v1.25.0+ - docker version 17.03+. - kubectl version v1.11.3+. - Access to a Kubernetes v1.11.3+ cluster. @@ -82,6 +82,10 @@ The project is written in Go and scaffolded with [kubebuilder](https://kubebuild Read the manual when you want/need to make changes. E.g. run `make test` before committing. +To update the scaffolding: +- Install the latest version of kubebuilder globally on your machine; +- Run: `kubebuilder alpha update --from-branch master` + ### Linting Install [golangci-lint](https://golangci-lint.run/usage/install/) and run `golangci-lint run` diff --git a/api/v2beta1/wfs_conversion.go b/api/v2beta1/wfs_conversion.go index 24664fc..be57931 100644 --- a/api/v2beta1/wfs_conversion.go +++ b/api/v2beta1/wfs_conversion.go @@ -207,7 +207,7 @@ func (dst *WFS) ConvertFrom(srcRaw conversion.Hub) error { dst.ObjectMeta = src.ObjectMeta - dst.Spec.General = LabelsToV2General(src.ObjectMeta.Labels) + dst.Spec.General = LabelsToV2General(src.Labels) dst.Spec.Kubernetes = NewV2KubernetesObject(src.Spec.Lifecycle, src.Spec.PodSpecPatch, src.Spec.HorizontalPodAutoscalerPatch) diff --git a/api/v2beta1/wms_conversion.go b/api/v2beta1/wms_conversion.go index 9c8f121..1b95d0a 100644 --- a/api/v2beta1/wms_conversion.go +++ b/api/v2beta1/wms_conversion.go @@ -198,7 +198,7 @@ func (dst *WMS) ConvertFrom(srcRaw conversion.Hub) error { dst.ObjectMeta = src.ObjectMeta - dst.Spec.General = LabelsToV2General(src.ObjectMeta.Labels) + dst.Spec.General = LabelsToV2General(src.Labels) dst.Spec.Kubernetes = NewV2KubernetesObject(src.Spec.Lifecycle, src.Spec.PodSpecPatch, src.Spec.HorizontalPodAutoscalerPatch) dst.Spec.Kubernetes.HealthCheck = convertHealthCheckToV2(src.Spec.HealthCheck) diff --git a/config/crd/bases/pdok.nl_wfs.yaml b/config/crd/bases/pdok.nl_wfs.yaml index 2d9378f..ddc6127 100644 --- a/config/crd/bases/pdok.nl_wfs.yaml +++ b/config/crd/bases/pdok.nl_wfs.yaml @@ -2,8 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.17.1 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.19.0 name: wfs.pdok.nl spec: group: pdok.nl diff --git a/config/crd/bases/pdok.nl_wms.yaml b/config/crd/bases/pdok.nl_wms.yaml index 35ed9aa..0fa86d8 100644 --- a/config/crd/bases/pdok.nl_wms.yaml +++ b/config/crd/bases/pdok.nl_wms.yaml @@ -2,8 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.17.1 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.19.0 name: wms.pdok.nl spec: group: pdok.nl diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index 82e4d3f..646c13c 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -69,6 +69,7 @@ spec: name: manager ports: [] securityContext: + readOnlyRootFilesystem: true allowPrivilegeEscalation: false capabilities: drop: diff --git a/config/rbac/kustomization.yaml b/config/rbac/kustomization.yaml index 06d35b7..55cda0b 100644 --- a/config/rbac/kustomization.yaml +++ b/config/rbac/kustomization.yaml @@ -20,7 +20,7 @@ resources: - metrics_reader_role.yaml # For each CRD, "Admin", "Editor" and "Viewer" roles are scaffolded by # default, aiding admins in cluster management. Those roles are -# not used by the {{ .ProjectName }} itself. You can comment the following lines +# not used by the mapserver-operator itself. You can comment the following lines # if you do not want those helpers be installed with your Project. - wfs_admin_role.yaml - wfs_editor_role.yaml diff --git a/go.mod b/go.mod index 192a905..25985e7 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,8 @@ module github.com/pdok/mapserver-operator -go 1.24.0 +go 1.25 -toolchain go1.24.2 - -godebug default=go1.23 +godebug default=go1.25 require ( github.com/cbroglie/mustache v1.4.0 @@ -17,11 +15,11 @@ require ( github.com/peterbourgon/ff v1.7.1 github.com/stretchr/testify v1.10.0 github.com/traefik/traefik/v3 v3.4.1 - k8s.io/api v0.33.1 - k8s.io/apimachinery v0.33.1 - k8s.io/client-go v0.33.1 - sigs.k8s.io/controller-runtime v0.21.0 - sigs.k8s.io/yaml v1.4.0 + k8s.io/api v0.34.1 + k8s.io/apimachinery v0.34.1 + k8s.io/client-go v0.34.1 + sigs.k8s.io/controller-runtime v0.22.4 + sigs.k8s.io/yaml v1.6.0 ) replace github.com/abbot/go-http-auth => github.com/abbot/go-http-auth v0.4.0 // for github.com/traefik/traefik/v3 @@ -47,8 +45,6 @@ require ( github.com/rs/zerolog v1.33.0 // indirect github.com/traefik/paerser v0.2.2 // indirect github.com/unrolled/render v1.0.2 // indirect - go.etcd.io/etcd/client/pkg/v3 v3.6.0 // indirect - go.etcd.io/etcd/client/v3 v3.6.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0 // indirect @@ -57,9 +53,12 @@ require ( go.opentelemetry.io/otel/log v0.8.0 // indirect go.opentelemetry.io/otel/sdk/log v0.8.0 // indirect go.uber.org/automaxprocs v1.6.0 // indirect + go.yaml.in/yaml/v2 v2.4.2 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/crypto v0.38.0 // indirect golang.org/x/mod v0.24.0 // indirect sigs.k8s.io/randfill v1.0.0 // indirect + sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect ) require ( @@ -74,7 +73,7 @@ require ( 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; indirectC - github.com/fxamacker/cbor/v2 v2.8.0 // indirect + github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-errors/errors v1.5.1 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -85,8 +84,8 @@ require ( github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/google/btree v1.1.3 // indirect - github.com/google/cel-go v0.25.0 // indirect - github.com/google/gnostic-models v0.6.9 // indirect + github.com/google/cel-go v0.26.0 // indirect + github.com/google/gnostic-models v0.7.0 // indirect github.com/google/go-cmp v0.7.0 github.com/google/pprof v0.0.0-20250501235452-c0086092b71a // indirect github.com/google/uuid v1.6.0 // indirect @@ -96,7 +95,7 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/mailru/easyjson v0.9.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.22.0 // indirect @@ -134,15 +133,14 @@ require ( 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 - k8s.io/apiextensions-apiserver v0.33.1 - k8s.io/apiserver v0.33.1 // indirect - k8s.io/component-base v0.33.1 // indirect + k8s.io/apiextensions-apiserver v0.34.1 + k8s.io/apiserver v0.34.1 // indirect + k8s.io/component-base v0.34.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect - k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979 + k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect + k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.33.0 // indirect sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect sigs.k8s.io/kustomize/api v0.19.0 // indirect sigs.k8s.io/kustomize/kyaml v0.19.0 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect ) diff --git a/go.sum b/go.sum index ac4bf9c..ba15bf3 100644 --- a/go.sum +++ b/go.sum @@ -40,8 +40,8 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2 github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU= -github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= +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/go-acme/lego/v4 v4.23.1 h1:lZ5fGtGESA2L9FB8dNTvrQUq3/X4QOb8ExkKyY7LSV4= github.com/go-acme/lego/v4 v4.23.1/go.mod h1:7UMVR7oQbIYw6V7mTgGwi4Er7B6Ww0c+c8feiBM0EgI= github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk= @@ -75,12 +75,11 @@ 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.25.0 h1:jsFw9Fhn+3y2kBbltZR4VEz5xKkcIFRPDnuEzAGv5GY= -github.com/google/cel-go v0.25.0/go.mod h1:hjEb6r5SuOSlhCHmFoLzu8HGCERvIsDAbxDAyNU/MmI= -github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= -github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= +github.com/google/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.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= +github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-github/v28 v28.1.1 h1:kORf5ekX5qwXO2mGzXXOjMe/g6ap8ahVe0sBEulhSxo= @@ -136,8 +135,9 @@ github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUb github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus= @@ -205,12 +205,12 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.etcd.io/etcd/api/v3 v3.6.0 h1:vdbkcUBGLf1vfopoGE/uS3Nv0KPyIpUV/HM6w9yx2kM= -go.etcd.io/etcd/api/v3 v3.6.0/go.mod h1:Wt5yZqEmxgTNJGHob7mTVBJDZNXiHPtXTcPab37iFOw= -go.etcd.io/etcd/client/pkg/v3 v3.6.0 h1:nchnPqpuxvv3UuGGHaz0DQKYi5EIW5wOYsgUNRc365k= -go.etcd.io/etcd/client/pkg/v3 v3.6.0/go.mod h1:Jv5SFWMnGvIBn8o3OaBq/PnT0jjsX8iNokAUessNjoA= -go.etcd.io/etcd/client/v3 v3.6.0 h1:/yjKzD+HW5v/3DVj9tpwFxzNbu8hjcKID183ug9duWk= -go.etcd.io/etcd/client/v3 v3.6.0/go.mod h1:Jzk/Knqe06pkOZPHXsQ0+vNDvMQrgIqJ0W8DwPdMJMg= +go.etcd.io/etcd/api/v3 v3.6.4 h1:7F6N7toCKcV72QmoUKa23yYLiiljMrT4xCeBL9BmXdo= +go.etcd.io/etcd/api/v3 v3.6.4/go.mod h1:eFhhvfR8Px1P6SEuLT600v+vrhdDTdcfMzmnxVXXSbk= +go.etcd.io/etcd/client/pkg/v3 v3.6.4 h1:9HBYrjppeOfFjBjaMTRxT3R7xT0GLK8EJMVC4xg6ok0= +go.etcd.io/etcd/client/pkg/v3 v3.6.4/go.mod h1:sbdzr2cl3HzVmxNw//PH7aLGVtY4QySjQFuaCgcRFAI= +go.etcd.io/etcd/client/v3 v3.6.4 h1:YOMrCfMhRzY8NgtzUsHl8hC2EBSnuqbR3dh84Uryl7A= +go.etcd.io/etcd/client/v3 v3.6.4/go.mod h1:jaNNHCyg2FdALyKWnd7hxZXZxZANb0+KGY+YQaEMISo= 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/collector/pdata v1.10.0 h1:oLyPLGvPTQrcRT64ZVruwvmH/u3SHTfNo01pteS4WOE= @@ -253,6 +253,10 @@ 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/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= +go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= +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= @@ -329,38 +333,37 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 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.33.1 h1:tA6Cf3bHnLIrUK4IqEgb2v++/GYUtqiu9sRVk3iBXyw= -k8s.io/api v0.33.1/go.mod h1:87esjTn9DRSRTD4fWMXamiXxJhpOIREjWOSjsW1kEHw= -k8s.io/apiextensions-apiserver v0.33.1 h1:N7ccbSlRN6I2QBcXevB73PixX2dQNIW0ZRuguEE91zI= -k8s.io/apiextensions-apiserver v0.33.1/go.mod h1:uNQ52z1A1Gu75QSa+pFK5bcXc4hq7lpOXbweZgi4dqA= -k8s.io/apimachinery v0.33.1 h1:mzqXWV8tW9Rw4VeW9rEkqvnxj59k1ezDUl20tFK/oM4= -k8s.io/apimachinery v0.33.1/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= -k8s.io/apiserver v0.33.1 h1:yLgLUPDVC6tHbNcw5uE9mo1T6ELhJj7B0geifra3Qdo= -k8s.io/apiserver v0.33.1/go.mod h1:VMbE4ArWYLO01omz+k8hFjAdYfc3GVAYPrhP2tTKccs= -k8s.io/client-go v0.33.1 h1:ZZV/Ks2g92cyxWkRRnfUDsnhNn28eFpt26aGc8KbXF4= -k8s.io/client-go v0.33.1/go.mod h1:JAsUrl1ArO7uRVFWfcj6kOomSlCv+JpvIsp6usAGefA= -k8s.io/component-base v0.33.1 h1:EoJ0xA+wr77T+G8p6T3l4efT2oNwbqBVKR71E0tBIaI= -k8s.io/component-base v0.33.1/go.mod h1:guT/w/6piyPfTgq7gfvgetyXMIh10zuXA6cRRm3rDuY= +k8s.io/api v0.34.1 h1:jC+153630BMdlFukegoEL8E/yT7aLyQkIVuwhmwDgJM= +k8s.io/api v0.34.1/go.mod h1:SB80FxFtXn5/gwzCoN6QCtPD7Vbu5w2n1S0J5gFfTYk= +k8s.io/apiextensions-apiserver v0.34.1 h1:NNPBva8FNAPt1iSVwIE0FsdrVriRXMsaWFMqJbII2CI= +k8s.io/apiextensions-apiserver v0.34.1/go.mod h1:hP9Rld3zF5Ay2Of3BeEpLAToP+l4s5UlxiHfqRaRcMc= +k8s.io/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4= +k8s.io/apimachinery v0.34.1/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= +k8s.io/apiserver v0.34.1 h1:U3JBGdgANK3dfFcyknWde1G6X1F4bg7PXuvlqt8lITA= +k8s.io/apiserver v0.34.1/go.mod h1:eOOc9nrVqlBI1AFCvVzsob0OxtPZUCPiUJL45JOTBG0= +k8s.io/client-go v0.34.1 h1:ZUPJKgXsnKwVwmKKdPfw4tB58+7/Ik3CrjOEhsiZ7mY= +k8s.io/client-go v0.34.1/go.mod h1:kA8v0FP+tk6sZA0yKLRG67LWjqufAoSHA2xVGKw9Of8= +k8s.io/component-base v0.34.1 h1:v7xFgG+ONhytZNFpIz5/kecwD+sUhVE6HU7qQUiRM4A= +k8s.io/component-base v0.34.1/go.mod h1:mknCpLlTSKHzAQJJnnHVKqjxR7gBeHRv0rPXA7gdtQ0= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= -k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= -k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979 h1:jgJW5IePPXLGB8e/1wvd0Ich9QE97RvvF3a8J3fP/Lg= -k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA= +k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts= +k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y= +k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.33.0 h1:qPrZsv1cwQiFeieFlRqT627fVZ+tyfou/+S5S0H5ua0= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.33.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= -sigs.k8s.io/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8= -sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM= +sigs.k8s.io/controller-runtime v0.22.4 h1:GEjV7KV3TY8e+tJ2LCTxUTanW4z/FmNB7l327UfMq9A= +sigs.k8s.io/controller-runtime v0.22.4/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8= 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/api v0.19.0 h1:F+2HB2mU1MSiR9Hp1NEgoU2q9ItNOaBJl0I4Dlus5SQ= sigs.k8s.io/kustomize/api v0.19.0/go.mod h1:/BbwnivGVcBh1r+8m3tH1VNxJmHSk1PzP5fkP6lbL1o= sigs.k8s.io/kustomize/kyaml v0.19.0 h1:RFge5qsO1uHhwJsu3ipV7RNolC7Uozc0jUBC/61XSlA= sigs.k8s.io/kustomize/kyaml v0.19.0/go.mod h1:FeKD5jEOH+FbZPpqUghBP8mrLjJ3+zD3/rf9NNu1cwY= -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.7.0 h1:qPeWmscJcXP0snki5IYF79Z8xrl8ETFxgMd7wez1XkI= -sigs.k8s.io/structured-merge-diff/v4 v4.7.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= +sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco= +sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= +sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= +sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= diff --git a/internal/controller/blobdownload/blob_download.go b/internal/controller/blobdownload/blob_download.go index 7c439f7..e2a4053 100644 --- a/internal/controller/blobdownload/blob_download.go +++ b/internal/controller/blobdownload/blob_download.go @@ -220,6 +220,6 @@ func getRootFromFilename(fileName string) (string, error) { return fileName[:index], nil } -func writeLine(sb *strings.Builder, format string, a ...any) { +func writeLine(sb *strings.Builder, format string, a ...any) { //nolint:goprintffuncname sb.WriteString(fmt.Sprintf(format, a...) + "\n") } diff --git a/internal/controller/capabilitiesgenerator/mapper.go b/internal/controller/capabilitiesgenerator/mapper.go index 7a3e8d9..7084228 100644 --- a/internal/controller/capabilitiesgenerator/mapper.go +++ b/internal/controller/capabilitiesgenerator/mapper.go @@ -81,7 +81,7 @@ func MapWFSToCapabilitiesGeneratorInput(wfs *pdoknlv3.WFS, ownerInfo *smoothoper metadataURL.MediaType = wfs.Spec.Service.Inspire.ServiceMetadataURL.Custom.Type } - config.Services.WFS200Config.Wfs200.Capabilities.OperationsMetadata = &wfs200.OperationsMetadata{ + config.Services.WFS200Config.Wfs200.OperationsMetadata = &wfs200.OperationsMetadata{ ExtendedCapabilities: &wfs200.ExtendedCapabilities{ ExtendedCapabilities: wfs200.NestedExtendedCapabilities{ MetadataURL: metadataURL, @@ -99,12 +99,12 @@ func MapWFSToCapabilitiesGeneratorInput(wfs *pdoknlv3.WFS, ownerInfo *smoothoper } } if wfs.Spec.Service.CountDefault != nil { - operationsMetadata := config.Services.WFS200Config.Wfs200.Capabilities.OperationsMetadata + operationsMetadata := config.Services.WFS200Config.Wfs200.OperationsMetadata if operationsMetadata == nil { operationsMetadata = &wfs200.OperationsMetadata{} } operationsMetadata.Constraint = getConstraints(strconv.Itoa(*wfs.Spec.Service.CountDefault)) - config.Services.WFS200Config.Wfs200.Capabilities.OperationsMetadata = operationsMetadata + config.Services.WFS200Config.Wfs200.OperationsMetadata = operationsMetadata } return &config, nil diff --git a/internal/controller/mapfilegenerator/mapper.go b/internal/controller/mapfilegenerator/mapper.go index ce3121e..a36c15f 100644 --- a/internal/controller/mapfilegenerator/mapper.go +++ b/internal/controller/mapfilegenerator/mapper.go @@ -228,13 +228,13 @@ func mapLayers(wms *pdoknlv3.WMS, extent string, result *WMSInput) { result.Layers = append(result.Layers, layer) } else if annotatedLayer.IsGroupLayer && !annotatedLayer.IsTopLayer { groupLayer := GroupLayer{ - Name: *annotatedLayer.Layer.Name, - Title: smoothoperatorutils.PointerVal(annotatedLayer.Layer.Title, ""), - Abstract: smoothoperatorutils.PointerVal(annotatedLayer.Layer.Abstract, ""), + Name: *annotatedLayer.Name, + Title: smoothoperatorutils.PointerVal(annotatedLayer.Title, ""), + Abstract: smoothoperatorutils.PointerVal(annotatedLayer.Abstract, ""), StyleName: "", StyleTitle: "", } - if len(annotatedLayer.Layer.Styles) > 0 { + if len(annotatedLayer.Styles) > 0 { groupLayer.StyleName = annotatedLayer.Layer.Styles[0].Name groupLayer.StyleTitle = smoothoperatorutils.PointerVal(annotatedLayer.Layer.Styles[0].Title, "") } diff --git a/internal/controller/mapfilegenerator/types.go b/internal/controller/mapfilegenerator/types.go index affd32d..084cb4a 100644 --- a/internal/controller/mapfilegenerator/types.go +++ b/internal/controller/mapfilegenerator/types.go @@ -119,13 +119,13 @@ func SetDataFields[O pdoknlv3.WMSWFS](obj O, wmsLayer *WMSLayer, data pdoknlv3.D case data.TIF != nil: tif := data.TIF wmsLayer.GeometryType = smoothoperatorutils.Pointer("Raster") - wmsLayer.BaseLayer.TifPath = smoothoperatorutils.Pointer(path.Join(tifPath, path.Base(tif.BlobKey))) + wmsLayer.TifPath = smoothoperatorutils.Pointer(path.Join(tifPath, path.Base(tif.BlobKey))) if !obj.Options().PrefetchData { reReplace := regexp.MustCompile(`$[a-zA-Z0-9_]*]/`) - wmsLayer.BaseLayer.TifPath = smoothoperatorutils.Pointer(path.Join("/vsiaz", reReplace.ReplaceAllString(tif.BlobKey, ""))) + wmsLayer.TifPath = smoothoperatorutils.Pointer(path.Join("/vsiaz", reReplace.ReplaceAllString(tif.BlobKey, ""))) } - wmsLayer.BaseLayer.Resample = &tif.Resample - wmsLayer.BaseLayer.OversampleRatio = &tif.OversampleRatio + wmsLayer.Resample = &tif.Resample + wmsLayer.OversampleRatio = &tif.OversampleRatio wmsLayer.Offsite = smoothoperatorutils.PointerVal(tif.Offsite, "") wmsLayer.GetFeatureInfoIncludesClass = &tif.GetFeatureInfoIncludesClass case data.Postgis != nil: diff --git a/internal/controller/types/types.go b/internal/controller/types/types.go index 34e7d03..79ff022 100644 --- a/internal/controller/types/types.go +++ b/internal/controller/types/types.go @@ -1,4 +1,4 @@ -package types +package types //nolint:revive type HashedConfigMapNames struct { Mapserver string diff --git a/internal/controller/utils/utils.go b/internal/controller/utils/utils.go index 37fee7e..f705adc 100644 --- a/internal/controller/utils/utils.go +++ b/internal/controller/utils/utils.go @@ -1,4 +1,4 @@ -package utils +package utils //nolint:revive import ( //nolint:gosec diff --git a/internal/controller/wfs_controller.go b/internal/controller/wfs_controller.go index 7b0aeb4..61e2b9e 100644 --- a/internal/controller/wfs_controller.go +++ b/internal/controller/wfs_controller.go @@ -76,7 +76,7 @@ func (r *WFSReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result // Fetch the WFS instance wfs := &pdoknlv3.WFS{} - if err = r.Client.Get(ctx, req.NamespacedName, wfs); err != nil { + if err = r.Get(ctx, req.NamespacedName, wfs); err != nil { if apierrors.IsNotFound(err) { lgr.Info("WFS resource not found", "name", req.NamespacedName) } else { @@ -92,7 +92,7 @@ func (r *WFSReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result Namespace: wfs.Namespace, Name: wfs.Spec.Service.OwnerInfoRef, } - if err := r.Client.Get(ctx, objectKey, ownerInfo); err != nil { + if err := r.Get(ctx, objectKey, ownerInfo); err != nil { if apierrors.IsNotFound(err) { lgr.Info("OwnerInfo resource not found", "name", req.NamespacedName) } else { @@ -111,7 +111,7 @@ func (r *WFSReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result // Check TTL, delete if expired if ttlExpired(wfs) { - err = r.Client.Delete(ctx, wfs) + err = r.Delete(ctx, wfs) return result, err } diff --git a/internal/controller/wms_controller.go b/internal/controller/wms_controller.go index e486233..6ae40d5 100644 --- a/internal/controller/wms_controller.go +++ b/internal/controller/wms_controller.go @@ -87,7 +87,7 @@ func (r *WMSReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result // Fetch the WMS instance wms := &pdoknlv3.WMS{} - if err = r.Client.Get(ctx, req.NamespacedName, wms); err != nil { + if err = r.Get(ctx, req.NamespacedName, wms); err != nil { if apierrors.IsNotFound(err) { lgr.Info("WMS resource not found", "name", req.NamespacedName) } else { @@ -103,7 +103,7 @@ func (r *WMSReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result Namespace: wms.Namespace, Name: wms.Spec.Service.OwnerInfoRef, } - if err := r.Client.Get(ctx, objectKey, ownerInfo); err != nil { + if err := r.Get(ctx, objectKey, ownerInfo); err != nil { if apierrors.IsNotFound(err) { lgr.Info("OwnerInfo resource not found", "name", req.NamespacedName) } else { @@ -122,7 +122,7 @@ func (r *WMSReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result // Check TTL, delete if expired if ttlExpired(wms) { - err = r.Client.Delete(ctx, wms) + err = r.Delete(ctx, wms) return result, err } diff --git a/internal/webhook/v3/wfs_webhook.go b/internal/webhook/v3/wfs_webhook.go index cae035e..49615db 100644 --- a/internal/webhook/v3/wfs_webhook.go +++ b/internal/webhook/v3/wfs_webhook.go @@ -51,8 +51,7 @@ func SetupWFSWebhookWithManager(mgr ctrl.Manager) error { } // TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. -// NOTE: The 'path' attribute must follow a specific pattern and should not be modified directly here. -// Modifying the path for an invalid path can cause API server errors; failing to locate the webhook. +// NOTE: If you want to customise the 'path', use the flags '--defaulting-path' or '--validation-path'. // +kubebuilder:webhook:path=/validate-pdok-nl-v3-wfs,mutating=false,failurePolicy=fail,sideEffects=None,groups=pdok.nl,resources=wfs,verbs=create;update,versions=v3,name=vwfs-v3.kb.io,admissionReviewVersions=v1 // WFSCustomValidator struct is responsible for validating the WFS resource diff --git a/internal/webhook/v3/wfs_webhook_test.go b/internal/webhook/v3/wfs_webhook_test.go index c406c9b..e2dc075 100644 --- a/internal/webhook/v3/wfs_webhook_test.go +++ b/internal/webhook/v3/wfs_webhook_test.go @@ -56,7 +56,7 @@ var _ = Describe("WFS Webhook", func() { sample := &pdoknlv3.WFS{} err := readSample(sample) - Expect(err).To(BeNil(), "Reading and parsing the WFS V3 sample failed") + Expect(err).ToNot(HaveOccurred(), "Reading and parsing the WFS V3 sample failed") obj = sample.DeepCopy() oldObj = sample.DeepCopy() @@ -81,7 +81,7 @@ var _ = Describe("WFS Webhook", func() { It("Creates the WFS from the sample", func() { warnings, err := validator.ValidateCreate(ctx, obj) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(warnings).To(BeEmpty()) }) @@ -97,10 +97,10 @@ var _ = Describe("WFS Webhook", func() { It("Should deny Create when URL not in IngressRouteURLs", func() { url, err := smoothoperatormodel.ParseURL("http://changed/changed") - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) obj.Spec.IngressRouteURLs = []smoothoperatormodel.IngressRouteURL{{URL: smoothoperatormodel.URL{URL: url}}} url, err = smoothoperatormodel.ParseURL("http://sample/sample") - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) obj.Spec.Service.URL = smoothoperatormodel.URL{URL: url} warnings, err := validator.ValidateCreate(ctx, obj) @@ -115,7 +115,7 @@ var _ = Describe("WFS Webhook", func() { It("Warns if the name contains WFS", func() { obj.Name += "-wfs" warnings, err := validator.ValidateCreate(ctx, obj) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(warnings).To(Equal(getValidationWarnings( obj, *field.NewPath("metadata").Child("name"), @@ -141,7 +141,7 @@ var _ = Describe("WFS Webhook", func() { Expect(obj.Spec.Service.Bbox).NotTo(BeNil()) obj.Spec.Service.Mapfile = &pdoknlv3.Mapfile{} warnings, err := validator.ValidateCreate(ctx, obj) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(warnings).To(Equal(getValidationWarnings( obj, *field.NewPath("spec").Child("service").Child("featureTypes").Index(0).Child("bbox").Child("defaultCrs"), @@ -172,7 +172,7 @@ var _ = Describe("WFS Webhook", func() { Expect(obj.Spec.Service.FeatureTypes[0].DatasetMetadataURL.CSW).NotTo(BeNil()) obj.Spec.Service.Inspire.SpatialDatasetIdentifier = obj.Spec.Service.FeatureTypes[0].DatasetMetadataURL.CSW.MetadataIdentifier warnings, err := validator.ValidateCreate(ctx, obj) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(warnings).To(Equal(admission.Warnings{field.Invalid( field.NewPath("spec").Child("service").Child("inspire").Child("spatialDatasetIdentifier"), obj.Spec.Service.Inspire.SpatialDatasetIdentifier, @@ -324,7 +324,7 @@ var _ = Describe("WFS Webhook", func() { It("Should deny update if a ingressRouteURL was removed", func() { url, err := smoothoperatormodel.ParseURL("http://new.url/path") - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) oldObj.Spec.IngressRouteURLs = []smoothoperatormodel.IngressRouteURL{ {URL: obj.URL()}, {URL: smoothoperatormodel.URL{URL: url}}, @@ -341,7 +341,7 @@ var _ = Describe("WFS Webhook", func() { It("Should accept update if a url was changed when it's in ingressRouteUrls", func() { url, err := smoothoperatormodel.ParseURL("http://new.url/path") - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) oldObj.Spec.IngressRouteURLs = []smoothoperatormodel.IngressRouteURL{ {URL: obj.URL()}, {URL: smoothoperatormodel.URL{URL: url}}, @@ -351,13 +351,13 @@ var _ = Describe("WFS Webhook", func() { obj.Spec.Service.URL = smoothoperatormodel.URL{URL: url} warnings, err := validator.ValidateUpdate(ctx, oldObj, obj) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(warnings).To(BeEmpty()) }) It("Should deny update if a url was changed and ingressRouteUrls = nil", func() { url, err := smoothoperatormodel.ParseURL("http://new.url/path") - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) obj.Spec.Service.URL = smoothoperatormodel.URL{URL: url} obj.Spec.IngressRouteURLs = nil oldObj.Spec.IngressRouteURLs = nil diff --git a/internal/webhook/v3/wms_webhook_test.go b/internal/webhook/v3/wms_webhook_test.go index eb54622..ccb6697 100644 --- a/internal/webhook/v3/wms_webhook_test.go +++ b/internal/webhook/v3/wms_webhook_test.go @@ -78,7 +78,7 @@ var _ = Describe("WMS Webhook", func() { It("Creates the WMS from the sample", func() { warnings, err := validator.ValidateCreate(ctx, obj) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(warnings).To(BeEmpty()) }) @@ -94,10 +94,10 @@ var _ = Describe("WMS Webhook", func() { It("Should deny Create when URL not in IngressRouteURLs", func() { url, err := smoothoperatormodel.ParseURL("http://changed/changed") - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) obj.Spec.IngressRouteURLs = []smoothoperatormodel.IngressRouteURL{{URL: smoothoperatormodel.URL{URL: url}}} url, err = smoothoperatormodel.ParseURL("http://sample/sample") - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) obj.Spec.Service.URL = smoothoperatormodel.URL{URL: url} warnings, err := validator.ValidateCreate(ctx, obj) @@ -112,7 +112,7 @@ var _ = Describe("WMS Webhook", func() { It("Warns when the name contains WMS", func() { obj.Name += "-wms" warnings, err := validator.ValidateCreate(ctx, obj) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(warnings).To(Equal(getValidationWarnings( obj, *field.NewPath("metadata").Child("name"), @@ -125,7 +125,7 @@ var _ = Describe("WMS Webhook", func() { withMapfile(obj) obj.Spec.Service.Resolution = ptr.To(int32(5)) warnings, err := validator.ValidateCreate(ctx, obj) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(warnings).To(Equal(getValidationWarnings( obj, *field.NewPath("spec").Child("service").Child("resolution"), @@ -138,7 +138,7 @@ var _ = Describe("WMS Webhook", func() { withMapfile(obj) obj.Spec.Service.DefResolution = ptr.To(int32(5)) warnings, err := validator.ValidateCreate(ctx, obj) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(warnings).To(Equal(getValidationWarnings( obj, *field.NewPath("spec").Child("service").Child("defResolution"), @@ -222,7 +222,7 @@ var _ = Describe("WMS Webhook", func() { BBox: smoothoperatormodel.BBox{}, }} warnings, err := validator.ValidateCreate(ctx, obj) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(warnings).To(Equal(getValidationWarnings( obj, *field.NewPath("spec").Child("service").Child("layer").Child("boundingBoxes"), @@ -254,7 +254,7 @@ var _ = Describe("WMS Webhook", func() { }} warnings, err := validator.ValidateCreate(ctx, obj) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(warnings).To(Equal(getValidationWarnings( obj, *field.NewPath("spec").Child("service").Child("layer").Child("layers").Index(0).Child("data").Child("tif").Child("getFeatureInfoIncludesClass"), @@ -298,7 +298,7 @@ var _ = Describe("WMS Webhook", func() { obj.Spec.Service.Layer.Layers[0].Styles[0].Abstract = ptr.To("abstract") warnings, err := validator.ValidateCreate(ctx, obj) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(warnings).To(Equal(getValidationWarnings( obj, *field.NewPath("spec").Child("service").Child("layer").Child("layers").Index(0).Child("styles").Index(0).Child("abstract"), @@ -527,7 +527,7 @@ var _ = Describe("WMS Webhook", func() { It("Should deny update if a ingressRouteURL was removed", func() { url, err := smoothoperatormodel.ParseURL("http://new.url/path") - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) oldObj.Spec.IngressRouteURLs = []smoothoperatormodel.IngressRouteURL{ {URL: obj.URL()}, {URL: smoothoperatormodel.URL{URL: url}}, @@ -544,7 +544,7 @@ var _ = Describe("WMS Webhook", func() { It("Should accept update if a url was changed when it's in ingressRouteUrls", func() { url, err := smoothoperatormodel.ParseURL("http://new.url/path") - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) oldObj.Spec.IngressRouteURLs = []smoothoperatormodel.IngressRouteURL{ {URL: obj.URL()}, {URL: smoothoperatormodel.URL{URL: url}}, @@ -554,13 +554,13 @@ var _ = Describe("WMS Webhook", func() { obj.Spec.Service.URL = smoothoperatormodel.URL{URL: url} warnings, err := validator.ValidateUpdate(ctx, oldObj, obj) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(warnings).To(BeEmpty()) }) It("Should deny update if a url was changed and ingressRouteUrls = nil", func() { url, err := smoothoperatormodel.ParseURL("http://new.url/path") - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) obj.Spec.Service.URL = smoothoperatormodel.URL{URL: url} obj.Spec.IngressRouteURLs = nil oldObj.Spec.IngressRouteURLs = nil diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index ed2d505..0876dbe 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -1,3 +1,6 @@ +//go:build e2e +// +build e2e + /* Copyright 2025. @@ -31,14 +34,10 @@ import ( var ( // Optional Environment Variables: - // - PROMETHEUS_INSTALL_SKIP=true: Skips Prometheus Operator installation during test setup. // - CERT_MANAGER_INSTALL_SKIP=true: Skips CertManager installation during test setup. - // These variables are useful if Prometheus or CertManager is already installed, avoiding + // These variables are useful if CertManager is already installed, avoiding // re-installation and conflicts. - skipPrometheusInstall = os.Getenv("PROMETHEUS_INSTALL_SKIP") == "true" skipCertManagerInstall = os.Getenv("CERT_MANAGER_INSTALL_SKIP") == "true" - // isPrometheusOperatorAlreadyInstalled will be set true when prometheus CRDs be found on the cluster - isPrometheusOperatorAlreadyInstalled = false // isCertManagerAlreadyInstalled will be set true when CertManager CRDs be found on the cluster isCertManagerAlreadyInstalled = false @@ -48,9 +47,9 @@ var ( ) // TestE2E runs the end-to-end (e2e) test suite for the project. These tests execute in an isolated, -// temporary environment to validate project changes with the the purposed to be used in CI jobs. +// temporary environment to validate project changes with the purpose of being used in CI jobs. // The default setup requires Kind, builds/loads the Manager Docker image locally, and installs -// CertManager and Prometheus. +// CertManager. func TestE2E(t *testing.T) { RegisterFailHandler(Fail) _, _ = fmt.Fprintf(GinkgoWriter, "Starting mapserver-operator integration test suite\n") @@ -58,9 +57,6 @@ func TestE2E(t *testing.T) { } var _ = BeforeSuite(func() { - By("Ensure that Prometheus is enabled") - _ = utils.UncommentCode("config/default/kustomization.yaml", "#- ../prometheus", "#") - By("building the manager(Operator) image") cmd := exec.Command("make", "docker-build", "IMG="+projectImage) _, err := utils.Run(cmd) @@ -73,19 +69,9 @@ var _ = BeforeSuite(func() { ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to load the manager(Operator) image into Kind") // The tests-e2e are intended to run on a temporary cluster that is created and destroyed for testing. - // To prevent errors when tests run in environments with Prometheus or CertManager already installed, - // we check for their presence before execution. - // Setup Prometheus and CertManager before the suite if not skipped and if not already installed - if !skipPrometheusInstall { - By("checking if prometheus is installed already") - isPrometheusOperatorAlreadyInstalled = utils.IsPrometheusCRDsInstalled() - if !isPrometheusOperatorAlreadyInstalled { - _, _ = fmt.Fprintf(GinkgoWriter, "Installing Prometheus Operator...\n") - Expect(utils.InstallPrometheusOperator()).To(Succeed(), "Failed to install Prometheus Operator") - } else { - _, _ = fmt.Fprintf(GinkgoWriter, "WARNING: Prometheus Operator is already installed. Skipping installation...\n") - } - } + // To prevent errors when tests run in environments with CertManager already installed, + // we check for its presence before execution. + // Setup CertManager before the suite if not skipped and if not already installed if !skipCertManagerInstall { By("checking if cert manager is installed already") isCertManagerAlreadyInstalled = utils.IsCertManagerCRDsInstalled() @@ -99,11 +85,7 @@ var _ = BeforeSuite(func() { }) var _ = AfterSuite(func() { - // Teardown Prometheus and CertManager after the suite if not skipped and if they were not already installed - if !skipPrometheusInstall && !isPrometheusOperatorAlreadyInstalled { - _, _ = fmt.Fprintf(GinkgoWriter, "Uninstalling Prometheus Operator...\n") - utils.UninstallPrometheusOperator() - } + // Teardown CertManager after the suite if not skipped and if it was not already installed if !skipCertManagerInstall && !isCertManagerAlreadyInstalled { _, _ = fmt.Fprintf(GinkgoWriter, "Uninstalling CertManager...\n") utils.UninstallCertManager() diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index 9989296..100715f 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -1,3 +1,6 @@ +//go:build e2e +// +build e2e + /* Copyright 2025. diff --git a/test/utils/utils.go b/test/utils/utils.go index fef95da..08b3557 100644 --- a/test/utils/utils.go +++ b/test/utils/utils.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package utils +package utils //nolint:revive import ( "bufio" @@ -24,16 +24,15 @@ import ( "os/exec" "strings" - . "github.com/onsi/ginkgo/v2" //nolint:golint,revive + . "github.com/onsi/ginkgo/v2" //nolint:revive,staticcheck ) const ( - prometheusOperatorVersion = "v0.77.1" - prometheusOperatorURL = "https://github.com/prometheus-operator/prometheus-operator/" + - "releases/download/%s/bundle.yaml" - - certmanagerVersion = "v1.16.3" + certmanagerVersion = "v1.19.1" certmanagerURLTmpl = "https://github.com/cert-manager/cert-manager/releases/download/%s/cert-manager.yaml" + + defaultKindBinary = "kind" + defaultKindCluster = "kind" ) func warnError(err error) { @@ -46,71 +45,40 @@ func Run(cmd *exec.Cmd) (string, error) { cmd.Dir = dir if err := os.Chdir(cmd.Dir); err != nil { - _, _ = fmt.Fprintf(GinkgoWriter, "chdir dir: %s\n", err) + _, _ = fmt.Fprintf(GinkgoWriter, "chdir dir: %q\n", err) } cmd.Env = append(os.Environ(), "GO111MODULE=on") command := strings.Join(cmd.Args, " ") - _, _ = fmt.Fprintf(GinkgoWriter, "running: %s\n", command) + _, _ = fmt.Fprintf(GinkgoWriter, "running: %q\n", command) output, err := cmd.CombinedOutput() if err != nil { - return string(output), fmt.Errorf("%s failed with error: (%w) %s", command, err, string(output)) + return string(output), fmt.Errorf("%q failed with error %q: %w", command, string(output), err) } return string(output), nil } -// InstallPrometheusOperator installs the prometheus Operator to be used to export the enabled metrics. -func InstallPrometheusOperator() error { - url := fmt.Sprintf(prometheusOperatorURL, prometheusOperatorVersion) - cmd := exec.Command("kubectl", "create", "-f", url) - _, err := Run(cmd) - return err -} - -// UninstallPrometheusOperator uninstalls the prometheus -func UninstallPrometheusOperator() { - url := fmt.Sprintf(prometheusOperatorURL, prometheusOperatorVersion) +// UninstallCertManager uninstalls the cert manager +func UninstallCertManager() { + url := fmt.Sprintf(certmanagerURLTmpl, certmanagerVersion) cmd := exec.Command("kubectl", "delete", "-f", url) if _, err := Run(cmd); err != nil { warnError(err) } -} - -// IsPrometheusCRDsInstalled checks if any Prometheus CRDs are installed -// by verifying the existence of key CRDs related to Prometheus. -func IsPrometheusCRDsInstalled() bool { - // List of common Prometheus CRDs - prometheusCRDs := []string{ - "prometheuses.monitoring.coreos.com", - "prometheusrules.monitoring.coreos.com", - "prometheusagents.monitoring.coreos.com", - } - cmd := exec.Command("kubectl", "get", "crds", "-o", "custom-columns=NAME:.metadata.name") - output, err := Run(cmd) - if err != nil { - return false + // Delete leftover leases in kube-system (not cleaned by default) + kubeSystemLeases := []string{ + "cert-manager-cainjector-leader-election", + "cert-manager-controller", } - crdList := GetNonEmptyLines(output) - for _, crd := range prometheusCRDs { - for _, line := range crdList { - if strings.Contains(line, crd) { - return true - } + for _, lease := range kubeSystemLeases { + cmd = exec.Command("kubectl", "delete", "lease", lease, + "-n", "kube-system", "--ignore-not-found", "--force", "--grace-period=0") + if _, err := Run(cmd); err != nil { + warnError(err) } } - - return false -} - -// UninstallCertManager uninstalls the cert manager -func UninstallCertManager() { - url := fmt.Sprintf(certmanagerURLTmpl, certmanagerVersion) - cmd := exec.Command("kubectl", "delete", "-f", url) - if _, err := Run(cmd); err != nil { - warnError(err) - } } // InstallCertManager installs the cert manager bundle. @@ -167,12 +135,16 @@ func IsCertManagerCRDsInstalled() bool { // LoadImageToKindClusterWithName loads a local docker image to the kind cluster func LoadImageToKindClusterWithName(name string) error { - cluster := "kind" + cluster := defaultKindCluster if v, ok := os.LookupEnv("KIND_CLUSTER"); ok { cluster = v } kindOptions := []string{"load", "docker-image", name, "--name", cluster} - cmd := exec.Command("kind", kindOptions...) + kindBinary := defaultKindBinary + if v, ok := os.LookupEnv("KIND"); ok { + kindBinary = v + } + cmd := exec.Command(kindBinary, kindOptions...) _, err := Run(cmd) return err } @@ -195,7 +167,7 @@ func GetNonEmptyLines(output string) []string { func GetProjectDir() (string, error) { wd, err := os.Getwd() if err != nil { - return wd, err + return wd, fmt.Errorf("failed to get current working directory: %w", err) } wd = strings.ReplaceAll(wd, "/test/e2e", "") return wd, nil @@ -207,19 +179,19 @@ func UncommentCode(filename, target, prefix string) error { // false positive content, err := os.ReadFile(filename) if err != nil { - return err + return fmt.Errorf("failed to read file %q: %w", filename, err) } strContent := string(content) idx := strings.Index(strContent, target) if idx < 0 { - return fmt.Errorf("unable to find the code %s to be uncomment", target) + return fmt.Errorf("unable to find the code %q to be uncomment", target) } out := new(bytes.Buffer) _, err = out.Write(content[:idx]) if err != nil { - return err + return fmt.Errorf("failed to write to output: %w", err) } scanner := bufio.NewScanner(bytes.NewBufferString(target)) @@ -227,24 +199,27 @@ func UncommentCode(filename, target, prefix string) error { return nil } for { - _, err := out.WriteString(strings.TrimPrefix(scanner.Text(), prefix)) - if err != nil { - return err + if _, err = out.WriteString(strings.TrimPrefix(scanner.Text(), prefix)); err != nil { + return fmt.Errorf("failed to write to output: %w", err) } // Avoid writing a newline in case the previous line was the last in target. if !scanner.Scan() { break } - if _, err := out.WriteString("\n"); err != nil { - return err + if _, err = out.WriteString("\n"); err != nil { + return fmt.Errorf("failed to write to output: %w", err) } } - _, err = out.Write(content[idx+len(target):]) - if err != nil { - return err + if _, err = out.Write(content[idx+len(target):]); err != nil { + return fmt.Errorf("failed to write to output: %w", err) } + // false positive //nolint:gosec - return os.WriteFile(filename, out.Bytes(), 0644) + if err = os.WriteFile(filename, out.Bytes(), 0644); err != nil { + return fmt.Errorf("failed to write file %q: %w", filename, err) + } + + return nil }