From a06f8d796b0fc8bd3f3e49687fc15a72f2181d68 Mon Sep 17 00:00:00 2001 From: Ida Novindasari Date: Fri, 19 Dec 2025 09:53:38 +0100 Subject: [PATCH 01/21] Do not promote operator image when merge to master (#3018) --- delivery.yaml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/delivery.yaml b/delivery.yaml index 9971d4070..c8a65422d 100644 --- a/delivery.yaml +++ b/delivery.yaml @@ -39,10 +39,6 @@ pipeline: -f docker/Dockerfile \ --push . - if [ -z ${CDP_SOURCE_BRANCH} ]; then - cdp-promote-image ${IMAGE}:${CDP_BUILD_VERSION} - fi - - id: build-operator-ui env: <<: *BUILD_ENV From f6839f87b9fb578fa790f47a557f7127efcf2945 Mon Sep 17 00:00:00 2001 From: Mikkel Oscar Lyderik Larsen Date: Fri, 9 Jan 2026 14:22:10 +0100 Subject: [PATCH 02/21] Modernize code generation (#3003) Signed-off-by: Mikkel Oscar Lyderik Larsen --- .github/workflows/publish_ghcr_image.yaml | 2 +- .github/workflows/run_e2e.yaml | 2 +- .github/workflows/run_tests.yaml | 2 +- Makefile | 33 ++-- build-ci.sh | 2 +- delivery.yaml | 2 +- docs/developer.md | 8 +- go.mod | 16 +- go.sum | 22 +-- hack/tools.go | 19 -- hack/update-codegen.sh | 75 ++++++-- pkg/apis/zalando.org/v1/fabriceventstream.go | 13 +- .../zalando.org/v1/zz_generated.deepcopy.go | 74 ++++---- pkg/cluster/cluster.go | 2 + pkg/cluster/sync.go | 1 + .../clientset/versioned/clientset.go | 7 +- .../versioned/fake/clientset_generated.go | 6 +- .../acid.zalan.do/v1/acid.zalan.do_client.go | 10 +- .../v1/fake/fake_acid.zalan.do_client.go | 6 +- .../v1/fake/fake_operatorconfiguration.go | 37 ++-- .../acid.zalan.do/v1/fake/fake_postgresql.go | 134 +++----------- .../v1/fake/fake_postgresteam.go | 124 +++---------- .../acid.zalan.do/v1/operatorconfiguration.go | 33 ++-- .../typed/acid.zalan.do/v1/postgresql.go | 163 ++---------------- .../typed/acid.zalan.do/v1/postgresteam.go | 144 ++-------------- .../typed/zalando.org/v1/fabriceventstream.go | 144 ++-------------- .../v1/fake/fake_fabriceventstream.go | 126 +++----------- .../v1/fake/fake_zalando.org_client.go | 2 +- .../zalando.org/v1/zalando.org_client.go | 10 +- .../acid.zalan.do/v1/postgresql.go | 16 +- .../acid.zalan.do/v1/postgresteam.go | 16 +- .../informers/externalversions/factory.go | 92 +++++++++- .../informers/externalversions/generic.go | 2 +- .../zalando.org/v1/fabriceventstream.go | 16 +- .../listers/acid.zalan.do/v1/postgresql.go | 51 ++---- .../listers/acid.zalan.do/v1/postgresteam.go | 51 ++---- .../zalando.org/v1/fabriceventstream.go | 51 ++---- pkg/util/httpclient/httpclient.go | 2 +- pkg/util/volumes/volumes.go | 2 +- run_operator_locally.sh | 2 +- 40 files changed, 467 insertions(+), 1053 deletions(-) delete mode 100644 hack/tools.go diff --git a/.github/workflows/publish_ghcr_image.yaml b/.github/workflows/publish_ghcr_image.yaml index 78815783a..3cead3503 100644 --- a/.github/workflows/publish_ghcr_image.yaml +++ b/.github/workflows/publish_ghcr_image.yaml @@ -26,7 +26,7 @@ jobs: go-version: "^1.25.3" - name: Run unit tests - run: make deps mocks test + run: make test - name: Define image name id: image diff --git a/.github/workflows/run_e2e.yaml b/.github/workflows/run_e2e.yaml index 86f861ec5..e7c04c0c5 100644 --- a/.github/workflows/run_e2e.yaml +++ b/.github/workflows/run_e2e.yaml @@ -16,7 +16,7 @@ jobs: with: go-version: "^1.25.3" - name: Make dependencies - run: make deps mocks + run: make mocks - name: Code generation run: make codegen - name: Run unit tests diff --git a/.github/workflows/run_tests.yaml b/.github/workflows/run_tests.yaml index ec3e5eaf6..7940b61f2 100644 --- a/.github/workflows/run_tests.yaml +++ b/.github/workflows/run_tests.yaml @@ -16,7 +16,7 @@ jobs: with: go-version: "^1.25.3" - name: Make dependencies - run: make deps mocks + run: make mocks - name: Compile run: make linux - name: Run unit tests diff --git a/Makefile b/Makefile index 385a03d09..f00f6039d 100644 --- a/Makefile +++ b/Makefile @@ -20,6 +20,8 @@ GITURL = $(shell git config --get remote.origin.url) GITSTATUS = $(shell git status --porcelain || echo "no changes") SOURCES = cmd/main.go VERSION ?= $(shell git describe --tags --always --dirty) +CRD_SOURCES = $(shell find pkg/apis/zalando.org pkg/apis/acid.zalan.do -name '*.go' -not -name '*.deepcopy.go') +GENERATED = pkg/apis/zalando.org/v1/zz_generated.deepcopy.go pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go DIRS := cmd pkg PKG := `go list ./... | grep -v /vendor/` @@ -52,15 +54,20 @@ default: local clean: rm -rf build -local: ${SOURCES} +verify: hack/verify-codegen.sh - CGO_ENABLED=${CGO_ENABLED} go build -o build/${BINARY} $(LOCAL_BUILD_FLAGS) -ldflags "$(LDFLAGS)" $^ -linux: ${SOURCES} - GOOS=linux GOARCH=amd64 CGO_ENABLED=${CGO_ENABLED} go build -o build/linux/${BINARY} ${BUILD_FLAGS} -ldflags "$(LDFLAGS)" $^ +$(GENERATED): go.mod $(CRD_SOURCES) + hack/update-codegen.sh -macos: ${SOURCES} - GOOS=darwin GOARCH=amd64 CGO_ENABLED=${CGO_ENABLED} go build -o build/macos/${BINARY} ${BUILD_FLAGS} -ldflags "$(LDFLAGS)" $^ +local: ${SOURCES} $(GENERATED) + CGO_ENABLED=${CGO_ENABLED} go build -o build/${BINARY} $(LOCAL_BUILD_FLAGS) -ldflags "$(LDFLAGS)" $(SOURCES) + +linux: ${SOURCES} $(GENERATED) + GOOS=linux GOARCH=amd64 CGO_ENABLED=${CGO_ENABLED} go build -o build/linux/${BINARY} ${BUILD_FLAGS} -ldflags "$(LDFLAGS)" $(SOURCES) + +macos: ${SOURCES} $(GENERATED) + GOOS=darwin GOARCH=amd64 CGO_ENABLED=${CGO_ENABLED} go build -o build/macos/${BINARY} ${BUILD_FLAGS} -ldflags "$(LDFLAGS)" $(SOURCES) docker: ${DOCKERDIR}/${DOCKERFILE} echo `(env)` @@ -76,11 +83,6 @@ indocker-race: mocks: GO111MODULE=on go generate ./... -tools: - GO111MODULE=on go get k8s.io/client-go@kubernetes-1.32.9 - GO111MODULE=on go install github.com/golang/mock/mockgen@v1.6.0 - GO111MODULE=on go mod tidy - fmt: @gofmt -l -w -s $(DIRS) @@ -88,15 +90,10 @@ vet: @go vet $(PKG) @staticcheck $(PKG) -deps: tools - GO111MODULE=on go mod vendor - -test: - hack/verify-codegen.sh +test: mocks $(GENERATED) GO111MODULE=on go test ./... -codegen: - hack/update-codegen.sh +codegen: $(GENERATED) e2e: docker # build operator image to be tested cd e2e; make e2etest diff --git a/build-ci.sh b/build-ci.sh index a4805ad28..16ef3d245 100755 --- a/build-ci.sh +++ b/build-ci.sh @@ -9,4 +9,4 @@ mkdir -p "$team_repo" ln -s "$PWD" "$project_dir" cd "$project_dir" -make deps clean docker push +make clean docker push diff --git a/delivery.yaml b/delivery.yaml index c8a65422d..11e17c012 100644 --- a/delivery.yaml +++ b/delivery.yaml @@ -22,7 +22,7 @@ pipeline: commands: - desc: Run unit tests cmd: | - make deps mocks test + make mocks test - desc: Build Docker image cmd: | diff --git a/docs/developer.md b/docs/developer.md index b5f59c246..141ee63de 100644 --- a/docs/developer.md +++ b/docs/developer.md @@ -33,12 +33,9 @@ by setting the `GO111MODULE` environment variable to `on`. The make targets do this for you, so simply run ```bash -make deps +make ``` -This would take a while to complete. You have to redo `make deps` every time -your dependencies list changes, i.e. after adding a new library dependency. - Build the operator with the `make docker` command. You may define the TAG variable to assign an explicit tag to your Docker image and the IMAGE to set the image name. By default, the tag is computed with @@ -223,14 +220,13 @@ dlv connect 127.0.0.1:DLV_PORT Prerequisites: ```bash -make deps make mocks ``` To run all unit tests, you can simply do: ```bash -go test ./pkg/... +make test ``` In case if you need to debug your unit test, it's possible to use delve: diff --git a/go.mod b/go.mod index df7c94b5b..da25c79ca 100644 --- a/go.mod +++ b/go.mod @@ -15,10 +15,9 @@ require ( golang.org/x/crypto v0.45.0 gopkg.in/yaml.v2 v2.4.0 k8s.io/api v0.32.9 - k8s.io/apiextensions-apiserver v0.25.9 + k8s.io/apiextensions-apiserver v0.32.9 k8s.io/apimachinery v0.32.9 k8s.io/client-go v0.32.9 - k8s.io/code-generator v0.25.9 ) require ( @@ -63,8 +62,8 @@ 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 // indirect - k8s.io/gengo v0.0.0-20220902162205-c0856e24416d // indirect - k8s.io/gengo/v2 v2.0.0-20240826214909-a7b603a56eb7 // indirect + k8s.io/code-generator v0.32.9 // indirect + k8s.io/gengo/v2 v2.0.0-20240911193312-2b36238f13e9 // 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-20241104100929-3ea5e8cea738 // indirect @@ -73,3 +72,12 @@ require ( sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) + +tool ( + github.com/golang/mock/mockgen + k8s.io/code-generator + k8s.io/code-generator/cmd/client-gen + k8s.io/code-generator/cmd/deepcopy-gen + k8s.io/code-generator/cmd/informer-gen + k8s.io/code-generator/cmd/lister-gen +) diff --git a/go.sum b/go.sum index 581054d7c..fcd66ba43 100644 --- a/go.sum +++ b/go.sum @@ -13,7 +13,6 @@ 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/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= -github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= 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-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= @@ -34,12 +33,10 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= -github.com/google/go-cmp v0.4.0/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/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.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-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= @@ -58,7 +55,6 @@ 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/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -163,7 +159,6 @@ golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= @@ -180,7 +175,6 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T 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= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= @@ -196,19 +190,16 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= k8s.io/api v0.32.9 h1:q/59kk8lnecgG0grJqzrmXC1Jcl2hPWp9ltz0FQuoLI= k8s.io/api v0.32.9/go.mod h1:jIfT3rwW4EU1IXZm9qjzSk/2j91k4CJL5vUULrxqp3Y= -k8s.io/apiextensions-apiserver v0.25.9 h1:Pycd6lm2auABp9wKQHCFSEPG+NPdFSTJXPST6NJFzB8= -k8s.io/apiextensions-apiserver v0.25.9/go.mod h1:ijGxmSG1GLOEaWhTuaEr0M7KUeia3mWCZa6FFQqpt1M= +k8s.io/apiextensions-apiserver v0.32.9 h1:tpT1dUgWqEsTyrdoGckyw8OBASW1JfU08tHGaYBzFHY= +k8s.io/apiextensions-apiserver v0.32.9/go.mod h1:FoCi4zCLK67LNCCssFa2Wr9q4Xbvjx7MW4tdze5tpoA= k8s.io/apimachinery v0.32.9 h1:fXk8ktfsxrdThaEOAQFgkhCK7iyoyvS8nbYJ83o/SSs= k8s.io/apimachinery v0.32.9/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= k8s.io/client-go v0.32.9 h1:ZMyIQ1TEpTDAQni3L2gH1NZzyOA/gHfNcAazzCxMJ0c= k8s.io/client-go v0.32.9/go.mod h1:2OT8aFSYvUjKGadaeT+AVbhkXQSpMAkiSb88Kz2WggI= -k8s.io/code-generator v0.25.9 h1:lgyAV9AIRYNxZxgLRXqsCAtqJLHvakot41CjEqD5W0w= -k8s.io/code-generator v0.25.9/go.mod h1:DHfpdhSUrwqF0f4oLqCtF8gYbqlndNetjBEz45nWzJI= -k8s.io/gengo v0.0.0-20220902162205-c0856e24416d h1:U9tB195lKdzwqicbJvyJeOXV7Klv+wNAWENRnXEGi08= -k8s.io/gengo v0.0.0-20220902162205-c0856e24416d/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/gengo/v2 v2.0.0-20240826214909-a7b603a56eb7 h1:cErOOTkQ3JW19o4lo91fFurouhP8NcoBvb7CkvhZZpk= -k8s.io/gengo/v2 v2.0.0-20240826214909-a7b603a56eb7/go.mod h1:EJykeLsmFC60UQbYJezXkEsG2FLrt0GPNkU5iK5GWxU= -k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= +k8s.io/code-generator v0.32.9 h1:F9Gti/8I+nVNnQw02J36/YlSD5JMg4qDJ7sfRqpUICU= +k8s.io/code-generator v0.32.9/go.mod h1:fLYBG9g52EJulRebmomL0vCU0PQeMr7mnscfZtAAGV4= +k8s.io/gengo/v2 v2.0.0-20240911193312-2b36238f13e9 h1:si3PfKm8dDYxgfbeA6orqrtLkvvIeH8UqffFJDl0bz4= +k8s.io/gengo/v2 v2.0.0-20240911193312-2b36238f13e9/go.mod h1:EJykeLsmFC60UQbYJezXkEsG2FLrt0GPNkU5iK5GWxU= 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= @@ -222,6 +213,5 @@ 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.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= 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/hack/tools.go b/hack/tools.go deleted file mode 100644 index 18432c1b1..000000000 --- a/hack/tools.go +++ /dev/null @@ -1,19 +0,0 @@ -// +build tools - -/* -Copyright 2019 The Kubernetes Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// This package imports things required by build scripts, to force `go mod` to see them as dependencies -package tools - -import _ "k8s.io/code-generator" diff --git a/hack/update-codegen.sh b/hack/update-codegen.sh index e6fcae78c..9d43bc512 100755 --- a/hack/update-codegen.sh +++ b/hack/update-codegen.sh @@ -1,26 +1,67 @@ -#!/usr/bin/env bash +#!/bin/bash + +# Copyright 2017 The Kubernetes Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. set -o errexit set -o nounset set -o pipefail -GENERATED_PACKAGE_ROOT="github.com" -OPERATOR_PACKAGE_ROOT="${GENERATED_PACKAGE_ROOT}/zalando/postgres-operator" -SCRIPT_ROOT=$(dirname ${BASH_SOURCE})/.. -TARGET_CODE_DIR=${1-${SCRIPT_ROOT}/pkg} -CODEGEN_PKG=${CODEGEN_PKG:-$(cd "${SCRIPT_ROOT}"; ls -d -1 ./vendor/k8s.io/code-generator 2>/dev/null || echo "${GOPATH}"/src/k8s.io/code-generator)} +SRC="github.com" +GOPKG="$SRC/zalando/postgres-operator" +CUSTOM_RESOURCE_NAME_ZAL="zalando.org" +CUSTOM_RESOURCE_NAME_ACID="acid.zalan.do" +CUSTOM_RESOURCE_VERSION="v1" -cleanup() { - rm -rf "${GENERATED_PACKAGE_ROOT}" -} -trap "cleanup" EXIT SIGINT +SCRIPT_ROOT="$(dirname "${BASH_SOURCE[0]}")/.." -bash "${CODEGEN_PKG}/generate-groups.sh" client,deepcopy,informer,lister \ - "${OPERATOR_PACKAGE_ROOT}/pkg/generated" "${OPERATOR_PACKAGE_ROOT}/pkg/apis" \ - "acid.zalan.do:v1 zalando.org:v1" \ - --go-header-file "${SCRIPT_ROOT}"/hack/custom-boilerplate.go.txt \ - -o ./ +OUTPUT_DIR="pkg/generated" +OUTPUT_PKG="${GOPKG}/${OUTPUT_DIR}" +APIS_PKG="${GOPKG}/pkg/apis" +GROUPS_WITH_VERSIONS="${CUSTOM_RESOURCE_NAME_ZAL}:${CUSTOM_RESOURCE_VERSION},${CUSTOM_RESOURCE_NAME_ACID}:${CUSTOM_RESOURCE_VERSION}" -cp -r "${OPERATOR_PACKAGE_ROOT}"/pkg/* "${TARGET_CODE_DIR}" +echo "Generating deepcopy funcs" +go tool deepcopy-gen \ + --output-file zz_generated.deepcopy.go \ + --bounding-dirs "${APIS_PKG}" \ + --go-header-file "${SCRIPT_ROOT}/hack/custom-boilerplate.go.txt" \ + "${APIS_PKG}/${CUSTOM_RESOURCE_NAME_ZAL}/${CUSTOM_RESOURCE_VERSION}" \ + "${APIS_PKG}/${CUSTOM_RESOURCE_NAME_ACID}/${CUSTOM_RESOURCE_VERSION}" -cleanup +echo "Generating clientset for ${GROUPS_WITH_VERSIONS} at ${OUTPUT_PKG}/${CLIENTSET_PKG_NAME:-clientset}" +go tool client-gen \ + --clientset-name versioned \ + --input-base "${APIS_PKG}" \ + --input "${CUSTOM_RESOURCE_NAME_ZAL}/${CUSTOM_RESOURCE_VERSION},${CUSTOM_RESOURCE_NAME_ACID}/${CUSTOM_RESOURCE_VERSION}" \ + --output-pkg "${OUTPUT_PKG}/clientset" \ + --go-header-file "${SCRIPT_ROOT}/hack/custom-boilerplate.go.txt" \ + --output-dir "${OUTPUT_DIR}/clientset" + +echo "Generating listers for ${GROUPS_WITH_VERSIONS} at ${OUTPUT_PKG}/listers" +go tool lister-gen \ + --output-pkg "${OUTPUT_PKG}/listers" \ + --go-header-file "${SCRIPT_ROOT}/hack/custom-boilerplate.go.txt" \ + --output-dir "${OUTPUT_DIR}/listers" \ + "${APIS_PKG}/${CUSTOM_RESOURCE_NAME_ZAL}/${CUSTOM_RESOURCE_VERSION}" \ + "${APIS_PKG}/${CUSTOM_RESOURCE_NAME_ACID}/${CUSTOM_RESOURCE_VERSION}" + +echo "Generating informers for ${GROUPS_WITH_VERSIONS} at ${OUTPUT_PKG}/informers" +go tool informer-gen \ + --versioned-clientset-package "${OUTPUT_PKG}/${CLIENTSET_PKG_NAME:-clientset}/${CLIENTSET_NAME_VERSIONED:-versioned}" \ + --listers-package "${OUTPUT_PKG}/listers" \ + --output-pkg "${OUTPUT_PKG}/informers" \ + --go-header-file "${SCRIPT_ROOT}/hack/custom-boilerplate.go.txt" \ + --output-dir "${OUTPUT_DIR}/informers" \ + "${APIS_PKG}/${CUSTOM_RESOURCE_NAME_ZAL}/${CUSTOM_RESOURCE_VERSION}" \ + "${APIS_PKG}/${CUSTOM_RESOURCE_NAME_ACID}/${CUSTOM_RESOURCE_VERSION}" diff --git a/pkg/apis/zalando.org/v1/fabriceventstream.go b/pkg/apis/zalando.org/v1/fabriceventstream.go index 41bb5e80c..cb2ccdef5 100644 --- a/pkg/apis/zalando.org/v1/fabriceventstream.go +++ b/pkg/apis/zalando.org/v1/fabriceventstream.go @@ -9,14 +9,16 @@ import ( // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // FabricEventStream defines FabricEventStream Custom Resource Definition Object. +// +k8s:deepcopy-gen=true type FabricEventStream struct { metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` + metav1.ObjectMeta `json:"metadata"` Spec FabricEventStreamSpec `json:"spec"` } // FabricEventStreamSpec defines the specification for the FabricEventStream TPR. +// +k8s:deepcopy-gen=true type FabricEventStreamSpec struct { ApplicationId string `json:"applicationId"` EventStreams []EventStream `json:"eventStreams"` @@ -25,6 +27,7 @@ type FabricEventStreamSpec struct { // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // FabricEventStreamList defines a list of FabricEventStreams . +// +k8s:deepcopy-gen=true type FabricEventStreamList struct { metav1.TypeMeta `json:",inline"` metav1.ListMeta `json:"metadata"` @@ -33,6 +36,7 @@ type FabricEventStreamList struct { } // EventStream defines the source, flow and sink of the event stream +// +k8s:deepcopy-gen=true type EventStream struct { EventStreamFlow EventStreamFlow `json:"flow"` EventStreamSink EventStreamSink `json:"sink"` @@ -41,12 +45,14 @@ type EventStream struct { } // EventStreamFlow defines the flow characteristics of the event stream +// +k8s:deepcopy-gen=true type EventStreamFlow struct { Type string `json:"type"` PayloadColumn *string `json:"payloadColumn,omitempty"` } // EventStreamSink defines the target of the event stream +// +k8s:deepcopy-gen=true type EventStreamSink struct { Type string `json:"type"` EventType string `json:"eventType,omitempty"` @@ -54,12 +60,14 @@ type EventStreamSink struct { } // EventStreamRecovery defines the target of dead letter queue +// +k8s:deepcopy-gen=true type EventStreamRecovery struct { Type string `json:"type"` Sink *EventStreamSink `json:"sink"` } // EventStreamSource defines the source of the event stream and connection for FES operator +// +k8s:deepcopy-gen=true type EventStreamSource struct { Type string `json:"type"` Schema string `json:"schema,omitempty" defaults:"public"` @@ -69,12 +77,14 @@ type EventStreamSource struct { } // EventStreamTable defines the name and ID column to be used for streaming +// +k8s:deepcopy-gen=true type EventStreamTable struct { Name string `json:"name"` IDColumn *string `json:"idColumn,omitempty"` } // Connection to be used for allowing the FES operator to connect to a database +// +k8s:deepcopy-gen=true type Connection struct { Url string `json:"jdbcUrl"` SlotName string `json:"slotName"` @@ -84,6 +94,7 @@ type Connection struct { } // DBAuth specifies the credentials to be used for connecting with the database +// +k8s:deepcopy-gen=true type DBAuth struct { Type string `json:"type"` Name string `json:"name,omitempty"` diff --git a/pkg/apis/zalando.org/v1/zz_generated.deepcopy.go b/pkg/apis/zalando.org/v1/zz_generated.deepcopy.go index 8a46b9a25..993b8d8a5 100644 --- a/pkg/apis/zalando.org/v1/zz_generated.deepcopy.go +++ b/pkg/apis/zalando.org/v1/zz_generated.deepcopy.go @@ -1,7 +1,8 @@ +//go:build !ignore_autogenerated // +build !ignore_autogenerated /* -Copyright 2021 Compose, Zalando SE +Copyright 2025 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -38,11 +39,11 @@ func (in *Connection) DeepCopyInto(out *Connection) { *out = new(string) **out = **in } - in.DBAuth.DeepCopyInto(&out.DBAuth) + out.DBAuth = in.DBAuth return } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Connection. func (in *Connection) DeepCopy() *Connection { if in == nil { return nil @@ -58,7 +59,7 @@ func (in *DBAuth) DeepCopyInto(out *DBAuth) { return } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DBAuth. func (in *DBAuth) DeepCopy() *DBAuth { if in == nil { return nil @@ -72,13 +73,13 @@ func (in *DBAuth) DeepCopy() *DBAuth { func (in *EventStream) DeepCopyInto(out *EventStream) { *out = *in in.EventStreamFlow.DeepCopyInto(&out.EventStreamFlow) - in.EventStreamRecovery.DeepCopyInto(&out.EventStreamRecovery) in.EventStreamSink.DeepCopyInto(&out.EventStreamSink) in.EventStreamSource.DeepCopyInto(&out.EventStreamSource) + in.EventStreamRecovery.DeepCopyInto(&out.EventStreamRecovery) return } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EventStream. func (in *EventStream) DeepCopy() *EventStream { if in == nil { return nil @@ -99,7 +100,7 @@ func (in *EventStreamFlow) DeepCopyInto(out *EventStreamFlow) { return } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EventStreamFlow. func (in *EventStreamFlow) DeepCopy() *EventStreamFlow { if in == nil { return nil @@ -120,7 +121,7 @@ func (in *EventStreamRecovery) DeepCopyInto(out *EventStreamRecovery) { return } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EventStreamRecovery. func (in *EventStreamRecovery) DeepCopy() *EventStreamRecovery { if in == nil { return nil @@ -141,7 +142,7 @@ func (in *EventStreamSink) DeepCopyInto(out *EventStreamSink) { return } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EventStreamSink. func (in *EventStreamSink) DeepCopy() *EventStreamSink { if in == nil { return nil @@ -154,17 +155,17 @@ func (in *EventStreamSink) DeepCopy() *EventStreamSink { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *EventStreamSource) DeepCopyInto(out *EventStreamSource) { *out = *in - in.Connection.DeepCopyInto(&out.Connection) + in.EventStreamTable.DeepCopyInto(&out.EventStreamTable) if in.Filter != nil { in, out := &in.Filter, &out.Filter *out = new(string) **out = **in } - in.EventStreamTable.DeepCopyInto(&out.EventStreamTable) + in.Connection.DeepCopyInto(&out.Connection) return } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EventStreamSource. func (in *EventStreamSource) DeepCopy() *EventStreamSource { if in == nil { return nil @@ -185,7 +186,7 @@ func (in *EventStreamTable) DeepCopyInto(out *EventStreamTable) { return } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EventStreamTable. func (in *EventStreamTable) DeepCopy() *EventStreamTable { if in == nil { return nil @@ -195,30 +196,6 @@ func (in *EventStreamTable) DeepCopy() *EventStreamTable { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *FabricEventStreamSpec) DeepCopyInto(out *FabricEventStreamSpec) { - *out = *in - if in.EventStreams != nil { - in, out := &in.EventStreams, &out.EventStreams - *out = make([]EventStream, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FabricEventStreamSpec. -func (in *FabricEventStreamSpec) DeepCopy() *FabricEventStreamSpec { - if in == nil { - return nil - } - out := new(FabricEventStreamSpec) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *FabricEventStream) DeepCopyInto(out *FabricEventStream) { *out = *in @@ -278,3 +255,26 @@ func (in *FabricEventStreamList) DeepCopyObject() runtime.Object { } return nil } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FabricEventStreamSpec) DeepCopyInto(out *FabricEventStreamSpec) { + *out = *in + if in.EventStreams != nil { + in, out := &in.EventStreams, &out.EventStreams + *out = make([]EventStream, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FabricEventStreamSpec. +func (in *FabricEventStreamSpec) DeepCopy() *FabricEventStreamSpec { + if in == nil { + return nil + } + out := new(FabricEventStreamSpec) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index b6a4e24a8..7204a92bd 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -283,6 +283,7 @@ func (c *Cluster) Create() (err error) { } if errStatus != nil { c.logger.Warningf("could not set cluster status: %v", errStatus) + return } if pgUpdatedStatus != nil { c.setSpec(pgUpdatedStatus) @@ -997,6 +998,7 @@ func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error { } if err != nil { c.logger.Warningf("could not set cluster status: %v", err) + return } if pgUpdatedStatus != nil { c.setSpec(pgUpdatedStatus) diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index ecf692702..9142f33bb 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -55,6 +55,7 @@ func (c *Cluster) Sync(newSpec *acidv1.Postgresql) error { } if errStatus != nil { c.logger.Warningf("could not set cluster status: %v", errStatus) + return } if pgUpdatedStatus != nil { c.setSpec(pgUpdatedStatus) diff --git a/pkg/generated/clientset/versioned/clientset.go b/pkg/generated/clientset/versioned/clientset.go index 69725a952..6d253b7e4 100644 --- a/pkg/generated/clientset/versioned/clientset.go +++ b/pkg/generated/clientset/versioned/clientset.go @@ -25,8 +25,8 @@ SOFTWARE. package versioned import ( - "fmt" - "net/http" + fmt "fmt" + http "net/http" acidv1 "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1" zalandov1 "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/zalando.org/v1" @@ -41,8 +41,7 @@ type Interface interface { ZalandoV1() zalandov1.ZalandoV1Interface } -// Clientset contains the clients for groups. Each group has exactly one -// version included in a Clientset. +// Clientset contains the clients for groups. type Clientset struct { *discovery.DiscoveryClient acidV1 *acidv1.AcidV1Client diff --git a/pkg/generated/clientset/versioned/fake/clientset_generated.go b/pkg/generated/clientset/versioned/fake/clientset_generated.go index c85ad76f9..afb3cf091 100644 --- a/pkg/generated/clientset/versioned/fake/clientset_generated.go +++ b/pkg/generated/clientset/versioned/fake/clientset_generated.go @@ -39,8 +39,12 @@ import ( // NewSimpleClientset returns a clientset that will respond with the provided objects. // It's backed by a very simple object tracker that processes creates, updates and deletions as-is, -// without applying any validations and/or defaults. It shouldn't be considered a replacement +// without applying any field management, validations and/or defaults. It shouldn't be considered a replacement // for a real clientset and is mostly useful in simple unit tests. +// +// DEPRECATED: NewClientset replaces this with support for field management, which significantly improves +// server side apply testing. NewClientset is only available when apply configurations are generated (e.g. +// via --with-applyconfig). func NewSimpleClientset(objects ...runtime.Object) *Clientset { o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder()) for _, obj := range objects { diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/acid.zalan.do_client.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/acid.zalan.do_client.go index e070c7098..5a094d62f 100644 --- a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/acid.zalan.do_client.go +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/acid.zalan.do_client.go @@ -25,10 +25,10 @@ SOFTWARE. package v1 import ( - "net/http" + http "net/http" - v1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" - "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/scheme" + acidzalandov1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" + scheme "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/scheme" rest "k8s.io/client-go/rest" ) @@ -101,10 +101,10 @@ func New(c rest.Interface) *AcidV1Client { } func setConfigDefaults(config *rest.Config) error { - gv := v1.SchemeGroupVersion + gv := acidzalandov1.SchemeGroupVersion config.GroupVersion = &gv config.APIPath = "/apis" - config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() + config.NegotiatedSerializer = rest.CodecFactoryForGeneratedClient(scheme.Scheme, scheme.Codecs).WithoutConversion() if config.UserAgent == "" { config.UserAgent = rest.DefaultKubernetesUserAgent() diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_acid.zalan.do_client.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_acid.zalan.do_client.go index d45375335..68e1c1eec 100644 --- a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_acid.zalan.do_client.go +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_acid.zalan.do_client.go @@ -35,15 +35,15 @@ type FakeAcidV1 struct { } func (c *FakeAcidV1) OperatorConfigurations(namespace string) v1.OperatorConfigurationInterface { - return &FakeOperatorConfigurations{c, namespace} + return newFakeOperatorConfigurations(c, namespace) } func (c *FakeAcidV1) PostgresTeams(namespace string) v1.PostgresTeamInterface { - return &FakePostgresTeams{c, namespace} + return newFakePostgresTeams(c, namespace) } func (c *FakeAcidV1) Postgresqls(namespace string) v1.PostgresqlInterface { - return &FakePostgresqls{c, namespace} + return newFakePostgresqls(c, namespace) } // RESTClient returns a RESTClient that is used to communicate diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_operatorconfiguration.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_operatorconfiguration.go index de1b9a0e3..a4b8ea2b7 100644 --- a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_operatorconfiguration.go +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_operatorconfiguration.go @@ -25,31 +25,26 @@ SOFTWARE. package fake import ( - "context" - - acidzalandov1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - schema "k8s.io/apimachinery/pkg/runtime/schema" - testing "k8s.io/client-go/testing" + v1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" + acidzalandov1 "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1" + gentype "k8s.io/client-go/gentype" ) -// FakeOperatorConfigurations implements OperatorConfigurationInterface -type FakeOperatorConfigurations struct { +// fakeOperatorConfigurations implements OperatorConfigurationInterface +type fakeOperatorConfigurations struct { + *gentype.FakeClient[*v1.OperatorConfiguration] Fake *FakeAcidV1 - ns string } -var operatorconfigurationsResource = schema.GroupVersionResource{Group: "acid.zalan.do", Version: "v1", Resource: "operatorconfigurations"} - -var operatorconfigurationsKind = schema.GroupVersionKind{Group: "acid.zalan.do", Version: "v1", Kind: "OperatorConfiguration"} - -// Get takes name of the operatorConfiguration, and returns the corresponding operatorConfiguration object, and an error if there is any. -func (c *FakeOperatorConfigurations) Get(ctx context.Context, name string, options v1.GetOptions) (result *acidzalandov1.OperatorConfiguration, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(operatorconfigurationsResource, c.ns, name), &acidzalandov1.OperatorConfiguration{}) - - if obj == nil { - return nil, err +func newFakeOperatorConfigurations(fake *FakeAcidV1, namespace string) acidzalandov1.OperatorConfigurationInterface { + return &fakeOperatorConfigurations{ + gentype.NewFakeClient[*v1.OperatorConfiguration]( + fake.Fake, + namespace, + v1.SchemeGroupVersion.WithResource("operatorconfigurations"), + v1.SchemeGroupVersion.WithKind("OperatorConfiguration"), + func() *v1.OperatorConfiguration { return &v1.OperatorConfiguration{} }, + ), + fake, } - return obj.(*acidzalandov1.OperatorConfiguration), err } diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_postgresql.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_postgresql.go index b472c6057..ebad23d3d 100644 --- a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_postgresql.go +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_postgresql.go @@ -25,124 +25,30 @@ SOFTWARE. package fake import ( - "context" - - acidzalandov1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" + v1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" + acidzalandov1 "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1" + gentype "k8s.io/client-go/gentype" ) -// FakePostgresqls implements PostgresqlInterface -type FakePostgresqls struct { +// fakePostgresqls implements PostgresqlInterface +type fakePostgresqls struct { + *gentype.FakeClientWithList[*v1.Postgresql, *v1.PostgresqlList] Fake *FakeAcidV1 - ns string } -var postgresqlsResource = schema.GroupVersionResource{Group: "acid.zalan.do", Version: "v1", Resource: "postgresqls"} - -var postgresqlsKind = schema.GroupVersionKind{Group: "acid.zalan.do", Version: "v1", Kind: "Postgresql"} - -// Get takes name of the postgresql, and returns the corresponding postgresql object, and an error if there is any. -func (c *FakePostgresqls) Get(ctx context.Context, name string, options v1.GetOptions) (result *acidzalandov1.Postgresql, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(postgresqlsResource, c.ns, name), &acidzalandov1.Postgresql{}) - - if obj == nil { - return nil, err +func newFakePostgresqls(fake *FakeAcidV1, namespace string) acidzalandov1.PostgresqlInterface { + return &fakePostgresqls{ + gentype.NewFakeClientWithList[*v1.Postgresql, *v1.PostgresqlList]( + fake.Fake, + namespace, + v1.SchemeGroupVersion.WithResource("postgresqls"), + v1.SchemeGroupVersion.WithKind("Postgresql"), + func() *v1.Postgresql { return &v1.Postgresql{} }, + func() *v1.PostgresqlList { return &v1.PostgresqlList{} }, + func(dst, src *v1.PostgresqlList) { dst.ListMeta = src.ListMeta }, + func(list *v1.PostgresqlList) []*v1.Postgresql { return gentype.ToPointerSlice(list.Items) }, + func(list *v1.PostgresqlList, items []*v1.Postgresql) { list.Items = gentype.FromPointerSlice(items) }, + ), + fake, } - return obj.(*acidzalandov1.Postgresql), err -} - -// List takes label and field selectors, and returns the list of Postgresqls that match those selectors. -func (c *FakePostgresqls) List(ctx context.Context, opts v1.ListOptions) (result *acidzalandov1.PostgresqlList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(postgresqlsResource, postgresqlsKind, c.ns, opts), &acidzalandov1.PostgresqlList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &acidzalandov1.PostgresqlList{ListMeta: obj.(*acidzalandov1.PostgresqlList).ListMeta} - for _, item := range obj.(*acidzalandov1.PostgresqlList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested postgresqls. -func (c *FakePostgresqls) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(postgresqlsResource, c.ns, opts)) - -} - -// Create takes the representation of a postgresql and creates it. Returns the server's representation of the postgresql, and an error, if there is any. -func (c *FakePostgresqls) Create(ctx context.Context, postgresql *acidzalandov1.Postgresql, opts v1.CreateOptions) (result *acidzalandov1.Postgresql, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(postgresqlsResource, c.ns, postgresql), &acidzalandov1.Postgresql{}) - - if obj == nil { - return nil, err - } - return obj.(*acidzalandov1.Postgresql), err -} - -// Update takes the representation of a postgresql and updates it. Returns the server's representation of the postgresql, and an error, if there is any. -func (c *FakePostgresqls) Update(ctx context.Context, postgresql *acidzalandov1.Postgresql, opts v1.UpdateOptions) (result *acidzalandov1.Postgresql, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(postgresqlsResource, c.ns, postgresql), &acidzalandov1.Postgresql{}) - - if obj == nil { - return nil, err - } - return obj.(*acidzalandov1.Postgresql), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakePostgresqls) UpdateStatus(ctx context.Context, postgresql *acidzalandov1.Postgresql, opts v1.UpdateOptions) (*acidzalandov1.Postgresql, error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateSubresourceAction(postgresqlsResource, "status", c.ns, postgresql), &acidzalandov1.Postgresql{}) - - if obj == nil { - return nil, err - } - return obj.(*acidzalandov1.Postgresql), err -} - -// Delete takes name of the postgresql and deletes it. Returns an error if one occurs. -func (c *FakePostgresqls) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteActionWithOptions(postgresqlsResource, c.ns, name, opts), &acidzalandov1.Postgresql{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakePostgresqls) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(postgresqlsResource, c.ns, listOpts) - - _, err := c.Fake.Invokes(action, &acidzalandov1.PostgresqlList{}) - return err -} - -// Patch applies the patch and returns the patched postgresql. -func (c *FakePostgresqls) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *acidzalandov1.Postgresql, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(postgresqlsResource, c.ns, name, pt, data, subresources...), &acidzalandov1.Postgresql{}) - - if obj == nil { - return nil, err - } - return obj.(*acidzalandov1.Postgresql), err } diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_postgresteam.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_postgresteam.go index 5801666c8..2d8f3338f 100644 --- a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_postgresteam.go +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_postgresteam.go @@ -25,112 +25,32 @@ SOFTWARE. package fake import ( - "context" - - acidzalandov1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" + v1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" + acidzalandov1 "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1" + gentype "k8s.io/client-go/gentype" ) -// FakePostgresTeams implements PostgresTeamInterface -type FakePostgresTeams struct { +// fakePostgresTeams implements PostgresTeamInterface +type fakePostgresTeams struct { + *gentype.FakeClientWithList[*v1.PostgresTeam, *v1.PostgresTeamList] Fake *FakeAcidV1 - ns string } -var postgresteamsResource = schema.GroupVersionResource{Group: "acid.zalan.do", Version: "v1", Resource: "postgresteams"} - -var postgresteamsKind = schema.GroupVersionKind{Group: "acid.zalan.do", Version: "v1", Kind: "PostgresTeam"} - -// Get takes name of the postgresTeam, and returns the corresponding postgresTeam object, and an error if there is any. -func (c *FakePostgresTeams) Get(ctx context.Context, name string, options v1.GetOptions) (result *acidzalandov1.PostgresTeam, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(postgresteamsResource, c.ns, name), &acidzalandov1.PostgresTeam{}) - - if obj == nil { - return nil, err +func newFakePostgresTeams(fake *FakeAcidV1, namespace string) acidzalandov1.PostgresTeamInterface { + return &fakePostgresTeams{ + gentype.NewFakeClientWithList[*v1.PostgresTeam, *v1.PostgresTeamList]( + fake.Fake, + namespace, + v1.SchemeGroupVersion.WithResource("postgresteams"), + v1.SchemeGroupVersion.WithKind("PostgresTeam"), + func() *v1.PostgresTeam { return &v1.PostgresTeam{} }, + func() *v1.PostgresTeamList { return &v1.PostgresTeamList{} }, + func(dst, src *v1.PostgresTeamList) { dst.ListMeta = src.ListMeta }, + func(list *v1.PostgresTeamList) []*v1.PostgresTeam { return gentype.ToPointerSlice(list.Items) }, + func(list *v1.PostgresTeamList, items []*v1.PostgresTeam) { + list.Items = gentype.FromPointerSlice(items) + }, + ), + fake, } - return obj.(*acidzalandov1.PostgresTeam), err -} - -// List takes label and field selectors, and returns the list of PostgresTeams that match those selectors. -func (c *FakePostgresTeams) List(ctx context.Context, opts v1.ListOptions) (result *acidzalandov1.PostgresTeamList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(postgresteamsResource, postgresteamsKind, c.ns, opts), &acidzalandov1.PostgresTeamList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &acidzalandov1.PostgresTeamList{ListMeta: obj.(*acidzalandov1.PostgresTeamList).ListMeta} - for _, item := range obj.(*acidzalandov1.PostgresTeamList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested postgresTeams. -func (c *FakePostgresTeams) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(postgresteamsResource, c.ns, opts)) - -} - -// Create takes the representation of a postgresTeam and creates it. Returns the server's representation of the postgresTeam, and an error, if there is any. -func (c *FakePostgresTeams) Create(ctx context.Context, postgresTeam *acidzalandov1.PostgresTeam, opts v1.CreateOptions) (result *acidzalandov1.PostgresTeam, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(postgresteamsResource, c.ns, postgresTeam), &acidzalandov1.PostgresTeam{}) - - if obj == nil { - return nil, err - } - return obj.(*acidzalandov1.PostgresTeam), err -} - -// Update takes the representation of a postgresTeam and updates it. Returns the server's representation of the postgresTeam, and an error, if there is any. -func (c *FakePostgresTeams) Update(ctx context.Context, postgresTeam *acidzalandov1.PostgresTeam, opts v1.UpdateOptions) (result *acidzalandov1.PostgresTeam, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(postgresteamsResource, c.ns, postgresTeam), &acidzalandov1.PostgresTeam{}) - - if obj == nil { - return nil, err - } - return obj.(*acidzalandov1.PostgresTeam), err -} - -// Delete takes name of the postgresTeam and deletes it. Returns an error if one occurs. -func (c *FakePostgresTeams) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteActionWithOptions(postgresteamsResource, c.ns, name, opts), &acidzalandov1.PostgresTeam{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakePostgresTeams) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(postgresteamsResource, c.ns, listOpts) - - _, err := c.Fake.Invokes(action, &acidzalandov1.PostgresTeamList{}) - return err -} - -// Patch applies the patch and returns the patched postgresTeam. -func (c *FakePostgresTeams) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *acidzalandov1.PostgresTeam, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(postgresteamsResource, c.ns, name, pt, data, subresources...), &acidzalandov1.PostgresTeam{}) - - if obj == nil { - return nil, err - } - return obj.(*acidzalandov1.PostgresTeam), err } diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/operatorconfiguration.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/operatorconfiguration.go index c941551ca..b11f28dde 100644 --- a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/operatorconfiguration.go +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/operatorconfiguration.go @@ -25,12 +25,12 @@ SOFTWARE. package v1 import ( - "context" + context "context" acidzalandov1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" scheme "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/scheme" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - rest "k8s.io/client-go/rest" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gentype "k8s.io/client-go/gentype" ) // OperatorConfigurationsGetter has a method to return a OperatorConfigurationInterface. @@ -41,33 +41,24 @@ type OperatorConfigurationsGetter interface { // OperatorConfigurationInterface has methods to work with OperatorConfiguration resources. type OperatorConfigurationInterface interface { - Get(ctx context.Context, name string, opts v1.GetOptions) (*acidzalandov1.OperatorConfiguration, error) + Get(ctx context.Context, name string, opts metav1.GetOptions) (*acidzalandov1.OperatorConfiguration, error) OperatorConfigurationExpansion } // operatorConfigurations implements OperatorConfigurationInterface type operatorConfigurations struct { - client rest.Interface - ns string + *gentype.Client[*acidzalandov1.OperatorConfiguration] } // newOperatorConfigurations returns a OperatorConfigurations func newOperatorConfigurations(c *AcidV1Client, namespace string) *operatorConfigurations { return &operatorConfigurations{ - client: c.RESTClient(), - ns: namespace, + gentype.NewClient[*acidzalandov1.OperatorConfiguration]( + "operatorconfigurations", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *acidzalandov1.OperatorConfiguration { return &acidzalandov1.OperatorConfiguration{} }, + ), } } - -// Get takes name of the operatorConfiguration, and returns the corresponding operatorConfiguration object, and an error if there is any. -func (c *operatorConfigurations) Get(ctx context.Context, name string, options v1.GetOptions) (result *acidzalandov1.OperatorConfiguration, err error) { - result = &acidzalandov1.OperatorConfiguration{} - err = c.client.Get(). - Namespace(c.ns). - Resource("operatorconfigurations"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(ctx). - Into(result) - return -} diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/postgresql.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/postgresql.go index 23133d22a..40abf69bb 100644 --- a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/postgresql.go +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/postgresql.go @@ -25,15 +25,14 @@ SOFTWARE. package v1 import ( - "context" - "time" + context "context" - v1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" + acidzalandov1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" scheme "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/scheme" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" types "k8s.io/apimachinery/pkg/types" watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" + gentype "k8s.io/client-go/gentype" ) // PostgresqlsGetter has a method to return a PostgresqlInterface. @@ -44,158 +43,34 @@ type PostgresqlsGetter interface { // PostgresqlInterface has methods to work with Postgresql resources. type PostgresqlInterface interface { - Create(ctx context.Context, postgresql *v1.Postgresql, opts metav1.CreateOptions) (*v1.Postgresql, error) - Update(ctx context.Context, postgresql *v1.Postgresql, opts metav1.UpdateOptions) (*v1.Postgresql, error) - UpdateStatus(ctx context.Context, postgresql *v1.Postgresql, opts metav1.UpdateOptions) (*v1.Postgresql, error) + Create(ctx context.Context, postgresql *acidzalandov1.Postgresql, opts metav1.CreateOptions) (*acidzalandov1.Postgresql, error) + Update(ctx context.Context, postgresql *acidzalandov1.Postgresql, opts metav1.UpdateOptions) (*acidzalandov1.Postgresql, error) + // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + UpdateStatus(ctx context.Context, postgresql *acidzalandov1.Postgresql, opts metav1.UpdateOptions) (*acidzalandov1.Postgresql, error) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error - Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.Postgresql, error) - List(ctx context.Context, opts metav1.ListOptions) (*v1.PostgresqlList, error) + Get(ctx context.Context, name string, opts metav1.GetOptions) (*acidzalandov1.Postgresql, error) + List(ctx context.Context, opts metav1.ListOptions) (*acidzalandov1.PostgresqlList, error) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) - Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.Postgresql, err error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *acidzalandov1.Postgresql, err error) PostgresqlExpansion } // postgresqls implements PostgresqlInterface type postgresqls struct { - client rest.Interface - ns string + *gentype.ClientWithList[*acidzalandov1.Postgresql, *acidzalandov1.PostgresqlList] } // newPostgresqls returns a Postgresqls func newPostgresqls(c *AcidV1Client, namespace string) *postgresqls { return &postgresqls{ - client: c.RESTClient(), - ns: namespace, + gentype.NewClientWithList[*acidzalandov1.Postgresql, *acidzalandov1.PostgresqlList]( + "postgresqls", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *acidzalandov1.Postgresql { return &acidzalandov1.Postgresql{} }, + func() *acidzalandov1.PostgresqlList { return &acidzalandov1.PostgresqlList{} }, + ), } } - -// Get takes name of the postgresql, and returns the corresponding postgresql object, and an error if there is any. -func (c *postgresqls) Get(ctx context.Context, name string, options metav1.GetOptions) (result *v1.Postgresql, err error) { - result = &v1.Postgresql{} - err = c.client.Get(). - Namespace(c.ns). - Resource("postgresqls"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(ctx). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of Postgresqls that match those selectors. -func (c *postgresqls) List(ctx context.Context, opts metav1.ListOptions) (result *v1.PostgresqlList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1.PostgresqlList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("postgresqls"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(ctx). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested postgresqls. -func (c *postgresqls) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("postgresqls"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch(ctx) -} - -// Create takes the representation of a postgresql and creates it. Returns the server's representation of the postgresql, and an error, if there is any. -func (c *postgresqls) Create(ctx context.Context, postgresql *v1.Postgresql, opts metav1.CreateOptions) (result *v1.Postgresql, err error) { - result = &v1.Postgresql{} - err = c.client.Post(). - Namespace(c.ns). - Resource("postgresqls"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(postgresql). - Do(ctx). - Into(result) - return -} - -// Update takes the representation of a postgresql and updates it. Returns the server's representation of the postgresql, and an error, if there is any. -func (c *postgresqls) Update(ctx context.Context, postgresql *v1.Postgresql, opts metav1.UpdateOptions) (result *v1.Postgresql, err error) { - result = &v1.Postgresql{} - err = c.client.Put(). - Namespace(c.ns). - Resource("postgresqls"). - Name(postgresql.Name). - VersionedParams(&opts, scheme.ParameterCodec). - Body(postgresql). - Do(ctx). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *postgresqls) UpdateStatus(ctx context.Context, postgresql *v1.Postgresql, opts metav1.UpdateOptions) (result *v1.Postgresql, err error) { - result = &v1.Postgresql{} - err = c.client.Put(). - Namespace(c.ns). - Resource("postgresqls"). - Name(postgresql.Name). - SubResource("status"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(postgresql). - Do(ctx). - Into(result) - return -} - -// Delete takes name of the postgresql and deletes it. Returns an error if one occurs. -func (c *postgresqls) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("postgresqls"). - Name(name). - Body(&opts). - Do(ctx). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *postgresqls) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error { - var timeout time.Duration - if listOpts.TimeoutSeconds != nil { - timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("postgresqls"). - VersionedParams(&listOpts, scheme.ParameterCodec). - Timeout(timeout). - Body(&opts). - Do(ctx). - Error() -} - -// Patch applies the patch and returns the patched postgresql. -func (c *postgresqls) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.Postgresql, err error) { - result = &v1.Postgresql{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("postgresqls"). - Name(name). - SubResource(subresources...). - VersionedParams(&opts, scheme.ParameterCodec). - Body(data). - Do(ctx). - Into(result) - return -} diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/postgresteam.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/postgresteam.go index c62f6c9d7..89005b5ed 100644 --- a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/postgresteam.go +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/postgresteam.go @@ -25,15 +25,14 @@ SOFTWARE. package v1 import ( - "context" - "time" + context "context" - v1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" + acidzalandov1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" scheme "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/scheme" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" types "k8s.io/apimachinery/pkg/types" watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" + gentype "k8s.io/client-go/gentype" ) // PostgresTeamsGetter has a method to return a PostgresTeamInterface. @@ -44,141 +43,32 @@ type PostgresTeamsGetter interface { // PostgresTeamInterface has methods to work with PostgresTeam resources. type PostgresTeamInterface interface { - Create(ctx context.Context, postgresTeam *v1.PostgresTeam, opts metav1.CreateOptions) (*v1.PostgresTeam, error) - Update(ctx context.Context, postgresTeam *v1.PostgresTeam, opts metav1.UpdateOptions) (*v1.PostgresTeam, error) + Create(ctx context.Context, postgresTeam *acidzalandov1.PostgresTeam, opts metav1.CreateOptions) (*acidzalandov1.PostgresTeam, error) + Update(ctx context.Context, postgresTeam *acidzalandov1.PostgresTeam, opts metav1.UpdateOptions) (*acidzalandov1.PostgresTeam, error) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error - Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.PostgresTeam, error) - List(ctx context.Context, opts metav1.ListOptions) (*v1.PostgresTeamList, error) + Get(ctx context.Context, name string, opts metav1.GetOptions) (*acidzalandov1.PostgresTeam, error) + List(ctx context.Context, opts metav1.ListOptions) (*acidzalandov1.PostgresTeamList, error) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) - Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.PostgresTeam, err error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *acidzalandov1.PostgresTeam, err error) PostgresTeamExpansion } // postgresTeams implements PostgresTeamInterface type postgresTeams struct { - client rest.Interface - ns string + *gentype.ClientWithList[*acidzalandov1.PostgresTeam, *acidzalandov1.PostgresTeamList] } // newPostgresTeams returns a PostgresTeams func newPostgresTeams(c *AcidV1Client, namespace string) *postgresTeams { return &postgresTeams{ - client: c.RESTClient(), - ns: namespace, + gentype.NewClientWithList[*acidzalandov1.PostgresTeam, *acidzalandov1.PostgresTeamList]( + "postgresteams", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *acidzalandov1.PostgresTeam { return &acidzalandov1.PostgresTeam{} }, + func() *acidzalandov1.PostgresTeamList { return &acidzalandov1.PostgresTeamList{} }, + ), } } - -// Get takes name of the postgresTeam, and returns the corresponding postgresTeam object, and an error if there is any. -func (c *postgresTeams) Get(ctx context.Context, name string, options metav1.GetOptions) (result *v1.PostgresTeam, err error) { - result = &v1.PostgresTeam{} - err = c.client.Get(). - Namespace(c.ns). - Resource("postgresteams"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(ctx). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of PostgresTeams that match those selectors. -func (c *postgresTeams) List(ctx context.Context, opts metav1.ListOptions) (result *v1.PostgresTeamList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1.PostgresTeamList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("postgresteams"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(ctx). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested postgresTeams. -func (c *postgresTeams) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("postgresteams"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch(ctx) -} - -// Create takes the representation of a postgresTeam and creates it. Returns the server's representation of the postgresTeam, and an error, if there is any. -func (c *postgresTeams) Create(ctx context.Context, postgresTeam *v1.PostgresTeam, opts metav1.CreateOptions) (result *v1.PostgresTeam, err error) { - result = &v1.PostgresTeam{} - err = c.client.Post(). - Namespace(c.ns). - Resource("postgresteams"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(postgresTeam). - Do(ctx). - Into(result) - return -} - -// Update takes the representation of a postgresTeam and updates it. Returns the server's representation of the postgresTeam, and an error, if there is any. -func (c *postgresTeams) Update(ctx context.Context, postgresTeam *v1.PostgresTeam, opts metav1.UpdateOptions) (result *v1.PostgresTeam, err error) { - result = &v1.PostgresTeam{} - err = c.client.Put(). - Namespace(c.ns). - Resource("postgresteams"). - Name(postgresTeam.Name). - VersionedParams(&opts, scheme.ParameterCodec). - Body(postgresTeam). - Do(ctx). - Into(result) - return -} - -// Delete takes name of the postgresTeam and deletes it. Returns an error if one occurs. -func (c *postgresTeams) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("postgresteams"). - Name(name). - Body(&opts). - Do(ctx). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *postgresTeams) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error { - var timeout time.Duration - if listOpts.TimeoutSeconds != nil { - timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("postgresteams"). - VersionedParams(&listOpts, scheme.ParameterCodec). - Timeout(timeout). - Body(&opts). - Do(ctx). - Error() -} - -// Patch applies the patch and returns the patched postgresTeam. -func (c *postgresTeams) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.PostgresTeam, err error) { - result = &v1.PostgresTeam{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("postgresteams"). - Name(name). - SubResource(subresources...). - VersionedParams(&opts, scheme.ParameterCodec). - Body(data). - Do(ctx). - Into(result) - return -} diff --git a/pkg/generated/clientset/versioned/typed/zalando.org/v1/fabriceventstream.go b/pkg/generated/clientset/versioned/typed/zalando.org/v1/fabriceventstream.go index ae4a267d3..8a342cb45 100644 --- a/pkg/generated/clientset/versioned/typed/zalando.org/v1/fabriceventstream.go +++ b/pkg/generated/clientset/versioned/typed/zalando.org/v1/fabriceventstream.go @@ -25,15 +25,14 @@ SOFTWARE. package v1 import ( - "context" - "time" + context "context" - v1 "github.com/zalando/postgres-operator/pkg/apis/zalando.org/v1" + zalandoorgv1 "github.com/zalando/postgres-operator/pkg/apis/zalando.org/v1" scheme "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/scheme" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" types "k8s.io/apimachinery/pkg/types" watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" + gentype "k8s.io/client-go/gentype" ) // FabricEventStreamsGetter has a method to return a FabricEventStreamInterface. @@ -44,141 +43,32 @@ type FabricEventStreamsGetter interface { // FabricEventStreamInterface has methods to work with FabricEventStream resources. type FabricEventStreamInterface interface { - Create(ctx context.Context, fabricEventStream *v1.FabricEventStream, opts metav1.CreateOptions) (*v1.FabricEventStream, error) - Update(ctx context.Context, fabricEventStream *v1.FabricEventStream, opts metav1.UpdateOptions) (*v1.FabricEventStream, error) + Create(ctx context.Context, fabricEventStream *zalandoorgv1.FabricEventStream, opts metav1.CreateOptions) (*zalandoorgv1.FabricEventStream, error) + Update(ctx context.Context, fabricEventStream *zalandoorgv1.FabricEventStream, opts metav1.UpdateOptions) (*zalandoorgv1.FabricEventStream, error) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error - Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.FabricEventStream, error) - List(ctx context.Context, opts metav1.ListOptions) (*v1.FabricEventStreamList, error) + Get(ctx context.Context, name string, opts metav1.GetOptions) (*zalandoorgv1.FabricEventStream, error) + List(ctx context.Context, opts metav1.ListOptions) (*zalandoorgv1.FabricEventStreamList, error) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) - Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.FabricEventStream, err error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *zalandoorgv1.FabricEventStream, err error) FabricEventStreamExpansion } // fabricEventStreams implements FabricEventStreamInterface type fabricEventStreams struct { - client rest.Interface - ns string + *gentype.ClientWithList[*zalandoorgv1.FabricEventStream, *zalandoorgv1.FabricEventStreamList] } // newFabricEventStreams returns a FabricEventStreams func newFabricEventStreams(c *ZalandoV1Client, namespace string) *fabricEventStreams { return &fabricEventStreams{ - client: c.RESTClient(), - ns: namespace, + gentype.NewClientWithList[*zalandoorgv1.FabricEventStream, *zalandoorgv1.FabricEventStreamList]( + "fabriceventstreams", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *zalandoorgv1.FabricEventStream { return &zalandoorgv1.FabricEventStream{} }, + func() *zalandoorgv1.FabricEventStreamList { return &zalandoorgv1.FabricEventStreamList{} }, + ), } } - -// Get takes name of the fabricEventStream, and returns the corresponding fabricEventStream object, and an error if there is any. -func (c *fabricEventStreams) Get(ctx context.Context, name string, options metav1.GetOptions) (result *v1.FabricEventStream, err error) { - result = &v1.FabricEventStream{} - err = c.client.Get(). - Namespace(c.ns). - Resource("fabriceventstreams"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(ctx). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of FabricEventStreams that match those selectors. -func (c *fabricEventStreams) List(ctx context.Context, opts metav1.ListOptions) (result *v1.FabricEventStreamList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1.FabricEventStreamList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("fabriceventstreams"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(ctx). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested fabricEventStreams. -func (c *fabricEventStreams) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("fabriceventstreams"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch(ctx) -} - -// Create takes the representation of a fabricEventStream and creates it. Returns the server's representation of the fabricEventStream, and an error, if there is any. -func (c *fabricEventStreams) Create(ctx context.Context, fabricEventStream *v1.FabricEventStream, opts metav1.CreateOptions) (result *v1.FabricEventStream, err error) { - result = &v1.FabricEventStream{} - err = c.client.Post(). - Namespace(c.ns). - Resource("fabriceventstreams"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(fabricEventStream). - Do(ctx). - Into(result) - return -} - -// Update takes the representation of a fabricEventStream and updates it. Returns the server's representation of the fabricEventStream, and an error, if there is any. -func (c *fabricEventStreams) Update(ctx context.Context, fabricEventStream *v1.FabricEventStream, opts metav1.UpdateOptions) (result *v1.FabricEventStream, err error) { - result = &v1.FabricEventStream{} - err = c.client.Put(). - Namespace(c.ns). - Resource("fabriceventstreams"). - Name(fabricEventStream.Name). - VersionedParams(&opts, scheme.ParameterCodec). - Body(fabricEventStream). - Do(ctx). - Into(result) - return -} - -// Delete takes name of the fabricEventStream and deletes it. Returns an error if one occurs. -func (c *fabricEventStreams) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("fabriceventstreams"). - Name(name). - Body(&opts). - Do(ctx). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *fabricEventStreams) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error { - var timeout time.Duration - if listOpts.TimeoutSeconds != nil { - timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("fabriceventstreams"). - VersionedParams(&listOpts, scheme.ParameterCodec). - Timeout(timeout). - Body(&opts). - Do(ctx). - Error() -} - -// Patch applies the patch and returns the patched fabricEventStream. -func (c *fabricEventStreams) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.FabricEventStream, err error) { - result = &v1.FabricEventStream{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("fabriceventstreams"). - Name(name). - SubResource(subresources...). - VersionedParams(&opts, scheme.ParameterCodec). - Body(data). - Do(ctx). - Into(result) - return -} diff --git a/pkg/generated/clientset/versioned/typed/zalando.org/v1/fake/fake_fabriceventstream.go b/pkg/generated/clientset/versioned/typed/zalando.org/v1/fake/fake_fabriceventstream.go index 9885d8755..b273891a1 100644 --- a/pkg/generated/clientset/versioned/typed/zalando.org/v1/fake/fake_fabriceventstream.go +++ b/pkg/generated/clientset/versioned/typed/zalando.org/v1/fake/fake_fabriceventstream.go @@ -25,112 +25,34 @@ SOFTWARE. package fake import ( - "context" - - zalandoorgv1 "github.com/zalando/postgres-operator/pkg/apis/zalando.org/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" + v1 "github.com/zalando/postgres-operator/pkg/apis/zalando.org/v1" + zalandoorgv1 "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/zalando.org/v1" + gentype "k8s.io/client-go/gentype" ) -// FakeFabricEventStreams implements FabricEventStreamInterface -type FakeFabricEventStreams struct { +// fakeFabricEventStreams implements FabricEventStreamInterface +type fakeFabricEventStreams struct { + *gentype.FakeClientWithList[*v1.FabricEventStream, *v1.FabricEventStreamList] Fake *FakeZalandoV1 - ns string } -var fabriceventstreamsResource = schema.GroupVersionResource{Group: "zalando.org", Version: "v1", Resource: "fabriceventstreams"} - -var fabriceventstreamsKind = schema.GroupVersionKind{Group: "zalando.org", Version: "v1", Kind: "FabricEventStream"} - -// Get takes name of the fabricEventStream, and returns the corresponding fabricEventStream object, and an error if there is any. -func (c *FakeFabricEventStreams) Get(ctx context.Context, name string, options v1.GetOptions) (result *zalandoorgv1.FabricEventStream, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(fabriceventstreamsResource, c.ns, name), &zalandoorgv1.FabricEventStream{}) - - if obj == nil { - return nil, err +func newFakeFabricEventStreams(fake *FakeZalandoV1, namespace string) zalandoorgv1.FabricEventStreamInterface { + return &fakeFabricEventStreams{ + gentype.NewFakeClientWithList[*v1.FabricEventStream, *v1.FabricEventStreamList]( + fake.Fake, + namespace, + v1.SchemeGroupVersion.WithResource("fabriceventstreams"), + v1.SchemeGroupVersion.WithKind("FabricEventStream"), + func() *v1.FabricEventStream { return &v1.FabricEventStream{} }, + func() *v1.FabricEventStreamList { return &v1.FabricEventStreamList{} }, + func(dst, src *v1.FabricEventStreamList) { dst.ListMeta = src.ListMeta }, + func(list *v1.FabricEventStreamList) []*v1.FabricEventStream { + return gentype.ToPointerSlice(list.Items) + }, + func(list *v1.FabricEventStreamList, items []*v1.FabricEventStream) { + list.Items = gentype.FromPointerSlice(items) + }, + ), + fake, } - return obj.(*zalandoorgv1.FabricEventStream), err -} - -// List takes label and field selectors, and returns the list of FabricEventStreams that match those selectors. -func (c *FakeFabricEventStreams) List(ctx context.Context, opts v1.ListOptions) (result *zalandoorgv1.FabricEventStreamList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(fabriceventstreamsResource, fabriceventstreamsKind, c.ns, opts), &zalandoorgv1.FabricEventStreamList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &zalandoorgv1.FabricEventStreamList{ListMeta: obj.(*zalandoorgv1.FabricEventStreamList).ListMeta} - for _, item := range obj.(*zalandoorgv1.FabricEventStreamList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested fabricEventStreams. -func (c *FakeFabricEventStreams) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(fabriceventstreamsResource, c.ns, opts)) - -} - -// Create takes the representation of a fabricEventStream and creates it. Returns the server's representation of the fabricEventStream, and an error, if there is any. -func (c *FakeFabricEventStreams) Create(ctx context.Context, fabricEventStream *zalandoorgv1.FabricEventStream, opts v1.CreateOptions) (result *zalandoorgv1.FabricEventStream, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(fabriceventstreamsResource, c.ns, fabricEventStream), &zalandoorgv1.FabricEventStream{}) - - if obj == nil { - return nil, err - } - return obj.(*zalandoorgv1.FabricEventStream), err -} - -// Update takes the representation of a fabricEventStream and updates it. Returns the server's representation of the fabricEventStream, and an error, if there is any. -func (c *FakeFabricEventStreams) Update(ctx context.Context, fabricEventStream *zalandoorgv1.FabricEventStream, opts v1.UpdateOptions) (result *zalandoorgv1.FabricEventStream, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(fabriceventstreamsResource, c.ns, fabricEventStream), &zalandoorgv1.FabricEventStream{}) - - if obj == nil { - return nil, err - } - return obj.(*zalandoorgv1.FabricEventStream), err -} - -// Delete takes name of the fabricEventStream and deletes it. Returns an error if one occurs. -func (c *FakeFabricEventStreams) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteActionWithOptions(fabriceventstreamsResource, c.ns, name, opts), &zalandoorgv1.FabricEventStream{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeFabricEventStreams) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(fabriceventstreamsResource, c.ns, listOpts) - - _, err := c.Fake.Invokes(action, &zalandoorgv1.FabricEventStreamList{}) - return err -} - -// Patch applies the patch and returns the patched fabricEventStream. -func (c *FakeFabricEventStreams) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *zalandoorgv1.FabricEventStream, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(fabriceventstreamsResource, c.ns, name, pt, data, subresources...), &zalandoorgv1.FabricEventStream{}) - - if obj == nil { - return nil, err - } - return obj.(*zalandoorgv1.FabricEventStream), err } diff --git a/pkg/generated/clientset/versioned/typed/zalando.org/v1/fake/fake_zalando.org_client.go b/pkg/generated/clientset/versioned/typed/zalando.org/v1/fake/fake_zalando.org_client.go index 049cc72b2..d0eefecf1 100644 --- a/pkg/generated/clientset/versioned/typed/zalando.org/v1/fake/fake_zalando.org_client.go +++ b/pkg/generated/clientset/versioned/typed/zalando.org/v1/fake/fake_zalando.org_client.go @@ -35,7 +35,7 @@ type FakeZalandoV1 struct { } func (c *FakeZalandoV1) FabricEventStreams(namespace string) v1.FabricEventStreamInterface { - return &FakeFabricEventStreams{c, namespace} + return newFakeFabricEventStreams(c, namespace) } // RESTClient returns a RESTClient that is used to communicate diff --git a/pkg/generated/clientset/versioned/typed/zalando.org/v1/zalando.org_client.go b/pkg/generated/clientset/versioned/typed/zalando.org/v1/zalando.org_client.go index a14c4dee3..a3947464e 100644 --- a/pkg/generated/clientset/versioned/typed/zalando.org/v1/zalando.org_client.go +++ b/pkg/generated/clientset/versioned/typed/zalando.org/v1/zalando.org_client.go @@ -25,10 +25,10 @@ SOFTWARE. package v1 import ( - "net/http" + http "net/http" - v1 "github.com/zalando/postgres-operator/pkg/apis/zalando.org/v1" - "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/scheme" + zalandoorgv1 "github.com/zalando/postgres-operator/pkg/apis/zalando.org/v1" + scheme "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/scheme" rest "k8s.io/client-go/rest" ) @@ -91,10 +91,10 @@ func New(c rest.Interface) *ZalandoV1Client { } func setConfigDefaults(config *rest.Config) error { - gv := v1.SchemeGroupVersion + gv := zalandoorgv1.SchemeGroupVersion config.GroupVersion = &gv config.APIPath = "/apis" - config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() + config.NegotiatedSerializer = rest.CodecFactoryForGeneratedClient(scheme.Scheme, scheme.Codecs).WithoutConversion() if config.UserAgent == "" { config.UserAgent = rest.DefaultKubernetesUserAgent() diff --git a/pkg/generated/informers/externalversions/acid.zalan.do/v1/postgresql.go b/pkg/generated/informers/externalversions/acid.zalan.do/v1/postgresql.go index 179562e4c..f44f0f143 100644 --- a/pkg/generated/informers/externalversions/acid.zalan.do/v1/postgresql.go +++ b/pkg/generated/informers/externalversions/acid.zalan.do/v1/postgresql.go @@ -25,13 +25,13 @@ SOFTWARE. package v1 import ( - "context" + context "context" time "time" - acidzalandov1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" + apisacidzalandov1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" versioned "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned" internalinterfaces "github.com/zalando/postgres-operator/pkg/generated/informers/externalversions/internalinterfaces" - v1 "github.com/zalando/postgres-operator/pkg/generated/listers/acid.zalan.do/v1" + acidzalandov1 "github.com/zalando/postgres-operator/pkg/generated/listers/acid.zalan.do/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" watch "k8s.io/apimachinery/pkg/watch" @@ -42,7 +42,7 @@ import ( // Postgresqls. type PostgresqlInformer interface { Informer() cache.SharedIndexInformer - Lister() v1.PostgresqlLister + Lister() acidzalandov1.PostgresqlLister } type postgresqlInformer struct { @@ -77,7 +77,7 @@ func NewFilteredPostgresqlInformer(client versioned.Interface, namespace string, return client.AcidV1().Postgresqls(namespace).Watch(context.TODO(), options) }, }, - &acidzalandov1.Postgresql{}, + &apisacidzalandov1.Postgresql{}, resyncPeriod, indexers, ) @@ -88,9 +88,9 @@ func (f *postgresqlInformer) defaultInformer(client versioned.Interface, resyncP } func (f *postgresqlInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&acidzalandov1.Postgresql{}, f.defaultInformer) + return f.factory.InformerFor(&apisacidzalandov1.Postgresql{}, f.defaultInformer) } -func (f *postgresqlInformer) Lister() v1.PostgresqlLister { - return v1.NewPostgresqlLister(f.Informer().GetIndexer()) +func (f *postgresqlInformer) Lister() acidzalandov1.PostgresqlLister { + return acidzalandov1.NewPostgresqlLister(f.Informer().GetIndexer()) } diff --git a/pkg/generated/informers/externalversions/acid.zalan.do/v1/postgresteam.go b/pkg/generated/informers/externalversions/acid.zalan.do/v1/postgresteam.go index 79e6e872a..0aed5893f 100644 --- a/pkg/generated/informers/externalversions/acid.zalan.do/v1/postgresteam.go +++ b/pkg/generated/informers/externalversions/acid.zalan.do/v1/postgresteam.go @@ -25,13 +25,13 @@ SOFTWARE. package v1 import ( - "context" + context "context" time "time" - acidzalandov1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" + apisacidzalandov1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" versioned "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned" internalinterfaces "github.com/zalando/postgres-operator/pkg/generated/informers/externalversions/internalinterfaces" - v1 "github.com/zalando/postgres-operator/pkg/generated/listers/acid.zalan.do/v1" + acidzalandov1 "github.com/zalando/postgres-operator/pkg/generated/listers/acid.zalan.do/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" watch "k8s.io/apimachinery/pkg/watch" @@ -42,7 +42,7 @@ import ( // PostgresTeams. type PostgresTeamInformer interface { Informer() cache.SharedIndexInformer - Lister() v1.PostgresTeamLister + Lister() acidzalandov1.PostgresTeamLister } type postgresTeamInformer struct { @@ -77,7 +77,7 @@ func NewFilteredPostgresTeamInformer(client versioned.Interface, namespace strin return client.AcidV1().PostgresTeams(namespace).Watch(context.TODO(), options) }, }, - &acidzalandov1.PostgresTeam{}, + &apisacidzalandov1.PostgresTeam{}, resyncPeriod, indexers, ) @@ -88,9 +88,9 @@ func (f *postgresTeamInformer) defaultInformer(client versioned.Interface, resyn } func (f *postgresTeamInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&acidzalandov1.PostgresTeam{}, f.defaultInformer) + return f.factory.InformerFor(&apisacidzalandov1.PostgresTeam{}, f.defaultInformer) } -func (f *postgresTeamInformer) Lister() v1.PostgresTeamLister { - return v1.NewPostgresTeamLister(f.Informer().GetIndexer()) +func (f *postgresTeamInformer) Lister() acidzalandov1.PostgresTeamLister { + return acidzalandov1.NewPostgresTeamLister(f.Informer().GetIndexer()) } diff --git a/pkg/generated/informers/externalversions/factory.go b/pkg/generated/informers/externalversions/factory.go index 2169366b5..eee1ed98b 100644 --- a/pkg/generated/informers/externalversions/factory.go +++ b/pkg/generated/informers/externalversions/factory.go @@ -49,11 +49,17 @@ type sharedInformerFactory struct { lock sync.Mutex defaultResync time.Duration customResync map[reflect.Type]time.Duration + transform cache.TransformFunc informers map[reflect.Type]cache.SharedIndexInformer // startedInformers is used for tracking which informers have been started. // This allows Start() to be called multiple times safely. startedInformers map[reflect.Type]bool + // wg tracks how many goroutines were started. + wg sync.WaitGroup + // shuttingDown is true when Shutdown has been called. It may still be running + // because it needs to wait for goroutines. + shuttingDown bool } // WithCustomResyncConfig sets a custom resync period for the specified informer types. @@ -82,6 +88,14 @@ func WithNamespace(namespace string) SharedInformerOption { } } +// WithTransform sets a transform on all informers. +func WithTransform(transform cache.TransformFunc) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + factory.transform = transform + return factory + } +} + // NewSharedInformerFactory constructs a new instance of sharedInformerFactory for all namespaces. func NewSharedInformerFactory(client versioned.Interface, defaultResync time.Duration) SharedInformerFactory { return NewSharedInformerFactoryWithOptions(client, defaultResync) @@ -114,20 +128,39 @@ func NewSharedInformerFactoryWithOptions(client versioned.Interface, defaultResy return factory } -// Start initializes all requested informers. func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) { f.lock.Lock() defer f.lock.Unlock() + if f.shuttingDown { + return + } + for informerType, informer := range f.informers { if !f.startedInformers[informerType] { - go informer.Run(stopCh) + f.wg.Add(1) + // We need a new variable in each loop iteration, + // otherwise the goroutine would use the loop variable + // and that keeps changing. + informer := informer + go func() { + defer f.wg.Done() + informer.Run(stopCh) + }() f.startedInformers[informerType] = true } } } -// WaitForCacheSync waits for all started informers' cache were synced. +func (f *sharedInformerFactory) Shutdown() { + f.lock.Lock() + f.shuttingDown = true + f.lock.Unlock() + + // Will return immediately if there is nothing to wait for. + f.wg.Wait() +} + func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool { informers := func() map[reflect.Type]cache.SharedIndexInformer { f.lock.Lock() @@ -149,7 +182,7 @@ func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[ref return res } -// InternalInformerFor returns the SharedIndexInformer for obj using an internal +// InformerFor returns the SharedIndexInformer for obj using an internal // client. func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer { f.lock.Lock() @@ -167,6 +200,7 @@ func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internal } informer = newFunc(f.client, resyncPeriod) + informer.SetTransform(f.transform) f.informers[informerType] = informer return informer @@ -174,11 +208,59 @@ func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internal // SharedInformerFactory provides shared informers for resources in all known // API group versions. +// +// It is typically used like this: +// +// ctx, cancel := context.Background() +// defer cancel() +// factory := NewSharedInformerFactory(client, resyncPeriod) +// defer factory.WaitForStop() // Returns immediately if nothing was started. +// genericInformer := factory.ForResource(resource) +// typedInformer := factory.SomeAPIGroup().V1().SomeType() +// factory.Start(ctx.Done()) // Start processing these informers. +// synced := factory.WaitForCacheSync(ctx.Done()) +// for v, ok := range synced { +// if !ok { +// fmt.Fprintf(os.Stderr, "caches failed to sync: %v", v) +// return +// } +// } +// +// // Creating informers can also be created after Start, but then +// // Start must be called again: +// anotherGenericInformer := factory.ForResource(resource) +// factory.Start(ctx.Done()) type SharedInformerFactory interface { internalinterfaces.SharedInformerFactory - ForResource(resource schema.GroupVersionResource) (GenericInformer, error) + + // Start initializes all requested informers. They are handled in goroutines + // which run until the stop channel gets closed. + // Warning: Start does not block. When run in a go-routine, it will race with a later WaitForCacheSync. + Start(stopCh <-chan struct{}) + + // Shutdown marks a factory as shutting down. At that point no new + // informers can be started anymore and Start will return without + // doing anything. + // + // In addition, Shutdown blocks until all goroutines have terminated. For that + // to happen, the close channel(s) that they were started with must be closed, + // either before Shutdown gets called or while it is waiting. + // + // Shutdown may be called multiple times, even concurrently. All such calls will + // block until all goroutines have terminated. + Shutdown() + + // WaitForCacheSync blocks until all started informers' caches were synced + // or the stop channel gets closed. WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool + // ForResource gives generic access to a shared informer of the matching type. + ForResource(resource schema.GroupVersionResource) (GenericInformer, error) + + // InformerFor returns the SharedIndexInformer for obj using an internal + // client. + InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer + Acid() acidzalando.Interface Zalando() zalandoorg.Interface } diff --git a/pkg/generated/informers/externalversions/generic.go b/pkg/generated/informers/externalversions/generic.go index 66d94b2a2..9f83e0bd6 100644 --- a/pkg/generated/informers/externalversions/generic.go +++ b/pkg/generated/informers/externalversions/generic.go @@ -25,7 +25,7 @@ SOFTWARE. package externalversions import ( - "fmt" + fmt "fmt" v1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" zalandoorgv1 "github.com/zalando/postgres-operator/pkg/apis/zalando.org/v1" diff --git a/pkg/generated/informers/externalversions/zalando.org/v1/fabriceventstream.go b/pkg/generated/informers/externalversions/zalando.org/v1/fabriceventstream.go index 2e767f426..7f3a0bc23 100644 --- a/pkg/generated/informers/externalversions/zalando.org/v1/fabriceventstream.go +++ b/pkg/generated/informers/externalversions/zalando.org/v1/fabriceventstream.go @@ -25,13 +25,13 @@ SOFTWARE. package v1 import ( - "context" + context "context" time "time" - zalandoorgv1 "github.com/zalando/postgres-operator/pkg/apis/zalando.org/v1" + apiszalandoorgv1 "github.com/zalando/postgres-operator/pkg/apis/zalando.org/v1" versioned "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned" internalinterfaces "github.com/zalando/postgres-operator/pkg/generated/informers/externalversions/internalinterfaces" - v1 "github.com/zalando/postgres-operator/pkg/generated/listers/zalando.org/v1" + zalandoorgv1 "github.com/zalando/postgres-operator/pkg/generated/listers/zalando.org/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" watch "k8s.io/apimachinery/pkg/watch" @@ -42,7 +42,7 @@ import ( // FabricEventStreams. type FabricEventStreamInformer interface { Informer() cache.SharedIndexInformer - Lister() v1.FabricEventStreamLister + Lister() zalandoorgv1.FabricEventStreamLister } type fabricEventStreamInformer struct { @@ -77,7 +77,7 @@ func NewFilteredFabricEventStreamInformer(client versioned.Interface, namespace return client.ZalandoV1().FabricEventStreams(namespace).Watch(context.TODO(), options) }, }, - &zalandoorgv1.FabricEventStream{}, + &apiszalandoorgv1.FabricEventStream{}, resyncPeriod, indexers, ) @@ -88,9 +88,9 @@ func (f *fabricEventStreamInformer) defaultInformer(client versioned.Interface, } func (f *fabricEventStreamInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&zalandoorgv1.FabricEventStream{}, f.defaultInformer) + return f.factory.InformerFor(&apiszalandoorgv1.FabricEventStream{}, f.defaultInformer) } -func (f *fabricEventStreamInformer) Lister() v1.FabricEventStreamLister { - return v1.NewFabricEventStreamLister(f.Informer().GetIndexer()) +func (f *fabricEventStreamInformer) Lister() zalandoorgv1.FabricEventStreamLister { + return zalandoorgv1.NewFabricEventStreamLister(f.Informer().GetIndexer()) } diff --git a/pkg/generated/listers/acid.zalan.do/v1/postgresql.go b/pkg/generated/listers/acid.zalan.do/v1/postgresql.go index de713421f..0ee5bcf4f 100644 --- a/pkg/generated/listers/acid.zalan.do/v1/postgresql.go +++ b/pkg/generated/listers/acid.zalan.do/v1/postgresql.go @@ -25,10 +25,10 @@ SOFTWARE. package v1 import ( - v1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" + acidzalandov1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" + labels "k8s.io/apimachinery/pkg/labels" + listers "k8s.io/client-go/listers" + cache "k8s.io/client-go/tools/cache" ) // PostgresqlLister helps list Postgresqls. @@ -36,7 +36,7 @@ import ( type PostgresqlLister interface { // List lists all Postgresqls in the indexer. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1.Postgresql, err error) + List(selector labels.Selector) (ret []*acidzalandov1.Postgresql, err error) // Postgresqls returns an object that can list and get Postgresqls. Postgresqls(namespace string) PostgresqlNamespaceLister PostgresqlListerExpansion @@ -44,25 +44,17 @@ type PostgresqlLister interface { // postgresqlLister implements the PostgresqlLister interface. type postgresqlLister struct { - indexer cache.Indexer + listers.ResourceIndexer[*acidzalandov1.Postgresql] } // NewPostgresqlLister returns a new PostgresqlLister. func NewPostgresqlLister(indexer cache.Indexer) PostgresqlLister { - return &postgresqlLister{indexer: indexer} -} - -// List lists all Postgresqls in the indexer. -func (s *postgresqlLister) List(selector labels.Selector) (ret []*v1.Postgresql, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1.Postgresql)) - }) - return ret, err + return &postgresqlLister{listers.New[*acidzalandov1.Postgresql](indexer, acidzalandov1.Resource("postgresql"))} } // Postgresqls returns an object that can list and get Postgresqls. func (s *postgresqlLister) Postgresqls(namespace string) PostgresqlNamespaceLister { - return postgresqlNamespaceLister{indexer: s.indexer, namespace: namespace} + return postgresqlNamespaceLister{listers.NewNamespaced[*acidzalandov1.Postgresql](s.ResourceIndexer, namespace)} } // PostgresqlNamespaceLister helps list and get Postgresqls. @@ -70,36 +62,15 @@ func (s *postgresqlLister) Postgresqls(namespace string) PostgresqlNamespaceList type PostgresqlNamespaceLister interface { // List lists all Postgresqls in the indexer for a given namespace. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1.Postgresql, err error) + List(selector labels.Selector) (ret []*acidzalandov1.Postgresql, err error) // Get retrieves the Postgresql from the indexer for a given namespace and name. // Objects returned here must be treated as read-only. - Get(name string) (*v1.Postgresql, error) + Get(name string) (*acidzalandov1.Postgresql, error) PostgresqlNamespaceListerExpansion } // postgresqlNamespaceLister implements the PostgresqlNamespaceLister // interface. type postgresqlNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all Postgresqls in the indexer for a given namespace. -func (s postgresqlNamespaceLister) List(selector labels.Selector) (ret []*v1.Postgresql, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1.Postgresql)) - }) - return ret, err -} - -// Get retrieves the Postgresql from the indexer for a given namespace and name. -func (s postgresqlNamespaceLister) Get(name string) (*v1.Postgresql, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1.Resource("postgresql"), name) - } - return obj.(*v1.Postgresql), nil + listers.ResourceIndexer[*acidzalandov1.Postgresql] } diff --git a/pkg/generated/listers/acid.zalan.do/v1/postgresteam.go b/pkg/generated/listers/acid.zalan.do/v1/postgresteam.go index 52256d158..dbbea2918 100644 --- a/pkg/generated/listers/acid.zalan.do/v1/postgresteam.go +++ b/pkg/generated/listers/acid.zalan.do/v1/postgresteam.go @@ -25,10 +25,10 @@ SOFTWARE. package v1 import ( - v1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" + acidzalandov1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" + labels "k8s.io/apimachinery/pkg/labels" + listers "k8s.io/client-go/listers" + cache "k8s.io/client-go/tools/cache" ) // PostgresTeamLister helps list PostgresTeams. @@ -36,7 +36,7 @@ import ( type PostgresTeamLister interface { // List lists all PostgresTeams in the indexer. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1.PostgresTeam, err error) + List(selector labels.Selector) (ret []*acidzalandov1.PostgresTeam, err error) // PostgresTeams returns an object that can list and get PostgresTeams. PostgresTeams(namespace string) PostgresTeamNamespaceLister PostgresTeamListerExpansion @@ -44,25 +44,17 @@ type PostgresTeamLister interface { // postgresTeamLister implements the PostgresTeamLister interface. type postgresTeamLister struct { - indexer cache.Indexer + listers.ResourceIndexer[*acidzalandov1.PostgresTeam] } // NewPostgresTeamLister returns a new PostgresTeamLister. func NewPostgresTeamLister(indexer cache.Indexer) PostgresTeamLister { - return &postgresTeamLister{indexer: indexer} -} - -// List lists all PostgresTeams in the indexer. -func (s *postgresTeamLister) List(selector labels.Selector) (ret []*v1.PostgresTeam, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1.PostgresTeam)) - }) - return ret, err + return &postgresTeamLister{listers.New[*acidzalandov1.PostgresTeam](indexer, acidzalandov1.Resource("postgresteam"))} } // PostgresTeams returns an object that can list and get PostgresTeams. func (s *postgresTeamLister) PostgresTeams(namespace string) PostgresTeamNamespaceLister { - return postgresTeamNamespaceLister{indexer: s.indexer, namespace: namespace} + return postgresTeamNamespaceLister{listers.NewNamespaced[*acidzalandov1.PostgresTeam](s.ResourceIndexer, namespace)} } // PostgresTeamNamespaceLister helps list and get PostgresTeams. @@ -70,36 +62,15 @@ func (s *postgresTeamLister) PostgresTeams(namespace string) PostgresTeamNamespa type PostgresTeamNamespaceLister interface { // List lists all PostgresTeams in the indexer for a given namespace. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1.PostgresTeam, err error) + List(selector labels.Selector) (ret []*acidzalandov1.PostgresTeam, err error) // Get retrieves the PostgresTeam from the indexer for a given namespace and name. // Objects returned here must be treated as read-only. - Get(name string) (*v1.PostgresTeam, error) + Get(name string) (*acidzalandov1.PostgresTeam, error) PostgresTeamNamespaceListerExpansion } // postgresTeamNamespaceLister implements the PostgresTeamNamespaceLister // interface. type postgresTeamNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all PostgresTeams in the indexer for a given namespace. -func (s postgresTeamNamespaceLister) List(selector labels.Selector) (ret []*v1.PostgresTeam, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1.PostgresTeam)) - }) - return ret, err -} - -// Get retrieves the PostgresTeam from the indexer for a given namespace and name. -func (s postgresTeamNamespaceLister) Get(name string) (*v1.PostgresTeam, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1.Resource("postgresteam"), name) - } - return obj.(*v1.PostgresTeam), nil + listers.ResourceIndexer[*acidzalandov1.PostgresTeam] } diff --git a/pkg/generated/listers/zalando.org/v1/fabriceventstream.go b/pkg/generated/listers/zalando.org/v1/fabriceventstream.go index 7c04027bf..044635655 100644 --- a/pkg/generated/listers/zalando.org/v1/fabriceventstream.go +++ b/pkg/generated/listers/zalando.org/v1/fabriceventstream.go @@ -25,10 +25,10 @@ SOFTWARE. package v1 import ( - v1 "github.com/zalando/postgres-operator/pkg/apis/zalando.org/v1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" + zalandoorgv1 "github.com/zalando/postgres-operator/pkg/apis/zalando.org/v1" + labels "k8s.io/apimachinery/pkg/labels" + listers "k8s.io/client-go/listers" + cache "k8s.io/client-go/tools/cache" ) // FabricEventStreamLister helps list FabricEventStreams. @@ -36,7 +36,7 @@ import ( type FabricEventStreamLister interface { // List lists all FabricEventStreams in the indexer. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1.FabricEventStream, err error) + List(selector labels.Selector) (ret []*zalandoorgv1.FabricEventStream, err error) // FabricEventStreams returns an object that can list and get FabricEventStreams. FabricEventStreams(namespace string) FabricEventStreamNamespaceLister FabricEventStreamListerExpansion @@ -44,25 +44,17 @@ type FabricEventStreamLister interface { // fabricEventStreamLister implements the FabricEventStreamLister interface. type fabricEventStreamLister struct { - indexer cache.Indexer + listers.ResourceIndexer[*zalandoorgv1.FabricEventStream] } // NewFabricEventStreamLister returns a new FabricEventStreamLister. func NewFabricEventStreamLister(indexer cache.Indexer) FabricEventStreamLister { - return &fabricEventStreamLister{indexer: indexer} -} - -// List lists all FabricEventStreams in the indexer. -func (s *fabricEventStreamLister) List(selector labels.Selector) (ret []*v1.FabricEventStream, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1.FabricEventStream)) - }) - return ret, err + return &fabricEventStreamLister{listers.New[*zalandoorgv1.FabricEventStream](indexer, zalandoorgv1.Resource("fabriceventstream"))} } // FabricEventStreams returns an object that can list and get FabricEventStreams. func (s *fabricEventStreamLister) FabricEventStreams(namespace string) FabricEventStreamNamespaceLister { - return fabricEventStreamNamespaceLister{indexer: s.indexer, namespace: namespace} + return fabricEventStreamNamespaceLister{listers.NewNamespaced[*zalandoorgv1.FabricEventStream](s.ResourceIndexer, namespace)} } // FabricEventStreamNamespaceLister helps list and get FabricEventStreams. @@ -70,36 +62,15 @@ func (s *fabricEventStreamLister) FabricEventStreams(namespace string) FabricEve type FabricEventStreamNamespaceLister interface { // List lists all FabricEventStreams in the indexer for a given namespace. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1.FabricEventStream, err error) + List(selector labels.Selector) (ret []*zalandoorgv1.FabricEventStream, err error) // Get retrieves the FabricEventStream from the indexer for a given namespace and name. // Objects returned here must be treated as read-only. - Get(name string) (*v1.FabricEventStream, error) + Get(name string) (*zalandoorgv1.FabricEventStream, error) FabricEventStreamNamespaceListerExpansion } // fabricEventStreamNamespaceLister implements the FabricEventStreamNamespaceLister // interface. type fabricEventStreamNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all FabricEventStreams in the indexer for a given namespace. -func (s fabricEventStreamNamespaceLister) List(selector labels.Selector) (ret []*v1.FabricEventStream, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1.FabricEventStream)) - }) - return ret, err -} - -// Get retrieves the FabricEventStream from the indexer for a given namespace and name. -func (s fabricEventStreamNamespaceLister) Get(name string) (*v1.FabricEventStream, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1.Resource("fabriceventstream"), name) - } - return obj.(*v1.FabricEventStream), nil + listers.ResourceIndexer[*zalandoorgv1.FabricEventStream] } diff --git a/pkg/util/httpclient/httpclient.go b/pkg/util/httpclient/httpclient.go index 706f8c5aa..80716c548 100644 --- a/pkg/util/httpclient/httpclient.go +++ b/pkg/util/httpclient/httpclient.go @@ -1,6 +1,6 @@ package httpclient -//go:generate mockgen -package mocks -destination=../../../mocks/$GOFILE -source=$GOFILE -build_flags=-mod=vendor +//go:generate go tool mockgen -package mocks -destination=../../../mocks/$GOFILE -source=$GOFILE import "net/http" diff --git a/pkg/util/volumes/volumes.go b/pkg/util/volumes/volumes.go index 5ff923920..32f68c65e 100644 --- a/pkg/util/volumes/volumes.go +++ b/pkg/util/volumes/volumes.go @@ -1,6 +1,6 @@ package volumes -//go:generate mockgen -package mocks -destination=../../../mocks/$GOFILE -source=$GOFILE -build_flags=-mod=vendor +//go:generate go tool mockgen -package mocks -destination=../../../mocks/$GOFILE -source=$GOFILE import v1 "k8s.io/api/core/v1" diff --git a/run_operator_locally.sh b/run_operator_locally.sh index 600cc2f60..47dbb6071 100755 --- a/run_operator_locally.sh +++ b/run_operator_locally.sh @@ -98,7 +98,7 @@ function build_operator_binary(){ # redirecting stderr greatly reduces non-informative output during normal builds echo "Build operator binary (stderr redirected to /dev/null)..." - make clean deps local test > /dev/null 2>&1 + make clean local test > /dev/null 2>&1 } From 55cc167fcaea2535a800172ef955d523d3205e87 Mon Sep 17 00:00:00 2001 From: Mikkel Oscar Lyderik Larsen Date: Fri, 9 Jan 2026 15:38:16 +0100 Subject: [PATCH 03/21] Regenerate code for 2026 header (#3029) Signed-off-by: Mikkel Oscar Lyderik Larsen --- pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go | 2 +- pkg/apis/zalando.org/v1/zz_generated.deepcopy.go | 2 +- pkg/generated/clientset/versioned/clientset.go | 2 +- pkg/generated/clientset/versioned/fake/clientset_generated.go | 2 +- pkg/generated/clientset/versioned/fake/doc.go | 2 +- pkg/generated/clientset/versioned/fake/register.go | 2 +- pkg/generated/clientset/versioned/scheme/doc.go | 2 +- pkg/generated/clientset/versioned/scheme/register.go | 2 +- .../versioned/typed/acid.zalan.do/v1/acid.zalan.do_client.go | 2 +- pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/doc.go | 2 +- .../clientset/versioned/typed/acid.zalan.do/v1/fake/doc.go | 2 +- .../typed/acid.zalan.do/v1/fake/fake_acid.zalan.do_client.go | 2 +- .../typed/acid.zalan.do/v1/fake/fake_operatorconfiguration.go | 2 +- .../versioned/typed/acid.zalan.do/v1/fake/fake_postgresql.go | 2 +- .../versioned/typed/acid.zalan.do/v1/fake/fake_postgresteam.go | 2 +- .../versioned/typed/acid.zalan.do/v1/generated_expansion.go | 2 +- .../versioned/typed/acid.zalan.do/v1/operatorconfiguration.go | 2 +- .../clientset/versioned/typed/acid.zalan.do/v1/postgresql.go | 2 +- .../clientset/versioned/typed/acid.zalan.do/v1/postgresteam.go | 2 +- pkg/generated/clientset/versioned/typed/zalando.org/v1/doc.go | 2 +- .../versioned/typed/zalando.org/v1/fabriceventstream.go | 2 +- .../clientset/versioned/typed/zalando.org/v1/fake/doc.go | 2 +- .../typed/zalando.org/v1/fake/fake_fabriceventstream.go | 2 +- .../typed/zalando.org/v1/fake/fake_zalando.org_client.go | 2 +- .../versioned/typed/zalando.org/v1/generated_expansion.go | 2 +- .../versioned/typed/zalando.org/v1/zalando.org_client.go | 2 +- .../informers/externalversions/acid.zalan.do/interface.go | 2 +- .../informers/externalversions/acid.zalan.do/v1/interface.go | 2 +- .../informers/externalversions/acid.zalan.do/v1/postgresql.go | 2 +- .../informers/externalversions/acid.zalan.do/v1/postgresteam.go | 2 +- pkg/generated/informers/externalversions/factory.go | 2 +- pkg/generated/informers/externalversions/generic.go | 2 +- .../externalversions/internalinterfaces/factory_interfaces.go | 2 +- .../informers/externalversions/zalando.org/interface.go | 2 +- .../externalversions/zalando.org/v1/fabriceventstream.go | 2 +- .../informers/externalversions/zalando.org/v1/interface.go | 2 +- pkg/generated/listers/acid.zalan.do/v1/expansion_generated.go | 2 +- pkg/generated/listers/acid.zalan.do/v1/postgresql.go | 2 +- pkg/generated/listers/acid.zalan.do/v1/postgresteam.go | 2 +- pkg/generated/listers/zalando.org/v1/expansion_generated.go | 2 +- pkg/generated/listers/zalando.org/v1/fabriceventstream.go | 2 +- 41 files changed, 41 insertions(+), 41 deletions(-) diff --git a/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go b/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go index 5d0a5b341..5816023c2 100644 --- a/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go +++ b/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go @@ -2,7 +2,7 @@ // +build !ignore_autogenerated /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/apis/zalando.org/v1/zz_generated.deepcopy.go b/pkg/apis/zalando.org/v1/zz_generated.deepcopy.go index 993b8d8a5..f7c555bcb 100644 --- a/pkg/apis/zalando.org/v1/zz_generated.deepcopy.go +++ b/pkg/apis/zalando.org/v1/zz_generated.deepcopy.go @@ -2,7 +2,7 @@ // +build !ignore_autogenerated /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/clientset.go b/pkg/generated/clientset/versioned/clientset.go index 6d253b7e4..e2a14718a 100644 --- a/pkg/generated/clientset/versioned/clientset.go +++ b/pkg/generated/clientset/versioned/clientset.go @@ -1,5 +1,5 @@ /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/fake/clientset_generated.go b/pkg/generated/clientset/versioned/fake/clientset_generated.go index afb3cf091..381ce23bd 100644 --- a/pkg/generated/clientset/versioned/fake/clientset_generated.go +++ b/pkg/generated/clientset/versioned/fake/clientset_generated.go @@ -1,5 +1,5 @@ /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/fake/doc.go b/pkg/generated/clientset/versioned/fake/doc.go index 7548400fa..bd1f71a60 100644 --- a/pkg/generated/clientset/versioned/fake/doc.go +++ b/pkg/generated/clientset/versioned/fake/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/fake/register.go b/pkg/generated/clientset/versioned/fake/register.go index 225705881..d6bf8f312 100644 --- a/pkg/generated/clientset/versioned/fake/register.go +++ b/pkg/generated/clientset/versioned/fake/register.go @@ -1,5 +1,5 @@ /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/scheme/doc.go b/pkg/generated/clientset/versioned/scheme/doc.go index 1f79f0496..987597d94 100644 --- a/pkg/generated/clientset/versioned/scheme/doc.go +++ b/pkg/generated/clientset/versioned/scheme/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/scheme/register.go b/pkg/generated/clientset/versioned/scheme/register.go index 6bbec0e5e..45a31ab41 100644 --- a/pkg/generated/clientset/versioned/scheme/register.go +++ b/pkg/generated/clientset/versioned/scheme/register.go @@ -1,5 +1,5 @@ /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/acid.zalan.do_client.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/acid.zalan.do_client.go index 5a094d62f..b53b029fb 100644 --- a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/acid.zalan.do_client.go +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/acid.zalan.do_client.go @@ -1,5 +1,5 @@ /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/doc.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/doc.go index 5c6f06565..5d2970a5f 100644 --- a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/doc.go +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/doc.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/doc.go index 63b4b5b8f..5f237162a 100644 --- a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/doc.go +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_acid.zalan.do_client.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_acid.zalan.do_client.go index 68e1c1eec..643b8fd26 100644 --- a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_acid.zalan.do_client.go +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_acid.zalan.do_client.go @@ -1,5 +1,5 @@ /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_operatorconfiguration.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_operatorconfiguration.go index a4b8ea2b7..8c9790d18 100644 --- a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_operatorconfiguration.go +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_operatorconfiguration.go @@ -1,5 +1,5 @@ /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_postgresql.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_postgresql.go index ebad23d3d..b2ac1e9e1 100644 --- a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_postgresql.go +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_postgresql.go @@ -1,5 +1,5 @@ /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_postgresteam.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_postgresteam.go index 2d8f3338f..3067aac18 100644 --- a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_postgresteam.go +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_postgresteam.go @@ -1,5 +1,5 @@ /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/generated_expansion.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/generated_expansion.go index 8a5e126d7..228ef2924 100644 --- a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/generated_expansion.go +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/generated_expansion.go @@ -1,5 +1,5 @@ /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/operatorconfiguration.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/operatorconfiguration.go index b11f28dde..91dc27037 100644 --- a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/operatorconfiguration.go +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/operatorconfiguration.go @@ -1,5 +1,5 @@ /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/postgresql.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/postgresql.go index 40abf69bb..75793c57a 100644 --- a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/postgresql.go +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/postgresql.go @@ -1,5 +1,5 @@ /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/postgresteam.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/postgresteam.go index 89005b5ed..99df6b6cc 100644 --- a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/postgresteam.go +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/postgresteam.go @@ -1,5 +1,5 @@ /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/typed/zalando.org/v1/doc.go b/pkg/generated/clientset/versioned/typed/zalando.org/v1/doc.go index 5c6f06565..5d2970a5f 100644 --- a/pkg/generated/clientset/versioned/typed/zalando.org/v1/doc.go +++ b/pkg/generated/clientset/versioned/typed/zalando.org/v1/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/typed/zalando.org/v1/fabriceventstream.go b/pkg/generated/clientset/versioned/typed/zalando.org/v1/fabriceventstream.go index 8a342cb45..2681ac467 100644 --- a/pkg/generated/clientset/versioned/typed/zalando.org/v1/fabriceventstream.go +++ b/pkg/generated/clientset/versioned/typed/zalando.org/v1/fabriceventstream.go @@ -1,5 +1,5 @@ /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/typed/zalando.org/v1/fake/doc.go b/pkg/generated/clientset/versioned/typed/zalando.org/v1/fake/doc.go index 63b4b5b8f..5f237162a 100644 --- a/pkg/generated/clientset/versioned/typed/zalando.org/v1/fake/doc.go +++ b/pkg/generated/clientset/versioned/typed/zalando.org/v1/fake/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/typed/zalando.org/v1/fake/fake_fabriceventstream.go b/pkg/generated/clientset/versioned/typed/zalando.org/v1/fake/fake_fabriceventstream.go index b273891a1..c3ae49663 100644 --- a/pkg/generated/clientset/versioned/typed/zalando.org/v1/fake/fake_fabriceventstream.go +++ b/pkg/generated/clientset/versioned/typed/zalando.org/v1/fake/fake_fabriceventstream.go @@ -1,5 +1,5 @@ /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/typed/zalando.org/v1/fake/fake_zalando.org_client.go b/pkg/generated/clientset/versioned/typed/zalando.org/v1/fake/fake_zalando.org_client.go index d0eefecf1..588a2bb94 100644 --- a/pkg/generated/clientset/versioned/typed/zalando.org/v1/fake/fake_zalando.org_client.go +++ b/pkg/generated/clientset/versioned/typed/zalando.org/v1/fake/fake_zalando.org_client.go @@ -1,5 +1,5 @@ /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/typed/zalando.org/v1/generated_expansion.go b/pkg/generated/clientset/versioned/typed/zalando.org/v1/generated_expansion.go index 4d1d3e37e..014071418 100644 --- a/pkg/generated/clientset/versioned/typed/zalando.org/v1/generated_expansion.go +++ b/pkg/generated/clientset/versioned/typed/zalando.org/v1/generated_expansion.go @@ -1,5 +1,5 @@ /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/typed/zalando.org/v1/zalando.org_client.go b/pkg/generated/clientset/versioned/typed/zalando.org/v1/zalando.org_client.go index a3947464e..99d795d76 100644 --- a/pkg/generated/clientset/versioned/typed/zalando.org/v1/zalando.org_client.go +++ b/pkg/generated/clientset/versioned/typed/zalando.org/v1/zalando.org_client.go @@ -1,5 +1,5 @@ /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/informers/externalversions/acid.zalan.do/interface.go b/pkg/generated/informers/externalversions/acid.zalan.do/interface.go index 74f5b0458..a75e6ae2b 100644 --- a/pkg/generated/informers/externalversions/acid.zalan.do/interface.go +++ b/pkg/generated/informers/externalversions/acid.zalan.do/interface.go @@ -1,5 +1,5 @@ /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/informers/externalversions/acid.zalan.do/v1/interface.go b/pkg/generated/informers/externalversions/acid.zalan.do/v1/interface.go index 24950b6fd..1ea652a3f 100644 --- a/pkg/generated/informers/externalversions/acid.zalan.do/v1/interface.go +++ b/pkg/generated/informers/externalversions/acid.zalan.do/v1/interface.go @@ -1,5 +1,5 @@ /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/informers/externalversions/acid.zalan.do/v1/postgresql.go b/pkg/generated/informers/externalversions/acid.zalan.do/v1/postgresql.go index f44f0f143..1601f2bd6 100644 --- a/pkg/generated/informers/externalversions/acid.zalan.do/v1/postgresql.go +++ b/pkg/generated/informers/externalversions/acid.zalan.do/v1/postgresql.go @@ -1,5 +1,5 @@ /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/informers/externalversions/acid.zalan.do/v1/postgresteam.go b/pkg/generated/informers/externalversions/acid.zalan.do/v1/postgresteam.go index 0aed5893f..b53862c78 100644 --- a/pkg/generated/informers/externalversions/acid.zalan.do/v1/postgresteam.go +++ b/pkg/generated/informers/externalversions/acid.zalan.do/v1/postgresteam.go @@ -1,5 +1,5 @@ /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/informers/externalversions/factory.go b/pkg/generated/informers/externalversions/factory.go index eee1ed98b..d25563014 100644 --- a/pkg/generated/informers/externalversions/factory.go +++ b/pkg/generated/informers/externalversions/factory.go @@ -1,5 +1,5 @@ /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/informers/externalversions/generic.go b/pkg/generated/informers/externalversions/generic.go index 9f83e0bd6..ed27d5743 100644 --- a/pkg/generated/informers/externalversions/generic.go +++ b/pkg/generated/informers/externalversions/generic.go @@ -1,5 +1,5 @@ /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go b/pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go index a5d7b2299..2037af01e 100644 --- a/pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go +++ b/pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go @@ -1,5 +1,5 @@ /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/informers/externalversions/zalando.org/interface.go b/pkg/generated/informers/externalversions/zalando.org/interface.go index aab6846cb..a05b7ea7d 100644 --- a/pkg/generated/informers/externalversions/zalando.org/interface.go +++ b/pkg/generated/informers/externalversions/zalando.org/interface.go @@ -1,5 +1,5 @@ /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/informers/externalversions/zalando.org/v1/fabriceventstream.go b/pkg/generated/informers/externalversions/zalando.org/v1/fabriceventstream.go index 7f3a0bc23..264ebb985 100644 --- a/pkg/generated/informers/externalversions/zalando.org/v1/fabriceventstream.go +++ b/pkg/generated/informers/externalversions/zalando.org/v1/fabriceventstream.go @@ -1,5 +1,5 @@ /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/informers/externalversions/zalando.org/v1/interface.go b/pkg/generated/informers/externalversions/zalando.org/v1/interface.go index 3b61f68a1..825e5f4a4 100644 --- a/pkg/generated/informers/externalversions/zalando.org/v1/interface.go +++ b/pkg/generated/informers/externalversions/zalando.org/v1/interface.go @@ -1,5 +1,5 @@ /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/listers/acid.zalan.do/v1/expansion_generated.go b/pkg/generated/listers/acid.zalan.do/v1/expansion_generated.go index dff5ce3f1..b71f44767 100644 --- a/pkg/generated/listers/acid.zalan.do/v1/expansion_generated.go +++ b/pkg/generated/listers/acid.zalan.do/v1/expansion_generated.go @@ -1,5 +1,5 @@ /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/listers/acid.zalan.do/v1/postgresql.go b/pkg/generated/listers/acid.zalan.do/v1/postgresql.go index 0ee5bcf4f..b2fe09749 100644 --- a/pkg/generated/listers/acid.zalan.do/v1/postgresql.go +++ b/pkg/generated/listers/acid.zalan.do/v1/postgresql.go @@ -1,5 +1,5 @@ /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/listers/acid.zalan.do/v1/postgresteam.go b/pkg/generated/listers/acid.zalan.do/v1/postgresteam.go index dbbea2918..74fcc81d0 100644 --- a/pkg/generated/listers/acid.zalan.do/v1/postgresteam.go +++ b/pkg/generated/listers/acid.zalan.do/v1/postgresteam.go @@ -1,5 +1,5 @@ /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/listers/zalando.org/v1/expansion_generated.go b/pkg/generated/listers/zalando.org/v1/expansion_generated.go index 201fa4ecf..95dfc74cf 100644 --- a/pkg/generated/listers/zalando.org/v1/expansion_generated.go +++ b/pkg/generated/listers/zalando.org/v1/expansion_generated.go @@ -1,5 +1,5 @@ /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/listers/zalando.org/v1/fabriceventstream.go b/pkg/generated/listers/zalando.org/v1/fabriceventstream.go index 044635655..25e57e56e 100644 --- a/pkg/generated/listers/zalando.org/v1/fabriceventstream.go +++ b/pkg/generated/listers/zalando.org/v1/fabriceventstream.go @@ -1,5 +1,5 @@ /* -Copyright 2025 Compose, Zalando SE +Copyright 2026 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From c331fd94340c6f9836f247f4a8a4dc916ad63f24 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 10 Jan 2026 09:22:44 +0100 Subject: [PATCH 04/21] Bump werkzeug from 3.1.4 to 3.1.5 in /ui (#3028) Bumps [werkzeug](https://github.com/pallets/werkzeug) from 3.1.4 to 3.1.5. - [Release notes](https://github.com/pallets/werkzeug/releases) - [Changelog](https://github.com/pallets/werkzeug/blob/main/CHANGES.rst) - [Commits](https://github.com/pallets/werkzeug/compare/3.1.4...3.1.5) --- updated-dependencies: - dependency-name: werkzeug dependency-version: 3.1.5 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ui/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/requirements.txt b/ui/requirements.txt index eaeafe3c1..2e43ccb0e 100644 --- a/ui/requirements.txt +++ b/ui/requirements.txt @@ -11,4 +11,4 @@ kubernetes==11.0.0 python-json-logger==2.0.7 requests==2.32.4 stups-tokens>=1.1.19 -werkzeug==3.1.4 +werkzeug==3.1.5 From 0a44252534686ecfd7f8012bf214c95215b84d4b Mon Sep 17 00:00:00 2001 From: Mikkel Oscar Lyderik Larsen Date: Sat, 10 Jan 2026 19:39:08 +0100 Subject: [PATCH 05/21] Generate CRD for postgresteam resource (#3004) * Sort postgresteam.crd.yaml Signed-off-by: Mikkel Oscar Lyderik Larsen * Generate CRD for postgresteam resource Signed-off-by: Mikkel Oscar Lyderik Larsen --------- Signed-off-by: Mikkel Oscar Lyderik Larsen Co-authored-by: Felix Kunde --- Makefile | 14 ++- go.mod | 10 +- go.sum | 35 +++++- manifests/postgresteam.crd.yaml | 112 ++++++++++-------- .../acid.zalan.do/v1/postgres_team_type.go | 23 +++- .../acid.zalan.do/v1/zz_generated.deepcopy.go | 72 ++++++++++- pkg/spec/types.go | 39 +++++- pkg/teams/postgres_team.go | 14 ++- pkg/teams/postgres_team_test.go | 18 +-- 9 files changed, 252 insertions(+), 85 deletions(-) diff --git a/Makefile b/Makefile index f00f6039d..c0e2e8e34 100644 --- a/Makefile +++ b/Makefile @@ -21,6 +21,7 @@ GITSTATUS = $(shell git status --porcelain || echo "no changes") SOURCES = cmd/main.go VERSION ?= $(shell git describe --tags --always --dirty) CRD_SOURCES = $(shell find pkg/apis/zalando.org pkg/apis/acid.zalan.do -name '*.go' -not -name '*.deepcopy.go') +GENERATED_CRDS = manifests/postgresteam.crd.yaml GENERATED = pkg/apis/zalando.org/v1/zz_generated.deepcopy.go pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go DIRS := cmd pkg PKG := `go list ./... | grep -v /vendor/` @@ -60,13 +61,20 @@ verify: $(GENERATED): go.mod $(CRD_SOURCES) hack/update-codegen.sh -local: ${SOURCES} $(GENERATED) +$(GENERATED_CRDS): $(GENERATED) + go tool controller-gen crd:crdVersions=v1,allowDangerousTypes=true paths=./pkg/apis/acid.zalan.do/... output:crd:dir=manifests + # only generate postgresteam.crd.yaml for now + @rm manifests/acid.zalan.do_operatorconfigurations.yaml + @rm manifests/acid.zalan.do_postgresqls.yaml + @mv manifests/acid.zalan.do_postgresteams.yaml manifests/postgresteam.crd.yaml + +local: ${SOURCES} $(GENERATED_CRDS) CGO_ENABLED=${CGO_ENABLED} go build -o build/${BINARY} $(LOCAL_BUILD_FLAGS) -ldflags "$(LDFLAGS)" $(SOURCES) -linux: ${SOURCES} $(GENERATED) +linux: ${SOURCES} $(GENERATED_CRDS) GOOS=linux GOARCH=amd64 CGO_ENABLED=${CGO_ENABLED} go build -o build/linux/${BINARY} ${BUILD_FLAGS} -ldflags "$(LDFLAGS)" $(SOURCES) -macos: ${SOURCES} $(GENERATED) +macos: ${SOURCES} $(GENERATED_CRDS) GOOS=darwin GOARCH=amd64 CGO_ENABLED=${CGO_ENABLED} go build -o build/macos/${BINARY} ${BUILD_FLAGS} -ldflags "$(LDFLAGS)" $(SOURCES) docker: ${DOCKERDIR}/${DOCKERFILE} diff --git a/go.mod b/go.mod index da25c79ca..08966bd95 100644 --- a/go.mod +++ b/go.mod @@ -23,11 +23,13 @@ require ( require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/fatih/color v1.18.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.23.0 // indirect + github.com/gobuffalo/flect v1.0.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/gnostic-models v0.6.9 // indirect @@ -35,18 +37,22 @@ require ( github.com/google/gofuzz v1.2.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kr/text v0.2.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/moby/spdystream v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/cobra v1.9.1 // indirect + github.com/spf13/pflag v1.0.6 // indirect github.com/x448/float16 v0.8.4 // indirect golang.org/x/mod v0.29.0 // indirect golang.org/x/net v0.47.0 // indirect @@ -67,6 +73,7 @@ require ( k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect + sigs.k8s.io/controller-tools v0.17.3 // indirect sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect @@ -80,4 +87,5 @@ tool ( k8s.io/code-generator/cmd/deepcopy-gen k8s.io/code-generator/cmd/informer-gen k8s.io/code-generator/cmd/lister-gen + sigs.k8s.io/controller-tools/cmd/controller-gen ) diff --git a/go.sum b/go.sum index fcd66ba43..463b37211 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,7 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aws/aws-sdk-go v1.55.8 h1:JRmEUbU52aJQZ2AjX4q4Wu7t4uZjOu71uyNmaWlUkJQ= github.com/aws/aws-sdk-go v1.55.8/go.mod h1:ZkViS9AqA6otK+JBBNH2++sx1sgxrPKcSzPPvQkUtXk= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -11,6 +12,10 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= @@ -25,6 +30,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/gobuffalo/flect v1.0.3 h1:xeWBM2nui+qnVvNM4S3foBhCAL2XgPU+a7FdpelbTq4= +github.com/gobuffalo/flect v1.0.3/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= @@ -45,6 +52,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.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= @@ -66,6 +75,11 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 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= @@ -79,10 +93,14 @@ 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/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= -github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= -github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= +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/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -92,10 +110,13 @@ github.com/r3labs/diff v1.1.0 h1:V53xhrbTHrWFWq3gI4b94AjgEJOerO1+1l0xyHOBi8M= github.com/r3labs/diff v1.1.0/go.mod h1:7WjXasNzi0vJetRcB/RqNl5dlIsmXcTTLmF5IoH6Xig= 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/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -146,6 +167,8 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -181,6 +204,8 @@ 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/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.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.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= @@ -206,6 +231,8 @@ k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUy k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/controller-tools v0.17.3 h1:lwFPLicpBKLgIepah+c8ikRBubFW5kOQyT88r3EwfNw= +sigs.k8s.io/controller-tools v0.17.3/go.mod h1:1ii+oXcYZkxcBXzwv3YZBlzjt1fvkrCGjVF73blosJI= sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= diff --git a/manifests/postgresteam.crd.yaml b/manifests/postgresteam.crd.yaml index 2588e53b1..2245c6253 100644 --- a/manifests/postgresteam.crd.yaml +++ b/manifests/postgresteam.crd.yaml @@ -1,68 +1,82 @@ +--- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.17.3 name: postgresteams.acid.zalan.do spec: group: acid.zalan.do names: + categories: + - all kind: PostgresTeam listKind: PostgresTeamList plural: postgresteams - singular: postgresteam shortNames: - pgteam - categories: - - all + singular: postgresteam scope: Namespaced versions: - name: v1 + schema: + openAPIV3Schema: + description: PostgresTeam defines Custom Resource Definition Object for team + management. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: PostgresTeamSpec defines the specification for the PostgresTeam + TPR. + properties: + additionalMembers: + additionalProperties: + description: List of users who will also be added to the Postgres + cluster. + items: + type: string + type: array + description: Map for teamId and associated additional users + type: object + additionalSuperuserTeams: + additionalProperties: + description: List of teams to become Postgres superusers + items: + type: string + type: array + description: Map for teamId and associated additional superuser teams + type: object + additionalTeams: + additionalProperties: + description: List of teams whose members will also be added to the + Postgres cluster. + items: + type: string + type: array + description: Map for teamId and associated additional teams + type: object + type: object + required: + - metadata + - spec + type: object served: true storage: true subresources: status: {} - schema: - openAPIV3Schema: - type: object - required: - - kind - - apiVersion - - spec - properties: - kind: - type: string - enum: - - PostgresTeam - apiVersion: - type: string - enum: - - acid.zalan.do/v1 - spec: - type: object - properties: - additionalSuperuserTeams: - type: object - description: "Map for teamId and associated additional superuser teams" - additionalProperties: - type: array - nullable: true - description: "List of teams to become Postgres superusers" - items: - type: string - additionalTeams: - type: object - description: "Map for teamId and associated additional teams" - additionalProperties: - type: array - nullable: true - description: "List of teams whose members will also be added to the Postgres cluster" - items: - type: string - additionalMembers: - type: object - description: "Map for teamId and associated additional users" - additionalProperties: - type: array - nullable: true - description: "List of users who will also be added to the Postgres cluster" - items: - type: string diff --git a/pkg/apis/acid.zalan.do/v1/postgres_team_type.go b/pkg/apis/acid.zalan.do/v1/postgres_team_type.go index 5697c193e..ffedaef57 100644 --- a/pkg/apis/acid.zalan.do/v1/postgres_team_type.go +++ b/pkg/apis/acid.zalan.do/v1/postgres_team_type.go @@ -8,18 +8,33 @@ import ( // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // PostgresTeam defines Custom Resource Definition Object for team management. +// +k8s:deepcopy-gen=true +// +kubebuilder:resource:shortName=pgteam,categories=all +// +kubebuilder:subresource:status type PostgresTeam struct { metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` + metav1.ObjectMeta `json:"metadata"` Spec PostgresTeamSpec `json:"spec"` } +// List of users who will also be added to the Postgres cluster. +type Users []string + +// List of teams whose members will also be added to the Postgres cluster. +type Teams []string + +// List of teams to become Postgres superusers +type SuperUserTeams []string + // PostgresTeamSpec defines the specification for the PostgresTeam TPR. type PostgresTeamSpec struct { - AdditionalSuperuserTeams map[string][]string `json:"additionalSuperuserTeams,omitempty"` - AdditionalTeams map[string][]string `json:"additionalTeams,omitempty"` - AdditionalMembers map[string][]string `json:"additionalMembers,omitempty"` + // Map for teamId and associated additional superuser teams + AdditionalSuperuserTeams map[string]SuperUserTeams `json:"additionalSuperuserTeams,omitempty"` + // Map for teamId and associated additional teams + AdditionalTeams map[string]Teams `json:"additionalTeams,omitempty"` + // Map for teamId and associated additional users + AdditionalMembers map[string]Users `json:"additionalMembers,omitempty"` } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go b/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go index 5816023c2..79e0787bc 100644 --- a/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go +++ b/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go @@ -975,14 +975,14 @@ func (in *PostgresTeamSpec) DeepCopyInto(out *PostgresTeamSpec) { *out = *in if in.AdditionalSuperuserTeams != nil { in, out := &in.AdditionalSuperuserTeams, &out.AdditionalSuperuserTeams - *out = make(map[string][]string, len(*in)) + *out = make(map[string]SuperUserTeams, len(*in)) for key, val := range *in { var outVal []string if val == nil { (*out)[key] = nil } else { in, out := &val, &outVal - *out = make([]string, len(*in)) + *out = make(SuperUserTeams, len(*in)) copy(*out, *in) } (*out)[key] = outVal @@ -990,14 +990,14 @@ func (in *PostgresTeamSpec) DeepCopyInto(out *PostgresTeamSpec) { } if in.AdditionalTeams != nil { in, out := &in.AdditionalTeams, &out.AdditionalTeams - *out = make(map[string][]string, len(*in)) + *out = make(map[string]Teams, len(*in)) for key, val := range *in { var outVal []string if val == nil { (*out)[key] = nil } else { in, out := &val, &outVal - *out = make([]string, len(*in)) + *out = make(Teams, len(*in)) copy(*out, *in) } (*out)[key] = outVal @@ -1005,14 +1005,14 @@ func (in *PostgresTeamSpec) DeepCopyInto(out *PostgresTeamSpec) { } if in.AdditionalMembers != nil { in, out := &in.AdditionalMembers, &out.AdditionalMembers - *out = make(map[string][]string, len(*in)) + *out = make(map[string]Users, len(*in)) for key, val := range *in { var outVal []string if val == nil { (*out)[key] = nil } else { in, out := &val, &outVal - *out = make([]string, len(*in)) + *out = make(Users, len(*in)) copy(*out, *in) } (*out)[key] = outVal @@ -1400,6 +1400,26 @@ func (in *StreamTable) DeepCopy() *StreamTable { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in SuperUserTeams) DeepCopyInto(out *SuperUserTeams) { + { + in := &in + *out = make(SuperUserTeams, len(*in)) + copy(*out, *in) + return + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SuperUserTeams. +func (in SuperUserTeams) DeepCopy() SuperUserTeams { + if in == nil { + return nil + } + out := new(SuperUserTeams) + in.DeepCopyInto(out) + return *out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TLSDescription) DeepCopyInto(out *TLSDescription) { *out = *in @@ -1416,6 +1436,26 @@ func (in *TLSDescription) DeepCopy() *TLSDescription { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in Teams) DeepCopyInto(out *Teams) { + { + in := &in + *out = make(Teams, len(*in)) + copy(*out, *in) + return + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Teams. +func (in Teams) DeepCopy() Teams { + if in == nil { + return nil + } + out := new(Teams) + in.DeepCopyInto(out) + return *out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TeamsAPIConfiguration) DeepCopyInto(out *TeamsAPIConfiguration) { *out = *in @@ -1469,6 +1509,26 @@ func (in UserFlags) DeepCopy() UserFlags { return *out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in Users) DeepCopyInto(out *Users) { + { + in := &in + *out = make(Users, len(*in)) + copy(*out, *in) + return + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Users. +func (in Users) DeepCopy() Users { + if in == nil { + return nil + } + out := new(Users) + in.DeepCopyInto(out) + return *out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Volume) DeepCopyInto(out *Volume) { *out = *in diff --git a/pkg/spec/types.go b/pkg/spec/types.go index c08cc5c61..3f4abb70f 100644 --- a/pkg/spec/types.go +++ b/pkg/spec/types.go @@ -14,8 +14,39 @@ import ( "k8s.io/client-go/rest" ) -// NamespacedName describes the namespace/name pairs used in Kubernetes names. -type NamespacedName types.NamespacedName +// NamespacedName comprises a resource name, with a mandatory namespace, +// rendered as "/". Being a type captures intent and +// helps make sure that UIDs, namespaced names and non-namespaced names +// do not get conflated in code. For most use cases, namespace and name +// will already have been format validated at the API entry point, so we +// don't do that here. Where that's not the case (e.g. in testing), +// consider using NamespacedNameOrDie() in testing.go in this package. +// +// from: https://github.com/kubernetes/apimachinery/blob/master/pkg/types/namespacedname.go +type NamespacedName struct { + Namespace string `json:"namespace,omitempty"` + Name string `json:"name"` +} + +const ( + Separator = '/' +) + +// String returns the general purpose string representation +func (n NamespacedName) String() string { + return n.Namespace + string(Separator) + n.Name +} + +// MarshalLog emits a struct containing required key/value pair +func (n NamespacedName) MarshalLog() interface{} { + return struct { + Name string `json:"name"` + Namespace string `json:"namespace,omitempty"` + }{ + Name: n.Name, + Namespace: n.Namespace, + } +} const fileWithNamespace = "/var/run/secrets/kubernetes.io/serviceaccount/namespace" @@ -131,10 +162,6 @@ type ControllerConfig struct { // cached value for the GetOperatorNamespace var operatorNamespace string -func (n NamespacedName) String() string { - return types.NamespacedName(n).String() -} - // MarshalJSON defines marshaling rule for the namespaced name type. func (n NamespacedName) MarshalJSON() ([]byte, error) { return []byte("\"" + n.String() + "\""), nil diff --git a/pkg/teams/postgres_team.go b/pkg/teams/postgres_team.go index 856bd71d4..b682585ab 100644 --- a/pkg/teams/postgres_team.go +++ b/pkg/teams/postgres_team.go @@ -44,6 +44,14 @@ func (ths *teamHashSet) toMap() map[string][]string { return newTeamMap } +func mapStringSliceToStringSliceMap[T ~[]string](input map[string]T) map[string][]string { + output := make(map[string][]string) + for k, v := range input { + output[k] = []string(v) + } + return output +} + func (ths *teamHashSet) mergeCrdMap(crdTeamMap map[string][]string) { for t, at := range crdTeamMap { ths.add(t, at) @@ -110,9 +118,9 @@ func (ptm *PostgresTeamMap) Load(pgTeams *acidv1.PostgresTeamList) { teamIDs := make(map[string]struct{}) for _, pgTeam := range pgTeams.Items { - superuserTeamSet.mergeCrdMap(pgTeam.Spec.AdditionalSuperuserTeams) - teamSet.mergeCrdMap(pgTeam.Spec.AdditionalTeams) - teamMemberSet.mergeCrdMap(pgTeam.Spec.AdditionalMembers) + superuserTeamSet.mergeCrdMap(mapStringSliceToStringSliceMap(pgTeam.Spec.AdditionalSuperuserTeams)) + teamSet.mergeCrdMap(mapStringSliceToStringSliceMap(pgTeam.Spec.AdditionalTeams)) + teamMemberSet.mergeCrdMap(mapStringSliceToStringSliceMap(pgTeam.Spec.AdditionalMembers)) } fetchTeams(&teamIDs, superuserTeamSet) fetchTeams(&teamIDs, teamSet) diff --git a/pkg/teams/postgres_team_test.go b/pkg/teams/postgres_team_test.go index 29a00bb84..fe45ade05 100644 --- a/pkg/teams/postgres_team_test.go +++ b/pkg/teams/postgres_team_test.go @@ -24,9 +24,9 @@ var ( Name: "teamAB", }, Spec: acidv1.PostgresTeamSpec{ - AdditionalSuperuserTeams: map[string][]string{"teamA": []string{"teamB", "team24x7"}, "teamB": []string{"teamA", "teamC", "team24x7"}}, - AdditionalTeams: map[string][]string{"teamA": []string{"teamC"}, "teamB": []string{}}, - AdditionalMembers: map[string][]string{"team24x7": []string{"optimusprime"}, "teamB": []string{"drno"}}, + AdditionalSuperuserTeams: map[string]acidv1.SuperUserTeams{"teamA": []string{"teamB", "team24x7"}, "teamB": []string{"teamA", "teamC", "team24x7"}}, + AdditionalTeams: map[string]acidv1.Teams{"teamA": []string{"teamC"}, "teamB": []string{}}, + AdditionalMembers: map[string]acidv1.Users{"team24x7": []string{"optimusprime"}, "teamB": []string{"drno"}}, }, }, { TypeMeta: metav1.TypeMeta{ @@ -37,9 +37,9 @@ var ( Name: "teamC", }, Spec: acidv1.PostgresTeamSpec{ - AdditionalSuperuserTeams: map[string][]string{"teamC": []string{"team24x7"}}, - AdditionalTeams: map[string][]string{"teamA": []string{"teamC"}, "teamC": []string{"teamA", "teamB", "acid"}}, - AdditionalMembers: map[string][]string{"acid": []string{"batman"}}, + AdditionalSuperuserTeams: map[string]acidv1.SuperUserTeams{"teamC": []string{"team24x7"}}, + AdditionalTeams: map[string]acidv1.Teams{"teamA": []string{"teamC"}, "teamC": []string{"teamA", "teamB", "acid"}}, + AdditionalMembers: map[string]acidv1.Users{"acid": []string{"batman"}}, }, }, { @@ -51,9 +51,9 @@ var ( Name: "teamD", }, Spec: acidv1.PostgresTeamSpec{ - AdditionalSuperuserTeams: map[string][]string{}, - AdditionalTeams: map[string][]string{"teamA": []string{"teamD"}, "teamC": []string{"teamD"}, "teamD": []string{"teamA", "teamB", "teamC"}}, - AdditionalMembers: map[string][]string{"acid": []string{"batman"}}, + AdditionalSuperuserTeams: map[string]acidv1.SuperUserTeams{}, + AdditionalTeams: map[string]acidv1.Teams{"teamA": []string{"teamD"}, "teamC": []string{"teamD"}, "teamD": []string{"teamA", "teamB", "teamC"}}, + AdditionalMembers: map[string]acidv1.Users{"acid": []string{"batman"}}, }, }, }, From a585b177960d8803aad358b8cd8656748d03aad8 Mon Sep 17 00:00:00 2001 From: Mikkel Oscar Lyderik Larsen Date: Mon, 12 Jan 2026 17:33:28 +0100 Subject: [PATCH 06/21] Generate postgresql CRD from go structs (#3007) * Sort postgresql.crd.yaml * Generate postgresql CRD from go structs * Expand sidecars, env and initcontainers * Embed CRD to be submitted by the operator Signed-off-by: Mikkel Oscar Lyderik Larsen --------- Signed-off-by: Mikkel Oscar Lyderik Larsen --- .gitignore | 3 + Makefile | 15 +- hack/adjust_postgresql_crd.sh | 22 + manifests/postgresql.crd.yaml | 4181 ++++++++++++++++-- pkg/apis/acid.zalan.do/v1/crds.go | 1081 +---- pkg/apis/acid.zalan.do/v1/postgresql_type.go | 184 +- pkg/cluster/k8sres.go | 2 +- pkg/controller/util.go | 6 +- 8 files changed, 4036 insertions(+), 1458 deletions(-) create mode 100755 hack/adjust_postgresql_crd.sh diff --git a/.gitignore b/.gitignore index 5938db216..6ee090456 100644 --- a/.gitignore +++ b/.gitignore @@ -106,3 +106,6 @@ mocks ui/.npm/ .DS_Store + +# temp build files +pkg/apis/acid.zalan.do/v1/postgresql.crd.yaml diff --git a/Makefile b/Makefile index c0e2e8e34..241900a55 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ GITSTATUS = $(shell git status --porcelain || echo "no changes") SOURCES = cmd/main.go VERSION ?= $(shell git describe --tags --always --dirty) CRD_SOURCES = $(shell find pkg/apis/zalando.org pkg/apis/acid.zalan.do -name '*.go' -not -name '*.deepcopy.go') -GENERATED_CRDS = manifests/postgresteam.crd.yaml +GENERATED_CRDS = manifests/postgresteam.crd.yaml manifests/postgresql.crd.yaml GENERATED = pkg/apis/zalando.org/v1/zz_generated.deepcopy.go pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go DIRS := cmd pkg PKG := `go list ./... | grep -v /vendor/` @@ -54,6 +54,8 @@ default: local clean: rm -rf build + rm $(GENERATED) + rm pkg/apis/acid.zalan.do/v1/postgresql.crd.yaml verify: hack/verify-codegen.sh @@ -63,10 +65,15 @@ $(GENERATED): go.mod $(CRD_SOURCES) $(GENERATED_CRDS): $(GENERATED) go tool controller-gen crd:crdVersions=v1,allowDangerousTypes=true paths=./pkg/apis/acid.zalan.do/... output:crd:dir=manifests - # only generate postgresteam.crd.yaml for now + # only generate postgresteam.crd.yaml and postgresql.crd.yaml for now @rm manifests/acid.zalan.do_operatorconfigurations.yaml - @rm manifests/acid.zalan.do_postgresqls.yaml + @mv manifests/acid.zalan.do_postgresqls.yaml manifests/postgresql.crd.yaml + @# hack to use lowercase kind and listKind + @sed -i -e 's/kind: Postgresql/kind: postgresql/' manifests/postgresql.crd.yaml + @sed -i -e 's/listKind: PostgresqlList/listKind: postgresqlList/' manifests/postgresql.crd.yaml + @hack/adjust_postgresql_crd.sh @mv manifests/acid.zalan.do_postgresteams.yaml manifests/postgresteam.crd.yaml + @cp manifests/postgresql.crd.yaml pkg/apis/acid.zalan.do/v1/postgresql.crd.yaml local: ${SOURCES} $(GENERATED_CRDS) CGO_ENABLED=${CGO_ENABLED} go build -o build/${BINARY} $(LOCAL_BUILD_FLAGS) -ldflags "$(LDFLAGS)" $(SOURCES) @@ -98,7 +105,7 @@ vet: @go vet $(PKG) @staticcheck $(PKG) -test: mocks $(GENERATED) +test: mocks $(GENERATED) $(GENERATED_CRDS) GO111MODULE=on go test ./... codegen: $(GENERATED) diff --git a/hack/adjust_postgresql_crd.sh b/hack/adjust_postgresql_crd.sh new file mode 100755 index 000000000..cceb33f64 --- /dev/null +++ b/hack/adjust_postgresql_crd.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +# Hack to adjust the generated postgresql CRD YAML file and add missing field +# settings which can not be expressed via kubebuilder markers. +# +# Injections: +# +# * oneOf: for the standby field to enforce that only one of s3_wal_path, gs_wal_path or standby_host is set. +# * This can later be done with // +kubebuilder:validation:ExactlyOneOf marker, but this requires latest Kubernetes version. (Currently the operator depends on v1.32.9) +# * type: string and pattern for the maintenanceWindows items. + +file="${1:-"manifests/postgresql.crd.yaml"}" + +sed -i '/^[[:space:]]*standby:$/{ + # Capture the indentation + s/^\([[:space:]]*\)standby:$/\1standby:\n\1 oneOf:\n\1 - required:\n\1 - s3_wal_path\n\1 - required:\n\1 - gs_wal_path\n\1 - required:\n\1 - standby_host/ +}' "$file" + +sed -i '/^[[:space:]]*maintenanceWindows:$/{ + # Capture the indentation + s/^\([[:space:]]*\)maintenanceWindows:$/\1maintenanceWindows:\n\1 items:\n\1 pattern: '\''^\\ *((Mon|Tue|Wed|Thu|Fri|Sat|Sun):(2[0-3]|[01]?\\d):([0-5]?\\d)|(2[0-3]|[01]?\\d):([0-5]?\\d))-((2[0-3]|[01]?\\d):([0-5]?\\d)|(2[0-3]|[01]?\\d):([0-5]?\\d))\\ *$'\''\n\1 type: string/ +}' "$file" diff --git a/manifests/postgresql.crd.yaml b/manifests/postgresql.crd.yaml index 7a1b21a4d..44e6a1def 100644 --- a/manifests/postgresql.crd.yaml +++ b/manifests/postgresql.crd.yaml @@ -1,195 +1,283 @@ +--- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.17.3 name: postgresqls.acid.zalan.do spec: group: acid.zalan.do names: + categories: + - all kind: postgresql listKind: postgresqlList plural: postgresqls - singular: postgresql shortNames: - pg - categories: - - all + singular: postgresql scope: Namespaced versions: - - name: v1 - served: true - storage: true - subresources: - status: {} - additionalPrinterColumns: - - name: Team - type: string - description: Team responsible for Postgres cluster + - additionalPrinterColumns: + - description: Team responsible for Postgres cluster jsonPath: .spec.teamId - - name: Version + name: Team type: string - description: PostgreSQL version + - description: PostgreSQL version jsonPath: .spec.postgresql.version - - name: Pods - type: integer - description: Number of Pods per Postgres cluster + name: Version + type: string + - description: Number of Pods per Postgres cluster jsonPath: .spec.numberOfInstances - - name: Volume - type: string - description: Size of the bound volume + name: Pods + type: integer + - description: Size of the bound volume jsonPath: .spec.volume.size - - name: CPU-Request + name: Volume type: string - description: Requested CPU for Postgres containers + - description: Requested CPU for Postgres containers jsonPath: .spec.resources.requests.cpu - - name: Memory-Request + name: CPU-Request type: string - description: Requested memory for Postgres containers + - description: Requested memory for Postgres containers jsonPath: .spec.resources.requests.memory - - name: Age - type: date - jsonPath: .metadata.creationTimestamp - - name: Status + name: Memory-Request type: string - description: Current sync status of postgresql resource + - description: Age of the PostgreSQL cluster + jsonPath: .metadata.creationTimestamp + name: Age + type: date + - description: Current sync status of postgresql resource jsonPath: .status.PostgresClusterStatus + name: Status + type: string + name: v1 schema: openAPIV3Schema: - type: object - required: - - kind - - apiVersion - - spec + description: Postgresql defines PostgreSQL Custom Resource Definition Object. properties: - kind: - type: string - enum: - - postgresql apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string - enum: - - acid.zalan.do/v1 - spec: + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: type: object - required: - - numberOfInstances - - teamId - - postgresql - - volume + spec: + description: PostgresSpec defines the specification for the PostgreSQL + TPR. properties: additionalVolumes: - type: array items: - type: object - required: - - name - - mountPath - - volumeSource + description: AdditionalVolume specs additional optional volumes + for statefulset properties: isSubPathExpr: type: boolean - name: - type: string mountPath: type: string + name: + type: string subPath: type: string targetContainers: - type: array - nullable: true items: type: string + nullable: true + type: array volumeSource: type: object x-kubernetes-preserve-unknown-fields: true - allowedSourceRanges: + required: + - mountPath + - name + - volumeSource + type: object type: array - nullable: true + allowedSourceRanges: + description: load balancers' source ranges are the same for master + and replica services items: + pattern: ^(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\/(\d|[1-2]\d|3[0-2])$ type: string - pattern: '^(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\/(\d|[1-2]\d|3[0-2])$' + nullable: true + type: array clone: - type: object - required: - - cluster + description: CloneDescription describes which cluster the new should + clone and up to which point in time properties: cluster: type: string - s3_endpoint: - type: string s3_access_key_id: type: string - s3_secret_access_key: + s3_endpoint: type: string s3_force_path_style: type: boolean + s3_secret_access_key: + type: string s3_wal_path: type: string timestamp: + description: |- + The regexp matches the date-time format (RFC 3339 Section 5.6) that specifies a timezone as an offset relative to UTC + Example: 1996-12-19T16:39:57-08:00 + Note: this field requires a timezone + pattern: ^([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])[Tt]([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(([+-]([01][0-9]|2[0-3]):[0-5][0-9]))$ type: string - pattern: '^([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])[Tt]([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(([+-]([01][0-9]|2[0-3]):[0-5][0-9]))$' - # The regexp matches the date-time format (RFC 3339 Section 5.6) that specifies a timezone as an offset relative to UTC - # Example: 1996-12-19T16:39:57-08:00 - # Note: this field requires a timezone uid: format: uuid type: string - connectionPooler: + required: + - cluster type: object + connectionPooler: + description: |- + ConnectionPooler Options for connection pooler + + pgbouncer-large (with higher resources) or odyssey-small (with smaller + resources) + Type string `json:"type,omitempty"` + + makes sense to expose. E.g. pool size (min/max boundaries), max client + connections etc. properties: dockerImage: type: string maxDBConnections: + format: int32 type: integer mode: - type: string enum: - - "session" - - "transaction" + - session + - transaction + type: string numberOfInstances: - type: integer + format: int32 minimum: 1 + type: integer resources: - type: object + description: Resources describes requests and limits for the cluster + resouces. properties: limits: - type: object + description: ResourceDescription describes CPU and memory + resources defined for a cluster. properties: cpu: + description: |- + Decimal natural followed by m, or decimal natural followed by + dot followed by up to three decimal digits. + + This is because the Kubernetes CPU resource has millis as the + maximum precision. The actual values are checked in code + because the regular expression would be huge and horrible and + not very helpful in validation error messages; this one checks + only the format of the given number. + + https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu + + Note: the value specified here must not be zero or be lower + than the corresponding request. + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ + type: string + hugepages-1Gi: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + hugepages-2Mi: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ type: string - pattern: '^(\d+m|\d+(\.\d{1,3})?)$' memory: + description: |- + You can express memory as a plain integer or as a fixed-point + integer using one of these suffixes: E, P, T, G, M, k. You can + also use the power-of-two equivalents: Ei, Pi, Ti, Gi, Mi, Ki + + https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-memory + + Note: the value specified here must not be zero or be higher + than the corresponding limit. + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ type: string - pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' + type: object requests: - type: object + description: ResourceDescription describes CPU and memory + resources defined for a cluster. properties: cpu: + description: |- + Decimal natural followed by m, or decimal natural followed by + dot followed by up to three decimal digits. + + This is because the Kubernetes CPU resource has millis as the + maximum precision. The actual values are checked in code + because the regular expression would be huge and horrible and + not very helpful in validation error messages; this one checks + only the format of the given number. + + https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu + + Note: the value specified here must not be zero or be lower + than the corresponding request. + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ + type: string + hugepages-1Gi: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + hugepages-2Mi: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ type: string - pattern: '^(\d+m|\d+(\.\d{1,3})?)$' memory: + description: |- + You can express memory as a plain integer or as a fixed-point + integer using one of these suffixes: E, P, T, G, M, k. You can + also use the power-of-two equivalents: Ei, Pi, Ti, Gi, Mi, Ki + + https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-memory + + Note: the value specified here must not be zero or be higher + than the corresponding limit. + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ type: string - pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' + type: object + type: object schema: type: string user: type: string - databases: type: object + databases: additionalProperties: type: string - # Note: usernames specified here as database owners must be declared in the users key of the spec key. + description: |- + Note: usernames specified here as database owners must be declared + in the users key of the spec key. + type: object dockerImage: type: string enableConnectionPooler: type: boolean - enableReplicaConnectionPooler: - type: boolean enableLogicalBackup: type: boolean enableMasterLoadBalancer: + description: |- + vars that enable load balancers are pointers because it is important to know if any of them is omitted from the Postgres manifest + in that case the var evaluates to nil and the value is taken from the operator config type: boolean enableMasterPoolerLoadBalancer: type: boolean + enableReplicaConnectionPooler: + type: boolean enableReplicaLoadBalancer: type: boolean enableReplicaPoolerLoadBalancer: @@ -197,300 +285,3645 @@ spec: enableShmVolume: type: boolean env: - type: array - nullable: true items: + description: EnvVar represents an environment variable present in + a Container. + properties: + name: + description: Name of the environment variable. Must be a C_IDENTIFIER. + type: string + value: + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". + type: string + valueFrom: + description: Source for the environment variable's value. Cannot + be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: |- + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. + properties: + apiVersion: + description: Version of the schema the FieldPath is + written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified + API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the exposed + resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name type: object - x-kubernetes-preserve-unknown-fields: true + type: array init_containers: - type: array description: deprecated - nullable: true items: + description: A single application container that you want to run + within a pod. + properties: + args: + description: |- + Arguments to the entrypoint. + The container image's CMD is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + description: |- + Entrypoint array. Not executed within a shell. + The container image's ENTRYPOINT is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + description: |- + List of environment variables to set in the container. + Cannot be updated. + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be + a C_IDENTIFIER. + type: string + value: + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap or + its key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: |- + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + description: |- + List of sources to populate environment variables in the container. + The keys defined within a source must be a C_IDENTIFIER. All invalid keys + will be reported as an event when the container is starting. When a key exists in multiple + sources, the value associated with the last source will take precedence. + Values defined by an Env with a duplicate key will take precedence. + Cannot be updated. + items: + description: EnvFromSource represents the source of a set + of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap must be + defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: An optional identifier to prepend to each + key in the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + description: |- + Container image name. + More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config management to default or override + container images in workload controllers like Deployments and StatefulSets. + type: string + imagePullPolicy: + description: |- + Image pull policy. + One of Always, Never, IfNotPresent. + Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/containers/images#updating-images + type: string + lifecycle: + description: |- + Actions that the management system should take in response to container lifecycle events. + Cannot be updated. + properties: + postStart: + description: |- + PostStart is called immediately after a container is created. If the handler fails, + the container is terminated and restarted according to its restart policy. + Other management of the container blocks until the hook completes. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks + properties: + exec: + description: Exec specifies a command to execute in + the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to + perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + sleep: + description: Sleep represents a duration that the container + should sleep. + properties: + seconds: + description: Seconds is the number of seconds to + sleep. + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + description: |- + Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept + for backward compatibility. There is no validation of this field and + lifecycle hooks will fail at runtime when it is specified. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + description: |- + PreStop is called immediately before a container is terminated due to an + API request or management event such as liveness/startup probe failure, + preemption, resource contention, etc. The handler is not called if the + container crashes or exits. The Pod's termination grace period countdown begins before the + PreStop hook is executed. Regardless of the outcome of the handler, the + container will eventually terminate within the Pod's termination grace + period (unless delayed by finalizers). Other management of the container blocks until the hook completes + or until the termination grace period is reached. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks + properties: + exec: + description: Exec specifies a command to execute in + the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to + perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + sleep: + description: Sleep represents a duration that the container + should sleep. + properties: + seconds: + description: Seconds is the number of seconds to + sleep. + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + description: |- + Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept + for backward compatibility. There is no validation of this field and + lifecycle hooks will fail at runtime when it is specified. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + description: |- + Periodic probe of container liveness. + Container will be restarted if the probe fails. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + properties: + exec: + description: Exec specifies a command to execute in the + container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies a connection to a TCP port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + name: + description: |- + Name of the container specified as a DNS_LABEL. + Each container in a pod must have a unique name (DNS_LABEL). + Cannot be updated. + type: string + ports: + description: |- + List of ports to expose from the container. Not specifying a port here + DOES NOT prevent that port from being exposed. Any port which is + listening on the default "0.0.0.0" address inside a container will be + accessible from the network. + Modifying this array with strategic merge patch may corrupt the data. + For more information See https://github.com/kubernetes/kubernetes/issues/108255. + Cannot be updated. + items: + description: ContainerPort represents a network port in a + single container. + properties: + containerPort: + description: |- + Number of port to expose on the pod's IP address. + This must be a valid port number, 0 < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the external port to. + type: string + hostPort: + description: |- + Number of port to expose on the host. + If specified, this must be a valid port number, 0 < x < 65536. + If HostNetwork is specified, this must match ContainerPort. + Most containers do not need this. + format: int32 + type: integer + name: + description: |- + If specified, this must be an IANA_SVC_NAME and unique within the pod. Each + named port in a pod must have a unique name. Name for the port that can be + referred to by services. + type: string + protocol: + default: TCP + description: |- + Protocol for port. Must be UDP, TCP, or SCTP. + Defaults to "TCP". + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + description: |- + Periodic probe of container service readiness. + Container will be removed from service endpoints if the probe fails. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + properties: + exec: + description: Exec specifies a command to execute in the + container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies a connection to a TCP port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + resizePolicy: + description: Resources resize policy for the container. + items: + description: ContainerResizePolicy represents resource resize + policy for the container. + properties: + resourceName: + description: |- + Name of the resource to which this resource resize policy applies. + Supported values: cpu, memory. + type: string + restartPolicy: + description: |- + Restart policy to apply when specified resource is resized. + If not specified, it defaults to NotRequired. + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + description: |- + Compute Resources required by this container. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + restartPolicy: + description: |- + RestartPolicy defines the restart behavior of individual containers in a pod. + This field may only be set for init containers, and the only allowed value is "Always". + For non-init containers or when this field is not specified, + the restart behavior is defined by the Pod's restart policy and the container type. + Setting the RestartPolicy as "Always" for the init container will have the following effect: + this init container will be continually restarted on + exit until all regular containers have terminated. Once all regular + containers have completed, all init containers with restartPolicy "Always" + will be shut down. This lifecycle differs from normal init containers and + is often referred to as a "sidecar" container. Although this init + container still starts in the init container sequence, it does not wait + for the container to complete before proceeding to the next init + container. Instead, the next init container starts immediately after this + init container is started, or after any startupProbe has successfully + completed. + type: string + securityContext: + description: |- + SecurityContext defines the security options the container should be run with. + If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. + More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ + properties: + allowPrivilegeEscalation: + description: |- + AllowPrivilegeEscalation controls whether a process can gain more + privileges than its parent process. This bool directly controls if + the no_new_privs flag will be set on the container process. + AllowPrivilegeEscalation is true always when the container is: + 1) run as Privileged + 2) has CAP_SYS_ADMIN + Note that this field cannot be set when spec.os.name is windows. + type: boolean + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by this container. If set, this profile + overrides the pod's appArmorProfile. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object + capabilities: + description: |- + The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container runtime. + Note that this field cannot be set when spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + x-kubernetes-list-type: atomic + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + description: |- + Run container in privileged mode. + Processes in privileged containers are essentially equivalent to root on the host. + Defaults to false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + procMount: + description: |- + procMount denotes the type of proc mount to use for the containers. + The default value is Default which uses the container runtime defaults for + readonly paths and masked paths. + This requires the ProcMountType feature flag to be enabled. + Note that this field cannot be set when spec.os.name is windows. + type: string + readOnlyRootFilesystem: + description: |- + Whether this container has a read-only root filesystem. + Default is false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: |- + The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by this container. If seccomp options are + provided at both the pod & container level, the container options + override the pod options. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + windowsOptions: + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options from the PodSecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: string + type: object + type: object + startupProbe: + description: |- + StartupProbe indicates that the Pod has successfully initialized. + If specified, no other probes are executed until this completes successfully. + If this probe fails, the Pod will be restarted, just as if the livenessProbe failed. + This can be used to provide different probe parameters at the beginning of a Pod's lifecycle, + when it might take a long time to load data or warm a cache, than during steady-state operation. + This cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + properties: + exec: + description: Exec specifies a command to execute in the + container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies a connection to a TCP port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + stdin: + description: |- + Whether this container should allocate a buffer for stdin in the container runtime. If this + is not set, reads from stdin in the container will always result in EOF. + Default is false. + type: boolean + stdinOnce: + description: |- + Whether the container runtime should close the stdin channel after it has been opened by + a single attach. When stdin is true the stdin stream will remain open across multiple attach + sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the + first client attaches to stdin, and then remains open and accepts data until the client disconnects, + at which time stdin is closed and remains closed until the container is restarted. If this + flag is false, a container processes that reads from stdin will never receive an EOF. + Default is false + type: boolean + terminationMessagePath: + description: |- + Optional: Path at which the file to which the container's termination message + will be written is mounted into the container's filesystem. + Message written is intended to be brief final status, such as an assertion failure message. + Will be truncated by the node if greater than 4096 bytes. The total message length across + all containers will be limited to 12kb. + Defaults to /dev/termination-log. + Cannot be updated. + type: string + terminationMessagePolicy: + description: |- + Indicate how the termination message should be populated. File will use the contents of + terminationMessagePath to populate the container status message on both success and failure. + FallbackToLogsOnError will use the last chunk of container log output if the termination + message file is empty and the container exited with an error. + The log output is limited to 2048 bytes or 80 lines, whichever is smaller. + Defaults to File. + Cannot be updated. + type: string + tty: + description: |- + Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. + Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list of block devices to be + used by the container. + items: + description: volumeDevice describes a mapping of a raw block + device within a container. + properties: + devicePath: + description: devicePath is the path inside of the container + that the device will be mapped to. + type: string + name: + description: name must match the name of a persistentVolumeClaim + in the pod + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + description: |- + Pod volumes to mount into the container's filesystem. + Cannot be updated. + items: + description: VolumeMount describes a mounting of a Volume + within a container. + properties: + mountPath: + description: |- + Path within the container at which the volume should be mounted. Must + not contain ':'. + type: string + mountPropagation: + description: |- + mountPropagation determines how mounts are propagated from the host + to container and the other way around. + When not set, MountPropagationNone is used. + This field is beta in 1.10. + When RecursiveReadOnly is set to IfPossible or to Enabled, MountPropagation must be None or unspecified + (which defaults to None). + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: |- + Mounted read-only if true, read-write otherwise (false or unspecified). + Defaults to false. + type: boolean + recursiveReadOnly: + description: |- + RecursiveReadOnly specifies whether read-only mounts should be handled + recursively. + + If ReadOnly is false, this field has no meaning and must be unspecified. + + If ReadOnly is true, and this field is set to Disabled, the mount is not made + recursively read-only. If this field is set to IfPossible, the mount is made + recursively read-only, if it is supported by the container runtime. If this + field is set to Enabled, the mount is made recursively read-only if it is + supported by the container runtime, otherwise the pod will not be started and + an error will be generated to indicate the reason. + + If this field is set to IfPossible or Enabled, MountPropagation must be set to + None (or be unspecified, which defaults to None). + + If this field is not specified, it is treated as an equivalent of Disabled. + type: string + subPath: + description: |- + Path within the volume from which the container's volume should be mounted. + Defaults to "" (volume's root). + type: string + subPathExpr: + description: |- + Expanded path within the volume from which the container's volume should be mounted. + Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. + Defaults to "" (volume's root). + SubPathExpr and SubPath are mutually exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + description: |- + Container's working directory. + If not specified, the container runtime's default will be used, which + might be configured in the container image. + Cannot be updated. + type: string + required: + - name type: object - x-kubernetes-preserve-unknown-fields: true - initContainers: type: array - nullable: true + initContainers: items: + description: A single application container that you want to run + within a pod. + properties: + args: + description: |- + Arguments to the entrypoint. + The container image's CMD is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + description: |- + Entrypoint array. Not executed within a shell. + The container image's ENTRYPOINT is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + description: |- + List of environment variables to set in the container. + Cannot be updated. + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be + a C_IDENTIFIER. + type: string + value: + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap or + its key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: |- + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + description: |- + List of sources to populate environment variables in the container. + The keys defined within a source must be a C_IDENTIFIER. All invalid keys + will be reported as an event when the container is starting. When a key exists in multiple + sources, the value associated with the last source will take precedence. + Values defined by an Env with a duplicate key will take precedence. + Cannot be updated. + items: + description: EnvFromSource represents the source of a set + of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap must be + defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: An optional identifier to prepend to each + key in the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + description: |- + Container image name. + More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config management to default or override + container images in workload controllers like Deployments and StatefulSets. + type: string + imagePullPolicy: + description: |- + Image pull policy. + One of Always, Never, IfNotPresent. + Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/containers/images#updating-images + type: string + lifecycle: + description: |- + Actions that the management system should take in response to container lifecycle events. + Cannot be updated. + properties: + postStart: + description: |- + PostStart is called immediately after a container is created. If the handler fails, + the container is terminated and restarted according to its restart policy. + Other management of the container blocks until the hook completes. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks + properties: + exec: + description: Exec specifies a command to execute in + the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to + perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + sleep: + description: Sleep represents a duration that the container + should sleep. + properties: + seconds: + description: Seconds is the number of seconds to + sleep. + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + description: |- + Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept + for backward compatibility. There is no validation of this field and + lifecycle hooks will fail at runtime when it is specified. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + description: |- + PreStop is called immediately before a container is terminated due to an + API request or management event such as liveness/startup probe failure, + preemption, resource contention, etc. The handler is not called if the + container crashes or exits. The Pod's termination grace period countdown begins before the + PreStop hook is executed. Regardless of the outcome of the handler, the + container will eventually terminate within the Pod's termination grace + period (unless delayed by finalizers). Other management of the container blocks until the hook completes + or until the termination grace period is reached. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks + properties: + exec: + description: Exec specifies a command to execute in + the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to + perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + sleep: + description: Sleep represents a duration that the container + should sleep. + properties: + seconds: + description: Seconds is the number of seconds to + sleep. + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + description: |- + Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept + for backward compatibility. There is no validation of this field and + lifecycle hooks will fail at runtime when it is specified. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + description: |- + Periodic probe of container liveness. + Container will be restarted if the probe fails. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + properties: + exec: + description: Exec specifies a command to execute in the + container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies a connection to a TCP port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + name: + description: |- + Name of the container specified as a DNS_LABEL. + Each container in a pod must have a unique name (DNS_LABEL). + Cannot be updated. + type: string + ports: + description: |- + List of ports to expose from the container. Not specifying a port here + DOES NOT prevent that port from being exposed. Any port which is + listening on the default "0.0.0.0" address inside a container will be + accessible from the network. + Modifying this array with strategic merge patch may corrupt the data. + For more information See https://github.com/kubernetes/kubernetes/issues/108255. + Cannot be updated. + items: + description: ContainerPort represents a network port in a + single container. + properties: + containerPort: + description: |- + Number of port to expose on the pod's IP address. + This must be a valid port number, 0 < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the external port to. + type: string + hostPort: + description: |- + Number of port to expose on the host. + If specified, this must be a valid port number, 0 < x < 65536. + If HostNetwork is specified, this must match ContainerPort. + Most containers do not need this. + format: int32 + type: integer + name: + description: |- + If specified, this must be an IANA_SVC_NAME and unique within the pod. Each + named port in a pod must have a unique name. Name for the port that can be + referred to by services. + type: string + protocol: + default: TCP + description: |- + Protocol for port. Must be UDP, TCP, or SCTP. + Defaults to "TCP". + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + description: |- + Periodic probe of container service readiness. + Container will be removed from service endpoints if the probe fails. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + properties: + exec: + description: Exec specifies a command to execute in the + container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies a connection to a TCP port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + resizePolicy: + description: Resources resize policy for the container. + items: + description: ContainerResizePolicy represents resource resize + policy for the container. + properties: + resourceName: + description: |- + Name of the resource to which this resource resize policy applies. + Supported values: cpu, memory. + type: string + restartPolicy: + description: |- + Restart policy to apply when specified resource is resized. + If not specified, it defaults to NotRequired. + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + description: |- + Compute Resources required by this container. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + restartPolicy: + description: |- + RestartPolicy defines the restart behavior of individual containers in a pod. + This field may only be set for init containers, and the only allowed value is "Always". + For non-init containers or when this field is not specified, + the restart behavior is defined by the Pod's restart policy and the container type. + Setting the RestartPolicy as "Always" for the init container will have the following effect: + this init container will be continually restarted on + exit until all regular containers have terminated. Once all regular + containers have completed, all init containers with restartPolicy "Always" + will be shut down. This lifecycle differs from normal init containers and + is often referred to as a "sidecar" container. Although this init + container still starts in the init container sequence, it does not wait + for the container to complete before proceeding to the next init + container. Instead, the next init container starts immediately after this + init container is started, or after any startupProbe has successfully + completed. + type: string + securityContext: + description: |- + SecurityContext defines the security options the container should be run with. + If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. + More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ + properties: + allowPrivilegeEscalation: + description: |- + AllowPrivilegeEscalation controls whether a process can gain more + privileges than its parent process. This bool directly controls if + the no_new_privs flag will be set on the container process. + AllowPrivilegeEscalation is true always when the container is: + 1) run as Privileged + 2) has CAP_SYS_ADMIN + Note that this field cannot be set when spec.os.name is windows. + type: boolean + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by this container. If set, this profile + overrides the pod's appArmorProfile. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object + capabilities: + description: |- + The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container runtime. + Note that this field cannot be set when spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + x-kubernetes-list-type: atomic + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + description: |- + Run container in privileged mode. + Processes in privileged containers are essentially equivalent to root on the host. + Defaults to false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + procMount: + description: |- + procMount denotes the type of proc mount to use for the containers. + The default value is Default which uses the container runtime defaults for + readonly paths and masked paths. + This requires the ProcMountType feature flag to be enabled. + Note that this field cannot be set when spec.os.name is windows. + type: string + readOnlyRootFilesystem: + description: |- + Whether this container has a read-only root filesystem. + Default is false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: |- + The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by this container. If seccomp options are + provided at both the pod & container level, the container options + override the pod options. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + windowsOptions: + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options from the PodSecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: string + type: object + type: object + startupProbe: + description: |- + StartupProbe indicates that the Pod has successfully initialized. + If specified, no other probes are executed until this completes successfully. + If this probe fails, the Pod will be restarted, just as if the livenessProbe failed. + This can be used to provide different probe parameters at the beginning of a Pod's lifecycle, + when it might take a long time to load data or warm a cache, than during steady-state operation. + This cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + properties: + exec: + description: Exec specifies a command to execute in the + container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies a connection to a TCP port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + stdin: + description: |- + Whether this container should allocate a buffer for stdin in the container runtime. If this + is not set, reads from stdin in the container will always result in EOF. + Default is false. + type: boolean + stdinOnce: + description: |- + Whether the container runtime should close the stdin channel after it has been opened by + a single attach. When stdin is true the stdin stream will remain open across multiple attach + sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the + first client attaches to stdin, and then remains open and accepts data until the client disconnects, + at which time stdin is closed and remains closed until the container is restarted. If this + flag is false, a container processes that reads from stdin will never receive an EOF. + Default is false + type: boolean + terminationMessagePath: + description: |- + Optional: Path at which the file to which the container's termination message + will be written is mounted into the container's filesystem. + Message written is intended to be brief final status, such as an assertion failure message. + Will be truncated by the node if greater than 4096 bytes. The total message length across + all containers will be limited to 12kb. + Defaults to /dev/termination-log. + Cannot be updated. + type: string + terminationMessagePolicy: + description: |- + Indicate how the termination message should be populated. File will use the contents of + terminationMessagePath to populate the container status message on both success and failure. + FallbackToLogsOnError will use the last chunk of container log output if the termination + message file is empty and the container exited with an error. + The log output is limited to 2048 bytes or 80 lines, whichever is smaller. + Defaults to File. + Cannot be updated. + type: string + tty: + description: |- + Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. + Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list of block devices to be + used by the container. + items: + description: volumeDevice describes a mapping of a raw block + device within a container. + properties: + devicePath: + description: devicePath is the path inside of the container + that the device will be mapped to. + type: string + name: + description: name must match the name of a persistentVolumeClaim + in the pod + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + description: |- + Pod volumes to mount into the container's filesystem. + Cannot be updated. + items: + description: VolumeMount describes a mounting of a Volume + within a container. + properties: + mountPath: + description: |- + Path within the container at which the volume should be mounted. Must + not contain ':'. + type: string + mountPropagation: + description: |- + mountPropagation determines how mounts are propagated from the host + to container and the other way around. + When not set, MountPropagationNone is used. + This field is beta in 1.10. + When RecursiveReadOnly is set to IfPossible or to Enabled, MountPropagation must be None or unspecified + (which defaults to None). + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: |- + Mounted read-only if true, read-write otherwise (false or unspecified). + Defaults to false. + type: boolean + recursiveReadOnly: + description: |- + RecursiveReadOnly specifies whether read-only mounts should be handled + recursively. + + If ReadOnly is false, this field has no meaning and must be unspecified. + + If ReadOnly is true, and this field is set to Disabled, the mount is not made + recursively read-only. If this field is set to IfPossible, the mount is made + recursively read-only, if it is supported by the container runtime. If this + field is set to Enabled, the mount is made recursively read-only if it is + supported by the container runtime, otherwise the pod will not be started and + an error will be generated to indicate the reason. + + If this field is set to IfPossible or Enabled, MountPropagation must be set to + None (or be unspecified, which defaults to None). + + If this field is not specified, it is treated as an equivalent of Disabled. + type: string + subPath: + description: |- + Path within the volume from which the container's volume should be mounted. + Defaults to "" (volume's root). + type: string + subPathExpr: + description: |- + Expanded path within the volume from which the container's volume should be mounted. + Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. + Defaults to "" (volume's root). + SubPathExpr and SubPath are mutually exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + description: |- + Container's working directory. + If not specified, the container runtime's default will be used, which + might be configured in the container image. + Cannot be updated. + type: string + required: + - name type: object - x-kubernetes-preserve-unknown-fields: true + type: array logicalBackupRetention: type: string logicalBackupSchedule: + pattern: ^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$ type: string - pattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$' maintenanceWindows: - type: array items: - type: string pattern: '^\ *((Mon|Tue|Wed|Thu|Fri|Sat|Sun):(2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))-((2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))\ *$' + type: string + type: array masterServiceAnnotations: - type: object additionalProperties: type: string - nodeAffinity: + description: MasterServiceAnnotations takes precedence over ServiceAnnotations + for master role if not empty type: object + nodeAffinity: + description: Node affinity is a group of node affinity scheduling + rules. properties: preferredDuringSchedulingIgnoredDuringExecution: - type: array + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. items: - type: object + description: |- + An empty preferred scheduling term matches all objects with implicit weight 0 + (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated with the corresponding + weight. + properties: + matchExpressions: + description: A list of node selector requirements by + node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies + to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements by + node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies + to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + description: Weight associated with matching the corresponding + nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer required: - preference - weight - properties: - preference: - type: object - properties: - matchExpressions: - type: array - items: - type: object - required: - - key - - operator - properties: - key: - type: string - operator: - type: string - values: - type: array - items: - type: string - matchFields: - type: array - items: - type: object - required: - - key - - operator - properties: - key: - type: string - operator: - type: string - values: - type: array - items: - type: string - weight: - type: integer + type: object + type: array + x-kubernetes-list-type: atomic requiredDuringSchedulingIgnoredDuringExecution: - type: object - required: - - nodeSelectorTerms + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from its node. properties: nodeSelectorTerms: - type: array + description: Required. A list of node selector terms. The + terms are ORed. items: - type: object + description: |- + A null or empty node selector term matches no objects. The requirements of + them are ANDed. + The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. properties: matchExpressions: - type: array + description: A list of node selector requirements by + node's labels. items: - type: object + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies + to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic required: - key - operator - properties: - key: - type: string - operator: - type: string - values: - type: array - items: - type: string + type: object + type: array + x-kubernetes-list-type: atomic matchFields: - type: array + description: A list of node selector requirements by + node's fields. items: - type: object + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies + to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic required: - key - operator - properties: - key: - type: string - operator: - type: string - values: - type: array - items: - type: string - numberOfInstances: - type: integer - minimum: 0 - patroni: + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic type: object + numberOfInstances: + format: int32 + minimum: 0 + type: integer + patroni: + description: Patroni contains Patroni-specific configuration properties: failsafe_mode: type: boolean initdb: - type: object additionalProperties: type: string + type: object loop_wait: + format: int32 type: integer maximum_lag_on_failover: + format: int64 type: integer pg_hba: - type: array items: type: string + type: array retry_timeout: + format: int32 type: integer slots: - type: object additionalProperties: - type: object additionalProperties: type: string + type: object + type: object synchronous_mode: type: boolean synchronous_mode_strict: type: boolean synchronous_node_count: + format: int32 type: integer ttl: + format: int32 type: integer - podAnnotations: type: object + pod_priority_class_name: + description: deprecated + type: string + podAnnotations: additionalProperties: type: string - pod_priority_class_name: - type: string - description: deprecated + type: object podPriorityClassName: type: string postgresql: - type: object - required: - - version + description: PostgresqlParam describes PostgreSQL version and pairs + of configuration parameter name - values. properties: - version: - type: string - enum: - - "13" - - "14" - - "15" - - "16" - - "17" parameters: - type: object additionalProperties: type: string - preparedDatabases: + type: object + version: + enum: + - 13 + - 14 + - 15 + - 16 + - 17 + type: string + required: + - version type: object + preparedDatabases: additionalProperties: - type: object + description: PreparedDatabase describes elements to be bootstrapped properties: defaultUsers: type: boolean extensions: - type: object additionalProperties: type: string - schemas: type: object + schemas: additionalProperties: - type: object + description: PreparedSchema describes elements to be bootstrapped + per schema properties: - defaultUsers: - type: boolean defaultRoles: type: boolean + defaultUsers: + type: boolean + type: object + type: object secretNamespace: type: string - replicaLoadBalancer: - type: boolean - description: deprecated - replicaServiceAnnotations: + type: object type: object + replicaLoadBalancer: + description: deprecated + type: boolean + replicaServiceAnnotations: additionalProperties: type: string - resources: + description: ReplicaServiceAnnotations takes precedence over ServiceAnnotations + for replica role if not empty type: object + resources: + description: Resources describes requests and limits for the cluster + resouces. properties: limits: - type: object + description: ResourceDescription describes CPU and memory resources + defined for a cluster. properties: cpu: + description: |- + Decimal natural followed by m, or decimal natural followed by + dot followed by up to three decimal digits. + + This is because the Kubernetes CPU resource has millis as the + maximum precision. The actual values are checked in code + because the regular expression would be huge and horrible and + not very helpful in validation error messages; this one checks + only the format of the given number. + + https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu + + Note: the value specified here must not be zero or be lower + than the corresponding request. + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ type: string - # Decimal natural followed by m, or decimal natural followed by - # dot followed by up to three decimal digits. - # - # This is because the Kubernetes CPU resource has millis as the - # maximum precision. The actual values are checked in code - # because the regular expression would be huge and horrible and - # not very helpful in validation error messages; this one checks - # only the format of the given number. - # - # https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu - pattern: '^(\d+m|\d+(\.\d{1,3})?)$' - # Note: the value specified here must not be zero or be lower - # than the corresponding request. - memory: - type: string - # You can express memory as a plain integer or as a fixed-point - # integer using one of these suffixes: E, P, T, G, M, k. You can - # also use the power-of-two equivalents: Ei, Pi, Ti, Gi, Mi, Ki - # - # https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-memory - pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' - # Note: the value specified here must not be zero or be higher - # than the corresponding limit. - hugepages-2Mi: - type: string - pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' hugepages-1Gi: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ type: string - pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' + hugepages-2Mi: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + memory: + description: |- + You can express memory as a plain integer or as a fixed-point + integer using one of these suffixes: E, P, T, G, M, k. You can + also use the power-of-two equivalents: Ei, Pi, Ti, Gi, Mi, Ki + + https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-memory + + Note: the value specified here must not be zero or be higher + than the corresponding limit. + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + type: object requests: - type: object + description: ResourceDescription describes CPU and memory resources + defined for a cluster. properties: cpu: + description: |- + Decimal natural followed by m, or decimal natural followed by + dot followed by up to three decimal digits. + + This is because the Kubernetes CPU resource has millis as the + maximum precision. The actual values are checked in code + because the regular expression would be huge and horrible and + not very helpful in validation error messages; this one checks + only the format of the given number. + + https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu + + Note: the value specified here must not be zero or be lower + than the corresponding request. + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ type: string - pattern: '^(\d+m|\d+(\.\d{1,3})?)$' - memory: - type: string - pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' - hugepages-2Mi: - type: string - pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' hugepages-1Gi: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ type: string - pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' + hugepages-2Mi: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + memory: + description: |- + You can express memory as a plain integer or as a fixed-point + integer using one of these suffixes: E, P, T, G, M, k. You can + also use the power-of-two equivalents: Ei, Pi, Ti, Gi, Mi, Ki + + https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-memory + + Note: the value specified here must not be zero or be higher + than the corresponding limit. + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + type: object + type: object schedulerName: type: string serviceAnnotations: - type: object additionalProperties: type: string + type: object sidecars: - type: array - nullable: true items: + description: Sidecar defines a container to be run in the same pod + as the Postgres container. + properties: + command: + items: + type: string + type: array + env: + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be + a C_IDENTIFIER. + type: string + value: + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap or + its key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: |- + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + image: + type: string + name: + type: string + ports: + items: + description: ContainerPort represents a network port in a + single container. + properties: + containerPort: + description: |- + Number of port to expose on the pod's IP address. + This must be a valid port number, 0 < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the external port to. + type: string + hostPort: + description: |- + Number of port to expose on the host. + If specified, this must be a valid port number, 0 < x < 65536. + If HostNetwork is specified, this must match ContainerPort. + Most containers do not need this. + format: int32 + type: integer + name: + description: |- + If specified, this must be an IANA_SVC_NAME and unique within the pod. Each + named port in a pod must have a unique name. Name for the port that can be + referred to by services. + type: string + protocol: + default: TCP + description: |- + Protocol for port. Must be UDP, TCP, or SCTP. + Defaults to "TCP". + type: string + required: + - containerPort + type: object + type: array + resources: + description: Resources describes requests and limits for the + cluster resouces. + properties: + limits: + description: ResourceDescription describes CPU and memory + resources defined for a cluster. + properties: + cpu: + description: |- + Decimal natural followed by m, or decimal natural followed by + dot followed by up to three decimal digits. + + This is because the Kubernetes CPU resource has millis as the + maximum precision. The actual values are checked in code + because the regular expression would be huge and horrible and + not very helpful in validation error messages; this one checks + only the format of the given number. + + https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu + + Note: the value specified here must not be zero or be lower + than the corresponding request. + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ + type: string + hugepages-1Gi: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + hugepages-2Mi: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + memory: + description: |- + You can express memory as a plain integer or as a fixed-point + integer using one of these suffixes: E, P, T, G, M, k. You can + also use the power-of-two equivalents: Ei, Pi, Ti, Gi, Mi, Ki + + https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-memory + + Note: the value specified here must not be zero or be higher + than the corresponding limit. + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + type: object + requests: + description: ResourceDescription describes CPU and memory + resources defined for a cluster. + properties: + cpu: + description: |- + Decimal natural followed by m, or decimal natural followed by + dot followed by up to three decimal digits. + + This is because the Kubernetes CPU resource has millis as the + maximum precision. The actual values are checked in code + because the regular expression would be huge and horrible and + not very helpful in validation error messages; this one checks + only the format of the given number. + + https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu + + Note: the value specified here must not be zero or be lower + than the corresponding request. + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ + type: string + hugepages-1Gi: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + hugepages-2Mi: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + memory: + description: |- + You can express memory as a plain integer or as a fixed-point + integer using one of these suffixes: E, P, T, G, M, k. You can + also use the power-of-two equivalents: Ei, Pi, Ti, Gi, Mi, Ki + + https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-memory + + Note: the value specified here must not be zero or be higher + than the corresponding limit. + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + type: object + type: object type: object - x-kubernetes-preserve-unknown-fields: true - spiloRunAsUser: + type: array + spiloFSGroup: + format: int64 type: integer spiloRunAsGroup: + format: int64 type: integer - spiloFSGroup: + spiloRunAsUser: + format: int64 type: integer standby: - type: object - properties: - s3_wal_path: - type: string - gs_wal_path: - type: string - standby_host: - type: string - standby_port: - type: string oneOf: - required: - s3_wal_path @@ -498,39 +3931,46 @@ spec: - gs_wal_path - required: - standby_host + description: StandbyDescription contains remote primary config or + s3/gs wal path + properties: + gs_wal_path: + type: string + s3_wal_path: + type: string + standby_host: + type: string + standby_port: + type: string + type: object streams: - type: array items: - type: object - required: - - applicationId - - database - - tables + description: Stream defines properties for creating FabricEventStream + resources properties: applicationId: type: string batchSize: + format: int32 type: integer cpu: + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ type: string - pattern: '^(\d+m|\d+(\.\d{1,3})?)$' database: type: string enableRecovery: type: boolean filter: - type: object additionalProperties: type: string - memory: - type: string - pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' - tables: type: object + memory: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + tables: additionalProperties: - type: object - required: - - eventType + description: StreamTable defines properties of outbox tables + for FabricEventStreams properties: eventType: type: string @@ -542,55 +3982,82 @@ spec: type: string recoveryEventType: type: string + required: + - eventType + type: object + type: object + required: + - applicationId + - database + - tables + type: object + type: array teamId: type: string tls: - type: object - required: - - secretName + description: TLSDescription specs TLS properties properties: - secretName: + caFile: + type: string + caSecretName: type: string certificateFile: type: string privateKeyFile: type: string - caFile: - type: string - caSecretName: + secretName: type: string + required: + - secretName + type: object tolerations: - type: array items: - type: object + description: |- + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . properties: + effect: + description: |- + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string key: + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. type: string operator: + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. type: string - enum: - - Equal - - Exists - value: - type: string - effect: - type: string - enum: - - NoExecute - - NoSchedule - - PreferNoSchedule tolerationSeconds: + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. + format: int64 type: integer + value: + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array useLoadBalancer: + description: |- + deprecated load balancer settings maintained for backward compatibility + see "Load balancers" operator docs type: boolean - description: deprecated users: - type: object additionalProperties: - type: array - nullable: true + description: UserFlags defines flags (such as superuser, nologin) + that could be assigned to individual users items: - type: string enum: - bypassrls - BYPASSRLS @@ -620,68 +4087,116 @@ spec: - SUPERUSER - nosuperuser - NOSUPERUSER - usersIgnoringSecretRotation: - type: array - nullable: true - items: - type: string - usersWithInPlaceSecretRotation: - type: array - nullable: true - items: - type: string - usersWithSecretRotation: - type: array - nullable: true - items: - type: string - volume: + type: string + type: array type: object - required: - - size + usersIgnoringSecretRotation: + items: + type: string + nullable: true + type: array + usersWithInPlaceSecretRotation: + items: + type: string + nullable: true + type: array + usersWithSecretRotation: + items: + type: string + nullable: true + type: array + volume: + description: Volume describes a single volume in the manifest. properties: + iops: + format: int64 + type: integer isSubPathExpr: type: boolean - iops: - type: integer selector: - type: object + description: |- + A label selector is a label query over a set of resources. The result of matchLabels and + matchExpressions are ANDed. An empty label selector matches all objects. A null + label selector matches no objects. properties: matchExpressions: - type: array + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. items: - type: object - required: - - key - - operator + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: + description: key is the label key that the selector + applies to. type: string operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string - enum: - - DoesNotExist - - Exists - - In - - NotIn values: - type: array + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. items: type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object - x-kubernetes-preserve-unknown-fields: true + type: object + x-kubernetes-map-type: atomic size: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ type: string - pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' - # Note: the value specified here must not be zero. storageClass: type: string subPath: type: string throughput: + format: int64 type: integer - status: + type: + type: string + required: + - size + type: object + required: + - numberOfInstances + - postgresql + - teamId + - volume type: object - additionalProperties: - type: string + status: + description: PostgresStatus contains status of the PostgreSQL cluster + (running, creation failed etc.) + properties: + PostgresClusterStatus: + type: string + required: + - PostgresClusterStatus + type: object + required: + - metadata + - spec + type: object + served: true + storage: true + subresources: + status: {} diff --git a/pkg/apis/acid.zalan.do/v1/crds.go b/pkg/apis/acid.zalan.do/v1/crds.go index b89cb1448..d3339f396 100644 --- a/pkg/apis/acid.zalan.do/v1/crds.go +++ b/pkg/apis/acid.zalan.do/v1/crds.go @@ -1,22 +1,20 @@ package v1 import ( + _ "embed" "fmt" apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/stretchr/testify/assert/yaml" acidzalando "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do" "github.com/zalando/postgres-operator/pkg/util" ) // CRDResource* define names necesssary for the k8s CRD API const ( - PostgresCRDResourceKind = "postgresql" - PostgresCRDResourcePlural = "postgresqls" - PostgresCRDResourceList = PostgresCRDResourceKind + "List" - PostgresCRDResouceName = PostgresCRDResourcePlural + "." + acidzalando.GroupName - PostgresCRDResourceShort = "pg" + PostgresCRDResourceKind = "postgresql" OperatorConfigCRDResouceKind = "OperatorConfiguration" OperatorConfigCRDResourcePlural = "operatorconfigurations" @@ -25,57 +23,6 @@ const ( OperatorConfigCRDResourceShort = "opconfig" ) -// PostgresCRDResourceColumns definition of AdditionalPrinterColumns for postgresql CRD -var PostgresCRDResourceColumns = []apiextv1.CustomResourceColumnDefinition{ - { - Name: "Team", - Type: "string", - Description: "Team responsible for Postgres cluster", - JSONPath: ".spec.teamId", - }, - { - Name: "Version", - Type: "string", - Description: "PostgreSQL version", - JSONPath: ".spec.postgresql.version", - }, - { - Name: "Pods", - Type: "integer", - Description: "Number of Pods per Postgres cluster", - JSONPath: ".spec.numberOfInstances", - }, - { - Name: "Volume", - Type: "string", - Description: "Size of the bound volume", - JSONPath: ".spec.volume.size", - }, - { - Name: "CPU-Request", - Type: "string", - Description: "Requested CPU for Postgres containers", - JSONPath: ".spec.resources.requests.cpu", - }, - { - Name: "Memory-Request", - Type: "string", - Description: "Requested memory for Postgres containers", - JSONPath: ".spec.resources.requests.memory", - }, - { - Name: "Age", - Type: "date", - JSONPath: ".metadata.creationTimestamp", - }, - { - Name: "Status", - Type: "string", - Description: "Current sync status of postgresql resource", - JSONPath: ".status.PostgresClusterStatus", - }, -} - // OperatorConfigCRDResourceColumns definition of AdditionalPrinterColumns for OperatorConfiguration CRD var OperatorConfigCRDResourceColumns = []apiextv1.CustomResourceColumnDefinition{ { @@ -109,1009 +56,9 @@ var OperatorConfigCRDResourceColumns = []apiextv1.CustomResourceColumnDefinition }, } -var min0 = 0.0 var min1 = 1.0 var minDisable = -1.0 -// PostgresCRDResourceValidation to check applied manifest parameters -var PostgresCRDResourceValidation = apiextv1.CustomResourceValidation{ - OpenAPIV3Schema: &apiextv1.JSONSchemaProps{ - Type: "object", - Required: []string{"kind", "apiVersion", "spec"}, - Properties: map[string]apiextv1.JSONSchemaProps{ - "kind": { - Type: "string", - Enum: []apiextv1.JSON{ - { - Raw: []byte(`"postgresql"`), - }, - }, - }, - "apiVersion": { - Type: "string", - Enum: []apiextv1.JSON{ - { - Raw: []byte(`"acid.zalan.do/v1"`), - }, - }, - }, - "spec": { - Type: "object", - Required: []string{"numberOfInstances", "teamId", "postgresql", "volume"}, - Properties: map[string]apiextv1.JSONSchemaProps{ - "additionalVolumes": { - Type: "array", - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "object", - Required: []string{"name", "mountPath", "volumeSource"}, - Properties: map[string]apiextv1.JSONSchemaProps{ - "isSubPathExpr": { - Type: "boolean", - }, - "name": { - Type: "string", - }, - "mountPath": { - Type: "string", - }, - "subPath": { - Type: "string", - }, - "targetContainers": { - Type: "array", - Nullable: true, - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - "volumeSource": { - Type: "object", - XPreserveUnknownFields: util.True(), - }, - }, - }, - }, - }, - "allowedSourceRanges": { - Type: "array", - Nullable: true, - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - Pattern: "^(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\/(\\d|[1-2]\\d|3[0-2])$", - }, - }, - }, - "clone": { - Type: "object", - Required: []string{"cluster"}, - Properties: map[string]apiextv1.JSONSchemaProps{ - "cluster": { - Type: "string", - }, - "s3_endpoint": { - Type: "string", - }, - "s3_access_key_id": { - Type: "string", - }, - "s3_secret_access_key": { - Type: "string", - }, - "s3_force_path_style": { - Type: "boolean", - }, - "s3_wal_path": { - Type: "string", - }, - "timestamp": { - Type: "string", - Pattern: "^([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])[Tt]([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\\.[0-9]+)?(([+-]([01][0-9]|2[0-3]):[0-5][0-9]))$", - }, - "uid": { - Type: "string", - Format: "uuid", - }, - }, - }, - "connectionPooler": { - Type: "object", - Properties: map[string]apiextv1.JSONSchemaProps{ - "dockerImage": { - Type: "string", - }, - "maxDBConnections": { - Type: "integer", - }, - "mode": { - Type: "string", - Enum: []apiextv1.JSON{ - { - Raw: []byte(`"session"`), - }, - { - Raw: []byte(`"transaction"`), - }, - }, - }, - "numberOfInstances": { - Type: "integer", - Minimum: &min1, - }, - "resources": { - Type: "object", - Properties: map[string]apiextv1.JSONSchemaProps{ - "limits": { - Type: "object", - Properties: map[string]apiextv1.JSONSchemaProps{ - "cpu": { - Type: "string", - Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$", - }, - "memory": { - Type: "string", - Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$", - }, - }, - }, - "requests": { - Type: "object", - Properties: map[string]apiextv1.JSONSchemaProps{ - "cpu": { - Type: "string", - Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$", - }, - "memory": { - Type: "string", - Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$", - }, - }, - }, - }, - }, - "schema": { - Type: "string", - }, - "user": { - Type: "string", - }, - }, - }, - "databases": { - Type: "object", - AdditionalProperties: &apiextv1.JSONSchemaPropsOrBool{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - "dockerImage": { - Type: "string", - }, - "enableConnectionPooler": { - Type: "boolean", - }, - "enableReplicaConnectionPooler": { - Type: "boolean", - }, - "enableLogicalBackup": { - Type: "boolean", - }, - "enableMasterLoadBalancer": { - Type: "boolean", - }, - "enableMasterPoolerLoadBalancer": { - Type: "boolean", - }, - "enableReplicaLoadBalancer": { - Type: "boolean", - }, - "enableReplicaPoolerLoadBalancer": { - Type: "boolean", - }, - "enableShmVolume": { - Type: "boolean", - }, - "env": { - Type: "array", - Nullable: true, - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "object", - XPreserveUnknownFields: util.True(), - }, - }, - }, - "init_containers": { - Type: "array", - Description: "deprecated", - Nullable: true, - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "object", - XPreserveUnknownFields: util.True(), - }, - }, - }, - "initContainers": { - Type: "array", - Nullable: true, - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "object", - XPreserveUnknownFields: util.True(), - }, - }, - }, - "logicalBackupRetention": { - Type: "string", - }, - "logicalBackupSchedule": { - Type: "string", - Pattern: "^(\\d+|\\*)(/\\d+)?(\\s+(\\d+|\\*)(/\\d+)?){4}$", - }, - "maintenanceWindows": { - Type: "array", - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - Pattern: "^\\ *((Mon|Tue|Wed|Thu|Fri|Sat|Sun):(2[0-3]|[01]?\\d):([0-5]?\\d)|(2[0-3]|[01]?\\d):([0-5]?\\d))-((Mon|Tue|Wed|Thu|Fri|Sat|Sun):(2[0-3]|[01]?\\d):([0-5]?\\d)|(2[0-3]|[01]?\\d):([0-5]?\\d))\\ *$", - }, - }, - }, - "masterServiceAnnotations": { - Type: "object", - AdditionalProperties: &apiextv1.JSONSchemaPropsOrBool{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - "nodeAffinity": { - Type: "object", - Properties: map[string]apiextv1.JSONSchemaProps{ - "preferredDuringSchedulingIgnoredDuringExecution": { - Type: "array", - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "object", - Required: []string{"preference", "weight"}, - Properties: map[string]apiextv1.JSONSchemaProps{ - "preference": { - Type: "object", - Properties: map[string]apiextv1.JSONSchemaProps{ - "matchExpressions": { - Type: "array", - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "object", - Required: []string{"key", "operator"}, - Properties: map[string]apiextv1.JSONSchemaProps{ - "key": { - Type: "string", - }, - "operator": { - Type: "string", - }, - "values": { - Type: "array", - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - }, - }, - }, - }, - "matchFields": { - Type: "array", - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "object", - Required: []string{"key", "operator"}, - Properties: map[string]apiextv1.JSONSchemaProps{ - "key": { - Type: "string", - }, - "operator": { - Type: "string", - }, - "values": { - Type: "array", - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - }, - }, - }, - }, - }, - }, - "weight": { - Type: "integer", - }, - }, - }, - }, - }, - "requiredDuringSchedulingIgnoredDuringExecution": { - Type: "object", - Required: []string{"nodeSelectorTerms"}, - Properties: map[string]apiextv1.JSONSchemaProps{ - "nodeSelectorTerms": { - Type: "array", - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "object", - Properties: map[string]apiextv1.JSONSchemaProps{ - "matchExpressions": { - Type: "array", - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "object", - Required: []string{"key", "operator"}, - Properties: map[string]apiextv1.JSONSchemaProps{ - "key": { - Type: "string", - }, - "operator": { - Type: "string", - }, - "values": { - Type: "array", - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - }, - }, - }, - }, - "matchFields": { - Type: "array", - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "object", - Required: []string{"key", "operator"}, - Properties: map[string]apiextv1.JSONSchemaProps{ - "key": { - Type: "string", - }, - "operator": { - Type: "string", - }, - "values": { - Type: "array", - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - "numberOfInstances": { - Type: "integer", - Minimum: &min0, - }, - "patroni": { - Type: "object", - Properties: map[string]apiextv1.JSONSchemaProps{ - "failsafe_mode": { - Type: "boolean", - }, - "initdb": { - Type: "object", - AdditionalProperties: &apiextv1.JSONSchemaPropsOrBool{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - "loop_wait": { - Type: "integer", - }, - "maximum_lag_on_failover": { - Type: "integer", - }, - "pg_hba": { - Type: "array", - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - "retry_timeout": { - Type: "integer", - }, - "slots": { - Type: "object", - AdditionalProperties: &apiextv1.JSONSchemaPropsOrBool{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "object", - AdditionalProperties: &apiextv1.JSONSchemaPropsOrBool{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - }, - }, - "synchronous_mode": { - Type: "boolean", - }, - "synchronous_mode_strict": { - Type: "boolean", - }, - "synchronous_node_count": { - Type: "integer", - }, - "ttl": { - Type: "integer", - }, - }, - }, - "podAnnotations": { - Type: "object", - AdditionalProperties: &apiextv1.JSONSchemaPropsOrBool{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - "pod_priority_class_name": { - Type: "string", - Description: "deprecated", - }, - "podPriorityClassName": { - Type: "string", - }, - "postgresql": { - Type: "object", - Required: []string{"version"}, - Properties: map[string]apiextv1.JSONSchemaProps{ - "version": { - Type: "string", - Enum: []apiextv1.JSON{ - { - Raw: []byte(`"13"`), - }, - { - Raw: []byte(`"14"`), - }, - { - Raw: []byte(`"15"`), - }, - { - Raw: []byte(`"16"`), - }, - { - Raw: []byte(`"17"`), - }, - }, - }, - "parameters": { - Type: "object", - AdditionalProperties: &apiextv1.JSONSchemaPropsOrBool{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - }, - }, - "preparedDatabases": { - Type: "object", - AdditionalProperties: &apiextv1.JSONSchemaPropsOrBool{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "object", - Properties: map[string]apiextv1.JSONSchemaProps{ - "defaultUsers": { - Type: "boolean", - }, - "extensions": { - Type: "object", - AdditionalProperties: &apiextv1.JSONSchemaPropsOrBool{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - "schemas": { - Type: "object", - AdditionalProperties: &apiextv1.JSONSchemaPropsOrBool{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "object", - Properties: map[string]apiextv1.JSONSchemaProps{ - "defaultUsers": { - Type: "boolean", - }, - "defaultRoles": { - Type: "boolean", - }, - }, - }, - }, - }, - "secretNamespace": { - Type: "string", - }, - }, - }, - }, - }, - "replicaLoadBalancer": { - Type: "boolean", - Description: "deprecated", - }, - "replicaServiceAnnotations": { - Type: "object", - AdditionalProperties: &apiextv1.JSONSchemaPropsOrBool{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - "resources": { - Type: "object", - Properties: map[string]apiextv1.JSONSchemaProps{ - "limits": { - Type: "object", - Properties: map[string]apiextv1.JSONSchemaProps{ - "cpu": { - Type: "string", - Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$", - }, - "memory": { - Type: "string", - Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$", - }, - "hugepages-2Mi": { - Type: "string", - Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$", - }, - "hugepages-1Gi": { - Type: "string", - Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$", - }, - }, - }, - "requests": { - Type: "object", - Properties: map[string]apiextv1.JSONSchemaProps{ - "cpu": { - Type: "string", - Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$", - }, - "memory": { - Type: "string", - Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$", - }, - "hugepages-2Mi": { - Type: "string", - Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$", - }, - "hugepages-1Gi": { - Type: "string", - Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$", - }, - }, - }, - }, - }, - "schedulerName": { - Type: "string", - }, - "serviceAnnotations": { - Type: "object", - AdditionalProperties: &apiextv1.JSONSchemaPropsOrBool{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - "sidecars": { - Type: "array", - Nullable: true, - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "object", - XPreserveUnknownFields: util.True(), - }, - }, - }, - "spiloRunAsUser": { - Type: "integer", - }, - "spiloRunAsGroup": { - Type: "integer", - }, - "spiloFSGroup": { - Type: "integer", - }, - "standby": { - Type: "object", - Properties: map[string]apiextv1.JSONSchemaProps{ - "s3_wal_path": { - Type: "string", - }, - "gs_wal_path": { - Type: "string", - }, - "standby_host": { - Type: "string", - }, - "standby_port": { - Type: "string", - }, - }, - OneOf: []apiextv1.JSONSchemaProps{ - {Required: []string{"s3_wal_path"}}, - {Required: []string{"gs_wal_path"}}, - {Required: []string{"standby_host"}}, - }, - }, - "streams": { - Type: "array", - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "object", - Required: []string{"applicationId", "database", "tables"}, - Properties: map[string]apiextv1.JSONSchemaProps{ - "applicationId": { - Type: "string", - }, - "batchSize": { - Type: "integer", - }, - "database": { - Type: "string", - }, - "enableRecovery": { - Type: "boolean", - }, - "filter": { - Type: "object", - AdditionalProperties: &apiextv1.JSONSchemaPropsOrBool{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - "tables": { - Type: "object", - AdditionalProperties: &apiextv1.JSONSchemaPropsOrBool{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "object", - Required: []string{"eventType"}, - Properties: map[string]apiextv1.JSONSchemaProps{ - "eventType": { - Type: "string", - }, - "idColumn": { - Type: "string", - }, - "payloadColumn": { - Type: "string", - }, - "recoveryEventType": { - Type: "string", - }, - }, - }, - }, - }, - }, - }, - }, - }, - "teamId": { - Type: "string", - }, - "tls": { - Type: "object", - Required: []string{"secretName"}, - Properties: map[string]apiextv1.JSONSchemaProps{ - "secretName": { - Type: "string", - }, - "certificateFile": { - Type: "string", - }, - "privateKeyFile": { - Type: "string", - }, - "caFile": { - Type: "string", - }, - "caSecretName": { - Type: "string", - }, - }, - }, - "tolerations": { - Type: "array", - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "object", - Properties: map[string]apiextv1.JSONSchemaProps{ - "key": { - Type: "string", - }, - "operator": { - Type: "string", - Enum: []apiextv1.JSON{ - { - Raw: []byte(`"Equal"`), - }, - { - Raw: []byte(`"Exists"`), - }, - }, - }, - "value": { - Type: "string", - }, - "effect": { - Type: "string", - Enum: []apiextv1.JSON{ - { - Raw: []byte(`"NoExecute"`), - }, - { - Raw: []byte(`"NoSchedule"`), - }, - { - Raw: []byte(`"PreferNoSchedule"`), - }, - }, - }, - "tolerationSeconds": { - Type: "integer", - }, - }, - }, - }, - }, - "useLoadBalancer": { - Type: "boolean", - Description: "deprecated", - }, - "users": { - Type: "object", - AdditionalProperties: &apiextv1.JSONSchemaPropsOrBool{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "array", - Nullable: true, - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - Enum: []apiextv1.JSON{ - { - Raw: []byte(`"bypassrls"`), - }, - { - Raw: []byte(`"BYPASSRLS"`), - }, - { - Raw: []byte(`"nobypassrls"`), - }, - { - Raw: []byte(`"NOBYPASSRLS"`), - }, - { - Raw: []byte(`"createdb"`), - }, - { - Raw: []byte(`"CREATEDB"`), - }, - { - Raw: []byte(`"nocreatedb"`), - }, - { - Raw: []byte(`"NOCREATEDB"`), - }, - { - Raw: []byte(`"createrole"`), - }, - { - Raw: []byte(`"CREATEROLE"`), - }, - { - Raw: []byte(`"nocreaterole"`), - }, - { - Raw: []byte(`"NOCREATEROLE"`), - }, - { - Raw: []byte(`"inherit"`), - }, - { - Raw: []byte(`"INHERIT"`), - }, - { - Raw: []byte(`"noinherit"`), - }, - { - Raw: []byte(`"NOINHERIT"`), - }, - { - Raw: []byte(`"login"`), - }, - { - Raw: []byte(`"LOGIN"`), - }, - { - Raw: []byte(`"nologin"`), - }, - { - Raw: []byte(`"NOLOGIN"`), - }, - { - Raw: []byte(`"replication"`), - }, - { - Raw: []byte(`"REPLICATION"`), - }, - { - Raw: []byte(`"noreplication"`), - }, - { - Raw: []byte(`"NOREPLICATION"`), - }, - { - Raw: []byte(`"superuser"`), - }, - { - Raw: []byte(`"SUPERUSER"`), - }, - { - Raw: []byte(`"nosuperuser"`), - }, - { - Raw: []byte(`"NOSUPERUSER"`), - }, - }, - }, - }, - }, - }, - }, - "usersIgnoringSecretRotation": { - Type: "array", - Nullable: true, - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - "usersWithInPlaceSecretRotation": { - Type: "array", - Nullable: true, - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - "usersWithSecretRotation": { - Type: "array", - Nullable: true, - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - "volume": { - Type: "object", - Required: []string{"size"}, - Properties: map[string]apiextv1.JSONSchemaProps{ - "isSubPathExpr": { - Type: "boolean", - }, - "iops": { - Type: "integer", - }, - "selector": { - Type: "object", - Properties: map[string]apiextv1.JSONSchemaProps{ - "matchExpressions": { - Type: "array", - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "object", - Required: []string{"key", "operator"}, - Properties: map[string]apiextv1.JSONSchemaProps{ - "key": { - Type: "string", - }, - "operator": { - Type: "string", - Enum: []apiextv1.JSON{ - { - Raw: []byte(`"DoesNotExist"`), - }, - { - Raw: []byte(`"Exists"`), - }, - { - Raw: []byte(`"In"`), - }, - { - Raw: []byte(`"NotIn"`), - }, - }, - }, - "values": { - Type: "array", - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - }, - }, - }, - }, - "matchLabels": { - Type: "object", - XPreserveUnknownFields: util.True(), - }, - }, - }, - "size": { - Type: "string", - Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$", - }, - "storageClass": { - Type: "string", - }, - "subPath": { - Type: "string", - }, - "throughput": { - Type: "integer", - }, - }, - }, - }, - }, - "status": { - Type: "object", - AdditionalProperties: &apiextv1.JSONSchemaPropsOrBool{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - }, - }, -} - // OperatorConfigCRDResourceValidation to check applied manifest parameters var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{ OpenAPIV3Schema: &apiextv1.JSONSchemaProps{ @@ -2019,16 +966,20 @@ func buildCRD(name, kind, plural, list, short string, } } +//go:embed postgresql.crd.yaml +var postgresqlCRDYAML []byte + // PostgresCRD returns CustomResourceDefinition built from PostgresCRDResource -func PostgresCRD(crdCategories []string) *apiextv1.CustomResourceDefinition { - return buildCRD(PostgresCRDResouceName, - PostgresCRDResourceKind, - PostgresCRDResourcePlural, - PostgresCRDResourceList, - PostgresCRDResourceShort, - crdCategories, - PostgresCRDResourceColumns, - PostgresCRDResourceValidation) +func PostgresCRD(crdCategories []string) (*apiextv1.CustomResourceDefinition, error) { + var crd apiextv1.CustomResourceDefinition + err := yaml.Unmarshal(postgresqlCRDYAML, &crd) + if err != nil { + return nil, err + } + + crd.Spec.Names.Categories = crdCategories + + return &crd, nil } // ConfigurationCRD returns CustomResourceDefinition built from OperatorConfigCRDResource diff --git a/pkg/apis/acid.zalan.do/v1/postgresql_type.go b/pkg/apis/acid.zalan.do/v1/postgresql_type.go index ef6dfe7ff..2676aa07e 100644 --- a/pkg/apis/acid.zalan.do/v1/postgresql_type.go +++ b/pkg/apis/acid.zalan.do/v1/postgresql_type.go @@ -11,13 +11,25 @@ import ( // +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:object:root=true // Postgresql defines PostgreSQL Custom Resource Definition Object. +// +kubebuilder:resource:categories=all,shortName=pg,scope=Namespaced +// +kubebuilder:printcolumn:name="Team",type=string,JSONPath=`.spec.teamId`,description="Team responsible for Postgres cluster" +// +kubebuilder:printcolumn:name="Version",type=string,JSONPath=`.spec.postgresql.version`,description="PostgreSQL version" +// +kubebuilder:printcolumn:name="Pods",type=integer,JSONPath=`.spec.numberOfInstances`,description="Number of Pods per Postgres cluster" +// +kubebuilder:printcolumn:name="Volume",type=string,JSONPath=`.spec.volume.size`,description="Size of the bound volume" +// +kubebuilder:printcolumn:name="CPU-Request",type=string,JSONPath=`.spec.resources.requests.cpu`,description="Requested CPU for Postgres containers" +// +kubebuilder:printcolumn:name="Memory-Request",type=string,JSONPath=`.spec.resources.requests.memory`,description="Requested memory for Postgres containers" +// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`,description="Age of the PostgreSQL cluster" +// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.PostgresClusterStatus`,description="Current sync status of postgresql resource" +// +kubebuilder:subresource:status type Postgresql struct { metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` + metav1.ObjectMeta `json:"metadata"` - Spec PostgresSpec `json:"spec"` + Spec PostgresSpec `json:"spec"` + // +optional Status PostgresStatus `json:"status"` Error string `json:"-"` } @@ -25,9 +37,10 @@ type Postgresql struct { // PostgresSpec defines the specification for the PostgreSQL TPR. type PostgresSpec struct { PostgresqlParam `json:"postgresql"` - Volume `json:"volume,omitempty"` - Patroni `json:"patroni,omitempty"` - *Resources `json:"resources,omitempty"` + Volume `json:"volume"` + // +optional + Patroni `json:"patroni"` + *Resources `json:"resources,omitempty"` EnableConnectionPooler *bool `json:"enableConnectionPooler,omitempty"` EnableReplicaConnectionPooler *bool `json:"enableReplicaConnectionPooler,omitempty"` @@ -52,20 +65,33 @@ type PostgresSpec struct { // deprecated load balancer settings maintained for backward compatibility // see "Load balancers" operator docs - UseLoadBalancer *bool `json:"useLoadBalancer,omitempty"` + UseLoadBalancer *bool `json:"useLoadBalancer,omitempty"` + // deprecated ReplicaLoadBalancer *bool `json:"replicaLoadBalancer,omitempty"` // load balancers' source ranges are the same for master and replica services + // +nullable + // +kubebuilder:validation:items:Pattern=`^(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\/(\d|[1-2]\d|3[0-2])$` + // +optional AllowedSourceRanges []string `json:"allowedSourceRanges"` - Users map[string]UserFlags `json:"users,omitempty"` - UsersIgnoringSecretRotation []string `json:"usersIgnoringSecretRotation,omitempty"` - UsersWithSecretRotation []string `json:"usersWithSecretRotation,omitempty"` - UsersWithInPlaceSecretRotation []string `json:"usersWithInPlaceSecretRotation,omitempty"` + Users map[string]UserFlags `json:"users,omitempty"` + // +nullable + UsersIgnoringSecretRotation []string `json:"usersIgnoringSecretRotation,omitempty"` + // +nullable + UsersWithSecretRotation []string `json:"usersWithSecretRotation,omitempty"` + // +nullable + UsersWithInPlaceSecretRotation []string `json:"usersWithInPlaceSecretRotation,omitempty"` - NumberOfInstances int32 `json:"numberOfInstances"` - MaintenanceWindows []MaintenanceWindow `json:"maintenanceWindows,omitempty"` - Clone *CloneDescription `json:"clone,omitempty"` + // +kubebuilder:validation:Minimum=0 + NumberOfInstances int32 `json:"numberOfInstances"` + // +kubebuilder:validation:Schemaless + // +kubebuilder:validation:Type=array + // +kubebuilde:validation:items:Type=string + MaintenanceWindows []MaintenanceWindow `json:"maintenanceWindows,omitempty"` + Clone *CloneDescription `json:"clone,omitempty"` + // Note: usernames specified here as database owners must be declared + // in the users key of the spec key. Databases map[string]string `json:"databases,omitempty"` PreparedDatabases map[string]PreparedDatabase `json:"preparedDatabases,omitempty"` SchedulerName *string `json:"schedulerName,omitempty"` @@ -77,10 +103,11 @@ type PostgresSpec struct { ShmVolume *bool `json:"enableShmVolume,omitempty"` EnableLogicalBackup bool `json:"enableLogicalBackup,omitempty"` LogicalBackupRetention string `json:"logicalBackupRetention,omitempty"` - LogicalBackupSchedule string `json:"logicalBackupSchedule,omitempty"` - StandbyCluster *StandbyDescription `json:"standby,omitempty"` - PodAnnotations map[string]string `json:"podAnnotations,omitempty"` - ServiceAnnotations map[string]string `json:"serviceAnnotations,omitempty"` + // +kubebuilder:validation:Pattern=`^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$` + LogicalBackupSchedule string `json:"logicalBackupSchedule,omitempty"` + StandbyCluster *StandbyDescription `json:"standby,omitempty"` + PodAnnotations map[string]string `json:"podAnnotations,omitempty"` + ServiceAnnotations map[string]string `json:"serviceAnnotations,omitempty"` // MasterServiceAnnotations takes precedence over ServiceAnnotations for master role if not empty MasterServiceAnnotations map[string]string `json:"masterServiceAnnotations,omitempty"` // ReplicaServiceAnnotations takes precedence over ServiceAnnotations for replica role if not empty @@ -90,9 +117,10 @@ type PostgresSpec struct { Streams []Stream `json:"streams,omitempty"` Env []v1.EnvVar `json:"env,omitempty"` - // deprecated json tags - InitContainersOld []v1.Container `json:"init_containers,omitempty"` - PodPriorityClassNameOld string `json:"pod_priority_class_name,omitempty"` + // deprecated + InitContainersOld []v1.Container `json:"init_containers,omitempty"` + // deprecated + PodPriorityClassNameOld string `json:"pod_priority_class_name,omitempty"` } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object @@ -123,50 +151,84 @@ type PreparedSchema struct { type MaintenanceWindow struct { Everyday bool `json:"everyday,omitempty"` Weekday time.Weekday `json:"weekday,omitempty"` - StartTime metav1.Time `json:"startTime,omitempty"` - EndTime metav1.Time `json:"endTime,omitempty"` + StartTime metav1.Time `json:"startTime"` + EndTime metav1.Time `json:"endTime"` } // Volume describes a single volume in the manifest. type Volume struct { - Selector *metav1.LabelSelector `json:"selector,omitempty"` - Size string `json:"size"` - StorageClass string `json:"storageClass,omitempty"` - SubPath string `json:"subPath,omitempty"` - IsSubPathExpr *bool `json:"isSubPathExpr,omitempty"` - Iops *int64 `json:"iops,omitempty"` - Throughput *int64 `json:"throughput,omitempty"` - VolumeType string `json:"type,omitempty"` + Selector *metav1.LabelSelector `json:"selector,omitempty"` + // +kubebuilder:validation:Pattern=`^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$` + Size string `json:"size"` + StorageClass string `json:"storageClass,omitempty"` + SubPath string `json:"subPath,omitempty"` + IsSubPathExpr *bool `json:"isSubPathExpr,omitempty"` + Iops *int64 `json:"iops,omitempty"` + Throughput *int64 `json:"throughput,omitempty"` + VolumeType string `json:"type,omitempty"` } // AdditionalVolume specs additional optional volumes for statefulset type AdditionalVolume struct { - Name string `json:"name"` - MountPath string `json:"mountPath"` - SubPath string `json:"subPath,omitempty"` - IsSubPathExpr *bool `json:"isSubPathExpr,omitempty"` - TargetContainers []string `json:"targetContainers"` - VolumeSource v1.VolumeSource `json:"volumeSource"` + Name string `json:"name"` + MountPath string `json:"mountPath"` + SubPath string `json:"subPath,omitempty"` + IsSubPathExpr *bool `json:"isSubPathExpr,omitempty"` + // +nullable + // +optional + TargetContainers []string `json:"targetContainers"` + // +kubebuilder:validation:XPreserveUnknownFields + // +kubebuilder:validation:Type=object + // +kubebuilder:validation:Schemaless + VolumeSource v1.VolumeSource `json:"volumeSource"` } // PostgresqlParam describes PostgreSQL version and pairs of configuration parameter name - values. type PostgresqlParam struct { + // +kubebuilder:validation:Enum=13;14;15;16;17 PgVersion string `json:"version"` Parameters map[string]string `json:"parameters,omitempty"` } // ResourceDescription describes CPU and memory resources defined for a cluster. type ResourceDescription struct { - CPU *string `json:"cpu,omitempty"` - Memory *string `json:"memory,omitempty"` + // Decimal natural followed by m, or decimal natural followed by + // dot followed by up to three decimal digits. + // + // This is because the Kubernetes CPU resource has millis as the + // maximum precision. The actual values are checked in code + // because the regular expression would be huge and horrible and + // not very helpful in validation error messages; this one checks + // only the format of the given number. + // + // https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu + // + // Note: the value specified here must not be zero or be lower + // than the corresponding request. + // +kubebuilder:validation:Pattern=`^(\d+m|\d+(\.\d{1,3})?)$` + CPU *string `json:"cpu,omitempty"` + // You can express memory as a plain integer or as a fixed-point + // integer using one of these suffixes: E, P, T, G, M, k. You can + // also use the power-of-two equivalents: Ei, Pi, Ti, Gi, Mi, Ki + // + // https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-memory + // + // Note: the value specified here must not be zero or be higher + // than the corresponding limit. + // +kubebuilder:validation:Pattern=`^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$` + Memory *string `json:"memory,omitempty"` + // +kubebuilder:validation:Pattern=`^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$` HugePages2Mi *string `json:"hugepages-2Mi,omitempty"` + // +kubebuilder:validation:Pattern=`^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$` HugePages1Gi *string `json:"hugepages-1Gi,omitempty"` } // Resources describes requests and limits for the cluster resouces. type Resources struct { - ResourceRequests ResourceDescription `json:"requests,omitempty"` - ResourceLimits ResourceDescription `json:"limits,omitempty"` + // +optional + ResourceRequests ResourceDescription `json:"requests"` + // +optional + ResourceLimits ResourceDescription `json:"limits"` } // Patroni contains Patroni-specific configuration @@ -176,7 +238,7 @@ type Patroni struct { TTL uint32 `json:"ttl,omitempty"` LoopWait uint32 `json:"loop_wait,omitempty"` RetryTimeout uint32 `json:"retry_timeout,omitempty"` - MaximumLagOnFailover float32 `json:"maximum_lag_on_failover,omitempty"` // float32 because https://github.com/kubernetes/kubernetes/issues/30213 + MaximumLagOnFailover int64 `json:"maximum_lag_on_failover,omitempty"` Slots map[string]map[string]string `json:"slots,omitempty"` SynchronousMode bool `json:"synchronous_mode,omitempty"` SynchronousModeStrict bool `json:"synchronous_mode_strict,omitempty"` @@ -185,6 +247,7 @@ type Patroni struct { } // StandbyDescription contains remote primary config or s3/gs wal path +// +kubebuilder:validation:ExactlyOneOf=s3_wal_path;gs_wal_path;standby_host type StandbyDescription struct { S3WalPath string `json:"s3_wal_path,omitempty"` GSWalPath string `json:"gs_wal_path,omitempty"` @@ -194,6 +257,7 @@ type StandbyDescription struct { // TLSDescription specs TLS properties type TLSDescription struct { + // +required SecretName string `json:"secretName,omitempty"` CertificateFile string `json:"certificateFile,omitempty"` PrivateKeyFile string `json:"privateKeyFile,omitempty"` @@ -203,8 +267,14 @@ type TLSDescription struct { // CloneDescription describes which cluster the new should clone and up to which point in time type CloneDescription struct { - ClusterName string `json:"cluster,omitempty"` - UID string `json:"uid,omitempty"` + // +required + ClusterName string `json:"cluster,omitempty"` + // +kubebuilder:validation:Format=uuid + UID string `json:"uid,omitempty"` + // The regexp matches the date-time format (RFC 3339 Section 5.6) that specifies a timezone as an offset relative to UTC + // Example: 1996-12-19T16:39:57-08:00 + // Note: this field requires a timezone + // +kubebuilder:validation:Pattern=`^([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])[Tt]([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(([+-]([01][0-9]|2[0-3]):[0-5][0-9]))$` EndTimestamp string `json:"timestamp,omitempty"` S3WalPath string `json:"s3_wal_path,omitempty"` S3Endpoint string `json:"s3_endpoint,omitempty"` @@ -224,6 +294,8 @@ type Sidecar struct { } // UserFlags defines flags (such as superuser, nologin) that could be assigned to individual users +// +kubebuilder:validation:items:Enum=bypassrls;BYPASSRLS;nobypassrls;NOBYPASSRLS;createdb;CREATEDB;nocreatedb;NOCREATEDB;createrole;CREATEROLE;nocreaterole;NOCREATEROLE;inherit;INHERIT;noinherit;NOINHERIT;login;LOGIN;nologin;NOLOGIN;replication;REPLICATION;noreplication;NOREPLICATION;superuser;SUPERUSER;nosuperuser;NOSUPERUSER +// +nullable type UserFlags []string // PostgresStatus contains status of the PostgreSQL cluster (running, creation failed etc.) @@ -242,26 +314,30 @@ type PostgresStatus struct { // makes sense to expose. E.g. pool size (min/max boundaries), max client // connections etc. type ConnectionPooler struct { + // +kubebuilder:validation:Minimum=1 NumberOfInstances *int32 `json:"numberOfInstances,omitempty"` Schema string `json:"schema,omitempty"` User string `json:"user,omitempty"` - Mode string `json:"mode,omitempty"` - DockerImage string `json:"dockerImage,omitempty"` - MaxDBConnections *int32 `json:"maxDBConnections,omitempty"` + // +kubebuilder:validation:Enum=session;transaction + Mode string `json:"mode,omitempty"` + DockerImage string `json:"dockerImage,omitempty"` + MaxDBConnections *int32 `json:"maxDBConnections,omitempty"` *Resources `json:"resources,omitempty"` } // Stream defines properties for creating FabricEventStream resources type Stream struct { - ApplicationId string `json:"applicationId"` - Database string `json:"database"` - Tables map[string]StreamTable `json:"tables"` - Filter map[string]*string `json:"filter,omitempty"` - BatchSize *uint32 `json:"batchSize,omitempty"` - CPU *string `json:"cpu,omitempty"` - Memory *string `json:"memory,omitempty"` - EnableRecovery *bool `json:"enableRecovery,omitempty"` + ApplicationId string `json:"applicationId"` + Database string `json:"database"` + Tables map[string]StreamTable `json:"tables"` + Filter map[string]*string `json:"filter,omitempty"` + BatchSize *uint32 `json:"batchSize,omitempty"` + // +kubebuilder:validation:Pattern=`^(\d+m|\d+(\.\d{1,3})?)$` + CPU *string `json:"cpu,omitempty"` + // +kubebuilder:validation:Pattern=`^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$` + Memory *string `json:"memory,omitempty"` + EnableRecovery *bool `json:"enableRecovery,omitempty"` } // StreamTable defines properties of outbox tables for FabricEventStreams diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index 9bc39a9db..bb45ba31f 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -412,7 +412,7 @@ PatroniInitDBParams: } if patroni.MaximumLagOnFailover >= 0 { - config.Bootstrap.DCS.MaximumLagOnFailover = patroni.MaximumLagOnFailover + config.Bootstrap.DCS.MaximumLagOnFailover = float32(patroni.MaximumLagOnFailover) } if patroni.LoopWait != 0 { config.Bootstrap.DCS.LoopWait = patroni.LoopWait diff --git a/pkg/controller/util.go b/pkg/controller/util.go index df043dfd9..f5ee8cb86 100644 --- a/pkg/controller/util.go +++ b/pkg/controller/util.go @@ -103,7 +103,11 @@ func (c *Controller) createOperatorCRD(desiredCrd *apiextv1.CustomResourceDefini } func (c *Controller) createPostgresCRD() error { - return c.createOperatorCRD(acidv1.PostgresCRD(c.opConfig.CRDCategories)) + crd, err := acidv1.PostgresCRD(c.opConfig.CRDCategories) + if err != nil { + return fmt.Errorf("could not create Postgres CRD object: %v", err) + } + return c.createOperatorCRD(crd) } func (c *Controller) createConfigurationCRD() error { From 97115d6e3d72b02c10bd60c16bb7d9e0d8da107e Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Tue, 13 Jan 2026 09:33:24 +0100 Subject: [PATCH 07/21] add annotation to ignore resources thresholds (#3030) * add annotation to ignore resources thresholds * add test case when annotation key is set but value is not true --- .../crds/operatorconfigurations.yaml | 2 + charts/postgres-operator/values.yaml | 3 + docs/reference/operator_parameters.md | 10 ++- manifests/configmap.yaml | 1 + manifests/operatorconfiguration.crd.yaml | 2 + ...gresql-operator-default-configuration.yaml | 1 + pkg/apis/acid.zalan.do/v1/crds.go | 3 + .../v1/operator_configuration_type.go | 2 + pkg/cluster/k8sres.go | 12 ++- pkg/cluster/k8sres_test.go | 75 +++++++++++++++++-- pkg/controller/operator_config.go | 1 + pkg/util/config/config.go | 2 + 12 files changed, 105 insertions(+), 9 deletions(-) diff --git a/charts/postgres-operator/crds/operatorconfigurations.yaml b/charts/postgres-operator/crds/operatorconfigurations.yaml index 58e84bd2f..c903a9319 100644 --- a/charts/postgres-operator/crds/operatorconfigurations.yaml +++ b/charts/postgres-operator/crds/operatorconfigurations.yaml @@ -96,6 +96,8 @@ spec: default: "" ignore_instance_limits_annotation_key: type: string + ignore_resources_limits_annotation_key: + type: string kubernetes_use_configmaps: type: boolean default: false diff --git a/charts/postgres-operator/values.yaml b/charts/postgres-operator/values.yaml index 426e4267d..4e5d9b7cb 100644 --- a/charts/postgres-operator/values.yaml +++ b/charts/postgres-operator/values.yaml @@ -43,6 +43,9 @@ configGeneral: # key name for annotation to ignore globally configured instance limits # ignore_instance_limits_annotation_key: "" + # key name for annotation to ignore globally configured resources thresholds + # ignore_resources_limits_annotation_key: "" + # Select if setup uses endpoints (default), or configmaps to manage leader (DCS=k8s) # kubernetes_use_configmaps: false diff --git a/docs/reference/operator_parameters.md b/docs/reference/operator_parameters.md index 5662d6b8e..4327dc45f 100644 --- a/docs/reference/operator_parameters.md +++ b/docs/reference/operator_parameters.md @@ -163,7 +163,15 @@ Those are top-level keys, containing both leaf keys and groups. for some clusters it might be required to scale beyond the limits that can be configured with `min_instances` and `max_instances` options. You can define an annotation key that can be used as a toggle in cluster manifests to ignore - globally configured instance limits. The default is empty. + globally configured instance limits. The value must be `"true"` to be + effective. The default is empty which means the feature is disabled. + +* **ignore_resources_limits_annotation_key** + for some clusters it might be required to request resources beyond the globally + configured thresholds for maximum requests and minimum limits. You can define + an annotation key that can be used as a toggle in cluster manifests to ignore + the thresholds. The value must be `"true"` to be effective. The default is empty + which means the feature is disabled. * **resync_period** period between consecutive sync requests. The default is `30m`. diff --git a/manifests/configmap.yaml b/manifests/configmap.yaml index fcf08c3f8..6d51053bb 100644 --- a/manifests/configmap.yaml +++ b/manifests/configmap.yaml @@ -75,6 +75,7 @@ data: # infrastructure_roles_secret_name: "postgresql-infrastructure-roles" # infrastructure_roles_secrets: "secretname:monitoring-roles,userkey:user,passwordkey:password,rolekey:inrole" # ignore_instance_limits_annotation_key: "" + # ignore_resources_limits_annotation_key: "" # inherited_annotations: owned-by # inherited_labels: application,environment # kube_iam_role: "" diff --git a/manifests/operatorconfiguration.crd.yaml b/manifests/operatorconfiguration.crd.yaml index 466f5190e..c78ceb77a 100644 --- a/manifests/operatorconfiguration.crd.yaml +++ b/manifests/operatorconfiguration.crd.yaml @@ -94,6 +94,8 @@ spec: default: "" ignore_instance_limits_annotation_key: type: string + ignore_resources_limits_annotation_key: + type: string kubernetes_use_configmaps: type: boolean default: false diff --git a/manifests/postgresql-operator-default-configuration.yaml b/manifests/postgresql-operator-default-configuration.yaml index 5251f9a06..d4f9fc812 100644 --- a/manifests/postgresql-operator-default-configuration.yaml +++ b/manifests/postgresql-operator-default-configuration.yaml @@ -14,6 +14,7 @@ configuration: enable_team_id_clustername_prefix: false etcd_host: "" # ignore_instance_limits_annotation_key: "" + # ignore_resources_limits_annotation_key: "" # kubernetes_use_configmaps: false max_instances: -1 min_instances: -1 diff --git a/pkg/apis/acid.zalan.do/v1/crds.go b/pkg/apis/acid.zalan.do/v1/crds.go index d3339f396..b3cb341a0 100644 --- a/pkg/apis/acid.zalan.do/v1/crds.go +++ b/pkg/apis/acid.zalan.do/v1/crds.go @@ -122,6 +122,9 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{ "ignore_instance_limits_annotation_key": { Type: "string", }, + "ignore_resources_limits_annotation_key": { + Type: "string", + }, "kubernetes_use_configmaps": { Type: "boolean", }, diff --git a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go index cd11b9173..80cfbbcd7 100644 --- a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go +++ b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go @@ -288,6 +288,8 @@ type OperatorConfigurationData struct { MinInstances int32 `json:"min_instances,omitempty"` MaxInstances int32 `json:"max_instances,omitempty"` IgnoreInstanceLimitsAnnotationKey string `json:"ignore_instance_limits_annotation_key,omitempty"` + + IgnoreResourcesLimitsAnnotationKey string `json:"ignore_resources_limits_annotation_key,omitempty"` } // Duration shortens this frequently used name diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index bb45ba31f..7c267fcee 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -313,6 +313,14 @@ func (c *Cluster) generateResourceRequirements( specLimits := acidv1.ResourceDescription{} result := v1.ResourceRequirements{} + enforceThresholds := true + resourcesLimitAnnotationKey := c.OpConfig.IgnoreResourcesLimitsAnnotationKey + if resourcesLimitAnnotationKey != "" { + if value, exists := c.ObjectMeta.Annotations[resourcesLimitAnnotationKey]; exists && value == "true" { + enforceThresholds = false + } + } + if resources != nil { specRequests = resources.ResourceRequests specLimits = resources.ResourceLimits @@ -329,7 +337,7 @@ func (c *Cluster) generateResourceRequirements( } // enforce minimum cpu and memory limits for Postgres containers only - if containerName == constants.PostgresContainerName { + if containerName == constants.PostgresContainerName && enforceThresholds { if err = c.enforceMinResourceLimits(&result); err != nil { return nil, fmt.Errorf("could not enforce minimum resource limits: %v", err) } @@ -344,7 +352,7 @@ func (c *Cluster) generateResourceRequirements( } // enforce maximum cpu and memory requests for Postgres containers only - if containerName == constants.PostgresContainerName { + if containerName == constants.PostgresContainerName && enforceThresholds { if err = c.enforceMaxResourceRequests(&result); err != nil { return nil, fmt.Errorf("could not enforce maximum resource requests: %v", err) } diff --git a/pkg/cluster/k8sres_test.go b/pkg/cluster/k8sres_test.go index 6bd87366d..43b4725e4 100644 --- a/pkg/cluster/k8sres_test.go +++ b/pkg/cluster/k8sres_test.go @@ -3130,6 +3130,9 @@ func TestGenerateResourceRequirements(t *testing.T) { PodRoleLabel: "spilo-role", } + configWithEnabledIgnoreResourcesLimits := configResources + configWithEnabledIgnoreResourcesLimits.IgnoreResourcesLimitsAnnotationKey = "zalando.org/ignore-resources-limits" + tests := []struct { subTest string config config.Config @@ -3465,14 +3468,15 @@ func TestGenerateResourceRequirements(t *testing.T) { { subTest: "test enforcing min cpu and memory limit", config: config.Config{ - Resources: configResources, + Resources: configWithEnabledIgnoreResourcesLimits, PodManagementPolicy: "ordered_ready", SetMemoryRequestToLimit: false, }, pgSpec: acidv1.Postgresql{ ObjectMeta: metav1.ObjectMeta{ - Name: clusterName, - Namespace: namespace, + Name: clusterName, + Namespace: namespace, + Annotations: map[string]string{"zalando.org/ignore-resources-limits": "false"}, }, Spec: acidv1.PostgresSpec{ Resources: &acidv1.Resources{ @@ -3490,6 +3494,35 @@ func TestGenerateResourceRequirements(t *testing.T) { ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("250m"), Memory: k8sutil.StringToPointer("250Mi")}, }, }, + { + subTest: "ingnore min cpu and memory limit threshold", + config: config.Config{ + Resources: configWithEnabledIgnoreResourcesLimits, + PodManagementPolicy: "ordered_ready", + SetMemoryRequestToLimit: false, + }, + pgSpec: acidv1.Postgresql{ + ObjectMeta: metav1.ObjectMeta{ + Name: clusterName, + Namespace: namespace, + Annotations: map[string]string{"zalando.org/ignore-resources-limits": "true"}, + }, + Spec: acidv1.PostgresSpec{ + Resources: &acidv1.Resources{ + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("100m"), Memory: k8sutil.StringToPointer("100Mi")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("200m"), Memory: k8sutil.StringToPointer("200Mi")}, + }, + TeamID: "acid", + Volume: acidv1.Volume{ + Size: "1G", + }, + }, + }, + expectedResources: acidv1.Resources{ + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("100m"), Memory: k8sutil.StringToPointer("100Mi")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("200m"), Memory: k8sutil.StringToPointer("200Mi")}, + }, + }, { subTest: "test min cpu and memory limit are not enforced on sidecar", config: config.Config{ @@ -3527,14 +3560,15 @@ func TestGenerateResourceRequirements(t *testing.T) { { subTest: "test enforcing max cpu and memory requests", config: config.Config{ - Resources: configResources, + Resources: configWithEnabledIgnoreResourcesLimits, PodManagementPolicy: "ordered_ready", SetMemoryRequestToLimit: false, }, pgSpec: acidv1.Postgresql{ ObjectMeta: metav1.ObjectMeta{ - Name: clusterName, - Namespace: namespace, + Name: clusterName, + Namespace: namespace, + Annotations: map[string]string{"zalando.org/ignore-resources-limits": "yes"}, }, Spec: acidv1.PostgresSpec{ Resources: &acidv1.Resources{ @@ -3552,6 +3586,35 @@ func TestGenerateResourceRequirements(t *testing.T) { ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("2"), Memory: k8sutil.StringToPointer("4Gi")}, }, }, + { + subTest: "ignore max cpu and memory requests limit", + config: config.Config{ + Resources: configWithEnabledIgnoreResourcesLimits, + PodManagementPolicy: "ordered_ready", + SetMemoryRequestToLimit: false, + }, + pgSpec: acidv1.Postgresql{ + ObjectMeta: metav1.ObjectMeta{ + Name: clusterName, + Namespace: namespace, + Annotations: map[string]string{"zalando.org/ignore-resources-limits": "true"}, + }, + Spec: acidv1.PostgresSpec{ + Resources: &acidv1.Resources{ + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("2Gi")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("2"), Memory: k8sutil.StringToPointer("4Gi")}, + }, + TeamID: "acid", + Volume: acidv1.Volume{ + Size: "1G", + }, + }, + }, + expectedResources: acidv1.Resources{ + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("2Gi")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("2"), Memory: k8sutil.StringToPointer("4Gi")}, + }, + }, { subTest: "test SetMemoryRequestToLimit flag but raise only until max memory request", config: config.Config{ diff --git a/pkg/controller/operator_config.go b/pkg/controller/operator_config.go index 4650fe8d7..24d4ffcd3 100644 --- a/pkg/controller/operator_config.go +++ b/pkg/controller/operator_config.go @@ -44,6 +44,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur result.MinInstances = fromCRD.MinInstances result.MaxInstances = fromCRD.MaxInstances result.IgnoreInstanceLimitsAnnotationKey = fromCRD.IgnoreInstanceLimitsAnnotationKey + result.IgnoreResourcesLimitsAnnotationKey = fromCRD.IgnoreResourcesLimitsAnnotationKey result.ResyncPeriod = util.CoalesceDuration(time.Duration(fromCRD.ResyncPeriod), "30m") result.RepairPeriod = util.CoalesceDuration(time.Duration(fromCRD.RepairPeriod), "5m") result.SetMemoryRequestToLimit = fromCRD.SetMemoryRequestToLimit diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index aca9754a9..858a58b8c 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -66,6 +66,8 @@ type Resources struct { MaxInstances int32 `name:"max_instances" default:"-1"` MinInstances int32 `name:"min_instances" default:"-1"` IgnoreInstanceLimitsAnnotationKey string `name:"ignore_instance_limits_annotation_key"` + + IgnoreResourcesLimitsAnnotationKey string `name:"ignore_resources_limits_annotation_key"` } type InfrastructureRole struct { From 32d6d0a7a7c87e43f38b8a7feeed00264fc5a3de Mon Sep 17 00:00:00 2001 From: Mikkel Oscar Lyderik Larsen Date: Tue, 13 Jan 2026 17:23:56 +0100 Subject: [PATCH 08/21] Fix serving CRD at runtime (#3031) * Fix serving CRD at runtime Signed-off-by: Mikkel Oscar Lyderik Larsen * Correctly string quote version enum Signed-off-by: Mikkel Oscar Lyderik Larsen --------- Signed-off-by: Mikkel Oscar Lyderik Larsen --- Makefile | 4 ++-- manifests/postgresql.crd.yaml | 10 +++++----- pkg/apis/acid.zalan.do/v1/crds.go | 7 +++---- pkg/apis/acid.zalan.do/v1/postgresql_type.go | 2 +- pkg/controller/util.go | 12 ++---------- 5 files changed, 13 insertions(+), 22 deletions(-) diff --git a/Makefile b/Makefile index 241900a55..4f5441cf1 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ GITSTATUS = $(shell git status --porcelain || echo "no changes") SOURCES = cmd/main.go VERSION ?= $(shell git describe --tags --always --dirty) CRD_SOURCES = $(shell find pkg/apis/zalando.org pkg/apis/acid.zalan.do -name '*.go' -not -name '*.deepcopy.go') -GENERATED_CRDS = manifests/postgresteam.crd.yaml manifests/postgresql.crd.yaml +GENERATED_CRDS = manifests/postgresteam.crd.yaml manifests/postgresql.crd.yaml pkg/apis/acid.zalan.do/v1/postgresql.crd.yaml GENERATED = pkg/apis/zalando.org/v1/zz_generated.deepcopy.go pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go DIRS := cmd pkg PKG := `go list ./... | grep -v /vendor/` @@ -55,7 +55,7 @@ default: local clean: rm -rf build rm $(GENERATED) - rm pkg/apis/acid.zalan.do/v1/postgresql.crd.yaml + rm $(GENERATED_CRDS) verify: hack/verify-codegen.sh diff --git a/manifests/postgresql.crd.yaml b/manifests/postgresql.crd.yaml index 44e6a1def..c16d3cf14 100644 --- a/manifests/postgresql.crd.yaml +++ b/manifests/postgresql.crd.yaml @@ -3523,11 +3523,11 @@ spec: type: object version: enum: - - 13 - - 14 - - 15 - - 16 - - 17 + - "13" + - "14" + - "15" + - "16" + - "17" type: string required: - version diff --git a/pkg/apis/acid.zalan.do/v1/crds.go b/pkg/apis/acid.zalan.do/v1/crds.go index b3cb341a0..1ab85c905 100644 --- a/pkg/apis/acid.zalan.do/v1/crds.go +++ b/pkg/apis/acid.zalan.do/v1/crds.go @@ -4,12 +4,11 @@ import ( _ "embed" "fmt" - apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "github.com/stretchr/testify/assert/yaml" acidzalando "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do" "github.com/zalando/postgres-operator/pkg/util" + apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/yaml" ) // CRDResource* define names necesssary for the k8s CRD API diff --git a/pkg/apis/acid.zalan.do/v1/postgresql_type.go b/pkg/apis/acid.zalan.do/v1/postgresql_type.go index 2676aa07e..17fecd7a1 100644 --- a/pkg/apis/acid.zalan.do/v1/postgresql_type.go +++ b/pkg/apis/acid.zalan.do/v1/postgresql_type.go @@ -185,7 +185,7 @@ type AdditionalVolume struct { // PostgresqlParam describes PostgreSQL version and pairs of configuration parameter name - values. type PostgresqlParam struct { - // +kubebuilder:validation:Enum=13;14;15;16;17 + // +kubebuilder:validation:Enum="13";"14";"15";"16";"17" PgVersion string `json:"version"` Parameters map[string]string `json:"parameters,omitempty"` } diff --git a/pkg/controller/util.go b/pkg/controller/util.go index f5ee8cb86..87962f7b9 100644 --- a/pkg/controller/util.go +++ b/pkg/controller/util.go @@ -2,14 +2,12 @@ package controller import ( "context" - "encoding/json" "fmt" "strings" v1 "k8s.io/api/core/v1" apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/wait" acidv1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" @@ -65,15 +63,9 @@ func (c *Controller) createOperatorCRD(desiredCrd *apiextv1.CustomResourceDefini } if crd != nil { c.logger.Infof("customResourceDefinition %q is already registered and will only be updated", crd.Name) - // copy annotations and labels from existing CRD since we do not define them - desiredCrd.Annotations = crd.Annotations - desiredCrd.Labels = crd.Labels - patch, err := json.Marshal(desiredCrd) + crd.Spec = desiredCrd.Spec + _, err := c.KubeClient.CustomResourceDefinitions().Update(context.TODO(), crd, metav1.UpdateOptions{}) if err != nil { - return fmt.Errorf("could not marshal new customResourceDefintion %q: %v", desiredCrd.Name, err) - } - if _, err := c.KubeClient.CustomResourceDefinitions().Patch( - context.TODO(), crd.Name, types.MergePatchType, patch, metav1.PatchOptions{}); err != nil { return fmt.Errorf("could not update customResourceDefinition %q: %v", crd.Name, err) } } From ad9ae4ec1b5db0025bd89458464cac45c483b973 Mon Sep 17 00:00:00 2001 From: Mikkel Oscar Lyderik Larsen Date: Fri, 16 Jan 2026 17:56:06 +0100 Subject: [PATCH 09/21] Fix linting issues in delivery.yaml (#3032) Signed-off-by: Mikkel Oscar Lyderik Larsen --- delivery.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/delivery.yaml b/delivery.yaml index 11e17c012..a8716399c 100644 --- a/delivery.yaml +++ b/delivery.yaml @@ -17,8 +17,8 @@ pipeline: image: cdp-runtime/go cache: paths: - - /go/pkg/mod # pkg cache for Go modules - - ~/.cache/go-build # Go build cache + - /go/pkg/mod # pkg cache for Go modules + - ~/.cache/go-build # Go build cache commands: - desc: Run unit tests cmd: | @@ -65,7 +65,7 @@ pipeline: else IMAGE=${MULTI_ARCH_REGISTRY}/postgres-operator-ui-test fi - + make appjs docker buildx create --config /etc/cdp-buildkitd.toml --driver-opt network=host --bootstrap --use docker buildx build --platform linux/amd64,linux/arm64 \ From b97de5d7f17c82701da5da1b50c044864d5d36bb Mon Sep 17 00:00:00 2001 From: Polina Bungina <27892524+hughcapet@users.noreply.github.com> Date: Mon, 19 Jan 2026 13:54:27 +0100 Subject: [PATCH 10/21] Standby section improvements (#3033) - Allow standby_host to be specified together with wal_path - Add standby_primary_slot_name --- .../postgres-operator/crds/postgresqls.yaml | 8 +++- docs/administrator.md | 8 ++-- docs/reference/cluster_manifest.md | 13 +++++- docs/user.md | 15 ++++++- hack/adjust_postgresql_crd.sh | 8 ++-- manifests/postgresql.crd.yaml | 15 +++++-- manifests/standby-manifest.yaml | 4 +- pkg/apis/acid.zalan.do/v1/postgresql_type.go | 14 +++--- pkg/cluster/k8sres.go | 32 ++++++++------ pkg/cluster/k8sres_test.go | 43 ++++++++++++++++++- pkg/cluster/sync.go | 17 ++++++++ pkg/cluster/sync_test.go | 35 +++++++++++++++ 12 files changed, 176 insertions(+), 36 deletions(-) diff --git a/charts/postgres-operator/crds/postgresqls.yaml b/charts/postgres-operator/crds/postgresqls.yaml index 667c58efa..cbf5c98ef 100644 --- a/charts/postgres-operator/crds/postgresqls.yaml +++ b/charts/postgres-operator/crds/postgresqls.yaml @@ -493,13 +493,19 @@ spec: type: string standby_port: type: string - oneOf: + standby_primary_slot_name: + type: string + anyOf: - required: - s3_wal_path - required: - gs_wal_path - required: - standby_host + not: + required: + - s3_wal_path + - gs_wal_path streams: type: array items: diff --git a/docs/administrator.md b/docs/administrator.md index d7cda8e8b..fe36e3744 100644 --- a/docs/administrator.md +++ b/docs/administrator.md @@ -1346,10 +1346,12 @@ If you are using [additional environment variables](#custom-pod-environment-vari to access your backup location you have to copy those variables and prepend the `STANDBY_` prefix for Spilo to find the backups and WAL files to stream. -Alternatively, standby clusters can also stream from a remote primary cluster. +Standby clusters can also stream from a remote primary cluster. You have to specify the host address. Port is optional and defaults to 5432. -Note, that only one of the options (`s3_wal_path`, `gs_wal_path`, -`standby_host`) can be present under the `standby` top-level key. +You can combine `standby_host` with either `s3_wal_path` or `gs_wal_path` +for additional redundancy. Note that `s3_wal_path` and `gs_wal_path` are +mutually exclusive. At least one of `s3_wal_path`, `gs_wal_path`, or +`standby_host` must be specified under the `standby` top-level key. ## Logical backups diff --git a/docs/reference/cluster_manifest.md b/docs/reference/cluster_manifest.md index ab0353202..7b4ef7ada 100644 --- a/docs/reference/cluster_manifest.md +++ b/docs/reference/cluster_manifest.md @@ -457,22 +457,31 @@ under the `clone` top-level key and do not affect the already running cluster. On startup, an existing `standby` top-level key creates a standby Postgres cluster streaming from a remote location - either from a S3 or GCS WAL -archive or a remote primary. Only one of options is allowed and required -if the `standby` key is present. +archive, a remote primary, or a combination of both. At least one of +`s3_wal_path`, `gs_wal_path`, or `standby_host` must be specified. +Note that `s3_wal_path` and `gs_wal_path` are mutually exclusive. * **s3_wal_path** the url to S3 bucket containing the WAL archive of the remote primary. + Can be combined with `standby_host` for additional redundancy. * **gs_wal_path** the url to GS bucket containing the WAL archive of the remote primary. + Can be combined with `standby_host` for additional redundancy. * **standby_host** hostname or IP address of the primary to stream from. + Can be specified alone or combined with either `s3_wal_path` or `gs_wal_path`. * **standby_port** TCP port on which the primary is listening for connections. Patroni will use `"5432"` if not set. +* **standby_primary_slot_name** + name of the replication slot to use on the primary server when streaming + from a remote primary. See the Patroni documentation + [here](https://patroni.readthedocs.io/en/latest/standby_cluster.html) for more details. Optional. + ## Volume properties Those parameters are grouped under the `volume` top-level key and define the diff --git a/docs/user.md b/docs/user.md index c1a7c7d45..db33d0bd6 100644 --- a/docs/user.md +++ b/docs/user.md @@ -900,8 +900,9 @@ the PostgreSQL version between source and target cluster has to be the same. To start a cluster as standby, add the following `standby` section in the YAML file. You can stream changes from archived WAL files (AWS S3 or Google Cloud -Storage) or from a remote primary. Only one option can be specified in the -manifest: +Storage), from a remote primary, or combine a remote primary with a WAL archive. +At least one of `s3_wal_path`, `gs_wal_path`, or `standby_host` must be specified. +Note that `s3_wal_path` and `gs_wal_path` are mutually exclusive. ```yaml spec: @@ -929,6 +930,16 @@ spec: standby_port: "5433" ``` +You can also combine a remote primary with a WAL archive for additional redundancy: + +```yaml +spec: + standby: + standby_host: "acid-minimal-cluster.default" + standby_port: "5433" + s3_wal_path: "s3:///spilo///wal/" +``` + Note, that the pods and services use the same role labels like for normal clusters: The standby leader is labeled as `master`. When using the `standby_host` option you have to copy the credentials from the source cluster's secrets to successfully diff --git a/hack/adjust_postgresql_crd.sh b/hack/adjust_postgresql_crd.sh index cceb33f64..d06b74a2d 100755 --- a/hack/adjust_postgresql_crd.sh +++ b/hack/adjust_postgresql_crd.sh @@ -5,15 +5,17 @@ # # Injections: # -# * oneOf: for the standby field to enforce that only one of s3_wal_path, gs_wal_path or standby_host is set. -# * This can later be done with // +kubebuilder:validation:ExactlyOneOf marker, but this requires latest Kubernetes version. (Currently the operator depends on v1.32.9) +# * oneOf: for the standby field to enforce validation rules: +# - s3_wal_path and gs_wal_path are mutually exclusive +# - standby_host can be specified alone or with either s3_wal_path OR gs_wal_path +# - at least one of s3_wal_path, gs_wal_path, or standby_host must be set # * type: string and pattern for the maintenanceWindows items. file="${1:-"manifests/postgresql.crd.yaml"}" sed -i '/^[[:space:]]*standby:$/{ # Capture the indentation - s/^\([[:space:]]*\)standby:$/\1standby:\n\1 oneOf:\n\1 - required:\n\1 - s3_wal_path\n\1 - required:\n\1 - gs_wal_path\n\1 - required:\n\1 - standby_host/ + s/^\([[:space:]]*\)standby:$/\1standby:\n\1 anyOf:\n\1 - required:\n\1 - s3_wal_path\n\1 - required:\n\1 - gs_wal_path\n\1 - required:\n\1 - standby_host\n\1 not:\n\1 required:\n\1 - s3_wal_path\n\1 - gs_wal_path/ }' "$file" sed -i '/^[[:space:]]*maintenanceWindows:$/{ diff --git a/manifests/postgresql.crd.yaml b/manifests/postgresql.crd.yaml index c16d3cf14..c8bf1b07d 100644 --- a/manifests/postgresql.crd.yaml +++ b/manifests/postgresql.crd.yaml @@ -3924,15 +3924,22 @@ spec: format: int64 type: integer standby: - oneOf: + anyOf: - required: - s3_wal_path - required: - gs_wal_path - required: - standby_host - description: StandbyDescription contains remote primary config or - s3/gs wal path + not: + required: + - s3_wal_path + - gs_wal_path + description: StandbyDescription contains remote primary config and/or + s3/gs wal path. standby_host can be specified alone or together with + either s3_wal_path OR gs_wal_path (mutually exclusive). At least + one field must be specified. s3_wal_path and gs_wal_path are mutually + exclusive. properties: gs_wal_path: type: string @@ -3942,6 +3949,8 @@ spec: type: string standby_port: type: string + standby_primary_slot_name: + type: string type: object streams: items: diff --git a/manifests/standby-manifest.yaml b/manifests/standby-manifest.yaml index eb90464a6..b06956a1b 100644 --- a/manifests/standby-manifest.yaml +++ b/manifests/standby-manifest.yaml @@ -9,7 +9,9 @@ spec: numberOfInstances: 1 postgresql: version: "17" - # Make this a standby cluster and provide either the s3 bucket path of source cluster or the remote primary host for continuous streaming. + # Make this a standby cluster. You can specify s3_wal_path or gs_wal_path for WAL archive, + # standby_host for remote primary streaming, or combine standby_host with either WAL path. + # Note: s3_wal_path and gs_wal_path are mutually exclusive. standby: # s3_wal_path: "s3://mybucket/spilo/acid-minimal-cluster/abcd1234-2a4b-4b2a-8c9c-c1234defg567/wal/14/" standby_host: "acid-minimal-cluster.default" diff --git a/pkg/apis/acid.zalan.do/v1/postgresql_type.go b/pkg/apis/acid.zalan.do/v1/postgresql_type.go index 17fecd7a1..b304db652 100644 --- a/pkg/apis/acid.zalan.do/v1/postgresql_type.go +++ b/pkg/apis/acid.zalan.do/v1/postgresql_type.go @@ -246,13 +246,15 @@ type Patroni struct { FailsafeMode *bool `json:"failsafe_mode,omitempty"` } -// StandbyDescription contains remote primary config or s3/gs wal path -// +kubebuilder:validation:ExactlyOneOf=s3_wal_path;gs_wal_path;standby_host +// StandbyDescription contains remote primary config and/or s3/gs wal path. +// standby_host can be specified alone or together with either s3_wal_path OR gs_wal_path (mutually exclusive). +// At least one field must be specified. s3_wal_path and gs_wal_path are mutually exclusive. type StandbyDescription struct { - S3WalPath string `json:"s3_wal_path,omitempty"` - GSWalPath string `json:"gs_wal_path,omitempty"` - StandbyHost string `json:"standby_host,omitempty"` - StandbyPort string `json:"standby_port,omitempty"` + S3WalPath string `json:"s3_wal_path,omitempty"` + GSWalPath string `json:"gs_wal_path,omitempty"` + StandbyHost string `json:"standby_host,omitempty"` + StandbyPort string `json:"standby_port,omitempty"` + StandbyPrimarySlotName string `json:"standby_primary_slot_name,omitempty"` } // TLSDescription specs TLS properties diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index 7c267fcee..224ef26f6 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -2207,23 +2207,29 @@ func (c *Cluster) generateStandbyEnvironment(description *acidv1.StandbyDescript Value: description.StandbyPort, }) } - } else { - c.logger.Info("standby cluster streaming from WAL location") - if description.S3WalPath != "" { + if description.StandbyPrimarySlotName != "" { result = append(result, v1.EnvVar{ - Name: "STANDBY_WALE_S3_PREFIX", - Value: description.S3WalPath, + Name: "STANDBY_PRIMARY_SLOT_NAME", + Value: description.StandbyPrimarySlotName, }) - } else if description.GSWalPath != "" { - result = append(result, v1.EnvVar{ - Name: "STANDBY_WALE_GS_PREFIX", - Value: description.GSWalPath, - }) - } else { - c.logger.Error("no WAL path specified in standby section") - return result } + } + // WAL archive can be specified with or without standby_host + if description.S3WalPath != "" { + c.logger.Info("standby cluster using S3 WAL archive") + result = append(result, v1.EnvVar{ + Name: "STANDBY_WALE_S3_PREFIX", + Value: description.S3WalPath, + }) + result = append(result, v1.EnvVar{Name: "STANDBY_METHOD", Value: "STANDBY_WITH_WALE"}) + result = append(result, v1.EnvVar{Name: "STANDBY_WAL_BUCKET_SCOPE_PREFIX", Value: ""}) + } else if description.GSWalPath != "" { + c.logger.Info("standby cluster using GCS WAL archive") + result = append(result, v1.EnvVar{ + Name: "STANDBY_WALE_GS_PREFIX", + Value: description.GSWalPath, + }) result = append(result, v1.EnvVar{Name: "STANDBY_METHOD", Value: "STANDBY_WITH_WALE"}) result = append(result, v1.EnvVar{Name: "STANDBY_WAL_BUCKET_SCOPE_PREFIX", Value: ""}) } diff --git a/pkg/cluster/k8sres_test.go b/pkg/cluster/k8sres_test.go index 43b4725e4..e39e18cd5 100644 --- a/pkg/cluster/k8sres_test.go +++ b/pkg/cluster/k8sres_test.go @@ -1370,7 +1370,33 @@ func TestStandbyEnv(t *testing.T) { envLen: 2, }, { - subTest: "from remote primary - ignore WAL path", + subTest: "from remote primary with S3 WAL path", + standbyOpts: &acidv1.StandbyDescription{ + S3WalPath: "s3://some/path/", + StandbyHost: "remote-primary", + }, + env: v1.EnvVar{ + Name: "STANDBY_HOST", + Value: "remote-primary", + }, + envPos: 0, + envLen: 4, + }, + { + subTest: "verify S3 WAL env with standby host", + standbyOpts: &acidv1.StandbyDescription{ + S3WalPath: "s3://some/path/", + StandbyHost: "remote-primary", + }, + env: v1.EnvVar{ + Name: "STANDBY_WALE_S3_PREFIX", + Value: "s3://some/path/", + }, + envPos: 1, + envLen: 4, + }, + { + subTest: "from remote primary with GCS WAL path", standbyOpts: &acidv1.StandbyDescription{ GSWalPath: "gs://some/path/", StandbyHost: "remote-primary", @@ -1380,7 +1406,20 @@ func TestStandbyEnv(t *testing.T) { Value: "remote-primary", }, envPos: 0, - envLen: 1, + envLen: 4, + }, + { + subTest: "from remote primary with slot name", + standbyOpts: &acidv1.StandbyDescription{ + StandbyHost: "remote-primary", + StandbyPrimarySlotName: "my_slot", + }, + env: v1.EnvVar{ + Name: "STANDBY_PRIMARY_SLOT_NAME", + Value: "my_slot", + }, + envPos: 1, + envLen: 2, }, } diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index 9142f33bb..908806dda 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -1031,6 +1031,23 @@ func (c *Cluster) syncStandbyClusterConfiguration() error { standbyOptionsToSet["create_replica_methods"] = []string{"bootstrap_standby_with_wale", "basebackup_fast_xlog"} standbyOptionsToSet["restore_command"] = "envdir \"/run/etc/wal-e.d/env-standby\" /scripts/restore_command.sh \"%f\" \"%p\"" + if c.Spec.StandbyCluster.StandbyHost != "" { + standbyOptionsToSet["host"] = c.Spec.StandbyCluster.StandbyHost + } else { + standbyOptionsToSet["host"] = nil + } + + if c.Spec.StandbyCluster.StandbyPort != "" { + standbyOptionsToSet["port"] = c.Spec.StandbyCluster.StandbyPort + } else { + standbyOptionsToSet["port"] = nil + } + + if c.Spec.StandbyCluster.StandbyPrimarySlotName != "" { + standbyOptionsToSet["primary_slot_name"] = c.Spec.StandbyCluster.StandbyPrimarySlotName + } else { + standbyOptionsToSet["primary_slot_name"] = nil + } } else { c.logger.Infof("promoting standby cluster and detach from source") standbyOptionsToSet = nil diff --git a/pkg/cluster/sync_test.go b/pkg/cluster/sync_test.go index 87e9dc8a5..e2b242d9d 100644 --- a/pkg/cluster/sync_test.go +++ b/pkg/cluster/sync_test.go @@ -801,6 +801,41 @@ func TestSyncStandbyClusterConfiguration(t *testing.T) { // this should update the Patroni config again err = cluster.syncStandbyClusterConfiguration() assert.NoError(t, err) + + // test with standby_host, standby_port and standby_primary_slot_name + cluster.Spec.StandbyCluster = &acidv1.StandbyDescription{ + StandbyHost: "remote-primary.example.com", + StandbyPort: "5433", + StandbyPrimarySlotName: "standby_slot", + } + cluster.syncStatefulSet() + updatedSts4 := cluster.Statefulset + + // check that pods have all three STANDBY_* environment variables + assert.Contains(t, updatedSts4.Spec.Template.Spec.Containers[0].Env, v1.EnvVar{Name: "STANDBY_HOST", Value: "remote-primary.example.com"}) + assert.Contains(t, updatedSts4.Spec.Template.Spec.Containers[0].Env, v1.EnvVar{Name: "STANDBY_PORT", Value: "5433"}) + assert.Contains(t, updatedSts4.Spec.Template.Spec.Containers[0].Env, v1.EnvVar{Name: "STANDBY_PRIMARY_SLOT_NAME", Value: "standby_slot"}) + + // this should update the Patroni config with host, port and primary_slot_name + err = cluster.syncStandbyClusterConfiguration() + assert.NoError(t, err) + + // test property deletion: remove standby_primary_slot_name + cluster.Spec.StandbyCluster = &acidv1.StandbyDescription{ + StandbyHost: "remote-primary.example.com", + StandbyPort: "5433", + } + cluster.syncStatefulSet() + updatedSts5 := cluster.Statefulset + + // check that STANDBY_PRIMARY_SLOT_NAME is not present + assert.Contains(t, updatedSts5.Spec.Template.Spec.Containers[0].Env, v1.EnvVar{Name: "STANDBY_HOST", Value: "remote-primary.example.com"}) + assert.Contains(t, updatedSts5.Spec.Template.Spec.Containers[0].Env, v1.EnvVar{Name: "STANDBY_PORT", Value: "5433"}) + assert.NotContains(t, updatedSts5.Spec.Template.Spec.Containers[0].Env, v1.EnvVar{Name: "STANDBY_PRIMARY_SLOT_NAME", Value: "standby_slot"}) + + // this should update the Patroni config and set primary_slot_name to nil + err = cluster.syncStandbyClusterConfiguration() + assert.NoError(t, err) } func TestUpdateSecret(t *testing.T) { From f05150a81e20abf2a1f0b90f0bb0f5b32ceb072e Mon Sep 17 00:00:00 2001 From: Mikkel Oscar Lyderik Larsen Date: Tue, 27 Jan 2026 10:44:30 +0100 Subject: [PATCH 11/21] Use UpdateStatus instead of patch (#3005) Signed-off-by: Mikkel Oscar Lyderik Larsen Co-authored-by: Felix Kunde --- pkg/cluster/cluster.go | 62 ++++++++++++++++++++---------------- pkg/cluster/sync.go | 21 ++++++------ pkg/cluster/util_test.go | 8 ++++- pkg/controller/postgresql.go | 21 +++++++++--- pkg/util/k8sutil/k8sutil.go | 20 ++---------- 5 files changed, 71 insertions(+), 61 deletions(-) diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index 7204a92bd..ecb794993 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -32,6 +32,7 @@ import ( v1 "k8s.io/api/core/v1" policyv1 "k8s.io/api/policy/v1" rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/api/equality" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/rest" @@ -271,26 +272,29 @@ func (c *Cluster) Create() (err error) { ) defer func() { - var ( - pgUpdatedStatus *acidv1.Postgresql - errStatus error - ) - if err == nil { - pgUpdatedStatus, errStatus = c.KubeClient.SetPostgresCRDStatus(c.clusterName(), acidv1.ClusterStatusRunning) //TODO: are you sure it's running? - } else { + currentStatus := c.Status.DeepCopy() + pg := c.Postgresql.DeepCopy() + pg.Status.PostgresClusterStatus = acidv1.ClusterStatusRunning + + if err != nil { c.logger.Warningf("cluster created failed: %v", err) - pgUpdatedStatus, errStatus = c.KubeClient.SetPostgresCRDStatus(c.clusterName(), acidv1.ClusterStatusAddFailed) + pg.Status.PostgresClusterStatus = acidv1.ClusterStatusAddFailed } - if errStatus != nil { - c.logger.Warningf("could not set cluster status: %v", errStatus) - return - } - if pgUpdatedStatus != nil { + + if !equality.Semantic.DeepEqual(currentStatus, pg.Status) { + pgUpdatedStatus, err := c.KubeClient.SetPostgresCRDStatus(c.clusterName(), pg) + if err != nil { + c.logger.Warningf("could not set cluster status: %v", err) + return + } c.setSpec(pgUpdatedStatus) } }() - pgCreateStatus, err = c.KubeClient.SetPostgresCRDStatus(c.clusterName(), acidv1.ClusterStatusCreating) + pg := c.Postgresql.DeepCopy() + pg.Status.PostgresClusterStatus = acidv1.ClusterStatusCreating + + pgCreateStatus, err = c.KubeClient.SetPostgresCRDStatus(c.clusterName(), pg) if err != nil { return fmt.Errorf("could not set cluster status: %v", err) } @@ -978,7 +982,12 @@ func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error { c.mu.Lock() defer c.mu.Unlock() - c.KubeClient.SetPostgresCRDStatus(c.clusterName(), acidv1.ClusterStatusUpdating) + newSpec.Status.PostgresClusterStatus = acidv1.ClusterStatusUpdating + + newSpec, err := c.KubeClient.SetPostgresCRDStatus(c.clusterName(), newSpec) + if err != nil { + return fmt.Errorf("could not set cluster status to updating: %w", err) + } if !isInMaintenanceWindow(newSpec.Spec.MaintenanceWindows) { // do not apply any major version related changes yet @@ -987,20 +996,19 @@ func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error { c.setSpec(newSpec) defer func() { - var ( - pgUpdatedStatus *acidv1.Postgresql - err error - ) + currentStatus := newSpec.Status.DeepCopy() + newSpec.Status.PostgresClusterStatus = acidv1.ClusterStatusRunning + if updateFailed { - pgUpdatedStatus, err = c.KubeClient.SetPostgresCRDStatus(c.clusterName(), acidv1.ClusterStatusUpdateFailed) - } else { - pgUpdatedStatus, err = c.KubeClient.SetPostgresCRDStatus(c.clusterName(), acidv1.ClusterStatusRunning) + newSpec.Status.PostgresClusterStatus = acidv1.ClusterStatusUpdateFailed } - if err != nil { - c.logger.Warningf("could not set cluster status: %v", err) - return - } - if pgUpdatedStatus != nil { + + if !equality.Semantic.DeepEqual(currentStatus, newSpec.Status) { + pgUpdatedStatus, err := c.KubeClient.SetPostgresCRDStatus(c.clusterName(), newSpec) + if err != nil { + c.logger.Warningf("could not set cluster status: %v", err) + return + } c.setSpec(pgUpdatedStatus) } }() diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index 908806dda..d13945caa 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -20,6 +20,7 @@ import ( batchv1 "k8s.io/api/batch/v1" v1 "k8s.io/api/core/v1" policyv1 "k8s.io/api/policy/v1" + "k8s.io/apimachinery/pkg/api/equality" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" ) @@ -43,21 +44,19 @@ func (c *Cluster) Sync(newSpec *acidv1.Postgresql) error { c.setSpec(newSpec) defer func() { - var ( - pgUpdatedStatus *acidv1.Postgresql - errStatus error - ) if err != nil { c.logger.Warningf("error while syncing cluster state: %v", err) - pgUpdatedStatus, errStatus = c.KubeClient.SetPostgresCRDStatus(c.clusterName(), acidv1.ClusterStatusSyncFailed) + newSpec.Status.PostgresClusterStatus = acidv1.ClusterStatusSyncFailed } else if !c.Status.Running() { - pgUpdatedStatus, errStatus = c.KubeClient.SetPostgresCRDStatus(c.clusterName(), acidv1.ClusterStatusRunning) + newSpec.Status.PostgresClusterStatus = acidv1.ClusterStatusRunning } - if errStatus != nil { - c.logger.Warningf("could not set cluster status: %v", errStatus) - return - } - if pgUpdatedStatus != nil { + + if !equality.Semantic.DeepEqual(oldSpec.Status, newSpec.Status) { + pgUpdatedStatus, err := c.KubeClient.SetPostgresCRDStatus(c.clusterName(), newSpec) + if err != nil { + c.logger.Warningf("could not set cluster status: %v", err) + return + } c.setSpec(pgUpdatedStatus) } }() diff --git a/pkg/cluster/util_test.go b/pkg/cluster/util_test.go index 9cd7dc7e9..2a8b0b90a 100644 --- a/pkg/cluster/util_test.go +++ b/pkg/cluster/util_test.go @@ -288,6 +288,12 @@ func newInheritedAnnotationsCluster(client k8sutil.KubernetesClient) (*Cluster, }, } + // add postgresql cluster to fake client + _, err := client.PostgresqlsGetter.Postgresqls(namespace).Create(context.TODO(), &pg, metav1.CreateOptions{}) + if err != nil { + return nil, err + } + cluster := New( Config{ OpConfig: config.Config{ @@ -321,7 +327,7 @@ func newInheritedAnnotationsCluster(client k8sutil.KubernetesClient) (*Cluster, }, client, pg, logger, eventRecorder) cluster.Name = clusterName cluster.Namespace = namespace - _, err := cluster.createStatefulSet() + _, err = cluster.createStatefulSet() if err != nil { return nil, err } diff --git a/pkg/controller/postgresql.go b/pkg/controller/postgresql.go index 824a030f4..0725ffc1d 100644 --- a/pkg/controller/postgresql.go +++ b/pkg/controller/postgresql.go @@ -161,7 +161,8 @@ func (c *Controller) acquireInitialListOfClusters() error { func (c *Controller) addCluster(lg *logrus.Entry, clusterName spec.NamespacedName, pgSpec *acidv1.Postgresql) (*cluster.Cluster, error) { if c.opConfig.EnableTeamIdClusternamePrefix { if _, err := acidv1.ExtractClusterName(clusterName.Name, pgSpec.Spec.TeamID); err != nil { - c.KubeClient.SetPostgresCRDStatus(clusterName, acidv1.ClusterStatusInvalid) + pgSpec.Status.PostgresClusterStatus = acidv1.ClusterStatusInvalid + c.KubeClient.SetPostgresCRDStatus(clusterName, pgSpec) return nil, err } } @@ -470,13 +471,25 @@ func (c *Controller) queueClusterEvent(informerOldSpec, informerNewSpec *acidv1. switch eventType { case EventAdd: - c.KubeClient.SetPostgresCRDStatus(clusterName, acidv1.ClusterStatusAddFailed) + informerNewSpec.Status.PostgresClusterStatus = acidv1.ClusterStatusAddFailed + _, err := c.KubeClient.SetPostgresCRDStatus(clusterName, informerNewSpec) + if err != nil { + c.logger.WithField("cluster-name", clusterName).Errorf("could not set PostgresCRD status: %v", err) + } c.eventRecorder.Eventf(c.GetReference(informerNewSpec), v1.EventTypeWarning, "Create", "%v", clusterError) case EventUpdate: - c.KubeClient.SetPostgresCRDStatus(clusterName, acidv1.ClusterStatusUpdateFailed) + informerNewSpec.Status.PostgresClusterStatus = acidv1.ClusterStatusUpdateFailed + _, err := c.KubeClient.SetPostgresCRDStatus(clusterName, informerNewSpec) + if err != nil { + c.logger.WithField("cluster-name", clusterName).Errorf("could not set PostgresCRD status: %v", err) + } c.eventRecorder.Eventf(c.GetReference(informerNewSpec), v1.EventTypeWarning, "Update", "%v", clusterError) default: - c.KubeClient.SetPostgresCRDStatus(clusterName, acidv1.ClusterStatusSyncFailed) + informerNewSpec.Status.PostgresClusterStatus = acidv1.ClusterStatusSyncFailed + _, err := c.KubeClient.SetPostgresCRDStatus(clusterName, informerNewSpec) + if err != nil { + c.logger.WithField("cluster-name", clusterName).Errorf("could not set PostgresCRD status: %v", err) + } c.eventRecorder.Eventf(c.GetReference(informerNewSpec), v1.EventTypeWarning, "Sync", "%v", clusterError) } diff --git a/pkg/util/k8sutil/k8sutil.go b/pkg/util/k8sutil/k8sutil.go index de1fb605a..c34faddd4 100644 --- a/pkg/util/k8sutil/k8sutil.go +++ b/pkg/util/k8sutil/k8sutil.go @@ -191,24 +191,8 @@ func NewFromConfig(cfg *rest.Config) (KubernetesClient, error) { } // SetPostgresCRDStatus of Postgres cluster -func (client *KubernetesClient) SetPostgresCRDStatus(clusterName spec.NamespacedName, status string) (*apiacidv1.Postgresql, error) { - var pg *apiacidv1.Postgresql - var pgStatus apiacidv1.PostgresStatus - pgStatus.PostgresClusterStatus = status - - patch, err := json.Marshal(struct { - PgStatus interface{} `json:"status"` - }{&pgStatus}) - - if err != nil { - return pg, fmt.Errorf("could not marshal status: %v", err) - } - - // we cannot do a full scale update here without fetching the previous manifest (as the resourceVersion may differ), - // however, we could do patch without it. In the future, once /status subresource is there (starting Kubernetes 1.11) - // we should take advantage of it. - pg, err = client.PostgresqlsGetter.Postgresqls(clusterName.Namespace).Patch( - context.TODO(), clusterName.Name, types.MergePatchType, patch, metav1.PatchOptions{}, "status") +func (client *KubernetesClient) SetPostgresCRDStatus(clusterName spec.NamespacedName, pg *apiacidv1.Postgresql) (*apiacidv1.Postgresql, error) { + pg, err := client.PostgresqlsGetter.Postgresqls(clusterName.Namespace).UpdateStatus(context.TODO(), pg, metav1.UpdateOptions{}) if err != nil { return pg, fmt.Errorf("could not update status: %v", err) } From 137f3e769d489b19b5349bd3b98fcf19a9cce571 Mon Sep 17 00:00:00 2001 From: Ida Novindasari Date: Thu, 29 Jan 2026 11:22:50 +0100 Subject: [PATCH 12/21] bugfix: fix image naming to use it correctly in e2e test (#3037) * bugfix: fix image naming to use it in e2e test * Fix embeded crd * Add crd generation as dependency for docker --------- Co-authored-by: Polina Bungina --- Makefile | 4 ++-- e2e/run.sh | 16 +++++++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 4f5441cf1..02c9c73f5 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ LDFLAGS ?= -X=main.version=$(VERSION) DOCKERDIR = docker BASE_IMAGE ?= alpine:latest -IMAGE ?= $(BINARY) +IMAGE ?= ghcr.io/zalando/$(BINARY) TAG ?= $(VERSION) GITHEAD = $(shell git rev-parse --short HEAD) GITURL = $(shell git config --get remote.origin.url) @@ -84,7 +84,7 @@ linux: ${SOURCES} $(GENERATED_CRDS) macos: ${SOURCES} $(GENERATED_CRDS) GOOS=darwin GOARCH=amd64 CGO_ENABLED=${CGO_ENABLED} go build -o build/macos/${BINARY} ${BUILD_FLAGS} -ldflags "$(LDFLAGS)" $(SOURCES) -docker: ${DOCKERDIR}/${DOCKERFILE} +docker: $(GENERATED_CRDS) ${DOCKERDIR}/${DOCKERFILE} echo `(env)` echo "Tag ${TAG}" echo "Version ${VERSION}" diff --git a/e2e/run.sh b/e2e/run.sh index b0f13f92e..5ebf2a7cb 100755 --- a/e2e/run.sh +++ b/e2e/run.sh @@ -7,7 +7,7 @@ set -o pipefail IFS=$'\n\t' readonly cluster_name="postgres-operator-e2e-tests" -readonly kubeconfig_path="/tmp/kind-config-${cluster_name}" +readonly kubeconfig_path="${HOME}/kind-config-${cluster_name}" readonly spilo_image="registry.opensource.zalan.do/acid/spilo-17-e2e:0.3" readonly e2e_test_runner_image="ghcr.io/zalando/postgres-operator-e2e-tests-runner:latest" @@ -19,11 +19,17 @@ echo "Kubeconfig path: ${kubeconfig_path}" function pull_images(){ operator_tag=$(git describe --tags --always --dirty) - if [[ -z $(docker images -q ghcr.io/zalando/postgres-operator:${operator_tag}) ]] + image_name="ghcr.io/zalando/postgres-operator:${operator_tag}" + if [[ -z $(docker images -q "${image_name}") ]] then - docker pull ghcr.io/zalando/postgres-operator:latest + if ! docker pull "${image_name}" + then + echo "Failed to pull operator image: ${image_name}" + exit 1 + fi fi - operator_image=$(docker images --filter=reference="ghcr.io/zalando/postgres-operator" --format "{{.Repository}}:{{.Tag}}" | head -1) + operator_image="${image_name}" + echo "Using operator image: ${operator_image}" } function start_kind(){ @@ -52,7 +58,7 @@ function set_kind_api_server_ip(){ # but update the IP address of the API server to the one from the Docker 'bridge' network readonly local kind_api_server_port=6443 # well-known in the 'kind' codebase readonly local kind_api_server=$(docker inspect --format "{{ .NetworkSettings.Networks.kind.IPAddress }}:${kind_api_server_port}" "${cluster_name}"-control-plane) - sed -i "s/server.*$/server: https:\/\/$kind_api_server/g" "${kubeconfig_path}" + sed "s/server.*$/server: https:\/\/$kind_api_server/g" "${kubeconfig_path}" > "${kubeconfig_path}".tmp && mv "${kubeconfig_path}".tmp "${kubeconfig_path}" } function generate_certificate(){ From b84c58c2a638f6e7f6ea027a24e4cecf0dfc20f5 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Fri, 30 Jan 2026 11:37:21 +0100 Subject: [PATCH 13/21] add support for global maintenance windows (#3038) * add support for global maintenance windows * fix schema validation and trim \ when unmarshalling maintenance window --- .../crds/operatorconfigurations.yaml | 5 ++ charts/postgres-operator/values.yaml | 4 ++ docs/reference/cluster_manifest.md | 6 +-- docs/reference/operator_parameters.md | 8 +++ go.mod | 2 +- manifests/operatorconfiguration.crd.yaml | 5 ++ pkg/apis/acid.zalan.do/v1/crds.go | 9 ++++ pkg/apis/acid.zalan.do/v1/marshal.go | 3 +- .../v1/operator_configuration_type.go | 8 +-- pkg/apis/acid.zalan.do/v1/util_test.go | 7 +++ .../acid.zalan.do/v1/zz_generated.deepcopy.go | 7 +++ pkg/cluster/cluster.go | 2 +- pkg/cluster/majorversionupgrade.go | 2 +- pkg/cluster/pod.go | 2 +- pkg/cluster/sync.go | 2 +- pkg/cluster/util.go | 19 +++++-- pkg/cluster/util_test.go | 51 +++++++++++++++---- pkg/controller/operator_config.go | 10 ++++ pkg/util/config/config.go | 8 +-- 19 files changed, 131 insertions(+), 29 deletions(-) diff --git a/charts/postgres-operator/crds/operatorconfigurations.yaml b/charts/postgres-operator/crds/operatorconfigurations.yaml index c903a9319..373a295c5 100644 --- a/charts/postgres-operator/crds/operatorconfigurations.yaml +++ b/charts/postgres-operator/crds/operatorconfigurations.yaml @@ -101,6 +101,11 @@ spec: kubernetes_use_configmaps: type: boolean default: false + maintenance_windows: + items: + pattern: '^\ *((Mon|Tue|Wed|Thu|Fri|Sat|Sun):(2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))-((2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))\ *$' + type: string + type: array max_instances: type: integer description: "-1 = disabled" diff --git a/charts/postgres-operator/values.yaml b/charts/postgres-operator/values.yaml index 4e5d9b7cb..e8e26494d 100644 --- a/charts/postgres-operator/values.yaml +++ b/charts/postgres-operator/values.yaml @@ -49,6 +49,10 @@ configGeneral: # Select if setup uses endpoints (default), or configmaps to manage leader (DCS=k8s) # kubernetes_use_configmaps: false + # maintenance windows applied to all Postgres clusters unless overridden in the manifest + # maintenance_windows: + # - "Sun:01:00-06:00" + # min number of instances in Postgres cluster. -1 = no limit min_instances: -1 # max number of instances in Postgres cluster. -1 = no limit diff --git a/docs/reference/cluster_manifest.md b/docs/reference/cluster_manifest.md index 7b4ef7ada..0524e1a86 100644 --- a/docs/reference/cluster_manifest.md +++ b/docs/reference/cluster_manifest.md @@ -116,9 +116,9 @@ These parameters are grouped directly under the `spec` key in the manifest. * **maintenanceWindows** a list which defines specific time frames when certain maintenance operations - such as automatic major upgrades or master pod migration. Accepted formats - are "01:00-06:00" for daily maintenance windows or "Sat:00:00-04:00" for specific - days, with all times in UTC. + such as automatic major upgrades or master pod migration are allowed to happen. + Accepted formats are "01:00-06:00" for daily maintenance windows or + "Sat:00:00-04:00" for specific days, with all times in UTC. * **users** a map of usernames to user flags for the users that should be created in the diff --git a/docs/reference/operator_parameters.md b/docs/reference/operator_parameters.md index 4327dc45f..206131b7b 100644 --- a/docs/reference/operator_parameters.md +++ b/docs/reference/operator_parameters.md @@ -173,6 +173,14 @@ Those are top-level keys, containing both leaf keys and groups. the thresholds. The value must be `"true"` to be effective. The default is empty which means the feature is disabled. +* **maintenance_windows** + a list which defines specific time frames when certain maintenance + operations such as automatic major upgrades or master pod migration are + allowed to happen for all database clusters. Accepted formats are + "01:00-06:00" for daily maintenance windows or "Sat:00:00-04:00" for + specific days, with all times in UTC. Locally defined maintenance + windows take precedence over globally configured ones. + * **resync_period** period between consecutive sync requests. The default is `30m`. diff --git a/go.mod b/go.mod index 08966bd95..a25723a44 100644 --- a/go.mod +++ b/go.mod @@ -18,6 +18,7 @@ require ( k8s.io/apiextensions-apiserver v0.32.9 k8s.io/apimachinery v0.32.9 k8s.io/client-go v0.32.9 + sigs.k8s.io/yaml v1.4.0 ) require ( @@ -77,7 +78,6 @@ require ( sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect - sigs.k8s.io/yaml v1.4.0 // indirect ) tool ( diff --git a/manifests/operatorconfiguration.crd.yaml b/manifests/operatorconfiguration.crd.yaml index c78ceb77a..f792078f5 100644 --- a/manifests/operatorconfiguration.crd.yaml +++ b/manifests/operatorconfiguration.crd.yaml @@ -99,6 +99,11 @@ spec: kubernetes_use_configmaps: type: boolean default: false + maintenance_windows: + items: + pattern: '^\ *((Mon|Tue|Wed|Thu|Fri|Sat|Sun):(2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))-((2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))\ *$' + type: string + type: array max_instances: type: integer description: "-1 = disabled" diff --git a/pkg/apis/acid.zalan.do/v1/crds.go b/pkg/apis/acid.zalan.do/v1/crds.go index 1ab85c905..46739e46d 100644 --- a/pkg/apis/acid.zalan.do/v1/crds.go +++ b/pkg/apis/acid.zalan.do/v1/crds.go @@ -127,6 +127,15 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{ "kubernetes_use_configmaps": { Type: "boolean", }, + "maintenance_windows": { + Type: "array", + Items: &apiextv1.JSONSchemaPropsOrArray{ + Schema: &apiextv1.JSONSchemaProps{ + Type: "string", + Pattern: "^\\ *((Mon|Tue|Wed|Thu|Fri|Sat|Sun):(2[0-3]|[01]?\\d):([0-5]?\\d)|(2[0-3]|[01]?\\d):([0-5]?\\d))-((Mon|Tue|Wed|Thu|Fri|Sat|Sun):(2[0-3]|[01]?\\d):([0-5]?\\d)|(2[0-3]|[01]?\\d):([0-5]?\\d))\\ *$", + }, + }, + }, "max_instances": { Type: "integer", Description: "-1 = disabled", diff --git a/pkg/apis/acid.zalan.do/v1/marshal.go b/pkg/apis/acid.zalan.do/v1/marshal.go index a221d622b..014214fed 100644 --- a/pkg/apis/acid.zalan.do/v1/marshal.go +++ b/pkg/apis/acid.zalan.do/v1/marshal.go @@ -31,7 +31,8 @@ func (m *MaintenanceWindow) UnmarshalJSON(data []byte) error { err error ) - parts := strings.Split(string(data[1:len(data)-1]), "-") + dataStr := strings.Trim(string(data), "\"") + parts := strings.Split(dataStr, "-") if len(parts) != 2 { return fmt.Errorf("incorrect maintenance window format") } diff --git a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go index 80cfbbcd7..290b45ee3 100644 --- a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go +++ b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go @@ -266,6 +266,7 @@ type OperatorConfigurationData struct { Workers uint32 `json:"workers,omitempty"` ResyncPeriod Duration `json:"resync_period,omitempty"` RepairPeriod Duration `json:"repair_period,omitempty"` + MaintenanceWindows []MaintenanceWindow `json:"maintenance_windows,omitempty"` SetMemoryRequestToLimit bool `json:"set_memory_request_to_limit,omitempty"` ShmVolume *bool `json:"enable_shm_volume,omitempty"` SidecarImages map[string]string `json:"sidecar_docker_images,omitempty"` // deprecated in favour of SidecarContainers @@ -285,10 +286,9 @@ type OperatorConfigurationData struct { ConnectionPooler ConnectionPoolerConfiguration `json:"connection_pooler"` Patroni PatroniConfiguration `json:"patroni"` - MinInstances int32 `json:"min_instances,omitempty"` - MaxInstances int32 `json:"max_instances,omitempty"` - IgnoreInstanceLimitsAnnotationKey string `json:"ignore_instance_limits_annotation_key,omitempty"` - + MinInstances int32 `json:"min_instances,omitempty"` + MaxInstances int32 `json:"max_instances,omitempty"` + IgnoreInstanceLimitsAnnotationKey string `json:"ignore_instance_limits_annotation_key,omitempty"` IgnoreResourcesLimitsAnnotationKey string `json:"ignore_resources_limits_annotation_key,omitempty"` } diff --git a/pkg/apis/acid.zalan.do/v1/util_test.go b/pkg/apis/acid.zalan.do/v1/util_test.go index 9f3fe9bde..e060379fd 100644 --- a/pkg/apis/acid.zalan.do/v1/util_test.go +++ b/pkg/apis/acid.zalan.do/v1/util_test.go @@ -91,6 +91,13 @@ var maintenanceWindows = []struct { StartTime: mustParseTime("10:00"), EndTime: mustParseTime("20:00"), }, nil}, + {"regular every day scenario", + []byte(`"05:00-07:00"`), + MaintenanceWindow{ + Everyday: true, + StartTime: mustParseTime("05:00"), + EndTime: mustParseTime("07:00"), + }, nil}, {"starts and ends at the same time", []byte(`"Mon:10:00-10:00"`), MaintenanceWindow{ diff --git a/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go b/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go index 79e0787bc..159a87f35 100644 --- a/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go +++ b/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go @@ -433,6 +433,13 @@ func (in *OperatorConfigurationData) DeepCopyInto(out *OperatorConfigurationData *out = make([]string, len(*in)) copy(*out, *in) } + if in.MaintenanceWindows != nil { + in, out := &in.MaintenanceWindows, &out.MaintenanceWindows + *out = make([]MaintenanceWindow, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } if in.ShmVolume != nil { in, out := &in.ShmVolume, &out.ShmVolume *out = new(bool) diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index ecb794993..dd1516ba3 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -989,7 +989,7 @@ func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error { return fmt.Errorf("could not set cluster status to updating: %w", err) } - if !isInMaintenanceWindow(newSpec.Spec.MaintenanceWindows) { + if !c.isInMaintenanceWindow(newSpec.Spec.MaintenanceWindows) { // do not apply any major version related changes yet newSpec.Spec.PostgresqlParam.PgVersion = oldSpec.Spec.PostgresqlParam.PgVersion } diff --git a/pkg/cluster/majorversionupgrade.go b/pkg/cluster/majorversionupgrade.go index b80cbaa09..e7e08e8c6 100644 --- a/pkg/cluster/majorversionupgrade.go +++ b/pkg/cluster/majorversionupgrade.go @@ -197,7 +197,7 @@ func (c *Cluster) majorVersionUpgrade() error { return nil } - if !isInMaintenanceWindow(c.Spec.MaintenanceWindows) { + if !c.isInMaintenanceWindow(c.Spec.MaintenanceWindows) { c.logger.Infof("skipping major version upgrade, not in maintenance window") return nil } diff --git a/pkg/cluster/pod.go b/pkg/cluster/pod.go index 12a18b9b3..959bacb54 100644 --- a/pkg/cluster/pod.go +++ b/pkg/cluster/pod.go @@ -280,7 +280,7 @@ func (c *Cluster) MigrateMasterPod(podName spec.NamespacedName) error { } scheduleSwitchover := false - if !isInMaintenanceWindow(c.Spec.MaintenanceWindows) { + if !c.isInMaintenanceWindow(c.Spec.MaintenanceWindows) { c.logger.Infof("postponing switchover, not in maintenance window") scheduleSwitchover = true } diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index d13945caa..189498e7f 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -97,7 +97,7 @@ func (c *Cluster) Sync(newSpec *acidv1.Postgresql) error { } } - if !isInMaintenanceWindow(newSpec.Spec.MaintenanceWindows) { + if !c.isInMaintenanceWindow(newSpec.Spec.MaintenanceWindows) { // do not apply any major version related changes yet newSpec.Spec.PostgresqlParam.PgVersion = oldSpec.Spec.PostgresqlParam.PgVersion } diff --git a/pkg/cluster/util.go b/pkg/cluster/util.go index 06a35f1b7..c9b4b4428 100644 --- a/pkg/cluster/util.go +++ b/pkg/cluster/util.go @@ -663,15 +663,28 @@ func parseResourceRequirements(resourcesRequirement v1.ResourceRequirements) (ac return resources, nil } -func isInMaintenanceWindow(specMaintenanceWindows []acidv1.MaintenanceWindow) bool { - if len(specMaintenanceWindows) == 0 { +func (c *Cluster) isInMaintenanceWindow(specMaintenanceWindows []acidv1.MaintenanceWindow) bool { + if len(specMaintenanceWindows) == 0 && len(c.OpConfig.MaintenanceWindows) == 0 { return true } now := time.Now() currentDay := now.Weekday() currentTime := now.Format("15:04") - for _, window := range specMaintenanceWindows { + maintenanceWindows := specMaintenanceWindows + if len(maintenanceWindows) == 0 { + maintenanceWindows = make([]acidv1.MaintenanceWindow, 0, len(c.OpConfig.MaintenanceWindows)) + for _, windowStr := range c.OpConfig.MaintenanceWindows { + var window acidv1.MaintenanceWindow + if err := window.UnmarshalJSON([]byte(windowStr)); err != nil { + c.logger.Errorf("could not parse default maintenance window %q: %v", windowStr, err) + continue + } + maintenanceWindows = append(maintenanceWindows, window) + } + } + + for _, window := range maintenanceWindows { startTime := window.StartTime.Format("15:04") endTime := window.EndTime.Format("15:04") diff --git a/pkg/cluster/util_test.go b/pkg/cluster/util_test.go index 2a8b0b90a..e5622b9ac 100644 --- a/pkg/cluster/util_test.go +++ b/pkg/cluster/util_test.go @@ -657,6 +657,22 @@ func Test_trimCronjobName(t *testing.T) { } func TestIsInMaintenanceWindow(t *testing.T) { + cluster := New( + Config{ + OpConfig: config.Config{ + Resources: config.Resources{ + ClusterLabels: map[string]string{"application": "spilo"}, + ClusterNameLabel: "cluster-name", + DefaultCPURequest: "300m", + DefaultCPULimit: "300m", + DefaultMemoryRequest: "300Mi", + DefaultMemoryLimit: "300Mi", + }, + }, + }, k8sutil.KubernetesClient{}, acidv1.Postgresql{}, logger, eventRecorder) + cluster.Name = clusterName + cluster.Namespace = namespace + now := time.Now() futureTimeStart := now.Add(1 * time.Hour) futureTimeStartFormatted := futureTimeStart.Format("15:04") @@ -664,14 +680,16 @@ func TestIsInMaintenanceWindow(t *testing.T) { futureTimeEndFormatted := futureTimeEnd.Format("15:04") tests := []struct { - name string - windows []acidv1.MaintenanceWindow - expected bool + name string + windows []acidv1.MaintenanceWindow + configWindows []string + expected bool }{ { - name: "no maintenance windows", - windows: nil, - expected: true, + name: "no maintenance windows", + windows: nil, + configWindows: nil, + expected: true, }, { name: "maintenance windows with everyday", @@ -682,7 +700,8 @@ func TestIsInMaintenanceWindow(t *testing.T) { EndTime: mustParseTime("23:59"), }, }, - expected: true, + configWindows: nil, + expected: true, }, { name: "maintenance windows with weekday", @@ -693,7 +712,8 @@ func TestIsInMaintenanceWindow(t *testing.T) { EndTime: mustParseTime("23:59"), }, }, - expected: true, + configWindows: nil, + expected: true, }, { name: "maintenance windows with future interval time", @@ -706,12 +726,25 @@ func TestIsInMaintenanceWindow(t *testing.T) { }, expected: false, }, + { + name: "global maintenance windows with future interval time", + windows: nil, + configWindows: []string{fmt.Sprintf("%s-%s", futureTimeStartFormatted, futureTimeEndFormatted)}, + expected: false, + }, + { + name: "global maintenance windows all day", + windows: nil, + configWindows: []string{"00:00-23:59"}, + expected: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + cluster.OpConfig.MaintenanceWindows = tt.configWindows cluster.Spec.MaintenanceWindows = tt.windows - if isInMaintenanceWindow(cluster.Spec.MaintenanceWindows) != tt.expected { + if cluster.isInMaintenanceWindow(cluster.Spec.MaintenanceWindows) != tt.expected { t.Errorf("Expected isInMaintenanceWindow to return %t", tt.expected) } }) diff --git a/pkg/controller/operator_config.go b/pkg/controller/operator_config.go index 24d4ffcd3..db7166ccd 100644 --- a/pkg/controller/operator_config.go +++ b/pkg/controller/operator_config.go @@ -51,6 +51,16 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur result.ShmVolume = util.CoalesceBool(fromCRD.ShmVolume, util.True()) result.SidecarImages = fromCRD.SidecarImages result.SidecarContainers = fromCRD.SidecarContainers + if len(fromCRD.MaintenanceWindows) > 0 { + result.MaintenanceWindows = make([]string, 0, len(fromCRD.MaintenanceWindows)) + for _, window := range fromCRD.MaintenanceWindows { + w, err := window.MarshalJSON() + if err != nil { + panic(fmt.Errorf("could not marshal configured maintenance window: %v", err)) + } + result.MaintenanceWindows = append(result.MaintenanceWindows, string(w)) + } + } // user config result.SuperUsername = util.Coalesce(fromCRD.PostgresUsersConfiguration.SuperUsername, "postgres") diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index 858a58b8c..54bee0423 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -63,10 +63,9 @@ type Resources struct { NodeReadinessLabelMerge string `name:"node_readiness_label_merge" default:"OR"` ShmVolume *bool `name:"enable_shm_volume" default:"true"` - MaxInstances int32 `name:"max_instances" default:"-1"` - MinInstances int32 `name:"min_instances" default:"-1"` - IgnoreInstanceLimitsAnnotationKey string `name:"ignore_instance_limits_annotation_key"` - + MaxInstances int32 `name:"max_instances" default:"-1"` + MinInstances int32 `name:"min_instances" default:"-1"` + IgnoreInstanceLimitsAnnotationKey string `name:"ignore_instance_limits_annotation_key"` IgnoreResourcesLimitsAnnotationKey string `name:"ignore_resources_limits_annotation_key"` } @@ -178,6 +177,7 @@ type Config struct { KubernetesUseConfigMaps bool `name:"kubernetes_use_configmaps" default:"false"` EtcdHost string `name:"etcd_host" default:""` // special values: the empty string "" means Patroni will use K8s as a DCS DockerImage string `name:"docker_image" default:"ghcr.io/zalando/spilo-17:4.0-p3"` + MaintenanceWindows []string `name:"maintenance_windows"` SidecarImages map[string]string `name:"sidecar_docker_images"` // deprecated in favour of SidecarContainers SidecarContainers []v1.Container `name:"sidecars"` PodServiceAccountName string `name:"pod_service_account_name" default:"postgres-pod"` From 4f130f9cce36049fe803c12efdd0e344bbcc0da3 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Mon, 2 Feb 2026 16:35:01 +0100 Subject: [PATCH 14/21] provide examples for maintenance_windows in manifest examples (#3040) --- manifests/configmap.yaml | 1 + manifests/postgresql-operator-default-configuration.yaml | 3 +++ pkg/cluster/util_test.go | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/manifests/configmap.yaml b/manifests/configmap.yaml index 6d51053bb..7db161500 100644 --- a/manifests/configmap.yaml +++ b/manifests/configmap.yaml @@ -102,6 +102,7 @@ data: logical_backup_s3_sse: "AES256" logical_backup_s3_retention_time: "" logical_backup_schedule: "30 00 * * *" + # maintenance_windows: "Sat:22:00-23:59,Sun:00:00-01:00" major_version_upgrade_mode: "manual" # major_version_upgrade_team_allow_list: "" master_dns_name_format: "{cluster}.{namespace}.{hostedzone}" diff --git a/manifests/postgresql-operator-default-configuration.yaml b/manifests/postgresql-operator-default-configuration.yaml index d4f9fc812..0817a200a 100644 --- a/manifests/postgresql-operator-default-configuration.yaml +++ b/manifests/postgresql-operator-default-configuration.yaml @@ -16,6 +16,9 @@ configuration: # ignore_instance_limits_annotation_key: "" # ignore_resources_limits_annotation_key: "" # kubernetes_use_configmaps: false + # maintenance_windows: + # - "Sat:22:00-23:59" + # - "Sun:00:00-01:00" max_instances: -1 min_instances: -1 resync_period: 30m diff --git a/pkg/cluster/util_test.go b/pkg/cluster/util_test.go index e5622b9ac..ea3e81e89 100644 --- a/pkg/cluster/util_test.go +++ b/pkg/cluster/util_test.go @@ -735,7 +735,7 @@ func TestIsInMaintenanceWindow(t *testing.T) { { name: "global maintenance windows all day", windows: nil, - configWindows: []string{"00:00-23:59"}, + configWindows: []string{"00:00-02:00", "02:00-23:59"}, expected: true, }, } From 6ce7c50cec7a17abd9ce80337496a00d0b24678b Mon Sep 17 00:00:00 2001 From: Ida Novindasari Date: Tue, 17 Feb 2026 10:19:19 +0100 Subject: [PATCH 15/21] Add support for pg18 and remove pg13 (#3035) * Add support for pg18 and remove pg13 * Update general spilo image and use new rebuilt e2e spilo image --------- Co-authored-by: Polina Bungina --- README.md | 6 +-- .../templates/deployment.yaml | 2 +- .../crds/operatorconfigurations.yaml | 6 +-- .../postgres-operator/crds/postgresqls.yaml | 2 +- charts/postgres-operator/values.yaml | 8 ++-- docs/administrator.md | 2 +- docs/reference/cluster_manifest.md | 4 +- docs/reference/operator_parameters.md | 4 +- docs/user.md | 18 +++---- e2e/run.sh | 7 ++- e2e/tests/test_e2e.py | 47 ++++++++++--------- logical-backup/Dockerfile | 2 +- manifests/complete-postgres-manifest.yaml | 4 +- manifests/configmap.yaml | 6 +-- .../minimal-master-replica-svcmonitor.yaml | 2 +- ...imal-postgres-lowest-version-manifest.yaml | 2 +- manifests/minimal-postgres-manifest.yaml | 2 +- manifests/operatorconfiguration.crd.yaml | 6 +-- ...gresql-operator-default-configuration.yaml | 6 +-- manifests/postgresql.crd.yaml | 11 ++--- manifests/standby-manifest.yaml | 2 +- .../v1/operator_configuration_type.go | 4 +- pkg/apis/acid.zalan.do/v1/postgresql_type.go | 2 +- pkg/apis/acid.zalan.do/v1/util_test.go | 10 ++-- pkg/cluster/k8sres_test.go | 38 +++++++-------- pkg/cluster/majorversionupgrade.go | 4 +- pkg/controller/operator_config.go | 6 +-- pkg/util/config/config.go | 6 +-- ui/manifests/deployment.yaml | 2 +- ui/operator_ui/main.py | 2 +- ui/run_local.sh | 2 +- 31 files changed, 113 insertions(+), 112 deletions(-) diff --git a/README.md b/README.md index 7d54e9fd9..9e5bc886b 100644 --- a/README.md +++ b/README.md @@ -29,13 +29,13 @@ pipelines with no access to Kubernetes API directly, promoting infrastructure as ### PostgreSQL features -* Supports PostgreSQL 17, starting from 13+ +* Supports PostgreSQL 18, starting from 14+ * Streaming replication cluster via Patroni * Point-In-Time-Recovery with -[pg_basebackup](https://www.postgresql.org/docs/17/app-pgbasebackup.html) / +[pg_basebackup](https://www.postgresql.org/docs/18/app-pgbasebackup.html) / [WAL-G](https://github.com/wal-g/wal-g) or [WAL-E](https://github.com/wal-e/wal-e) via [Spilo](https://github.com/zalando/spilo) * Preload libraries: [bg_mon](https://github.com/CyberDem0n/bg_mon), -[pg_stat_statements](https://www.postgresql.org/docs/17/pgstatstatements.html), +[pg_stat_statements](https://www.postgresql.org/docs/18/pgstatstatements.html), [pgextwlist](https://github.com/dimitri/pgextwlist), [pg_auth_mon](https://github.com/RafiaSabih/pg_auth_mon) * Incl. popular Postgres extensions such as diff --git a/charts/postgres-operator-ui/templates/deployment.yaml b/charts/postgres-operator-ui/templates/deployment.yaml index fbb9ee086..c8797e42e 100644 --- a/charts/postgres-operator-ui/templates/deployment.yaml +++ b/charts/postgres-operator-ui/templates/deployment.yaml @@ -84,11 +84,11 @@ spec: "limit_iops": 16000, "limit_throughput": 1000, "postgresql_versions": [ + "18", "17", "16", "15", "14", - "13" ] } {{- if .Values.extraEnvs }} diff --git a/charts/postgres-operator/crds/operatorconfigurations.yaml b/charts/postgres-operator/crds/operatorconfigurations.yaml index 373a295c5..2c432a082 100644 --- a/charts/postgres-operator/crds/operatorconfigurations.yaml +++ b/charts/postgres-operator/crds/operatorconfigurations.yaml @@ -68,7 +68,7 @@ spec: type: string docker_image: type: string - default: "ghcr.io/zalando/spilo-17:4.0-p3" + default: "ghcr.io/zalando/spilo-18:4.1-p1" enable_crd_registration: type: boolean default: true @@ -174,10 +174,10 @@ spec: type: string minimal_major_version: type: string - default: "13" + default: "14" target_major_version: type: string - default: "17" + default: "18" kubernetes: type: object properties: diff --git a/charts/postgres-operator/crds/postgresqls.yaml b/charts/postgres-operator/crds/postgresqls.yaml index cbf5c98ef..c801346e4 100644 --- a/charts/postgres-operator/crds/postgresqls.yaml +++ b/charts/postgres-operator/crds/postgresqls.yaml @@ -374,11 +374,11 @@ spec: version: type: string enum: - - "13" - "14" - "15" - "16" - "17" + - "18" parameters: type: object additionalProperties: diff --git a/charts/postgres-operator/values.yaml b/charts/postgres-operator/values.yaml index e8e26494d..6d0161bfb 100644 --- a/charts/postgres-operator/values.yaml +++ b/charts/postgres-operator/values.yaml @@ -31,14 +31,12 @@ configGeneral: enable_pgversion_env_var: true # start any new database pod without limitations on shm memory enable_shm_volume: true - # enables backwards compatible path between Spilo 12 and Spilo 13+ images - enable_spilo_wal_path_compat: false # operator will sync only clusters where name starts with teamId prefix enable_team_id_clustername_prefix: false # etcd connection string for Patroni. Empty uses K8s-native DCS. etcd_host: "" # Spilo docker image - docker_image: ghcr.io/zalando/spilo-17:4.0-p3 + docker_image: ghcr.io/zalando/spilo-18:4.1-p1 # key name for annotation to ignore globally configured instance limits # ignore_instance_limits_annotation_key: "" @@ -96,9 +94,9 @@ configMajorVersionUpgrade: # - acid # minimal Postgres major version that will not automatically be upgraded - minimal_major_version: "13" + minimal_major_version: "14" # target Postgres major version when upgrading clusters automatically - target_major_version: "17" + target_major_version: "18" configKubernetes: # list of additional capabilities for postgres container diff --git a/docs/administrator.md b/docs/administrator.md index fe36e3744..60db0f3f1 100644 --- a/docs/administrator.md +++ b/docs/administrator.md @@ -1312,7 +1312,7 @@ aws_or_gcp: If cluster members have to be (re)initialized restoring physical backups happens automatically either from the backup location or by running -[pg_basebackup](https://www.postgresql.org/docs/17/app-pgbasebackup.html) +[pg_basebackup](https://www.postgresql.org/docs/18/app-pgbasebackup.html) on one of the other running instances (preferably replicas if they do not lag behind). You can test restoring backups by [cloning](user.md#how-to-clone-an-existing-postgresql-cluster) clusters. diff --git a/docs/reference/cluster_manifest.md b/docs/reference/cluster_manifest.md index 0524e1a86..ae23dabb9 100644 --- a/docs/reference/cluster_manifest.md +++ b/docs/reference/cluster_manifest.md @@ -647,7 +647,7 @@ the global configuration before adding the `tls` section'. ## Change data capture streams This sections enables change data capture (CDC) streams via Postgres' -[logical decoding](https://www.postgresql.org/docs/17/logicaldecoding.html) +[logical decoding](https://www.postgresql.org/docs/18/logicaldecoding.html) feature and `pgoutput` plugin. While the Postgres operator takes responsibility for providing the setup to publish change events, it relies on external tools to consume them. At Zalando, we are using a workflow based on @@ -680,7 +680,7 @@ can have the following properties: The CDC operator is following the [outbox pattern](https://debezium.io/blog/2019/02/19/reliable-microservices-data-exchange-with-the-outbox-pattern/). The application is responsible for putting events into a (JSON/B or VARCHAR) payload column of the outbox table in the structure of the specified target - event type. The operator will create a [PUBLICATION](https://www.postgresql.org/docs/17/logical-replication-publication.html) + event type. The operator will create a [PUBLICATION](https://www.postgresql.org/docs/18/logical-replication-publication.html) in Postgres for all tables specified for one `database` and `applicationId`. The CDC operator will consume from it shortly after transactions are committed to the outbox table. The `idColumn` will be used in telemetry for diff --git a/docs/reference/operator_parameters.md b/docs/reference/operator_parameters.md index 206131b7b..a62f67dfb 100644 --- a/docs/reference/operator_parameters.md +++ b/docs/reference/operator_parameters.md @@ -268,12 +268,12 @@ CRD-configuration, they are grouped under the `major_version_upgrade` key. * **minimal_major_version** The minimal Postgres major version that will not automatically be upgraded - when `major_version_upgrade_mode` is set to `"full"`. The default is `"13"`. + when `major_version_upgrade_mode` is set to `"full"`. The default is `"14"`. * **target_major_version** The target Postgres major version when upgrading clusters automatically which violate the configured allowed `minimal_major_version` when - `major_version_upgrade_mode` is set to `"full"`. The default is `"17"`. + `major_version_upgrade_mode` is set to `"full"`. The default is `"18"`. ## Kubernetes resources diff --git a/docs/user.md b/docs/user.md index db33d0bd6..236b439a8 100644 --- a/docs/user.md +++ b/docs/user.md @@ -30,7 +30,7 @@ spec: databases: foo: zalando postgresql: - version: "17" + version: "18" ``` Once you cloned the Postgres Operator [repository](https://github.com/zalando/postgres-operator) @@ -109,7 +109,7 @@ metadata: spec: [...] postgresql: - version: "17" + version: "18" parameters: password_encryption: scram-sha-256 ``` @@ -517,7 +517,7 @@ Postgres Operator will create the following NOLOGIN roles: The `_owner` role is the database owner and should be used when creating new database objects. All members of the `admin` role, e.g. teams API roles, can -become the owner with the `SET ROLE` command. [Default privileges](https://www.postgresql.org/docs/17/sql-alterdefaultprivileges.html) +become the owner with the `SET ROLE` command. [Default privileges](https://www.postgresql.org/docs/18/sql-alterdefaultprivileges.html) are configured for the owner role so that the `_reader` role automatically gets read-access (SELECT) to new tables and sequences and the `_writer` receives write-access (INSERT, UPDATE, DELETE on tables, @@ -594,7 +594,7 @@ spec: ### Schema `search_path` for default roles -The schema [`search_path`](https://www.postgresql.org/docs/17/ddl-schemas.html#DDL-SCHEMAS-PATH) +The schema [`search_path`](https://www.postgresql.org/docs/18/ddl-schemas.html#DDL-SCHEMAS-PATH) for each role will include the role name and the schemas, this role should have access to. So `foo_bar_writer` does not have to schema-qualify tables from schemas `foo_bar_writer, bar`, while `foo_writer` can look up `foo_writer` and @@ -695,7 +695,7 @@ handle it. ### HugePages support -The operator supports [HugePages](https://www.postgresql.org/docs/17/kernel-resources.html#LINUX-HUGEPAGES). +The operator supports [HugePages](https://www.postgresql.org/docs/18/kernel-resources.html#LINUX-HUGEPAGES). To enable HugePages, set the matching resource requests and/or limits in the manifest: ```yaml @@ -757,7 +757,7 @@ If you need to define a `nodeAffinity` for all your Postgres clusters use the ## In-place major version upgrade -Starting with Spilo 13, operator supports in-place major version upgrade to a +Starting with Spilo 14, operator supports in-place major version upgrade to a higher major version (e.g. from PG 14 to PG 16). To trigger the upgrade, simply increase the version in the manifest. It is your responsibility to test your applications against the new version before the upgrade; downgrading is @@ -792,7 +792,7 @@ spec: clone: uid: "efd12e58-5786-11e8-b5a7-06148230260c" cluster: "acid-minimal-cluster" - timestamp: "2017-12-19T12:40:33+01:00" + timestamp: "2025-12-19T12:40:33+01:00" ``` Here `cluster` is a name of a source cluster that is going to be cloned. A new @@ -827,7 +827,7 @@ spec: clone: uid: "efd12e58-5786-11e8-b5a7-06148230260c" cluster: "acid-minimal-cluster" - timestamp: "2017-12-19T12:40:33+01:00" + timestamp: "2025-12-19T12:40:33+01:00" s3_wal_path: "s3://custom/path/to/bucket" s3_endpoint: https://s3.acme.org s3_access_key_id: 0123456789abcdef0123456789abcdef @@ -838,7 +838,7 @@ spec: ### Clone directly Another way to get a fresh copy of your source DB cluster is via -[pg_basebackup](https://www.postgresql.org/docs/17/app-pgbasebackup.html). To +[pg_basebackup](https://www.postgresql.org/docs/18/app-pgbasebackup.html). To use this feature simply leave out the timestamp field from the clone section. The operator will connect to the service of the source cluster by name. If the cluster is called test, then the connection string will look like host=test diff --git a/e2e/run.sh b/e2e/run.sh index 5ebf2a7cb..8950e19dd 100755 --- a/e2e/run.sh +++ b/e2e/run.sh @@ -8,7 +8,7 @@ IFS=$'\n\t' readonly cluster_name="postgres-operator-e2e-tests" readonly kubeconfig_path="${HOME}/kind-config-${cluster_name}" -readonly spilo_image="registry.opensource.zalan.do/acid/spilo-17-e2e:0.3" +readonly spilo_image="ghcr.io/zalando/spilo-18:4.1-p1" readonly e2e_test_runner_image="ghcr.io/zalando/postgres-operator-e2e-tests-runner:latest" export GOPATH=${GOPATH-~/go} @@ -42,7 +42,10 @@ function start_kind(){ export KUBECONFIG="${kubeconfig_path}" kind create cluster --name ${cluster_name} --config kind-cluster-postgres-operator-e2e-tests.yaml - docker pull "${spilo_image}" + + # Pull all platforms to satisfy Kind's --all-platforms requirement + docker pull --platform linux/amd64 "${spilo_image}" + docker pull --platform linux/arm64 "${spilo_image}" kind load docker-image "${spilo_image}" --name ${cluster_name} } diff --git a/e2e/tests/test_e2e.py b/e2e/tests/test_e2e.py index f473b5cc4..70145f3e4 100644 --- a/e2e/tests/test_e2e.py +++ b/e2e/tests/test_e2e.py @@ -12,9 +12,9 @@ from kubernetes import client from tests.k8s_api import K8s from kubernetes.client.rest import ApiException -SPILO_CURRENT = "registry.opensource.zalan.do/acid/spilo-17-e2e:0.3" -SPILO_LAZY = "registry.opensource.zalan.do/acid/spilo-17-e2e:0.4" -SPILO_FULL_IMAGE = "ghcr.io/zalando/spilo-17:4.0-p3" +SPILO_CURRENT = "ghcr.io/zalando/spilo-e2e:dev-18.3" +SPILO_LAZY = "ghcr.io/zalando/spilo-e2e:dev-18.4" +SPILO_FULL_IMAGE = "ghcr.io/zalando/spilo-18:4.1-p1" def to_selector(labels): return ",".join(["=".join(lbl) for lbl in labels.items()]) @@ -151,6 +151,7 @@ class EndToEndTestCase(unittest.TestCase): 'default', label_selector='name=postgres-operator').items[0].spec.containers[0].image print("Tested operator image: {}".format(actual_operator_image)) # shows up after tests finish + # load minimal Postgres manifest and wait for cluster to be up and running result = k8s.create_with_kubectl("manifests/minimal-postgres-manifest.yaml") print('stdout: {}, stderr: {}'.format(result.stdout, result.stderr)) try: @@ -1211,25 +1212,25 @@ class EndToEndTestCase(unittest.TestCase): k8s.create_with_kubectl("manifests/minimal-postgres-lowest-version-manifest.yaml") self.eventuallyEqual(lambda: k8s.count_running_pods(labels=cluster_label), 2, "No 2 pods running") self.eventuallyEqual(lambda: k8s.get_operator_state(), {"0": "idle"}, "Operator does not get in sync") - self.eventuallyEqual(check_version, 13, "Version is not correct") + self.eventuallyEqual(check_version, 14, "Version is not correct") master_nodes, _ = k8s.get_cluster_nodes(cluster_labels=cluster_label) # should upgrade immediately - pg_patch_version_14 = { + pg_patch_version_higher_version = { "spec": { "postgresql": { - "version": "14" + "version": "15" } } } k8s.api.custom_objects_api.patch_namespaced_custom_object( - "acid.zalan.do", "v1", "default", "postgresqls", "acid-upgrade-test", pg_patch_version_14) + "acid.zalan.do", "v1", "default", "postgresqls", "acid-upgrade-test", pg_patch_version_higher_version) self.eventuallyEqual(lambda: k8s.get_operator_state(), {"0": "idle"}, "Operator does not get in sync") k8s.wait_for_pod_failover(master_nodes, 'spilo-role=replica,' + cluster_label) k8s.wait_for_pod_start('spilo-role=master,' + cluster_label) k8s.wait_for_pod_start('spilo-role=replica,' + cluster_label) - self.eventuallyEqual(check_version, 14, "Version should be upgraded from 13 to 14") + self.eventuallyEqual(check_version, 15, "Version should be upgraded from 14 to 15") # check if annotation for last upgrade's success is set annotations = get_annotations() @@ -1238,10 +1239,10 @@ class EndToEndTestCase(unittest.TestCase): # should not upgrade because current time is not in maintenanceWindow current_time = datetime.now() maintenance_window_future = f"{(current_time+timedelta(minutes=60)).strftime('%H:%M')}-{(current_time+timedelta(minutes=120)).strftime('%H:%M')}" - pg_patch_version_15_outside_mw = { + pg_patch_version_higher_version_outside_mw = { "spec": { "postgresql": { - "version": "15" + "version": "16" }, "maintenanceWindows": [ maintenance_window_future @@ -1249,23 +1250,23 @@ class EndToEndTestCase(unittest.TestCase): } } k8s.api.custom_objects_api.patch_namespaced_custom_object( - "acid.zalan.do", "v1", "default", "postgresqls", "acid-upgrade-test", pg_patch_version_15_outside_mw) + "acid.zalan.do", "v1", "default", "postgresqls", "acid-upgrade-test", pg_patch_version_higher_version_outside_mw) self.eventuallyEqual(lambda: k8s.get_operator_state(), {"0": "idle"}, "Operator does not get in sync") # no pod replacement outside of the maintenance window k8s.wait_for_pod_start('spilo-role=master,' + cluster_label) k8s.wait_for_pod_start('spilo-role=replica,' + cluster_label) - self.eventuallyEqual(check_version, 14, "Version should not be upgraded") + self.eventuallyEqual(check_version, 15, "Version should not be upgraded") second_annotations = get_annotations() self.assertIsNone(second_annotations.get("last-major-upgrade-failure"), "Annotation for last upgrade's failure should not be set") # change maintenanceWindows to current maintenance_window_current = f"{(current_time-timedelta(minutes=30)).strftime('%H:%M')}-{(current_time+timedelta(minutes=30)).strftime('%H:%M')}" - pg_patch_version_15_in_mw = { + pg_patch_version_higher_version_in_mw = { "spec": { "postgresql": { - "version": "15" + "version": "16" }, "maintenanceWindows": [ maintenance_window_current @@ -1274,13 +1275,13 @@ class EndToEndTestCase(unittest.TestCase): } k8s.api.custom_objects_api.patch_namespaced_custom_object( - "acid.zalan.do", "v1", "default", "postgresqls", "acid-upgrade-test", pg_patch_version_15_in_mw) + "acid.zalan.do", "v1", "default", "postgresqls", "acid-upgrade-test", pg_patch_version_higher_version_in_mw) self.eventuallyEqual(lambda: k8s.get_operator_state(), {"0": "idle"}, "Operator does not get in sync") k8s.wait_for_pod_failover(master_nodes, 'spilo-role=master,' + cluster_label) k8s.wait_for_pod_start('spilo-role=master,' + cluster_label) k8s.wait_for_pod_start('spilo-role=replica,' + cluster_label) - self.eventuallyEqual(check_version, 15, "Version should be upgraded from 14 to 15") + self.eventuallyEqual(check_version, 16, "Version should be upgraded from 15 to 16") # check if annotation for last upgrade's success is updated after second upgrade third_annotations = get_annotations() @@ -1288,7 +1289,7 @@ class EndToEndTestCase(unittest.TestCase): self.assertNotEqual(annotations.get("last-major-upgrade-success"), third_annotations.get("last-major-upgrade-success"), "Annotation for last upgrade's success is not updated") # test upgrade with failed upgrade annotation - pg_patch_version_17 = { + pg_patch_version_highest_version = { "metadata": { "annotations": { "last-major-upgrade-failure": "2024-01-02T15:04:05Z" @@ -1296,28 +1297,28 @@ class EndToEndTestCase(unittest.TestCase): }, "spec": { "postgresql": { - "version": "17" + "version": "18" }, }, } k8s.api.custom_objects_api.patch_namespaced_custom_object( - "acid.zalan.do", "v1", "default", "postgresqls", "acid-upgrade-test", pg_patch_version_17) + "acid.zalan.do", "v1", "default", "postgresqls", "acid-upgrade-test", pg_patch_version_highest_version) self.eventuallyEqual(lambda: k8s.get_operator_state(), {"0": "idle"}, "Operator does not get in sync") k8s.wait_for_pod_failover(master_nodes, 'spilo-role=replica,' + cluster_label) k8s.wait_for_pod_start('spilo-role=master,' + cluster_label) k8s.wait_for_pod_start('spilo-role=replica,' + cluster_label) - self.eventuallyEqual(check_version, 15, "Version should not be upgraded because annotation for last upgrade's failure is set") + self.eventuallyEqual(check_version, 16, "Version should not be upgraded because annotation for last upgrade's failure is set") - # change the version back to 15 and should remove failure annotation + # change the version back to 16 and should remove failure annotation k8s.api.custom_objects_api.patch_namespaced_custom_object( - "acid.zalan.do", "v1", "default", "postgresqls", "acid-upgrade-test", pg_patch_version_15_in_mw) + "acid.zalan.do", "v1", "default", "postgresqls", "acid-upgrade-test", pg_patch_version_higher_version_in_mw) self.eventuallyEqual(lambda: k8s.get_operator_state(), {"0": "idle"}, "Operator does not get in sync") k8s.wait_for_pod_start('spilo-role=master,' + cluster_label) k8s.wait_for_pod_start('spilo-role=replica,' + cluster_label) - self.eventuallyEqual(check_version, 15, "Version should not be upgraded from 15") + self.eventuallyEqual(check_version, 16, "Version should not be upgraded from 16") fourth_annotations = get_annotations() self.assertIsNone(fourth_annotations.get("last-major-upgrade-failure"), "Annotation for last upgrade's failure is not removed") diff --git a/logical-backup/Dockerfile b/logical-backup/Dockerfile index 137f4efa8..94b8f1a35 100644 --- a/logical-backup/Dockerfile +++ b/logical-backup/Dockerfile @@ -25,11 +25,11 @@ RUN apt-get update \ && curl --silent https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - \ && apt-get update \ && apt-get install --no-install-recommends -y \ + postgresql-client-18 \ postgresql-client-17 \ postgresql-client-16 \ postgresql-client-15 \ postgresql-client-14 \ - postgresql-client-13 \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* diff --git a/manifests/complete-postgres-manifest.yaml b/manifests/complete-postgres-manifest.yaml index 7677dca62..7b347a9c8 100644 --- a/manifests/complete-postgres-manifest.yaml +++ b/manifests/complete-postgres-manifest.yaml @@ -10,7 +10,7 @@ metadata: # "delete-date": "2020-08-31" # can only be deleted on that day if "delete-date "key is configured # "delete-clustername": "acid-test-cluster" # can only be deleted when name matches if "delete-clustername" key is configured spec: - dockerImage: ghcr.io/zalando/spilo-17:4.0-p3 + dockerImage: ghcr.io/zalando/spilo-18:4.1-p1 teamId: "acid" numberOfInstances: 2 users: # Application/Robot users @@ -48,7 +48,7 @@ spec: defaultRoles: true defaultUsers: false postgresql: - version: "17" + version: "18" parameters: # Expert section shared_buffers: "32MB" max_connections: "10" diff --git a/manifests/configmap.yaml b/manifests/configmap.yaml index 7db161500..1cf455e57 100644 --- a/manifests/configmap.yaml +++ b/manifests/configmap.yaml @@ -34,7 +34,7 @@ data: default_memory_request: 100Mi # delete_annotation_date_key: delete-date # delete_annotation_name_key: delete-clustername - docker_image: ghcr.io/zalando/spilo-17:4.0-p3 + docker_image: ghcr.io/zalando/spilo-18:4.1-p1 # downscaler_annotations: "deployment-time,downscaler/*" enable_admin_role_for_users: "true" enable_crd_registration: "true" @@ -114,7 +114,7 @@ data: min_cpu_limit: 250m min_instances: "-1" min_memory_limit: 250Mi - minimal_major_version: "13" + minimal_major_version: "14" # node_readiness_label: "status:ready" # node_readiness_label_merge: "OR" oauth_token_secret_name: postgresql-operator @@ -164,7 +164,7 @@ data: spilo_privileged: "false" storage_resize_mode: "pvc" super_username: postgres - target_major_version: "17" + target_major_version: "18" team_admin_role: "admin" team_api_role_configuration: "log_statement:all" teams_api_url: http://fake-teams-api.default.svc.cluster.local diff --git a/manifests/minimal-master-replica-svcmonitor.yaml b/manifests/minimal-master-replica-svcmonitor.yaml index 049ea12eb..57bbd01d1 100644 --- a/manifests/minimal-master-replica-svcmonitor.yaml +++ b/manifests/minimal-master-replica-svcmonitor.yaml @@ -28,7 +28,7 @@ spec: preparedDatabases: bar: {} postgresql: - version: "13" + version: "18" sidecars: - name: "exporter" image: "quay.io/prometheuscommunity/postgres-exporter:v0.15.0" diff --git a/manifests/minimal-postgres-lowest-version-manifest.yaml b/manifests/minimal-postgres-lowest-version-manifest.yaml index 40abf0c9c..897925c56 100644 --- a/manifests/minimal-postgres-lowest-version-manifest.yaml +++ b/manifests/minimal-postgres-lowest-version-manifest.yaml @@ -17,4 +17,4 @@ spec: preparedDatabases: bar: {} postgresql: - version: "13" + version: "14" diff --git a/manifests/minimal-postgres-manifest.yaml b/manifests/minimal-postgres-manifest.yaml index 8b1ed275d..086ce553a 100644 --- a/manifests/minimal-postgres-manifest.yaml +++ b/manifests/minimal-postgres-manifest.yaml @@ -17,4 +17,4 @@ spec: preparedDatabases: bar: {} postgresql: - version: "17" + version: "18" diff --git a/manifests/operatorconfiguration.crd.yaml b/manifests/operatorconfiguration.crd.yaml index f792078f5..03534cefb 100644 --- a/manifests/operatorconfiguration.crd.yaml +++ b/manifests/operatorconfiguration.crd.yaml @@ -66,7 +66,7 @@ spec: type: string docker_image: type: string - default: "ghcr.io/zalando/spilo-17:4.0-p3" + default: "ghcr.io/zalando/spilo-18:4.1-p1" enable_crd_registration: type: boolean default: true @@ -172,10 +172,10 @@ spec: type: string minimal_major_version: type: string - default: "13" + default: "14" target_major_version: type: string - default: "17" + default: "18" kubernetes: type: object properties: diff --git a/manifests/postgresql-operator-default-configuration.yaml b/manifests/postgresql-operator-default-configuration.yaml index 0817a200a..9c54e4379 100644 --- a/manifests/postgresql-operator-default-configuration.yaml +++ b/manifests/postgresql-operator-default-configuration.yaml @@ -3,7 +3,7 @@ kind: OperatorConfiguration metadata: name: postgresql-operator-default-configuration configuration: - docker_image: ghcr.io/zalando/spilo-17:4.0-p3 + docker_image: ghcr.io/zalando/spilo-18:4.1-p1 # enable_crd_registration: true # crd_categories: # - all @@ -43,8 +43,8 @@ configuration: major_version_upgrade_mode: "manual" # major_version_upgrade_team_allow_list: # - acid - minimal_major_version: "13" - target_major_version: "17" + minimal_major_version: "14" + target_major_version: "18" kubernetes: # additional_pod_capabilities: # - "SYS_NICE" diff --git a/manifests/postgresql.crd.yaml b/manifests/postgresql.crd.yaml index c8bf1b07d..39811824e 100644 --- a/manifests/postgresql.crd.yaml +++ b/manifests/postgresql.crd.yaml @@ -3523,11 +3523,11 @@ spec: type: object version: enum: - - "13" - "14" - "15" - "16" - "17" + - "18" type: string required: - version @@ -3935,11 +3935,10 @@ spec: required: - s3_wal_path - gs_wal_path - description: StandbyDescription contains remote primary config and/or - s3/gs wal path. standby_host can be specified alone or together with - either s3_wal_path OR gs_wal_path (mutually exclusive). At least - one field must be specified. s3_wal_path and gs_wal_path are mutually - exclusive. + description: |- + StandbyDescription contains remote primary config and/or s3/gs wal path. + standby_host can be specified alone or together with either s3_wal_path OR gs_wal_path (mutually exclusive). + At least one field must be specified. s3_wal_path and gs_wal_path are mutually exclusive. properties: gs_wal_path: type: string diff --git a/manifests/standby-manifest.yaml b/manifests/standby-manifest.yaml index b06956a1b..de4f9edaf 100644 --- a/manifests/standby-manifest.yaml +++ b/manifests/standby-manifest.yaml @@ -8,7 +8,7 @@ spec: size: 1Gi numberOfInstances: 1 postgresql: - version: "17" + version: "18" # Make this a standby cluster. You can specify s3_wal_path or gs_wal_path for WAL archive, # standby_host for remote primary streaming, or combine standby_host with either WAL path. # Note: s3_wal_path and gs_wal_path are mutually exclusive. diff --git a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go index 290b45ee3..bfad24b0d 100644 --- a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go +++ b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go @@ -49,8 +49,8 @@ type PostgresUsersConfiguration struct { type MajorVersionUpgradeConfiguration struct { MajorVersionUpgradeMode string `json:"major_version_upgrade_mode" default:"manual"` // off - no actions, manual - manifest triggers action, full - manifest and minimal version violation trigger upgrade MajorVersionUpgradeTeamAllowList []string `json:"major_version_upgrade_team_allow_list,omitempty"` - MinimalMajorVersion string `json:"minimal_major_version" default:"13"` - TargetMajorVersion string `json:"target_major_version" default:"17"` + MinimalMajorVersion string `json:"minimal_major_version" default:"14"` + TargetMajorVersion string `json:"target_major_version" default:"18"` } // KubernetesMetaConfiguration defines k8s conf required for all Postgres clusters and the operator itself diff --git a/pkg/apis/acid.zalan.do/v1/postgresql_type.go b/pkg/apis/acid.zalan.do/v1/postgresql_type.go index b304db652..1dadfd06c 100644 --- a/pkg/apis/acid.zalan.do/v1/postgresql_type.go +++ b/pkg/apis/acid.zalan.do/v1/postgresql_type.go @@ -185,7 +185,7 @@ type AdditionalVolume struct { // PostgresqlParam describes PostgreSQL version and pairs of configuration parameter name - values. type PostgresqlParam struct { - // +kubebuilder:validation:Enum="13";"14";"15";"16";"17" + // +kubebuilder:validation:Enum="14";"15";"16";"17";"18" PgVersion string `json:"version"` Parameters map[string]string `json:"parameters,omitempty"` } diff --git a/pkg/apis/acid.zalan.do/v1/util_test.go b/pkg/apis/acid.zalan.do/v1/util_test.go index e060379fd..fcc5ae5fd 100644 --- a/pkg/apis/acid.zalan.do/v1/util_test.go +++ b/pkg/apis/acid.zalan.do/v1/util_test.go @@ -226,7 +226,7 @@ var unmarshalCluster = []struct { "127.0.0.1/32" ], "postgresql": { - "version": "17", + "version": "18", "parameters": { "shared_buffers": "32MB", "max_connections": "10", @@ -286,7 +286,7 @@ var unmarshalCluster = []struct { }, Spec: PostgresSpec{ PostgresqlParam: PostgresqlParam{ - PgVersion: "17", + PgVersion: "18", Parameters: map[string]string{ "shared_buffers": "32MB", "max_connections": "10", @@ -346,7 +346,7 @@ var unmarshalCluster = []struct { }, Error: "", }, - marshal: []byte(`{"kind":"Postgresql","apiVersion":"acid.zalan.do/v1","metadata":{"name":"acid-testcluster1","creationTimestamp":null},"spec":{"postgresql":{"version":"17","parameters":{"log_statement":"all","max_connections":"10","shared_buffers":"32MB"}},"pod_priority_class_name":"spilo-pod-priority","volume":{"size":"5Gi","storageClass":"SSD", "subPath": "subdir"},"enableShmVolume":false,"patroni":{"initdb":{"data-checksums":"true","encoding":"UTF8","locale":"en_US.UTF-8"},"pg_hba":["hostssl all all 0.0.0.0/0 md5","host all all 0.0.0.0/0 md5"],"ttl":30,"loop_wait":10,"retry_timeout":10,"maximum_lag_on_failover":33554432,"slots":{"permanent_logical_1":{"database":"foo","plugin":"pgoutput","type":"logical"}}},"resources":{"requests":{"cpu":"10m","memory":"50Mi"},"limits":{"cpu":"300m","memory":"3000Mi"}},"teamId":"acid","allowedSourceRanges":["127.0.0.1/32"],"numberOfInstances":2,"users":{"zalando":["superuser","createdb"]},"maintenanceWindows":["Mon:01:00-06:00","Sat:00:00-04:00","05:00-05:15"],"clone":{"cluster":"acid-batman"}},"status":{"PostgresClusterStatus":""}}`), + marshal: []byte(`{"kind":"Postgresql","apiVersion":"acid.zalan.do/v1","metadata":{"name":"acid-testcluster1","creationTimestamp":null},"spec":{"postgresql":{"version":"18","parameters":{"log_statement":"all","max_connections":"10","shared_buffers":"32MB"}},"pod_priority_class_name":"spilo-pod-priority","volume":{"size":"5Gi","storageClass":"SSD", "subPath": "subdir"},"enableShmVolume":false,"patroni":{"initdb":{"data-checksums":"true","encoding":"UTF8","locale":"en_US.UTF-8"},"pg_hba":["hostssl all all 0.0.0.0/0 md5","host all all 0.0.0.0/0 md5"],"ttl":30,"loop_wait":10,"retry_timeout":10,"maximum_lag_on_failover":33554432,"slots":{"permanent_logical_1":{"database":"foo","plugin":"pgoutput","type":"logical"}}},"resources":{"requests":{"cpu":"10m","memory":"50Mi"},"limits":{"cpu":"300m","memory":"3000Mi"}},"teamId":"acid","allowedSourceRanges":["127.0.0.1/32"],"numberOfInstances":2,"users":{"zalando":["superuser","createdb"]},"maintenanceWindows":["Mon:01:00-06:00","Sat:00:00-04:00","05:00-05:15"],"clone":{"cluster":"acid-batman"}},"status":{"PostgresClusterStatus":""}}`), err: nil}, { about: "example with clone", @@ -411,7 +411,7 @@ var postgresqlList = []struct { out PostgresqlList err error }{ - {"expect success", []byte(`{"apiVersion":"v1","items":[{"apiVersion":"acid.zalan.do/v1","kind":"Postgresql","metadata":{"labels":{"team":"acid"},"name":"acid-testcluster42","namespace":"default","resourceVersion":"30446957","selfLink":"/apis/acid.zalan.do/v1/namespaces/default/postgresqls/acid-testcluster42","uid":"857cd208-33dc-11e7-b20a-0699041e4b03"},"spec":{"allowedSourceRanges":["185.85.220.0/22"],"numberOfInstances":1,"postgresql":{"version":"17"},"teamId":"acid","volume":{"size":"10Gi"}},"status":{"PostgresClusterStatus":"Running"}}],"kind":"List","metadata":{},"resourceVersion":"","selfLink":""}`), + {"expect success", []byte(`{"apiVersion":"v1","items":[{"apiVersion":"acid.zalan.do/v1","kind":"Postgresql","metadata":{"labels":{"team":"acid"},"name":"acid-testcluster42","namespace":"default","resourceVersion":"30446957","selfLink":"/apis/acid.zalan.do/v1/namespaces/default/postgresqls/acid-testcluster42","uid":"857cd208-33dc-11e7-b20a-0699041e4b03"},"spec":{"allowedSourceRanges":["185.85.220.0/22"],"numberOfInstances":1,"postgresql":{"version":"18"},"teamId":"acid","volume":{"size":"10Gi"}},"status":{"PostgresClusterStatus":"Running"}}],"kind":"List","metadata":{},"resourceVersion":"","selfLink":""}`), PostgresqlList{ TypeMeta: metav1.TypeMeta{ Kind: "List", @@ -432,7 +432,7 @@ var postgresqlList = []struct { }, Spec: PostgresSpec{ ClusterName: "testcluster42", - PostgresqlParam: PostgresqlParam{PgVersion: "17"}, + PostgresqlParam: PostgresqlParam{PgVersion: "18"}, Volume: Volume{Size: "10Gi"}, TeamID: "acid", AllowedSourceRanges: []string{"185.85.220.0/22"}, diff --git a/pkg/cluster/k8sres_test.go b/pkg/cluster/k8sres_test.go index e39e18cd5..04f6476a6 100644 --- a/pkg/cluster/k8sres_test.go +++ b/pkg/cluster/k8sres_test.go @@ -72,18 +72,18 @@ func TestGenerateSpiloJSONConfiguration(t *testing.T) { }{ { subtest: "Patroni default configuration", - pgParam: &acidv1.PostgresqlParam{PgVersion: "17"}, + pgParam: &acidv1.PostgresqlParam{PgVersion: "18"}, patroni: &acidv1.Patroni{}, opConfig: &config.Config{ Auth: config.Auth{ PamRoleName: "zalandos", }, }, - result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/17/bin"},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"}],"dcs":{}}}`, + result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/18/bin"},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"}],"dcs":{}}}`, }, { subtest: "Patroni configured", - pgParam: &acidv1.PostgresqlParam{PgVersion: "17"}, + pgParam: &acidv1.PostgresqlParam{PgVersion: "18"}, patroni: &acidv1.Patroni{ InitDB: map[string]string{ "encoding": "UTF8", @@ -102,38 +102,38 @@ func TestGenerateSpiloJSONConfiguration(t *testing.T) { FailsafeMode: util.True(), }, opConfig: &config.Config{}, - result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/17/bin","pg_hba":["hostssl all all 0.0.0.0/0 md5","host all all 0.0.0.0/0 md5"]},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"},"data-checksums",{"encoding":"UTF8"},{"locale":"en_US.UTF-8"}],"dcs":{"ttl":30,"loop_wait":10,"retry_timeout":10,"maximum_lag_on_failover":33554432,"synchronous_mode":true,"synchronous_mode_strict":true,"synchronous_node_count":1,"slots":{"permanent_logical_1":{"database":"foo","plugin":"pgoutput","type":"logical"}},"failsafe_mode":true}}}`, + result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/18/bin","pg_hba":["hostssl all all 0.0.0.0/0 md5","host all all 0.0.0.0/0 md5"]},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"},"data-checksums",{"encoding":"UTF8"},{"locale":"en_US.UTF-8"}],"dcs":{"ttl":30,"loop_wait":10,"retry_timeout":10,"maximum_lag_on_failover":33554432,"synchronous_mode":true,"synchronous_mode_strict":true,"synchronous_node_count":1,"slots":{"permanent_logical_1":{"database":"foo","plugin":"pgoutput","type":"logical"}},"failsafe_mode":true}}}`, }, { subtest: "Patroni failsafe_mode configured globally", - pgParam: &acidv1.PostgresqlParam{PgVersion: "17"}, + pgParam: &acidv1.PostgresqlParam{PgVersion: "18"}, patroni: &acidv1.Patroni{}, opConfig: &config.Config{ EnablePatroniFailsafeMode: util.True(), }, - result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/17/bin"},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"}],"dcs":{"failsafe_mode":true}}}`, + result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/18/bin"},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"}],"dcs":{"failsafe_mode":true}}}`, }, { subtest: "Patroni failsafe_mode configured globally, disabled for cluster", - pgParam: &acidv1.PostgresqlParam{PgVersion: "17"}, + pgParam: &acidv1.PostgresqlParam{PgVersion: "18"}, patroni: &acidv1.Patroni{ FailsafeMode: util.False(), }, opConfig: &config.Config{ EnablePatroniFailsafeMode: util.True(), }, - result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/17/bin"},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"}],"dcs":{"failsafe_mode":false}}}`, + result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/18/bin"},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"}],"dcs":{"failsafe_mode":false}}}`, }, { subtest: "Patroni failsafe_mode disabled globally, configured for cluster", - pgParam: &acidv1.PostgresqlParam{PgVersion: "17"}, + pgParam: &acidv1.PostgresqlParam{PgVersion: "18"}, patroni: &acidv1.Patroni{ FailsafeMode: util.True(), }, opConfig: &config.Config{ EnablePatroniFailsafeMode: util.False(), }, - result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/17/bin"},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"}],"dcs":{"failsafe_mode":true}}}`, + result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/18/bin"},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"}],"dcs":{"failsafe_mode":true}}}`, }, } for _, tt := range tests { @@ -164,15 +164,15 @@ func TestExtractPgVersionFromBinPath(t *testing.T) { }, { subTest: "test current bin path against hard coded template", - binPath: "/usr/lib/postgresql/17/bin", + binPath: "/usr/lib/postgresql/18/bin", template: pgBinariesLocationTemplate, - expected: "17", + expected: "18", }, { subTest: "test alternative bin path against a matching template", - binPath: "/usr/pgsql-17/bin", + binPath: "/usr/pgsql-18/bin", template: "/usr/pgsql-%v/bin", - expected: "17", + expected: "18", }, } @@ -2188,7 +2188,7 @@ func TestSidecars(t *testing.T) { spec = acidv1.PostgresSpec{ PostgresqlParam: acidv1.PostgresqlParam{ - PgVersion: "17", + PgVersion: "18", Parameters: map[string]string{ "max_connections": "100", }, @@ -2381,7 +2381,7 @@ func TestContainerValidation(t *testing.T) { name: "init container without image", spec: acidv1.PostgresSpec{ PostgresqlParam: acidv1.PostgresqlParam{ - PgVersion: "17", + PgVersion: "18", }, TeamID: "myapp", NumberOfInstances: 1, @@ -2410,7 +2410,7 @@ func TestContainerValidation(t *testing.T) { name: "sidecar without name", spec: acidv1.PostgresSpec{ PostgresqlParam: acidv1.PostgresqlParam{ - PgVersion: "17", + PgVersion: "18", }, TeamID: "myapp", NumberOfInstances: 1, @@ -2439,7 +2439,7 @@ func TestContainerValidation(t *testing.T) { name: "sidecar without image", spec: acidv1.PostgresSpec{ PostgresqlParam: acidv1.PostgresqlParam{ - PgVersion: "17", + PgVersion: "18", }, TeamID: "myapp", NumberOfInstances: 1, @@ -2468,7 +2468,7 @@ func TestContainerValidation(t *testing.T) { name: "valid containers pass validation", spec: acidv1.PostgresSpec{ PostgresqlParam: acidv1.PostgresqlParam{ - PgVersion: "17", + PgVersion: "18", }, TeamID: "myapp", NumberOfInstances: 1, diff --git a/pkg/cluster/majorversionupgrade.go b/pkg/cluster/majorversionupgrade.go index e7e08e8c6..6ba75929c 100644 --- a/pkg/cluster/majorversionupgrade.go +++ b/pkg/cluster/majorversionupgrade.go @@ -16,11 +16,11 @@ import ( // VersionMap Map of version numbers var VersionMap = map[string]int{ - "13": 130000, "14": 140000, "15": 150000, "16": 160000, "17": 170000, + "18": 180000, } const ( @@ -44,7 +44,7 @@ func (c *Cluster) GetDesiredMajorVersionAsInt() int { func (c *Cluster) GetDesiredMajorVersion() string { if c.Config.OpConfig.MajorVersionUpgradeMode == "full" { - // e.g. current is 13, minimal is 13 allowing 13 to 17 clusters, everything below is upgraded + // e.g. current is 14, minimal is 14 allowing 14 to 18 clusters, everything below is upgraded if IsBiggerPostgresVersion(c.Spec.PgVersion, c.Config.OpConfig.MinimalMajorVersion) { c.logger.Infof("overwriting configured major version %s to %s", c.Spec.PgVersion, c.Config.OpConfig.TargetMajorVersion) return c.Config.OpConfig.TargetMajorVersion diff --git a/pkg/controller/operator_config.go b/pkg/controller/operator_config.go index db7166ccd..7719a2939 100644 --- a/pkg/controller/operator_config.go +++ b/pkg/controller/operator_config.go @@ -39,7 +39,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur result.EnableTeamIdClusternamePrefix = fromCRD.EnableTeamIdClusternamePrefix result.EtcdHost = fromCRD.EtcdHost result.KubernetesUseConfigMaps = fromCRD.KubernetesUseConfigMaps - result.DockerImage = util.Coalesce(fromCRD.DockerImage, "ghcr.io/zalando/spilo-17:4.0-p3") + result.DockerImage = util.Coalesce(fromCRD.DockerImage, "ghcr.io/zalando/spilo-18:4.1-p1") result.Workers = util.CoalesceUInt32(fromCRD.Workers, 8) result.MinInstances = fromCRD.MinInstances result.MaxInstances = fromCRD.MaxInstances @@ -73,8 +73,8 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur // major version upgrade config result.MajorVersionUpgradeMode = util.Coalesce(fromCRD.MajorVersionUpgrade.MajorVersionUpgradeMode, "manual") result.MajorVersionUpgradeTeamAllowList = fromCRD.MajorVersionUpgrade.MajorVersionUpgradeTeamAllowList - result.MinimalMajorVersion = util.Coalesce(fromCRD.MajorVersionUpgrade.MinimalMajorVersion, "13") - result.TargetMajorVersion = util.Coalesce(fromCRD.MajorVersionUpgrade.TargetMajorVersion, "17") + result.MinimalMajorVersion = util.Coalesce(fromCRD.MajorVersionUpgrade.MinimalMajorVersion, "14") + result.TargetMajorVersion = util.Coalesce(fromCRD.MajorVersionUpgrade.TargetMajorVersion, "18") // kubernetes config result.EnableOwnerReferences = util.CoalesceBool(fromCRD.Kubernetes.EnableOwnerReferences, util.False()) diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index 54bee0423..468cf9328 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -176,8 +176,8 @@ type Config struct { WatchedNamespace string `name:"watched_namespace"` // special values: "*" means 'watch all namespaces', the empty string "" means 'watch a namespace where operator is deployed to' KubernetesUseConfigMaps bool `name:"kubernetes_use_configmaps" default:"false"` EtcdHost string `name:"etcd_host" default:""` // special values: the empty string "" means Patroni will use K8s as a DCS - DockerImage string `name:"docker_image" default:"ghcr.io/zalando/spilo-17:4.0-p3"` MaintenanceWindows []string `name:"maintenance_windows"` + DockerImage string `name:"docker_image" default:"ghcr.io/zalando/spilo-18:4.1-p1"` SidecarImages map[string]string `name:"sidecar_docker_images"` // deprecated in favour of SidecarContainers SidecarContainers []v1.Container `name:"sidecars"` PodServiceAccountName string `name:"pod_service_account_name" default:"postgres-pod"` @@ -248,8 +248,8 @@ type Config struct { EnableTeamIdClusternamePrefix bool `name:"enable_team_id_clustername_prefix" default:"false"` MajorVersionUpgradeMode string `name:"major_version_upgrade_mode" default:"manual"` MajorVersionUpgradeTeamAllowList []string `name:"major_version_upgrade_team_allow_list" default:""` - MinimalMajorVersion string `name:"minimal_major_version" default:"13"` - TargetMajorVersion string `name:"target_major_version" default:"17"` + MinimalMajorVersion string `name:"minimal_major_version" default:"14"` + TargetMajorVersion string `name:"target_major_version" default:"18"` PatroniAPICheckInterval time.Duration `name:"patroni_api_check_interval" default:"1s"` PatroniAPICheckTimeout time.Duration `name:"patroni_api_check_timeout" default:"5s"` EnablePatroniFailsafeMode *bool `name:"enable_patroni_failsafe_mode" default:"false"` diff --git a/ui/manifests/deployment.yaml b/ui/manifests/deployment.yaml index 8c664de22..ad41c38c7 100644 --- a/ui/manifests/deployment.yaml +++ b/ui/manifests/deployment.yaml @@ -73,11 +73,11 @@ spec: "limit_iops": 16000, "limit_throughput": 1000, "postgresql_versions": [ + "18", "17", "16", "15", "14", - "13" ] } # Exemple of settings to make snapshot view working in the ui when using AWS diff --git a/ui/operator_ui/main.py b/ui/operator_ui/main.py index bf28df6eb..c91450d4c 100644 --- a/ui/operator_ui/main.py +++ b/ui/operator_ui/main.py @@ -259,7 +259,7 @@ DEFAULT_UI_CONFIG = { 'users_visible': True, 'databases_visible': True, 'resources_visible': RESOURCES_VISIBLE, - 'postgresql_versions': ['13', '14', '15', '16', '17'], + 'postgresql_versions': ['14', '15', '16', '17', '18'], 'dns_format_string': '{0}.{1}', 'pgui_link': '', 'static_network_whitelist': {}, diff --git a/ui/run_local.sh b/ui/run_local.sh index 37f8b1747..59729a92a 100755 --- a/ui/run_local.sh +++ b/ui/run_local.sh @@ -31,11 +31,11 @@ default_operator_ui_config='{ "limit_iops": 16000, "limit_throughput": 1000, "postgresql_versions": [ + "18", "17", "16", "15", "14", - "13" ], "static_network_whitelist": { "localhost": ["172.0.0.1/32"] From cffa0ee63ccec76de6ceecc86325c520576d1884 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Wed, 18 Feb 2026 08:38:17 +0100 Subject: [PATCH 16/21] try to set infra roles also if one fails (#3045) --- pkg/controller/controller.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go index e46b9ee44..13e4017c8 100644 --- a/pkg/controller/controller.go +++ b/pkg/controller/controller.go @@ -347,9 +347,11 @@ func (c *Controller) initController() { logMultiLineConfig(c.logger, c.opConfig.MustMarshal()) roleDefs := c.getInfrastructureRoleDefinitions() - if infraRoles, err := c.getInfrastructureRoles(roleDefs); err != nil { - c.logger.Warningf("could not get infrastructure roles: %v", err) - } else { + infraRoles, err := c.getInfrastructureRoles(roleDefs) + if err != nil { + c.logger.Warningf("could not get all infrastructure roles: %v", err) + } + if len(infraRoles) > 0 { c.config.InfrastructureRoles = infraRoles } From 9f9a3acb613abaf2eb974426de7086f7b9c96d5e Mon Sep 17 00:00:00 2001 From: Mikkel Oscar Lyderik Larsen Date: Wed, 18 Feb 2026 14:20:46 +0100 Subject: [PATCH 17/21] Checkin CRD to make go get work (#3047) Signed-off-by: Mikkel Oscar Lyderik Larsen --- .gitignore | 3 - delivery.yaml | 5 +- pkg/apis/acid.zalan.do/v1/postgresql.crd.yaml | 4210 +++++++++++++++++ 3 files changed, 4214 insertions(+), 4 deletions(-) create mode 100644 pkg/apis/acid.zalan.do/v1/postgresql.crd.yaml diff --git a/.gitignore b/.gitignore index 6ee090456..5938db216 100644 --- a/.gitignore +++ b/.gitignore @@ -106,6 +106,3 @@ mocks ui/.npm/ .DS_Store - -# temp build files -pkg/apis/acid.zalan.do/v1/postgresql.crd.yaml diff --git a/delivery.yaml b/delivery.yaml index a8716399c..933e72733 100644 --- a/delivery.yaml +++ b/delivery.yaml @@ -23,7 +23,10 @@ pipeline: - desc: Run unit tests cmd: | make mocks test - + if ! git diff --quiet; then + echo "Build resulted in files being changed, likely they were not checked in" + exit 1 + fi - desc: Build Docker image cmd: | if [ -z ${CDP_SOURCE_BRANCH} ]; then diff --git a/pkg/apis/acid.zalan.do/v1/postgresql.crd.yaml b/pkg/apis/acid.zalan.do/v1/postgresql.crd.yaml new file mode 100644 index 000000000..39811824e --- /dev/null +++ b/pkg/apis/acid.zalan.do/v1/postgresql.crd.yaml @@ -0,0 +1,4210 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.17.3 + name: postgresqls.acid.zalan.do +spec: + group: acid.zalan.do + names: + categories: + - all + kind: postgresql + listKind: postgresqlList + plural: postgresqls + shortNames: + - pg + singular: postgresql + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Team responsible for Postgres cluster + jsonPath: .spec.teamId + name: Team + type: string + - description: PostgreSQL version + jsonPath: .spec.postgresql.version + name: Version + type: string + - description: Number of Pods per Postgres cluster + jsonPath: .spec.numberOfInstances + name: Pods + type: integer + - description: Size of the bound volume + jsonPath: .spec.volume.size + name: Volume + type: string + - description: Requested CPU for Postgres containers + jsonPath: .spec.resources.requests.cpu + name: CPU-Request + type: string + - description: Requested memory for Postgres containers + jsonPath: .spec.resources.requests.memory + name: Memory-Request + type: string + - description: Age of the PostgreSQL cluster + jsonPath: .metadata.creationTimestamp + name: Age + type: date + - description: Current sync status of postgresql resource + jsonPath: .status.PostgresClusterStatus + name: Status + type: string + name: v1 + schema: + openAPIV3Schema: + description: Postgresql defines PostgreSQL Custom Resource Definition Object. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: PostgresSpec defines the specification for the PostgreSQL + TPR. + properties: + additionalVolumes: + items: + description: AdditionalVolume specs additional optional volumes + for statefulset + properties: + isSubPathExpr: + type: boolean + mountPath: + type: string + name: + type: string + subPath: + type: string + targetContainers: + items: + type: string + nullable: true + type: array + volumeSource: + type: object + x-kubernetes-preserve-unknown-fields: true + required: + - mountPath + - name + - volumeSource + type: object + type: array + allowedSourceRanges: + description: load balancers' source ranges are the same for master + and replica services + items: + pattern: ^(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\/(\d|[1-2]\d|3[0-2])$ + type: string + nullable: true + type: array + clone: + description: CloneDescription describes which cluster the new should + clone and up to which point in time + properties: + cluster: + type: string + s3_access_key_id: + type: string + s3_endpoint: + type: string + s3_force_path_style: + type: boolean + s3_secret_access_key: + type: string + s3_wal_path: + type: string + timestamp: + description: |- + The regexp matches the date-time format (RFC 3339 Section 5.6) that specifies a timezone as an offset relative to UTC + Example: 1996-12-19T16:39:57-08:00 + Note: this field requires a timezone + pattern: ^([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])[Tt]([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(([+-]([01][0-9]|2[0-3]):[0-5][0-9]))$ + type: string + uid: + format: uuid + type: string + required: + - cluster + type: object + connectionPooler: + description: |- + ConnectionPooler Options for connection pooler + + pgbouncer-large (with higher resources) or odyssey-small (with smaller + resources) + Type string `json:"type,omitempty"` + + makes sense to expose. E.g. pool size (min/max boundaries), max client + connections etc. + properties: + dockerImage: + type: string + maxDBConnections: + format: int32 + type: integer + mode: + enum: + - session + - transaction + type: string + numberOfInstances: + format: int32 + minimum: 1 + type: integer + resources: + description: Resources describes requests and limits for the cluster + resouces. + properties: + limits: + description: ResourceDescription describes CPU and memory + resources defined for a cluster. + properties: + cpu: + description: |- + Decimal natural followed by m, or decimal natural followed by + dot followed by up to three decimal digits. + + This is because the Kubernetes CPU resource has millis as the + maximum precision. The actual values are checked in code + because the regular expression would be huge and horrible and + not very helpful in validation error messages; this one checks + only the format of the given number. + + https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu + + Note: the value specified here must not be zero or be lower + than the corresponding request. + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ + type: string + hugepages-1Gi: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + hugepages-2Mi: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + memory: + description: |- + You can express memory as a plain integer or as a fixed-point + integer using one of these suffixes: E, P, T, G, M, k. You can + also use the power-of-two equivalents: Ei, Pi, Ti, Gi, Mi, Ki + + https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-memory + + Note: the value specified here must not be zero or be higher + than the corresponding limit. + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + type: object + requests: + description: ResourceDescription describes CPU and memory + resources defined for a cluster. + properties: + cpu: + description: |- + Decimal natural followed by m, or decimal natural followed by + dot followed by up to three decimal digits. + + This is because the Kubernetes CPU resource has millis as the + maximum precision. The actual values are checked in code + because the regular expression would be huge and horrible and + not very helpful in validation error messages; this one checks + only the format of the given number. + + https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu + + Note: the value specified here must not be zero or be lower + than the corresponding request. + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ + type: string + hugepages-1Gi: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + hugepages-2Mi: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + memory: + description: |- + You can express memory as a plain integer or as a fixed-point + integer using one of these suffixes: E, P, T, G, M, k. You can + also use the power-of-two equivalents: Ei, Pi, Ti, Gi, Mi, Ki + + https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-memory + + Note: the value specified here must not be zero or be higher + than the corresponding limit. + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + type: object + type: object + schema: + type: string + user: + type: string + type: object + databases: + additionalProperties: + type: string + description: |- + Note: usernames specified here as database owners must be declared + in the users key of the spec key. + type: object + dockerImage: + type: string + enableConnectionPooler: + type: boolean + enableLogicalBackup: + type: boolean + enableMasterLoadBalancer: + description: |- + vars that enable load balancers are pointers because it is important to know if any of them is omitted from the Postgres manifest + in that case the var evaluates to nil and the value is taken from the operator config + type: boolean + enableMasterPoolerLoadBalancer: + type: boolean + enableReplicaConnectionPooler: + type: boolean + enableReplicaLoadBalancer: + type: boolean + enableReplicaPoolerLoadBalancer: + type: boolean + enableShmVolume: + type: boolean + env: + items: + description: EnvVar represents an environment variable present in + a Container. + properties: + name: + description: Name of the environment variable. Must be a C_IDENTIFIER. + type: string + value: + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". + type: string + valueFrom: + description: Source for the environment variable's value. Cannot + be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: |- + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. + properties: + apiVersion: + description: Version of the schema the FieldPath is + written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified + API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the exposed + resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + init_containers: + description: deprecated + items: + description: A single application container that you want to run + within a pod. + properties: + args: + description: |- + Arguments to the entrypoint. + The container image's CMD is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + description: |- + Entrypoint array. Not executed within a shell. + The container image's ENTRYPOINT is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + description: |- + List of environment variables to set in the container. + Cannot be updated. + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be + a C_IDENTIFIER. + type: string + value: + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap or + its key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: |- + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + description: |- + List of sources to populate environment variables in the container. + The keys defined within a source must be a C_IDENTIFIER. All invalid keys + will be reported as an event when the container is starting. When a key exists in multiple + sources, the value associated with the last source will take precedence. + Values defined by an Env with a duplicate key will take precedence. + Cannot be updated. + items: + description: EnvFromSource represents the source of a set + of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap must be + defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: An optional identifier to prepend to each + key in the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + description: |- + Container image name. + More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config management to default or override + container images in workload controllers like Deployments and StatefulSets. + type: string + imagePullPolicy: + description: |- + Image pull policy. + One of Always, Never, IfNotPresent. + Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/containers/images#updating-images + type: string + lifecycle: + description: |- + Actions that the management system should take in response to container lifecycle events. + Cannot be updated. + properties: + postStart: + description: |- + PostStart is called immediately after a container is created. If the handler fails, + the container is terminated and restarted according to its restart policy. + Other management of the container blocks until the hook completes. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks + properties: + exec: + description: Exec specifies a command to execute in + the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to + perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + sleep: + description: Sleep represents a duration that the container + should sleep. + properties: + seconds: + description: Seconds is the number of seconds to + sleep. + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + description: |- + Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept + for backward compatibility. There is no validation of this field and + lifecycle hooks will fail at runtime when it is specified. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + description: |- + PreStop is called immediately before a container is terminated due to an + API request or management event such as liveness/startup probe failure, + preemption, resource contention, etc. The handler is not called if the + container crashes or exits. The Pod's termination grace period countdown begins before the + PreStop hook is executed. Regardless of the outcome of the handler, the + container will eventually terminate within the Pod's termination grace + period (unless delayed by finalizers). Other management of the container blocks until the hook completes + or until the termination grace period is reached. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks + properties: + exec: + description: Exec specifies a command to execute in + the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to + perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + sleep: + description: Sleep represents a duration that the container + should sleep. + properties: + seconds: + description: Seconds is the number of seconds to + sleep. + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + description: |- + Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept + for backward compatibility. There is no validation of this field and + lifecycle hooks will fail at runtime when it is specified. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + description: |- + Periodic probe of container liveness. + Container will be restarted if the probe fails. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + properties: + exec: + description: Exec specifies a command to execute in the + container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies a connection to a TCP port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + name: + description: |- + Name of the container specified as a DNS_LABEL. + Each container in a pod must have a unique name (DNS_LABEL). + Cannot be updated. + type: string + ports: + description: |- + List of ports to expose from the container. Not specifying a port here + DOES NOT prevent that port from being exposed. Any port which is + listening on the default "0.0.0.0" address inside a container will be + accessible from the network. + Modifying this array with strategic merge patch may corrupt the data. + For more information See https://github.com/kubernetes/kubernetes/issues/108255. + Cannot be updated. + items: + description: ContainerPort represents a network port in a + single container. + properties: + containerPort: + description: |- + Number of port to expose on the pod's IP address. + This must be a valid port number, 0 < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the external port to. + type: string + hostPort: + description: |- + Number of port to expose on the host. + If specified, this must be a valid port number, 0 < x < 65536. + If HostNetwork is specified, this must match ContainerPort. + Most containers do not need this. + format: int32 + type: integer + name: + description: |- + If specified, this must be an IANA_SVC_NAME and unique within the pod. Each + named port in a pod must have a unique name. Name for the port that can be + referred to by services. + type: string + protocol: + default: TCP + description: |- + Protocol for port. Must be UDP, TCP, or SCTP. + Defaults to "TCP". + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + description: |- + Periodic probe of container service readiness. + Container will be removed from service endpoints if the probe fails. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + properties: + exec: + description: Exec specifies a command to execute in the + container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies a connection to a TCP port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + resizePolicy: + description: Resources resize policy for the container. + items: + description: ContainerResizePolicy represents resource resize + policy for the container. + properties: + resourceName: + description: |- + Name of the resource to which this resource resize policy applies. + Supported values: cpu, memory. + type: string + restartPolicy: + description: |- + Restart policy to apply when specified resource is resized. + If not specified, it defaults to NotRequired. + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + description: |- + Compute Resources required by this container. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + restartPolicy: + description: |- + RestartPolicy defines the restart behavior of individual containers in a pod. + This field may only be set for init containers, and the only allowed value is "Always". + For non-init containers or when this field is not specified, + the restart behavior is defined by the Pod's restart policy and the container type. + Setting the RestartPolicy as "Always" for the init container will have the following effect: + this init container will be continually restarted on + exit until all regular containers have terminated. Once all regular + containers have completed, all init containers with restartPolicy "Always" + will be shut down. This lifecycle differs from normal init containers and + is often referred to as a "sidecar" container. Although this init + container still starts in the init container sequence, it does not wait + for the container to complete before proceeding to the next init + container. Instead, the next init container starts immediately after this + init container is started, or after any startupProbe has successfully + completed. + type: string + securityContext: + description: |- + SecurityContext defines the security options the container should be run with. + If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. + More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ + properties: + allowPrivilegeEscalation: + description: |- + AllowPrivilegeEscalation controls whether a process can gain more + privileges than its parent process. This bool directly controls if + the no_new_privs flag will be set on the container process. + AllowPrivilegeEscalation is true always when the container is: + 1) run as Privileged + 2) has CAP_SYS_ADMIN + Note that this field cannot be set when spec.os.name is windows. + type: boolean + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by this container. If set, this profile + overrides the pod's appArmorProfile. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object + capabilities: + description: |- + The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container runtime. + Note that this field cannot be set when spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + x-kubernetes-list-type: atomic + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + description: |- + Run container in privileged mode. + Processes in privileged containers are essentially equivalent to root on the host. + Defaults to false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + procMount: + description: |- + procMount denotes the type of proc mount to use for the containers. + The default value is Default which uses the container runtime defaults for + readonly paths and masked paths. + This requires the ProcMountType feature flag to be enabled. + Note that this field cannot be set when spec.os.name is windows. + type: string + readOnlyRootFilesystem: + description: |- + Whether this container has a read-only root filesystem. + Default is false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: |- + The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by this container. If seccomp options are + provided at both the pod & container level, the container options + override the pod options. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + windowsOptions: + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options from the PodSecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: string + type: object + type: object + startupProbe: + description: |- + StartupProbe indicates that the Pod has successfully initialized. + If specified, no other probes are executed until this completes successfully. + If this probe fails, the Pod will be restarted, just as if the livenessProbe failed. + This can be used to provide different probe parameters at the beginning of a Pod's lifecycle, + when it might take a long time to load data or warm a cache, than during steady-state operation. + This cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + properties: + exec: + description: Exec specifies a command to execute in the + container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies a connection to a TCP port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + stdin: + description: |- + Whether this container should allocate a buffer for stdin in the container runtime. If this + is not set, reads from stdin in the container will always result in EOF. + Default is false. + type: boolean + stdinOnce: + description: |- + Whether the container runtime should close the stdin channel after it has been opened by + a single attach. When stdin is true the stdin stream will remain open across multiple attach + sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the + first client attaches to stdin, and then remains open and accepts data until the client disconnects, + at which time stdin is closed and remains closed until the container is restarted. If this + flag is false, a container processes that reads from stdin will never receive an EOF. + Default is false + type: boolean + terminationMessagePath: + description: |- + Optional: Path at which the file to which the container's termination message + will be written is mounted into the container's filesystem. + Message written is intended to be brief final status, such as an assertion failure message. + Will be truncated by the node if greater than 4096 bytes. The total message length across + all containers will be limited to 12kb. + Defaults to /dev/termination-log. + Cannot be updated. + type: string + terminationMessagePolicy: + description: |- + Indicate how the termination message should be populated. File will use the contents of + terminationMessagePath to populate the container status message on both success and failure. + FallbackToLogsOnError will use the last chunk of container log output if the termination + message file is empty and the container exited with an error. + The log output is limited to 2048 bytes or 80 lines, whichever is smaller. + Defaults to File. + Cannot be updated. + type: string + tty: + description: |- + Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. + Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list of block devices to be + used by the container. + items: + description: volumeDevice describes a mapping of a raw block + device within a container. + properties: + devicePath: + description: devicePath is the path inside of the container + that the device will be mapped to. + type: string + name: + description: name must match the name of a persistentVolumeClaim + in the pod + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + description: |- + Pod volumes to mount into the container's filesystem. + Cannot be updated. + items: + description: VolumeMount describes a mounting of a Volume + within a container. + properties: + mountPath: + description: |- + Path within the container at which the volume should be mounted. Must + not contain ':'. + type: string + mountPropagation: + description: |- + mountPropagation determines how mounts are propagated from the host + to container and the other way around. + When not set, MountPropagationNone is used. + This field is beta in 1.10. + When RecursiveReadOnly is set to IfPossible or to Enabled, MountPropagation must be None or unspecified + (which defaults to None). + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: |- + Mounted read-only if true, read-write otherwise (false or unspecified). + Defaults to false. + type: boolean + recursiveReadOnly: + description: |- + RecursiveReadOnly specifies whether read-only mounts should be handled + recursively. + + If ReadOnly is false, this field has no meaning and must be unspecified. + + If ReadOnly is true, and this field is set to Disabled, the mount is not made + recursively read-only. If this field is set to IfPossible, the mount is made + recursively read-only, if it is supported by the container runtime. If this + field is set to Enabled, the mount is made recursively read-only if it is + supported by the container runtime, otherwise the pod will not be started and + an error will be generated to indicate the reason. + + If this field is set to IfPossible or Enabled, MountPropagation must be set to + None (or be unspecified, which defaults to None). + + If this field is not specified, it is treated as an equivalent of Disabled. + type: string + subPath: + description: |- + Path within the volume from which the container's volume should be mounted. + Defaults to "" (volume's root). + type: string + subPathExpr: + description: |- + Expanded path within the volume from which the container's volume should be mounted. + Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. + Defaults to "" (volume's root). + SubPathExpr and SubPath are mutually exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + description: |- + Container's working directory. + If not specified, the container runtime's default will be used, which + might be configured in the container image. + Cannot be updated. + type: string + required: + - name + type: object + type: array + initContainers: + items: + description: A single application container that you want to run + within a pod. + properties: + args: + description: |- + Arguments to the entrypoint. + The container image's CMD is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + description: |- + Entrypoint array. Not executed within a shell. + The container image's ENTRYPOINT is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + description: |- + List of environment variables to set in the container. + Cannot be updated. + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be + a C_IDENTIFIER. + type: string + value: + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap or + its key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: |- + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + description: |- + List of sources to populate environment variables in the container. + The keys defined within a source must be a C_IDENTIFIER. All invalid keys + will be reported as an event when the container is starting. When a key exists in multiple + sources, the value associated with the last source will take precedence. + Values defined by an Env with a duplicate key will take precedence. + Cannot be updated. + items: + description: EnvFromSource represents the source of a set + of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap must be + defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: An optional identifier to prepend to each + key in the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + description: |- + Container image name. + More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config management to default or override + container images in workload controllers like Deployments and StatefulSets. + type: string + imagePullPolicy: + description: |- + Image pull policy. + One of Always, Never, IfNotPresent. + Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/containers/images#updating-images + type: string + lifecycle: + description: |- + Actions that the management system should take in response to container lifecycle events. + Cannot be updated. + properties: + postStart: + description: |- + PostStart is called immediately after a container is created. If the handler fails, + the container is terminated and restarted according to its restart policy. + Other management of the container blocks until the hook completes. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks + properties: + exec: + description: Exec specifies a command to execute in + the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to + perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + sleep: + description: Sleep represents a duration that the container + should sleep. + properties: + seconds: + description: Seconds is the number of seconds to + sleep. + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + description: |- + Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept + for backward compatibility. There is no validation of this field and + lifecycle hooks will fail at runtime when it is specified. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + description: |- + PreStop is called immediately before a container is terminated due to an + API request or management event such as liveness/startup probe failure, + preemption, resource contention, etc. The handler is not called if the + container crashes or exits. The Pod's termination grace period countdown begins before the + PreStop hook is executed. Regardless of the outcome of the handler, the + container will eventually terminate within the Pod's termination grace + period (unless delayed by finalizers). Other management of the container blocks until the hook completes + or until the termination grace period is reached. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks + properties: + exec: + description: Exec specifies a command to execute in + the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to + perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + sleep: + description: Sleep represents a duration that the container + should sleep. + properties: + seconds: + description: Seconds is the number of seconds to + sleep. + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + description: |- + Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept + for backward compatibility. There is no validation of this field and + lifecycle hooks will fail at runtime when it is specified. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + description: |- + Periodic probe of container liveness. + Container will be restarted if the probe fails. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + properties: + exec: + description: Exec specifies a command to execute in the + container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies a connection to a TCP port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + name: + description: |- + Name of the container specified as a DNS_LABEL. + Each container in a pod must have a unique name (DNS_LABEL). + Cannot be updated. + type: string + ports: + description: |- + List of ports to expose from the container. Not specifying a port here + DOES NOT prevent that port from being exposed. Any port which is + listening on the default "0.0.0.0" address inside a container will be + accessible from the network. + Modifying this array with strategic merge patch may corrupt the data. + For more information See https://github.com/kubernetes/kubernetes/issues/108255. + Cannot be updated. + items: + description: ContainerPort represents a network port in a + single container. + properties: + containerPort: + description: |- + Number of port to expose on the pod's IP address. + This must be a valid port number, 0 < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the external port to. + type: string + hostPort: + description: |- + Number of port to expose on the host. + If specified, this must be a valid port number, 0 < x < 65536. + If HostNetwork is specified, this must match ContainerPort. + Most containers do not need this. + format: int32 + type: integer + name: + description: |- + If specified, this must be an IANA_SVC_NAME and unique within the pod. Each + named port in a pod must have a unique name. Name for the port that can be + referred to by services. + type: string + protocol: + default: TCP + description: |- + Protocol for port. Must be UDP, TCP, or SCTP. + Defaults to "TCP". + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + description: |- + Periodic probe of container service readiness. + Container will be removed from service endpoints if the probe fails. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + properties: + exec: + description: Exec specifies a command to execute in the + container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies a connection to a TCP port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + resizePolicy: + description: Resources resize policy for the container. + items: + description: ContainerResizePolicy represents resource resize + policy for the container. + properties: + resourceName: + description: |- + Name of the resource to which this resource resize policy applies. + Supported values: cpu, memory. + type: string + restartPolicy: + description: |- + Restart policy to apply when specified resource is resized. + If not specified, it defaults to NotRequired. + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + description: |- + Compute Resources required by this container. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + restartPolicy: + description: |- + RestartPolicy defines the restart behavior of individual containers in a pod. + This field may only be set for init containers, and the only allowed value is "Always". + For non-init containers or when this field is not specified, + the restart behavior is defined by the Pod's restart policy and the container type. + Setting the RestartPolicy as "Always" for the init container will have the following effect: + this init container will be continually restarted on + exit until all regular containers have terminated. Once all regular + containers have completed, all init containers with restartPolicy "Always" + will be shut down. This lifecycle differs from normal init containers and + is often referred to as a "sidecar" container. Although this init + container still starts in the init container sequence, it does not wait + for the container to complete before proceeding to the next init + container. Instead, the next init container starts immediately after this + init container is started, or after any startupProbe has successfully + completed. + type: string + securityContext: + description: |- + SecurityContext defines the security options the container should be run with. + If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. + More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ + properties: + allowPrivilegeEscalation: + description: |- + AllowPrivilegeEscalation controls whether a process can gain more + privileges than its parent process. This bool directly controls if + the no_new_privs flag will be set on the container process. + AllowPrivilegeEscalation is true always when the container is: + 1) run as Privileged + 2) has CAP_SYS_ADMIN + Note that this field cannot be set when spec.os.name is windows. + type: boolean + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by this container. If set, this profile + overrides the pod's appArmorProfile. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object + capabilities: + description: |- + The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container runtime. + Note that this field cannot be set when spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + x-kubernetes-list-type: atomic + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + description: |- + Run container in privileged mode. + Processes in privileged containers are essentially equivalent to root on the host. + Defaults to false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + procMount: + description: |- + procMount denotes the type of proc mount to use for the containers. + The default value is Default which uses the container runtime defaults for + readonly paths and masked paths. + This requires the ProcMountType feature flag to be enabled. + Note that this field cannot be set when spec.os.name is windows. + type: string + readOnlyRootFilesystem: + description: |- + Whether this container has a read-only root filesystem. + Default is false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: |- + The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by this container. If seccomp options are + provided at both the pod & container level, the container options + override the pod options. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + windowsOptions: + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options from the PodSecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: string + type: object + type: object + startupProbe: + description: |- + StartupProbe indicates that the Pod has successfully initialized. + If specified, no other probes are executed until this completes successfully. + If this probe fails, the Pod will be restarted, just as if the livenessProbe failed. + This can be used to provide different probe parameters at the beginning of a Pod's lifecycle, + when it might take a long time to load data or warm a cache, than during steady-state operation. + This cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + properties: + exec: + description: Exec specifies a command to execute in the + container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies a connection to a TCP port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + stdin: + description: |- + Whether this container should allocate a buffer for stdin in the container runtime. If this + is not set, reads from stdin in the container will always result in EOF. + Default is false. + type: boolean + stdinOnce: + description: |- + Whether the container runtime should close the stdin channel after it has been opened by + a single attach. When stdin is true the stdin stream will remain open across multiple attach + sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the + first client attaches to stdin, and then remains open and accepts data until the client disconnects, + at which time stdin is closed and remains closed until the container is restarted. If this + flag is false, a container processes that reads from stdin will never receive an EOF. + Default is false + type: boolean + terminationMessagePath: + description: |- + Optional: Path at which the file to which the container's termination message + will be written is mounted into the container's filesystem. + Message written is intended to be brief final status, such as an assertion failure message. + Will be truncated by the node if greater than 4096 bytes. The total message length across + all containers will be limited to 12kb. + Defaults to /dev/termination-log. + Cannot be updated. + type: string + terminationMessagePolicy: + description: |- + Indicate how the termination message should be populated. File will use the contents of + terminationMessagePath to populate the container status message on both success and failure. + FallbackToLogsOnError will use the last chunk of container log output if the termination + message file is empty and the container exited with an error. + The log output is limited to 2048 bytes or 80 lines, whichever is smaller. + Defaults to File. + Cannot be updated. + type: string + tty: + description: |- + Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. + Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list of block devices to be + used by the container. + items: + description: volumeDevice describes a mapping of a raw block + device within a container. + properties: + devicePath: + description: devicePath is the path inside of the container + that the device will be mapped to. + type: string + name: + description: name must match the name of a persistentVolumeClaim + in the pod + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + description: |- + Pod volumes to mount into the container's filesystem. + Cannot be updated. + items: + description: VolumeMount describes a mounting of a Volume + within a container. + properties: + mountPath: + description: |- + Path within the container at which the volume should be mounted. Must + not contain ':'. + type: string + mountPropagation: + description: |- + mountPropagation determines how mounts are propagated from the host + to container and the other way around. + When not set, MountPropagationNone is used. + This field is beta in 1.10. + When RecursiveReadOnly is set to IfPossible or to Enabled, MountPropagation must be None or unspecified + (which defaults to None). + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: |- + Mounted read-only if true, read-write otherwise (false or unspecified). + Defaults to false. + type: boolean + recursiveReadOnly: + description: |- + RecursiveReadOnly specifies whether read-only mounts should be handled + recursively. + + If ReadOnly is false, this field has no meaning and must be unspecified. + + If ReadOnly is true, and this field is set to Disabled, the mount is not made + recursively read-only. If this field is set to IfPossible, the mount is made + recursively read-only, if it is supported by the container runtime. If this + field is set to Enabled, the mount is made recursively read-only if it is + supported by the container runtime, otherwise the pod will not be started and + an error will be generated to indicate the reason. + + If this field is set to IfPossible or Enabled, MountPropagation must be set to + None (or be unspecified, which defaults to None). + + If this field is not specified, it is treated as an equivalent of Disabled. + type: string + subPath: + description: |- + Path within the volume from which the container's volume should be mounted. + Defaults to "" (volume's root). + type: string + subPathExpr: + description: |- + Expanded path within the volume from which the container's volume should be mounted. + Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. + Defaults to "" (volume's root). + SubPathExpr and SubPath are mutually exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + description: |- + Container's working directory. + If not specified, the container runtime's default will be used, which + might be configured in the container image. + Cannot be updated. + type: string + required: + - name + type: object + type: array + logicalBackupRetention: + type: string + logicalBackupSchedule: + pattern: ^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$ + type: string + maintenanceWindows: + items: + pattern: '^\ *((Mon|Tue|Wed|Thu|Fri|Sat|Sun):(2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))-((2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))\ *$' + type: string + type: array + masterServiceAnnotations: + additionalProperties: + type: string + description: MasterServiceAnnotations takes precedence over ServiceAnnotations + for master role if not empty + type: object + nodeAffinity: + description: Node affinity is a group of node affinity scheduling + rules. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. + items: + description: |- + An empty preferred scheduling term matches all objects with implicit weight 0 + (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated with the corresponding + weight. + properties: + matchExpressions: + description: A list of node selector requirements by + node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies + to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements by + node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies + to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + description: Weight associated with matching the corresponding + nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. The + terms are ORed. + items: + description: |- + A null or empty node selector term matches no objects. The requirements of + them are ANDed. + The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements by + node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies + to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements by + node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies + to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + numberOfInstances: + format: int32 + minimum: 0 + type: integer + patroni: + description: Patroni contains Patroni-specific configuration + properties: + failsafe_mode: + type: boolean + initdb: + additionalProperties: + type: string + type: object + loop_wait: + format: int32 + type: integer + maximum_lag_on_failover: + format: int64 + type: integer + pg_hba: + items: + type: string + type: array + retry_timeout: + format: int32 + type: integer + slots: + additionalProperties: + additionalProperties: + type: string + type: object + type: object + synchronous_mode: + type: boolean + synchronous_mode_strict: + type: boolean + synchronous_node_count: + format: int32 + type: integer + ttl: + format: int32 + type: integer + type: object + pod_priority_class_name: + description: deprecated + type: string + podAnnotations: + additionalProperties: + type: string + type: object + podPriorityClassName: + type: string + postgresql: + description: PostgresqlParam describes PostgreSQL version and pairs + of configuration parameter name - values. + properties: + parameters: + additionalProperties: + type: string + type: object + version: + enum: + - "14" + - "15" + - "16" + - "17" + - "18" + type: string + required: + - version + type: object + preparedDatabases: + additionalProperties: + description: PreparedDatabase describes elements to be bootstrapped + properties: + defaultUsers: + type: boolean + extensions: + additionalProperties: + type: string + type: object + schemas: + additionalProperties: + description: PreparedSchema describes elements to be bootstrapped + per schema + properties: + defaultRoles: + type: boolean + defaultUsers: + type: boolean + type: object + type: object + secretNamespace: + type: string + type: object + type: object + replicaLoadBalancer: + description: deprecated + type: boolean + replicaServiceAnnotations: + additionalProperties: + type: string + description: ReplicaServiceAnnotations takes precedence over ServiceAnnotations + for replica role if not empty + type: object + resources: + description: Resources describes requests and limits for the cluster + resouces. + properties: + limits: + description: ResourceDescription describes CPU and memory resources + defined for a cluster. + properties: + cpu: + description: |- + Decimal natural followed by m, or decimal natural followed by + dot followed by up to three decimal digits. + + This is because the Kubernetes CPU resource has millis as the + maximum precision. The actual values are checked in code + because the regular expression would be huge and horrible and + not very helpful in validation error messages; this one checks + only the format of the given number. + + https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu + + Note: the value specified here must not be zero or be lower + than the corresponding request. + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ + type: string + hugepages-1Gi: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + hugepages-2Mi: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + memory: + description: |- + You can express memory as a plain integer or as a fixed-point + integer using one of these suffixes: E, P, T, G, M, k. You can + also use the power-of-two equivalents: Ei, Pi, Ti, Gi, Mi, Ki + + https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-memory + + Note: the value specified here must not be zero or be higher + than the corresponding limit. + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + type: object + requests: + description: ResourceDescription describes CPU and memory resources + defined for a cluster. + properties: + cpu: + description: |- + Decimal natural followed by m, or decimal natural followed by + dot followed by up to three decimal digits. + + This is because the Kubernetes CPU resource has millis as the + maximum precision. The actual values are checked in code + because the regular expression would be huge and horrible and + not very helpful in validation error messages; this one checks + only the format of the given number. + + https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu + + Note: the value specified here must not be zero or be lower + than the corresponding request. + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ + type: string + hugepages-1Gi: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + hugepages-2Mi: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + memory: + description: |- + You can express memory as a plain integer or as a fixed-point + integer using one of these suffixes: E, P, T, G, M, k. You can + also use the power-of-two equivalents: Ei, Pi, Ti, Gi, Mi, Ki + + https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-memory + + Note: the value specified here must not be zero or be higher + than the corresponding limit. + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + type: object + type: object + schedulerName: + type: string + serviceAnnotations: + additionalProperties: + type: string + type: object + sidecars: + items: + description: Sidecar defines a container to be run in the same pod + as the Postgres container. + properties: + command: + items: + type: string + type: array + env: + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be + a C_IDENTIFIER. + type: string + value: + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap or + its key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: |- + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + image: + type: string + name: + type: string + ports: + items: + description: ContainerPort represents a network port in a + single container. + properties: + containerPort: + description: |- + Number of port to expose on the pod's IP address. + This must be a valid port number, 0 < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the external port to. + type: string + hostPort: + description: |- + Number of port to expose on the host. + If specified, this must be a valid port number, 0 < x < 65536. + If HostNetwork is specified, this must match ContainerPort. + Most containers do not need this. + format: int32 + type: integer + name: + description: |- + If specified, this must be an IANA_SVC_NAME and unique within the pod. Each + named port in a pod must have a unique name. Name for the port that can be + referred to by services. + type: string + protocol: + default: TCP + description: |- + Protocol for port. Must be UDP, TCP, or SCTP. + Defaults to "TCP". + type: string + required: + - containerPort + type: object + type: array + resources: + description: Resources describes requests and limits for the + cluster resouces. + properties: + limits: + description: ResourceDescription describes CPU and memory + resources defined for a cluster. + properties: + cpu: + description: |- + Decimal natural followed by m, or decimal natural followed by + dot followed by up to three decimal digits. + + This is because the Kubernetes CPU resource has millis as the + maximum precision. The actual values are checked in code + because the regular expression would be huge and horrible and + not very helpful in validation error messages; this one checks + only the format of the given number. + + https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu + + Note: the value specified here must not be zero or be lower + than the corresponding request. + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ + type: string + hugepages-1Gi: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + hugepages-2Mi: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + memory: + description: |- + You can express memory as a plain integer or as a fixed-point + integer using one of these suffixes: E, P, T, G, M, k. You can + also use the power-of-two equivalents: Ei, Pi, Ti, Gi, Mi, Ki + + https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-memory + + Note: the value specified here must not be zero or be higher + than the corresponding limit. + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + type: object + requests: + description: ResourceDescription describes CPU and memory + resources defined for a cluster. + properties: + cpu: + description: |- + Decimal natural followed by m, or decimal natural followed by + dot followed by up to three decimal digits. + + This is because the Kubernetes CPU resource has millis as the + maximum precision. The actual values are checked in code + because the regular expression would be huge and horrible and + not very helpful in validation error messages; this one checks + only the format of the given number. + + https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu + + Note: the value specified here must not be zero or be lower + than the corresponding request. + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ + type: string + hugepages-1Gi: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + hugepages-2Mi: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + memory: + description: |- + You can express memory as a plain integer or as a fixed-point + integer using one of these suffixes: E, P, T, G, M, k. You can + also use the power-of-two equivalents: Ei, Pi, Ti, Gi, Mi, Ki + + https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-memory + + Note: the value specified here must not be zero or be higher + than the corresponding limit. + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + type: object + type: object + type: object + type: array + spiloFSGroup: + format: int64 + type: integer + spiloRunAsGroup: + format: int64 + type: integer + spiloRunAsUser: + format: int64 + type: integer + standby: + anyOf: + - required: + - s3_wal_path + - required: + - gs_wal_path + - required: + - standby_host + not: + required: + - s3_wal_path + - gs_wal_path + description: |- + StandbyDescription contains remote primary config and/or s3/gs wal path. + standby_host can be specified alone or together with either s3_wal_path OR gs_wal_path (mutually exclusive). + At least one field must be specified. s3_wal_path and gs_wal_path are mutually exclusive. + properties: + gs_wal_path: + type: string + s3_wal_path: + type: string + standby_host: + type: string + standby_port: + type: string + standby_primary_slot_name: + type: string + type: object + streams: + items: + description: Stream defines properties for creating FabricEventStream + resources + properties: + applicationId: + type: string + batchSize: + format: int32 + type: integer + cpu: + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ + type: string + database: + type: string + enableRecovery: + type: boolean + filter: + additionalProperties: + type: string + type: object + memory: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + tables: + additionalProperties: + description: StreamTable defines properties of outbox tables + for FabricEventStreams + properties: + eventType: + type: string + idColumn: + type: string + ignoreRecovery: + type: boolean + payloadColumn: + type: string + recoveryEventType: + type: string + required: + - eventType + type: object + type: object + required: + - applicationId + - database + - tables + type: object + type: array + teamId: + type: string + tls: + description: TLSDescription specs TLS properties + properties: + caFile: + type: string + caSecretName: + type: string + certificateFile: + type: string + privateKeyFile: + type: string + secretName: + type: string + required: + - secretName + type: object + tolerations: + items: + description: |- + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . + properties: + effect: + description: |- + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + useLoadBalancer: + description: |- + deprecated load balancer settings maintained for backward compatibility + see "Load balancers" operator docs + type: boolean + users: + additionalProperties: + description: UserFlags defines flags (such as superuser, nologin) + that could be assigned to individual users + items: + enum: + - bypassrls + - BYPASSRLS + - nobypassrls + - NOBYPASSRLS + - createdb + - CREATEDB + - nocreatedb + - NOCREATEDB + - createrole + - CREATEROLE + - nocreaterole + - NOCREATEROLE + - inherit + - INHERIT + - noinherit + - NOINHERIT + - login + - LOGIN + - nologin + - NOLOGIN + - replication + - REPLICATION + - noreplication + - NOREPLICATION + - superuser + - SUPERUSER + - nosuperuser + - NOSUPERUSER + type: string + type: array + type: object + usersIgnoringSecretRotation: + items: + type: string + nullable: true + type: array + usersWithInPlaceSecretRotation: + items: + type: string + nullable: true + type: array + usersWithSecretRotation: + items: + type: string + nullable: true + type: array + volume: + description: Volume describes a single volume in the manifest. + properties: + iops: + format: int64 + type: integer + isSubPathExpr: + type: boolean + selector: + description: |- + A label selector is a label query over a set of resources. The result of matchLabels and + matchExpressions are ANDed. An empty label selector matches all objects. A null + label selector matches no objects. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + size: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + storageClass: + type: string + subPath: + type: string + throughput: + format: int64 + type: integer + type: + type: string + required: + - size + type: object + required: + - numberOfInstances + - postgresql + - teamId + - volume + type: object + status: + description: PostgresStatus contains status of the PostgreSQL cluster + (running, creation failed etc.) + properties: + PostgresClusterStatus: + type: string + required: + - PostgresClusterStatus + type: object + required: + - metadata + - spec + type: object + served: true + storage: true + subresources: + status: {} From aefe9d82981654964d33891361d1f9d889ca34bc Mon Sep 17 00:00:00 2001 From: Ida Novindasari Date: Thu, 19 Feb 2026 09:57:20 +0100 Subject: [PATCH 18/21] chore: add logging for major upgrade failure (#3046) --- pkg/cluster/majorversionupgrade.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/cluster/majorversionupgrade.go b/pkg/cluster/majorversionupgrade.go index 6ba75929c..2bbffabcc 100644 --- a/pkg/cluster/majorversionupgrade.go +++ b/pkg/cluster/majorversionupgrade.go @@ -275,6 +275,10 @@ func (c *Cluster) majorVersionUpgrade() error { if err != nil { isUpgradeSuccess = false c.annotatePostgresResource(isUpgradeSuccess) + c.logger.Errorf("upgrade action triggered but command failed: %v", err) + if strings.TrimSpace(scriptErrMsg) == "" { + scriptErrMsg = err.Error() + } c.eventRecorder.Eventf(c.GetReference(), v1.EventTypeWarning, "Major Version Upgrade", "upgrade from %d to %d FAILED: %v", c.currentMajorVersion, desiredVersion, scriptErrMsg) return fmt.Errorf("%s", scriptErrMsg) } From 2a31c403d0c894afb0d084d946e846e3dbd7df29 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Thu, 26 Feb 2026 17:27:47 +0100 Subject: [PATCH 19/21] do not reset secrets of standby clusters (#3044) * do not reset secrets of standby clusters align error message with unit test * check for other env vars, too --- pkg/cluster/cluster.go | 2 +- pkg/cluster/k8sres.go | 2 +- pkg/cluster/sync.go | 84 +++++++++++++++++++++++++--------------- pkg/cluster/sync_test.go | 2 +- pkg/cluster/util.go | 11 ++++++ 5 files changed, 67 insertions(+), 34 deletions(-) diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index dd1516ba3..f629d1528 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -385,7 +385,7 @@ func (c *Cluster) Create() (err error) { // create database objects unless we are running without pods or disabled // that feature explicitly - if !(c.databaseAccessDisabled() || c.getNumberOfInstances(&c.Spec) <= 0 || c.Spec.StandbyCluster != nil) { + if !(c.databaseAccessDisabled() || c.getNumberOfInstances(&c.Spec) <= 0 || isStandbyCluster(&c.Spec)) { c.logger.Infof("Create roles") if err = c.createRoles(); err != nil { return fmt.Errorf("could not create users: %v", err) diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index 224ef26f6..724986dbc 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -1691,7 +1691,7 @@ func (c *Cluster) getNumberOfInstances(spec *acidv1.PostgresSpec) int32 { } } - if spec.StandbyCluster != nil { + if isStandbyCluster(spec) { if newcur == 1 { min = newcur max = newcur diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index 189498e7f..ffebd306c 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -1174,42 +1174,15 @@ func (c *Cluster) updateSecret( pwdUser := userMap[userKey] secretName := util.NameFromMeta(secret.ObjectMeta) - // if password rotation is enabled update password and username if rotation interval has been passed - // rotation can be enabled globally or via the manifest (excluding the Postgres superuser) - rotationEnabledInManifest := secretUsername != constants.SuperuserKeyName && - (slices.Contains(c.Spec.UsersWithSecretRotation, secretUsername) || - slices.Contains(c.Spec.UsersWithInPlaceSecretRotation, secretUsername)) - - // globally enabled rotation is only allowed for manifest and bootstrapped roles - allowedRoleTypes := []spec.RoleOrigin{spec.RoleOriginManifest, spec.RoleOriginBootstrap} - rotationAllowed := !pwdUser.IsDbOwner && slices.Contains(allowedRoleTypes, pwdUser.Origin) && c.Spec.StandbyCluster == nil - - // users can ignore any kind of rotation - isIgnoringRotation := slices.Contains(c.Spec.UsersIgnoringSecretRotation, secretUsername) - - if ((c.OpConfig.EnablePasswordRotation && rotationAllowed) || rotationEnabledInManifest) && !isIgnoringRotation { - updateSecretMsg, err = c.rotatePasswordInSecret(secret, secretUsername, pwdUser.Origin, currentTime, retentionUsers) + // do not perform any rotation of reset for standby clusters + if !isStandbyCluster(&c.Spec) { + updateSecretMsg, err = c.checkForPasswordRotation(secret, secretUsername, pwdUser, retentionUsers, currentTime) if err != nil { - c.logger.Warnf("password rotation failed for user %s: %v", secretUsername, err) + return nil, fmt.Errorf("error while checking for password rotation: %v", err) } if updateSecretMsg != "" { updateSecret = true } - } else { - // username might not match if password rotation has been disabled again - usernameFromSecret := string(secret.Data["username"]) - if secretUsername != usernameFromSecret { - // handle edge case when manifest user conflicts with a user from prepared databases - if strings.Replace(usernameFromSecret, "-", "_", -1) == strings.Replace(secretUsername, "-", "_", -1) { - return nil, fmt.Errorf("could not update secret because of user name mismatch: expected: %s, got: %s", secretUsername, usernameFromSecret) - } - *retentionUsers = append(*retentionUsers, secretUsername) - secret.Data["username"] = []byte(secretUsername) - secret.Data["password"] = []byte(util.RandomPassword(constants.PasswordLength)) - secret.Data["nextRotation"] = []byte{} - updateSecret = true - updateSecretMsg = fmt.Sprintf("secret does not contain the role %s - updating username and resetting password", secretUsername) - } } // if this secret belongs to the infrastructure role and the password has changed - replace it in the secret @@ -1256,6 +1229,55 @@ func (c *Cluster) updateSecret( return secret, nil } +func (c *Cluster) checkForPasswordRotation( + secret *v1.Secret, + secretUsername string, + pwdUser spec.PgUser, + retentionUsers *[]string, + currentTime time.Time) (string, error) { + + var ( + passwordRotationMsg string + err error + ) + + // if password rotation is enabled update password and username if rotation interval has been passed + // rotation can be enabled globally or via the manifest (excluding the Postgres superuser) + rotationEnabledInManifest := secretUsername != constants.SuperuserKeyName && + (slices.Contains(c.Spec.UsersWithSecretRotation, secretUsername) || + slices.Contains(c.Spec.UsersWithInPlaceSecretRotation, secretUsername)) + + // globally enabled rotation is only allowed for manifest and bootstrapped roles + allowedRoleTypes := []spec.RoleOrigin{spec.RoleOriginManifest, spec.RoleOriginBootstrap} + rotationAllowed := !pwdUser.IsDbOwner && slices.Contains(allowedRoleTypes, pwdUser.Origin) + + // users can ignore any kind of rotation + isIgnoringRotation := slices.Contains(c.Spec.UsersIgnoringSecretRotation, secretUsername) + + if ((c.OpConfig.EnablePasswordRotation && rotationAllowed) || rotationEnabledInManifest) && !isIgnoringRotation { + passwordRotationMsg, err = c.rotatePasswordInSecret(secret, secretUsername, pwdUser.Origin, currentTime, retentionUsers) + if err != nil { + c.logger.Warnf("password rotation failed for user %s: %v", secretUsername, err) + } + } else { + // username might not match if password rotation has been disabled again + usernameFromSecret := string(secret.Data["username"]) + if secretUsername != usernameFromSecret { + // handle edge case when manifest user conflicts with a user from prepared databases + if strings.Replace(usernameFromSecret, "-", "_", -1) == strings.Replace(secretUsername, "-", "_", -1) { + return "", fmt.Errorf("could not update secret because of user name mismatch: expected: %s, got: %s", secretUsername, usernameFromSecret) + } + *retentionUsers = append(*retentionUsers, secretUsername) + secret.Data["username"] = []byte(secretUsername) + secret.Data["password"] = []byte(util.RandomPassword(constants.PasswordLength)) + secret.Data["nextRotation"] = []byte{} + passwordRotationMsg = fmt.Sprintf("secret does not contain the role %s - updating username and resetting password", secretUsername) + } + } + + return passwordRotationMsg, nil +} + func (c *Cluster) rotatePasswordInSecret( secret *v1.Secret, secretUsername string, diff --git a/pkg/cluster/sync_test.go b/pkg/cluster/sync_test.go index e2b242d9d..f7d46d427 100644 --- a/pkg/cluster/sync_test.go +++ b/pkg/cluster/sync_test.go @@ -1050,6 +1050,6 @@ func TestUpdateSecretNameConflict(t *testing.T) { assert.Error(t, err) // the order of secrets to sync is not deterministic, check only first part of the error message - expectedError := fmt.Sprintf("syncing secret %s failed: could not update secret because of user name mismatch", "default/prepared-owner-user.acid-test-cluster.credentials") + expectedError := fmt.Sprintf("syncing secret %s failed: error while checking for password rotation: could not update secret because of user name mismatch", "default/prepared-owner-user.acid-test-cluster.credentials") assert.Contains(t, err.Error(), expectedError) } diff --git a/pkg/cluster/util.go b/pkg/cluster/util.go index c9b4b4428..81e927d94 100644 --- a/pkg/cluster/util.go +++ b/pkg/cluster/util.go @@ -8,6 +8,7 @@ import ( "fmt" "net/http" "reflect" + "regexp" "sort" "strings" "time" @@ -663,6 +664,16 @@ func parseResourceRequirements(resourcesRequirement v1.ResourceRequirements) (ac return resources, nil } +func isStandbyCluster(spec *acidv1.PostgresSpec) bool { + for _, env := range spec.Env { + hasStandbyEnv, _ := regexp.MatchString(`^STANDBY_WALE_(S3|GS|GSC|SWIFT)_PREFIX$`, env.Name) + if hasStandbyEnv && env.Value != "" { + return true + } + } + return spec.StandbyCluster != nil +} + func (c *Cluster) isInMaintenanceWindow(specMaintenanceWindows []acidv1.MaintenanceWindow) bool { if len(specMaintenanceWindows) == 0 && len(c.OpConfig.MaintenanceWindows) == 0 { return true From d495825f4bcbc93cd7aa0c79268b246b4c0970d2 Mon Sep 17 00:00:00 2001 From: Jorge Solorzano Date: Mon, 2 Mar 2026 11:13:10 +0100 Subject: [PATCH 20/21] Remove hardcoded VersionMap from majorversionupgrade (#3043) Co-authored-by: Felix Kunde --- pkg/cluster/majorversionupgrade.go | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/pkg/cluster/majorversionupgrade.go b/pkg/cluster/majorversionupgrade.go index 2bbffabcc..6c754b14f 100644 --- a/pkg/cluster/majorversionupgrade.go +++ b/pkg/cluster/majorversionupgrade.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "strconv" "strings" "github.com/Masterminds/semver" @@ -14,15 +15,6 @@ import ( "k8s.io/apimachinery/pkg/types" ) -// VersionMap Map of version numbers -var VersionMap = map[string]int{ - "14": 140000, - "15": 150000, - "16": 160000, - "17": 170000, - "18": 180000, -} - const ( majorVersionUpgradeSuccessAnnotation = "last-major-upgrade-success" majorVersionUpgradeFailureAnnotation = "last-major-upgrade-failure" @@ -30,14 +22,15 @@ const ( // IsBiggerPostgresVersion Compare two Postgres version numbers func IsBiggerPostgresVersion(old string, new string) bool { - oldN := VersionMap[old] - newN := VersionMap[new] + oldN, _ := strconv.Atoi(old) + newN, _ := strconv.Atoi(new) return newN > oldN } // GetDesiredMajorVersionAsInt Convert string to comparable integer of PG version func (c *Cluster) GetDesiredMajorVersionAsInt() int { - return VersionMap[c.GetDesiredMajorVersion()] + version, _ := strconv.Atoi(c.GetDesiredMajorVersion()) + return version * 10000 } // GetDesiredMajorVersion returns major version to use, incl. potential auto upgrade From 421bd6d664ef826830b4e3a3c250fb5fb3f3a5fa Mon Sep 17 00:00:00 2001 From: Ida Novindasari Date: Tue, 24 Mar 2026 12:57:15 +0100 Subject: [PATCH 21/21] fix: invalid switchover scheduling with default maintenance windows (#3058) --- e2e/run.sh | 12 +++++++++--- pkg/cluster/cluster.go | 20 ++++++++++++++++++- pkg/cluster/cluster_test.go | 38 ++++++++++++++++++++++++++++++++++--- 3 files changed, 63 insertions(+), 7 deletions(-) diff --git a/e2e/run.sh b/e2e/run.sh index 8950e19dd..f74158240 100755 --- a/e2e/run.sh +++ b/e2e/run.sh @@ -14,6 +14,13 @@ readonly e2e_test_runner_image="ghcr.io/zalando/postgres-operator-e2e-tests-runn export GOPATH=${GOPATH-~/go} export PATH=${GOPATH}/bin:$PATH +# detect system architecture for pulling the correct Spilo image +case "$(uname -m)" in + x86_64) readonly PLATFORM="linux/amd64" ;; + aarch64|arm64) readonly PLATFORM="linux/arm64" ;; + *) echo "Unsupported architecture: $(uname -m)"; exit 1 ;; +esac + echo "Clustername: ${cluster_name}" echo "Kubeconfig path: ${kubeconfig_path}" @@ -43,9 +50,8 @@ function start_kind(){ export KUBECONFIG="${kubeconfig_path}" kind create cluster --name ${cluster_name} --config kind-cluster-postgres-operator-e2e-tests.yaml - # Pull all platforms to satisfy Kind's --all-platforms requirement - docker pull --platform linux/amd64 "${spilo_image}" - docker pull --platform linux/arm64 "${spilo_image}" + echo "Pulling Spilo image for platform ${PLATFORM}" + docker pull --platform ${PLATFORM} "${spilo_image}" kind load docker-image "${spilo_image}" --name ${cluster_name} } diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index f629d1528..8e0b3c79f 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -1784,7 +1784,20 @@ func (c *Cluster) GetSwitchoverSchedule() string { func (c *Cluster) getSwitchoverScheduleAtTime(now time.Time) string { var possibleSwitchover, schedule time.Time - for _, window := range c.Spec.MaintenanceWindows { + maintenanceWindows := c.Spec.MaintenanceWindows + if len(maintenanceWindows) == 0 { + maintenanceWindows = make([]acidv1.MaintenanceWindow, 0, len(c.OpConfig.MaintenanceWindows)) + for _, windowStr := range c.OpConfig.MaintenanceWindows { + var window acidv1.MaintenanceWindow + if err := window.UnmarshalJSON([]byte(windowStr)); err != nil { + c.logger.Errorf("could not parse default maintenance window %q: %v", windowStr, err) + continue + } + maintenanceWindows = append(maintenanceWindows, window) + } + } + + for _, window := range maintenanceWindows { // in the best case it is possible today possibleSwitchover = time.Date(now.Year(), now.Month(), now.Day(), window.StartTime.Hour(), window.StartTime.Minute(), 0, 0, time.UTC) if window.Everyday { @@ -1806,6 +1819,11 @@ func (c *Cluster) getSwitchoverScheduleAtTime(now time.Time) string { schedule = possibleSwitchover } } + + if schedule.IsZero() { + return "" + } + return schedule.Format("2006-01-02T15:04+00") } diff --git a/pkg/cluster/cluster_test.go b/pkg/cluster/cluster_test.go index d78d4c92e..c7181dbbc 100644 --- a/pkg/cluster/cluster_test.go +++ b/pkg/cluster/cluster_test.go @@ -2125,10 +2125,13 @@ func TestGetSwitchoverSchedule(t *testing.T) { pastWindowTimeStart := pastTimeStart.Format("15:04") pastWindowTimeEnd := now.Add(-1 * time.Hour).Format("15:04") + defaultWindowStr := fmt.Sprintf("%s-%s", futureWindowTimeStart, futureWindowTimeEnd) + tests := []struct { - name string - windows []acidv1.MaintenanceWindow - expected string + name string + windows []acidv1.MaintenanceWindow + defaultWindows []string + expected string }{ { name: "everyday maintenance windows is later today", @@ -2190,11 +2193,40 @@ func TestGetSwitchoverSchedule(t *testing.T) { }, expected: pastTimeStart.AddDate(0, 0, 1).Format("2006-01-02T15:04+00"), }, + { + name: "fallback to operator default window when spec is empty", + windows: []acidv1.MaintenanceWindow{}, + defaultWindows: []string{defaultWindowStr}, + expected: futureTimeStart.Format("2006-01-02T15:04+00"), + }, + { + name: "no windows defined returns empty string", + windows: []acidv1.MaintenanceWindow{}, + defaultWindows: nil, + expected: "", + }, + { + name: "choose the earliest window from multiple in spec", + windows: []acidv1.MaintenanceWindow{ + { + Weekday: now.AddDate(0, 0, 2).Weekday(), + StartTime: mustParseTime(futureWindowTimeStart), + EndTime: mustParseTime(futureWindowTimeEnd), + }, + { + Weekday: now.AddDate(0, 0, 1).Weekday(), + StartTime: mustParseTime(pastWindowTimeStart), + EndTime: mustParseTime(pastWindowTimeEnd), + }, + }, + expected: pastTimeStart.AddDate(0, 0, 1).Format("2006-01-02T15:04+00"), + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { cluster.Spec.MaintenanceWindows = tt.windows + cluster.OpConfig.MaintenanceWindows = tt.defaultWindows schedule := cluster.getSwitchoverScheduleAtTime(now) if schedule != tt.expected { t.Errorf("Expected GetSwitchoverSchedule to return %s, returned: %s", tt.expected, schedule)