From fa4bc21538ca0a6f071d4f2c30dba32747f99070 Mon Sep 17 00:00:00 2001 From: Jociele Padilha <45459238+jopadi@users.noreply.github.com> Date: Tue, 19 Aug 2025 14:40:39 +0200 Subject: [PATCH 01/58] upgrade Go from 1.23.4 to 1.25.0 (#2945) * upgrade go to 1.25 * add minor version to be Go 1.25.0 * revert the Go version on README to keep the history of the release --- .github/workflows/publish_ghcr_image.yaml | 2 +- .github/workflows/run_e2e.yaml | 2 +- .github/workflows/run_tests.yaml | 2 +- Makefile | 4 +-- docker/DebugDockerfile | 2 +- docker/Dockerfile | 2 +- docker/build_operator.sh | 2 +- go.mod | 20 +++++++------ go.sum | 36 +++++++++++++---------- kubectl-pg/go.mod | 2 +- pkg/cluster/k8sres.go | 4 +-- pkg/cluster/majorversionupgrade.go | 2 +- pkg/cluster/sync.go | 2 +- pkg/cluster/util.go | 4 +-- pkg/cluster/volumes.go | 2 +- pkg/controller/postgresql.go | 2 +- pkg/controller/util.go | 2 +- 17 files changed, 49 insertions(+), 43 deletions(-) diff --git a/.github/workflows/publish_ghcr_image.yaml b/.github/workflows/publish_ghcr_image.yaml index d56ff2f17..ff1e86a65 100644 --- a/.github/workflows/publish_ghcr_image.yaml +++ b/.github/workflows/publish_ghcr_image.yaml @@ -23,7 +23,7 @@ jobs: - uses: actions/setup-go@v2 with: - go-version: "^1.23.4" + go-version: "^1.25.0" - name: Run unit tests run: make deps mocks test diff --git a/.github/workflows/run_e2e.yaml b/.github/workflows/run_e2e.yaml index 16573046e..ae5433b5a 100644 --- a/.github/workflows/run_e2e.yaml +++ b/.github/workflows/run_e2e.yaml @@ -14,7 +14,7 @@ jobs: - uses: actions/checkout@v1 - uses: actions/setup-go@v2 with: - go-version: "^1.23.4" + go-version: "^1.25.0" - name: Make dependencies run: make deps mocks - name: Code generation diff --git a/.github/workflows/run_tests.yaml b/.github/workflows/run_tests.yaml index db47f6e40..51061bbf6 100644 --- a/.github/workflows/run_tests.yaml +++ b/.github/workflows/run_tests.yaml @@ -14,7 +14,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-go@v2 with: - go-version: "^1.23.4" + go-version: "^1.25.0" - name: Make dependencies run: make deps mocks - name: Compile diff --git a/Makefile b/Makefile index 8fc4b36f6..350675db6 100644 --- a/Makefile +++ b/Makefile @@ -43,7 +43,7 @@ ifndef GOPATH endif PATH := $(GOPATH)/bin:$(PATH) -SHELL := env PATH=$(PATH) $(SHELL) +SHELL := env PATH="$(PATH)" $(SHELL) default: local @@ -69,7 +69,7 @@ docker: ${DOCKERDIR}/${DOCKERFILE} docker build --rm -t "$(IMAGE):$(TAG)$(CDP_TAG)$(DEBUG_FRESH)$(DEBUG_POSTFIX)" -f "${DOCKERDIR}/${DOCKERFILE}" --build-arg VERSION="${VERSION}" . indocker-race: - docker run --rm -v "${GOPATH}":"${GOPATH}" -e GOPATH="${GOPATH}" -e RACE=1 -w ${PWD} golang:1.23.4 bash -c "make linux" + docker run --rm -v "${GOPATH}":"${GOPATH}" -e GOPATH="${GOPATH}" -e RACE=1 -w ${PWD} golang:1.25.0 bash -c "make linux" push: docker push "$(IMAGE):$(TAG)$(CDP_TAG)" diff --git a/docker/DebugDockerfile b/docker/DebugDockerfile index 18cb631fe..c44002984 100644 --- a/docker/DebugDockerfile +++ b/docker/DebugDockerfile @@ -1,4 +1,4 @@ -FROM golang:1.23-alpine +FROM golang:1.25-alpine LABEL maintainer="Team ACID @ Zalando " # We need root certificates to deal with teams api over https diff --git a/docker/Dockerfile b/docker/Dockerfile index 1fd2020d8..e50380003 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,5 +1,5 @@ ARG BASE_IMAGE=registry.opensource.zalan.do/library/alpine-3:latest -FROM golang:1.23-alpine AS builder +FROM golang:1.25-alpine AS builder ARG VERSION=latest COPY . /go/src/github.com/zalando/postgres-operator diff --git a/docker/build_operator.sh b/docker/build_operator.sh index 6c1817b1b..53afb581b 100644 --- a/docker/build_operator.sh +++ b/docker/build_operator.sh @@ -13,7 +13,7 @@ apt-get install -y wget ( cd /tmp - wget -q "https://storage.googleapis.com/golang/go1.23.4.linux-${arch}.tar.gz" -O go.tar.gz + wget -q "https://storage.googleapis.com/golang/go1.25.0.linux-${arch}.tar.gz" -O go.tar.gz tar -xf go.tar.gz mv go /usr/local ln -s /usr/local/go/bin/go /usr/bin/go diff --git a/go.mod b/go.mod index 9c0125229..d047b2453 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/zalando/postgres-operator -go 1.23.4 +go 1.25.0 require ( github.com/aws/aws-sdk-go v1.53.8 @@ -11,7 +11,7 @@ require ( github.com/r3labs/diff v1.1.0 github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.9.0 - golang.org/x/crypto v0.31.0 + golang.org/x/crypto v0.41.0 golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 gopkg.in/yaml.v2 v2.4.0 k8s.io/api v0.30.4 @@ -21,6 +21,8 @@ require ( k8s.io/code-generator v0.25.9 ) +require golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated // indirect + require ( github.com/Masterminds/semver v1.5.0 github.com/davecgh/go-spew v1.1.1 // indirect @@ -51,15 +53,15 @@ require ( github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/mod v0.17.0 // indirect - golang.org/x/net v0.25.0 // indirect + golang.org/x/mod v0.27.0 // indirect + golang.org/x/net v0.43.0 // indirect golang.org/x/oauth2 v0.10.0 // indirect - golang.org/x/sync v0.10.0 // indirect - golang.org/x/sys v0.28.0 // indirect - golang.org/x/term v0.27.0 // indirect - golang.org/x/text v0.21.0 // indirect + golang.org/x/sync v0.16.0 // indirect + golang.org/x/sys v0.35.0 // indirect + golang.org/x/term v0.34.0 // indirect + golang.org/x/text v0.28.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + golang.org/x/tools v0.36.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/go.sum b/go.sum index 0e55f2dd7..2d66aef46 100644 --- a/go.sum +++ b/go.sum @@ -119,31 +119,31 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= +golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o= golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= +golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= +golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -151,16 +151,16 @@ 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.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= -golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= +golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -169,8 +169,12 @@ golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roY 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= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= +golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= +golang.org/x/tools/go/expect v0.1.0-deprecated h1:jY2C5HGYR5lqex3gEniOQL0r7Dq5+VGVgY1nudX5lXY= +golang.org/x/tools/go/expect v0.1.0-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= +golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM= +golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/kubectl-pg/go.mod b/kubectl-pg/go.mod index 9b2e1bbc5..96a183fa9 100644 --- a/kubectl-pg/go.mod +++ b/kubectl-pg/go.mod @@ -1,6 +1,6 @@ module github.com/zalando/postgres-operator/kubectl-pg -go 1.23.4 +go 1.25 require ( github.com/spf13/cobra v1.8.1 diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index fedd6a917..4db23976c 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -171,7 +171,7 @@ func (c *Cluster) enforceMinResourceLimits(resources *v1.ResourceRequirements) e if isSmaller { msg = fmt.Sprintf("defined CPU limit %s for %q container is below required minimum %s and will be increased", cpuLimit.String(), constants.PostgresContainerName, minCPULimit) - c.logger.Warningf(msg) + c.logger.Warningf("%s", msg) c.eventRecorder.Eventf(c.GetReference(), v1.EventTypeWarning, "ResourceLimits", msg) resources.Limits[v1.ResourceCPU], _ = resource.ParseQuantity(minCPULimit) } @@ -188,7 +188,7 @@ func (c *Cluster) enforceMinResourceLimits(resources *v1.ResourceRequirements) e if isSmaller { msg = fmt.Sprintf("defined memory limit %s for %q container is below required minimum %s and will be increased", memoryLimit.String(), constants.PostgresContainerName, minMemoryLimit) - c.logger.Warningf(msg) + c.logger.Warningf("%s", msg) c.eventRecorder.Eventf(c.GetReference(), v1.EventTypeWarning, "ResourceLimits", msg) resources.Limits[v1.ResourceMemory], _ = resource.ParseQuantity(minMemoryLimit) } diff --git a/pkg/cluster/majorversionupgrade.go b/pkg/cluster/majorversionupgrade.go index d8a1fb917..675004982 100644 --- a/pkg/cluster/majorversionupgrade.go +++ b/pkg/cluster/majorversionupgrade.go @@ -277,7 +277,7 @@ func (c *Cluster) majorVersionUpgrade() error { isUpgradeSuccess = false c.annotatePostgresResource(isUpgradeSuccess) c.eventRecorder.Eventf(c.GetReference(), v1.EventTypeWarning, "Major Version Upgrade", "upgrade from %d to %d FAILED: %v", c.currentMajorVersion, desiredVersion, scriptErrMsg) - return fmt.Errorf(scriptErrMsg) + return fmt.Errorf("%s", scriptErrMsg) } c.annotatePostgresResource(isUpgradeSuccess) diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index 797e7a5aa..9067c8c46 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -1208,7 +1208,7 @@ func (c *Cluster) updateSecret( } if updateSecret { - c.logger.Infof(updateSecretMsg) + c.logger.Infof("%s", updateSecretMsg) if secret, err = c.KubeClient.Secrets(secret.Namespace).Update(context.TODO(), secret, metav1.UpdateOptions{}); err != nil { return fmt.Errorf("could not update secret %s: %v", secretName, err) } diff --git a/pkg/cluster/util.go b/pkg/cluster/util.go index 0e31ecc32..06a35f1b7 100644 --- a/pkg/cluster/util.go +++ b/pkg/cluster/util.go @@ -257,9 +257,9 @@ func (c *Cluster) getTeamMembers(teamID string) ([]string, error) { if teamID == "" { msg := "no teamId specified" if c.OpConfig.EnableTeamIdClusternamePrefix { - return nil, fmt.Errorf(msg) + return nil, fmt.Errorf("%s", msg) } - c.logger.Warnf(msg) + c.logger.Warnf("%s", msg) return nil, nil } diff --git a/pkg/cluster/volumes.go b/pkg/cluster/volumes.go index fee18beaf..7aa70a5d1 100644 --- a/pkg/cluster/volumes.go +++ b/pkg/cluster/volumes.go @@ -129,7 +129,7 @@ func (c *Cluster) syncUnderlyingEBSVolume() error { if len(errors) > 0 { for _, s := range errors { - c.logger.Warningf(s) + c.logger.Warningf("%s", s) } } return nil diff --git a/pkg/controller/postgresql.go b/pkg/controller/postgresql.go index 42d96278c..824a030f4 100644 --- a/pkg/controller/postgresql.go +++ b/pkg/controller/postgresql.go @@ -597,7 +597,7 @@ func (c *Controller) createPodServiceAccount(namespace string) error { _, err := c.KubeClient.ServiceAccounts(namespace).Get(context.TODO(), podServiceAccountName, metav1.GetOptions{}) if k8sutil.ResourceNotFound(err) { - c.logger.Infof(fmt.Sprintf("creating pod service account %q in the %q namespace", podServiceAccountName, namespace)) + c.logger.Infof("creating pod service account %q in the %q namespace", podServiceAccountName, namespace) // get a separate copy of service account // to prevent a race condition when setting a namespace for many clusters diff --git a/pkg/controller/util.go b/pkg/controller/util.go index 59e608ad0..df043dfd9 100644 --- a/pkg/controller/util.go +++ b/pkg/controller/util.go @@ -248,7 +248,7 @@ func (c *Controller) getInfrastructureRoles( } if len(errors) > 0 { - return uniqRoles, fmt.Errorf(strings.Join(errors, `', '`)) + return uniqRoles, fmt.Errorf("%s", strings.Join(errors, `', '`)) } return uniqRoles, nil From ad7e5909166d396cd41e6f842e9a7e86cadeebeb Mon Sep 17 00:00:00 2001 From: Morten Lied Johansen Date: Wed, 17 Sep 2025 15:57:36 +0200 Subject: [PATCH 02/58] Skip creation of OwnerReference if user is in a different namespace (#2912) Instead of doing a string compare on the username, check the actual namespace of the user to determine if an owner reference can be created. --- pkg/cluster/k8sres.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index 4db23976c..65ca76357 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -1928,7 +1928,7 @@ func (c *Cluster) generateSingleUserSecret(pgUser spec.PgUser) *v1.Secret { // if secret lives in another namespace we cannot set ownerReferences var ownerReferences []metav1.OwnerReference - if c.Config.OpConfig.EnableCrossNamespaceSecret && strings.Contains(username, ".") { + if c.Config.OpConfig.EnableCrossNamespaceSecret && c.Postgresql.ObjectMeta.Namespace != pgUser.Namespace { ownerReferences = nil } else { ownerReferences = c.ownerReferences() From cce263319262e25167b1cdc56b092b849803e0fe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Sep 2025 16:01:02 +0200 Subject: [PATCH 03/58] Bump requests from 2.32.2 to 2.32.4 in /ui (#2922) Bumps [requests](https://github.com/psf/requests) from 2.32.2 to 2.32.4. - [Release notes](https://github.com/psf/requests/releases) - [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md) - [Commits](https://github.com/psf/requests/compare/v2.32.2...v2.32.4) --- updated-dependencies: - dependency-name: requests dependency-version: 2.32.4 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 783c0aac3..75bcc1952 100644 --- a/ui/requirements.txt +++ b/ui/requirements.txt @@ -9,6 +9,6 @@ jq==1.7.0 json_delta>=2.0.2 kubernetes==11.0.0 python-json-logger==2.0.7 -requests==2.32.2 +requests==2.32.4 stups-tokens>=1.1.19 werkzeug==3.0.6 From d98fc2753aacab063e3b6fae79fe9a1a55c0dfb9 Mon Sep 17 00:00:00 2001 From: Alexander Gramovich <34745780+ggramal@users.noreply.github.com> Date: Wed, 17 Sep 2025 17:01:28 +0300 Subject: [PATCH 04/58] logical-backup:gcs_upload: try to use gcp metadata if LOGICAL_GOOGLE_APPLICATION_CREDENTIALS is not set (#2837) Co-authored-by: Felix Kunde --- logical-backup/dump.sh | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/logical-backup/dump.sh b/logical-backup/dump.sh index 25641c3b5..a250670a6 100755 --- a/logical-backup/dump.sh +++ b/logical-backup/dump.sh @@ -122,7 +122,21 @@ function aws_upload { function gcs_upload { PATH_TO_BACKUP=gs://$LOGICAL_BACKUP_S3_BUCKET"/"$LOGICAL_BACKUP_S3_BUCKET_PREFIX"/"$SCOPE$LOGICAL_BACKUP_S3_BUCKET_SCOPE_SUFFIX"/logical_backups/"$(date +%s).sql.gz - gsutil -o Credentials:gs_service_key_file=$LOGICAL_BACKUP_GOOGLE_APPLICATION_CREDENTIALS cp - "$PATH_TO_BACKUP" + #Set local LOGICAL_GOOGLE_APPLICATION_CREDENTIALS to nothing or + #value of LOGICAL_GOOGLE_APPLICATION_CREDENTIALS env var. Needed + #because `set -o nounset` is globally set + local LOGICAL_BACKUP_GOOGLE_APPLICATION_CREDENTIALS=${LOGICAL_BACKUP_GOOGLE_APPLICATION_CREDENTIALS:-} + + GSUTIL_OPTIONS=("-o" "Credentials:gs_service_key_file=$LOGICAL_BACKUP_GOOGLE_APPLICATION_CREDENTIALS") + + #If GOOGLE_APPLICATION_CREDENTIALS is not set try to get + #creds from metadata + if [[ -z $LOGICAL_BACKUP_GOOGLE_APPLICATION_CREDENTIALS ]] + then + GSUTIL_OPTIONS[1]="GoogleCompute:service_account=default" + fi + + gsutil ${GSUTIL_OPTIONS[@]} cp - "$PATH_TO_BACKUP" } function upload { From bcd729b2ccbac7abdea923f77b29ae4c32df4fcd Mon Sep 17 00:00:00 2001 From: Polina Bungina <27892524+hughcapet@users.noreply.github.com> Date: Fri, 19 Sep 2025 14:44:17 +0200 Subject: [PATCH 05/58] Add selector to master service when switching to CM (#2955) Add service selector comparison to compareServices This is necessary for the proper switch of `kubernetes_use_configmaps` configuration value, as master service should have different label selector setup for those. --- pkg/cluster/cluster.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index e9a691faa..f1d9ce164 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -841,6 +841,10 @@ func (c *Cluster) compareServices(old, new *v1.Service) (bool, string) { return false, "new service's owner references do not match the current ones" } + if !reflect.DeepEqual(old.Spec.Selector, new.Spec.Selector) { + return false, "new service's selector does not match the current one" + } + return true, "" } From dc294259692319a229e7677c16f88d3248124e08 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Tue, 23 Sep 2025 14:30:06 +0200 Subject: [PATCH 06/58] include external traffic policy comparison into service diffing (#2956) --- pkg/cluster/cluster.go | 4 ++ pkg/cluster/cluster_test.go | 77 ++++++++++++++++++++++++++++++------- 2 files changed, 67 insertions(+), 14 deletions(-) diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index f1d9ce164..b06386f39 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -845,6 +845,10 @@ func (c *Cluster) compareServices(old, new *v1.Service) (bool, string) { return false, "new service's selector does not match the current one" } + if old.Spec.ExternalTrafficPolicy != new.Spec.ExternalTrafficPolicy { + return false, "new service's ExternalTrafficPolicy does not match the current one" + } + return true, "" } diff --git a/pkg/cluster/cluster_test.go b/pkg/cluster/cluster_test.go index 09d9df972..25f61db98 100644 --- a/pkg/cluster/cluster_test.go +++ b/pkg/cluster/cluster_test.go @@ -1341,14 +1341,21 @@ func TestCompareEnv(t *testing.T) { } } -func newService(ann map[string]string, svcT v1.ServiceType, lbSr []string) *v1.Service { +func newService( + annotations map[string]string, + svcType v1.ServiceType, + sourceRanges []string, + selector map[string]string, + policy v1.ServiceExternalTrafficPolicyType) *v1.Service { svc := &v1.Service{ Spec: v1.ServiceSpec{ - Type: svcT, - LoadBalancerSourceRanges: lbSr, + Selector: selector, + Type: svcType, + LoadBalancerSourceRanges: sourceRanges, + ExternalTrafficPolicy: policy, }, } - svc.Annotations = ann + svc.Annotations = annotations return svc } @@ -1365,13 +1372,18 @@ func TestCompareServices(t *testing.T) { }, } + defaultPolicy := v1.ServiceExternalTrafficPolicyTypeCluster + serviceWithOwnerReference := newService( map[string]string{ constants.ZalandoDNSNameAnnotation: "clstr.acid.zalan.do", constants.ElbTimeoutAnnotationName: constants.ElbTimeoutAnnotationValue, }, v1.ServiceTypeClusterIP, - []string{"128.141.0.0/16", "137.138.0.0/16"}) + []string{"128.141.0.0/16", "137.138.0.0/16"}, + nil, + defaultPolicy, + ) ownerRef := metav1.OwnerReference{ APIVersion: "acid.zalan.do/v1", @@ -1397,14 +1409,16 @@ func TestCompareServices(t *testing.T) { constants.ElbTimeoutAnnotationName: constants.ElbTimeoutAnnotationValue, }, v1.ServiceTypeClusterIP, - []string{"128.141.0.0/16", "137.138.0.0/16"}), + []string{"128.141.0.0/16", "137.138.0.0/16"}, + nil, defaultPolicy), new: newService( map[string]string{ constants.ZalandoDNSNameAnnotation: "clstr.acid.zalan.do", constants.ElbTimeoutAnnotationName: constants.ElbTimeoutAnnotationValue, }, v1.ServiceTypeClusterIP, - []string{"128.141.0.0/16", "137.138.0.0/16"}), + []string{"128.141.0.0/16", "137.138.0.0/16"}, + nil, defaultPolicy), match: true, }, { @@ -1415,14 +1429,16 @@ func TestCompareServices(t *testing.T) { constants.ElbTimeoutAnnotationName: constants.ElbTimeoutAnnotationValue, }, v1.ServiceTypeClusterIP, - []string{"128.141.0.0/16", "137.138.0.0/16"}), + []string{"128.141.0.0/16", "137.138.0.0/16"}, + nil, defaultPolicy), new: newService( map[string]string{ constants.ZalandoDNSNameAnnotation: "clstr.acid.zalan.do", constants.ElbTimeoutAnnotationName: constants.ElbTimeoutAnnotationValue, }, v1.ServiceTypeLoadBalancer, - []string{"128.141.0.0/16", "137.138.0.0/16"}), + []string{"128.141.0.0/16", "137.138.0.0/16"}, + nil, defaultPolicy), match: false, reason: `new service's type "LoadBalancer" does not match the current one "ClusterIP"`, }, @@ -1434,14 +1450,16 @@ func TestCompareServices(t *testing.T) { constants.ElbTimeoutAnnotationName: constants.ElbTimeoutAnnotationValue, }, v1.ServiceTypeLoadBalancer, - []string{"128.141.0.0/16", "137.138.0.0/16"}), + []string{"128.141.0.0/16", "137.138.0.0/16"}, + nil, defaultPolicy), new: newService( map[string]string{ constants.ZalandoDNSNameAnnotation: "clstr.acid.zalan.do", constants.ElbTimeoutAnnotationName: constants.ElbTimeoutAnnotationValue, }, v1.ServiceTypeLoadBalancer, - []string{"185.249.56.0/22"}), + []string{"185.249.56.0/22"}, + nil, defaultPolicy), match: false, reason: `new service's LoadBalancerSourceRange does not match the current one`, }, @@ -1453,14 +1471,16 @@ func TestCompareServices(t *testing.T) { constants.ElbTimeoutAnnotationName: constants.ElbTimeoutAnnotationValue, }, v1.ServiceTypeLoadBalancer, - []string{"128.141.0.0/16", "137.138.0.0/16"}), + []string{"128.141.0.0/16", "137.138.0.0/16"}, + nil, defaultPolicy), new: newService( map[string]string{ constants.ZalandoDNSNameAnnotation: "clstr.acid.zalan.do", constants.ElbTimeoutAnnotationName: constants.ElbTimeoutAnnotationValue, }, v1.ServiceTypeLoadBalancer, - []string{}), + []string{}, + nil, defaultPolicy), match: false, reason: `new service's LoadBalancerSourceRange does not match the current one`, }, @@ -1472,10 +1492,39 @@ func TestCompareServices(t *testing.T) { constants.ElbTimeoutAnnotationName: constants.ElbTimeoutAnnotationValue, }, v1.ServiceTypeClusterIP, - []string{"128.141.0.0/16", "137.138.0.0/16"}), + []string{"128.141.0.0/16", "137.138.0.0/16"}, + nil, defaultPolicy), new: serviceWithOwnerReference, match: false, }, + { + about: "new service has a label selector", + current: newService( + map[string]string{}, + v1.ServiceTypeClusterIP, + []string{}, + nil, defaultPolicy), + new: newService( + map[string]string{}, + v1.ServiceTypeClusterIP, + []string{}, + map[string]string{"cluster-name": "clstr", "spilo-role": "master"}, defaultPolicy), + match: false, + }, + { + about: "services differ on external traffic policy", + current: newService( + map[string]string{}, + v1.ServiceTypeClusterIP, + []string{}, + nil, defaultPolicy), + new: newService( + map[string]string{}, + v1.ServiceTypeClusterIP, + []string{}, + nil, v1.ServiceExternalTrafficPolicyTypeLocal), + match: false, + }, } for _, tt := range tests { From 8ba57b28f5775895a6d710ada3931cf3defd7bc8 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Tue, 14 Oct 2025 10:59:43 +0200 Subject: [PATCH 07/58] extend RBAC in prepatation to switch to configmap-based cluster management (#2961) --- docs/reference/operator_parameters.md | 9 +++++-- manifests/operator-service-account-rbac.yaml | 27 +++++++++++++++++--- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/docs/reference/operator_parameters.md b/docs/reference/operator_parameters.md index 95bfb4cf3..7e7cbeaf0 100644 --- a/docs/reference/operator_parameters.md +++ b/docs/reference/operator_parameters.md @@ -107,8 +107,13 @@ Those are top-level keys, containing both leaf keys and groups. * **kubernetes_use_configmaps** Select if setup uses endpoints (default), or configmaps to manage leader when DCS is kubernetes (not etcd or similar). In OpenShift it is not possible to - use endpoints option, and configmaps is required. By default, - `kubernetes_use_configmaps: false`, meaning endpoints will be used. + use endpoints option, and configmaps is required. Starting with K8s 1.33, + endpoints are marked as deprecated. It's recommended to switch to config maps + instead. But, to do so make sure you scale the Postgres cluster down to just + one primary pod (e.g. using `max_instances` option). Otherwise, you risk + running into a split-brain scenario. + By default, `kubernetes_use_configmaps: false`, meaning endpoints will be used. + Starting from v1.16.0 the default will be changed to `true`. * **docker_image** Spilo Docker image for Postgres instances. For production, don't rely on the diff --git a/manifests/operator-service-account-rbac.yaml b/manifests/operator-service-account-rbac.yaml index bf27f99f1..2cc1edcd1 100644 --- a/manifests/operator-service-account-rbac.yaml +++ b/manifests/operator-service-account-rbac.yaml @@ -59,13 +59,20 @@ rules: - get - patch - update -# to read configuration from ConfigMaps +# to read configuration from ConfigMaps and help Patroni manage the cluster if endpoints are not used - apiGroups: - "" resources: - configmaps verbs: + - create + - delete + - deletecollection - get + - list + - patch + - update + - watch # to send events to the CRs - apiGroups: - "" @@ -78,7 +85,7 @@ rules: - patch - update - watch -# to manage endpoints which are also used by Patroni +# to manage endpoints which are also used by Patroni (if it is using config maps) - apiGroups: - "" resources: @@ -249,7 +256,21 @@ kind: ClusterRole metadata: name: postgres-pod rules: -# Patroni needs to watch and manage endpoints +# Patroni needs to watch and manage config maps (or endpoints) +- apiGroups: + - "" + resources: + - configmaps + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +# Patroni needs to watch and manage endpoints (or config maps) - apiGroups: - "" resources: From eddf521227ffbb7a0661a4bebfad4bf7fe25423f Mon Sep 17 00:00:00 2001 From: Eng Zer Jun Date: Tue, 14 Oct 2025 17:59:48 +0800 Subject: [PATCH 08/58] Replace `golang.org/x/exp` with stdlib (#2857) * Replace `golang.org/x/exp` with stdlib These experimental packages are now available in the Go standard library since Go 1.21. 1. golang.org/x/exp/slices -> slices [1] 2. golang.org/x/exp/maps -> maps [2] [1]: https://go.dev/doc/go1.21#slices [2]: https://go.dev/doc/go1.21#maps Signed-off-by: Eng Zer Jun * Run go mod tidy Signed-off-by: Eng Zer Jun --------- Signed-off-by: Eng Zer Jun Co-authored-by: Felix Kunde --- go.mod | 6 ++---- go.sum | 2 -- pkg/cluster/k8sres.go | 9 ++++----- pkg/cluster/pod.go | 3 +-- pkg/cluster/sync.go | 4 ++-- pkg/cluster/sync_test.go | 5 ++--- 6 files changed, 11 insertions(+), 18 deletions(-) diff --git a/go.mod b/go.mod index d047b2453..150074cf8 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/zalando/postgres-operator go 1.25.0 require ( + github.com/Masterminds/semver v1.5.0 github.com/aws/aws-sdk-go v1.53.8 github.com/golang/mock v1.6.0 github.com/lib/pq v1.10.9 @@ -12,7 +13,6 @@ require ( github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.9.0 golang.org/x/crypto v0.41.0 - golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 gopkg.in/yaml.v2 v2.4.0 k8s.io/api v0.30.4 k8s.io/apiextensions-apiserver v0.25.9 @@ -21,10 +21,7 @@ require ( k8s.io/code-generator v0.25.9 ) -require golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated // indirect - require ( - github.com/Masterminds/semver v1.5.0 github.com/davecgh/go-spew v1.1.1 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/evanphx/json-patch v4.12.0+incompatible // indirect @@ -62,6 +59,7 @@ require ( golang.org/x/text v0.28.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.36.0 // indirect + golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/go.sum b/go.sum index 2d66aef46..cab88ce55 100644 --- a/go.sum +++ b/go.sum @@ -121,8 +121,6 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= -golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o= -golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index 65ca76357..93d992c4b 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -4,7 +4,9 @@ import ( "context" "encoding/json" "fmt" + "maps" "path" + "slices" "sort" "strings" @@ -12,19 +14,16 @@ import ( "github.com/sirupsen/logrus" appsv1 "k8s.io/api/apps/v1" + batchv1 "k8s.io/api/batch/v1" v1 "k8s.io/api/core/v1" policyv1 "k8s.io/api/policy/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/intstr" - "golang.org/x/exp/maps" - "golang.org/x/exp/slices" - batchv1 "k8s.io/api/batch/v1" - "k8s.io/apimachinery/pkg/labels" - acidv1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" "github.com/zalando/postgres-operator/pkg/spec" "github.com/zalando/postgres-operator/pkg/util" diff --git a/pkg/cluster/pod.go b/pkg/cluster/pod.go index 7fc95090e..b28e03078 100644 --- a/pkg/cluster/pod.go +++ b/pkg/cluster/pod.go @@ -3,12 +3,11 @@ package cluster import ( "context" "fmt" + "slices" "sort" "strconv" "time" - "golang.org/x/exp/slices" - appsv1 "k8s.io/api/apps/v1" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index 9067c8c46..c632e598c 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -4,8 +4,10 @@ import ( "context" "encoding/json" "fmt" + "maps" "reflect" "regexp" + "slices" "strconv" "strings" "time" @@ -15,8 +17,6 @@ import ( "github.com/zalando/postgres-operator/pkg/util" "github.com/zalando/postgres-operator/pkg/util/constants" "github.com/zalando/postgres-operator/pkg/util/k8sutil" - "golang.org/x/exp/maps" - "golang.org/x/exp/slices" batchv1 "k8s.io/api/batch/v1" v1 "k8s.io/api/core/v1" policyv1 "k8s.io/api/policy/v1" diff --git a/pkg/cluster/sync_test.go b/pkg/cluster/sync_test.go index f9d1d7873..e670daa70 100644 --- a/pkg/cluster/sync_test.go +++ b/pkg/cluster/sync_test.go @@ -2,15 +2,14 @@ package cluster import ( "bytes" + "context" "fmt" "io" "net/http" + "slices" "testing" "time" - "context" - - "golang.org/x/exp/slices" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" From 3a85466cfdc02328e03dff81c49343f9d3bb5dbf Mon Sep 17 00:00:00 2001 From: Remi Rampin Date: Tue, 14 Oct 2025 07:49:06 -0400 Subject: [PATCH 09/58] DOC: Fix formatting of bullet points (#2948) Co-authored-by: Felix Kunde --- docs/administrator.md | 3 +++ docs/developer.md | 8 +++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/administrator.md b/docs/administrator.md index f394b70ab..f28bd93f0 100644 --- a/docs/administrator.md +++ b/docs/administrator.md @@ -195,12 +195,14 @@ from numerous escape characters in the latter log entry, view it in CLI with used internally in K8s. The StatefulSet is replaced if the following properties change: + - annotations - volumeClaimTemplates - template volumes The StatefulSet is replaced and a rolling updates is triggered if the following properties differ between the old and new state: + - container name, ports, image, resources, env, envFrom, securityContext and volumeMounts - template labels, annotations, service account, securityContext, affinity, priority class and termination grace period @@ -898,6 +900,7 @@ services: There are multiple options to specify service annotations that will be merged with each other and override in the following order (where latter take precedence): + 1. Default annotations if LoadBalancer is enabled 2. Globally configured `custom_service_annotations` 3. `serviceAnnotations` specified in the cluster manifest diff --git a/docs/developer.md b/docs/developer.md index c006aded0..0429f951c 100644 --- a/docs/developer.md +++ b/docs/developer.md @@ -16,7 +16,7 @@ under the ~/go/src sub directories. Given the schema above, the Postgres Operator source code located at `github.com/zalando/postgres-operator` should be put at --`~/go/src/github.com/zalando/postgres-operator`. +`~/go/src/github.com/zalando/postgres-operator`. ```bash export GOPATH=~/go @@ -105,6 +105,7 @@ and K8s-like APIs for its custom resource definitions, namely the Postgres CRD and the operator CRD. The usage of the code generation follows conventions from the K8s community. Relevant scripts live in the `hack` directory: + * `update-codegen.sh` triggers code generation for the APIs defined in `pkg/apis/acid.zalan.do/`, * `verify-codegen.sh` checks if the generated code is up-to-date (to be used within CI). @@ -112,6 +113,7 @@ The `/pkg/generated/` contains the resultant code. To make these scripts work, you may need to `export GOPATH=$(go env GOPATH)` References for code generation are: + * [Relevant pull request](https://github.com/zalando/postgres-operator/pull/369) See comments there for minor issues that can sometimes broke the generation process. * [Code generator source code](https://github.com/kubernetes/code-generator) @@ -315,6 +317,7 @@ precedence. Update the following Go files that obtain the configuration parameter from the manifest files: + * [operator_configuration_type.go](https://github.com/zalando/postgres-operator/blob/master/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go) * [operator_config.go](https://github.com/zalando/postgres-operator/blob/master/pkg/controller/operator_config.go) * [config.go](https://github.com/zalando/postgres-operator/blob/master/pkg/util/config/config.go) @@ -323,6 +326,7 @@ Postgres manifest parameters are defined in the [api package](https://github.com The operator behavior has to be implemented at least in [k8sres.go](https://github.com/zalando/postgres-operator/blob/master/pkg/cluster/k8sres.go). Validation of CRD parameters is controlled in [crds.go](https://github.com/zalando/postgres-operator/blob/master/pkg/apis/acid.zalan.do/v1/crds.go). Please, reflect your changes in tests, for example in: + * [config_test.go](https://github.com/zalando/postgres-operator/blob/master/pkg/util/config/config_test.go) * [k8sres_test.go](https://github.com/zalando/postgres-operator/blob/master/pkg/cluster/k8sres_test.go) * [util_test.go](https://github.com/zalando/postgres-operator/blob/master/pkg/apis/acid.zalan.do/v1/util_test.go) @@ -330,6 +334,7 @@ Please, reflect your changes in tests, for example in: ### Updating manifest files For the CRD-based configuration, please update the following files: + * the default [OperatorConfiguration](https://github.com/zalando/postgres-operator/blob/master/manifests/postgresql-operator-default-configuration.yaml) * the CRD's [validation](https://github.com/zalando/postgres-operator/blob/master/manifests/operatorconfiguration.crd.yaml) * the CRD's validation in the [Helm chart](https://github.com/zalando/postgres-operator/blob/master/charts/postgres-operator/crds/operatorconfigurations.yaml) @@ -342,6 +347,7 @@ Last but no least, update the [ConfigMap](https://github.com/zalando/postgres-op Finally, add a section for each new configuration option and/or cluster manifest parameter in the reference documents: + * [config reference](reference/operator_parameters.md) * [manifest reference](reference/cluster_manifest.md) From 8c2a290a121d3a2e072ed04503ef1fa25e7c340d Mon Sep 17 00:00:00 2001 From: Remi Rampin Date: Tue, 14 Oct 2025 07:50:11 -0400 Subject: [PATCH 10/58] DOC: Minikube has many drivers now, incl. Docker (#2949) Co-authored-by: Felix Kunde --- docs/quickstart.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/quickstart.md b/docs/quickstart.md index 2d6742354..fa16d1813 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -10,7 +10,7 @@ hence set it up first. For local tests we recommend to use one of the following solutions: * [minikube](https://github.com/kubernetes/minikube/releases), which creates a - single-node K8s cluster inside a VM (requires KVM or VirtualBox), + K8s cluster inside a container or VM (requires Docker, KVM, Hyper-V, HyperKit, VirtualBox, or similar), * [kind](https://kind.sigs.k8s.io/) and [k3d](https://k3d.io), which allows creating multi-nodes K8s clusters running on Docker (requires Docker) @@ -20,7 +20,7 @@ This quickstart assumes that you have started minikube or created a local kind cluster. Note that you can also use built-in K8s support in the Docker Desktop for Mac to follow the steps of this tutorial. You would have to replace `minikube start` and `minikube delete` with your launch actions for the Docker -built-in K8s support. +Desktop built-in K8s support. ## Configuration Options From 3bc244fe397ad084de8ddc3e02a1670f6b4c0949 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Thu, 16 Oct 2025 10:23:36 +0200 Subject: [PATCH 11/58] bump dependencies and reflect linter suggestions (#2963) --- .github/workflows/publish_ghcr_image.yaml | 2 +- .github/workflows/run_e2e.yaml | 2 +- .github/workflows/run_tests.yaml | 2 +- CODEOWNERS | 2 +- MAINTAINERS | 1 - Makefile | 4 +- README.md | 2 +- .../crds/operatorconfigurations.yaml | 2 +- charts/postgres-operator/values.yaml | 2 +- docker/build_operator.sh | 2 +- e2e/tests/test_e2e.py | 2 +- go.mod | 76 ++++---- go.sum | 171 +++++++++--------- kubectl-pg/go.mod | 2 +- kubectl-pg/go.sum | 4 +- manifests/complete-postgres-manifest.yaml | 2 +- manifests/configmap.yaml | 2 +- manifests/operatorconfiguration.crd.yaml | 2 +- ...gresql-operator-default-configuration.yaml | 2 +- pkg/cluster/cluster.go | 2 +- pkg/cluster/k8sres.go | 16 +- pkg/cluster/majorversionupgrade.go | 2 +- pkg/cluster/pod.go | 5 +- pkg/cluster/resources.go | 4 +- pkg/cluster/sync.go | 7 +- pkg/controller/operator_config.go | 2 +- pkg/util/config/config.go | 2 +- 27 files changed, 165 insertions(+), 159 deletions(-) diff --git a/.github/workflows/publish_ghcr_image.yaml b/.github/workflows/publish_ghcr_image.yaml index ff1e86a65..78815783a 100644 --- a/.github/workflows/publish_ghcr_image.yaml +++ b/.github/workflows/publish_ghcr_image.yaml @@ -23,7 +23,7 @@ jobs: - uses: actions/setup-go@v2 with: - go-version: "^1.25.0" + go-version: "^1.25.3" - name: Run unit tests run: make deps mocks test diff --git a/.github/workflows/run_e2e.yaml b/.github/workflows/run_e2e.yaml index ae5433b5a..86f861ec5 100644 --- a/.github/workflows/run_e2e.yaml +++ b/.github/workflows/run_e2e.yaml @@ -14,7 +14,7 @@ jobs: - uses: actions/checkout@v1 - uses: actions/setup-go@v2 with: - go-version: "^1.25.0" + go-version: "^1.25.3" - name: Make dependencies run: make deps mocks - name: Code generation diff --git a/.github/workflows/run_tests.yaml b/.github/workflows/run_tests.yaml index 51061bbf6..ec3e5eaf6 100644 --- a/.github/workflows/run_tests.yaml +++ b/.github/workflows/run_tests.yaml @@ -14,7 +14,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-go@v2 with: - go-version: "^1.25.0" + go-version: "^1.25.3" - name: Make dependencies run: make deps mocks - name: Compile diff --git a/CODEOWNERS b/CODEOWNERS index ca6f43a72..daca96b42 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1,2 +1,2 @@ # global owners -* @sdudoladov @Jan-M @FxKu @jopadi @idanovinda @hughcapet @macedigital +* @sdudoladov @Jan-M @FxKu @jopadi @idanovinda @hughcapet diff --git a/MAINTAINERS b/MAINTAINERS index cc07af957..7c4aa6675 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4,4 +4,3 @@ Jan Mussler Jociele Padilha Ida Novindasari Polina Bungina -Matthias Adler diff --git a/Makefile b/Makefile index 350675db6..50d070b9d 100644 --- a/Makefile +++ b/Makefile @@ -69,7 +69,7 @@ docker: ${DOCKERDIR}/${DOCKERFILE} docker build --rm -t "$(IMAGE):$(TAG)$(CDP_TAG)$(DEBUG_FRESH)$(DEBUG_POSTFIX)" -f "${DOCKERDIR}/${DOCKERFILE}" --build-arg VERSION="${VERSION}" . indocker-race: - docker run --rm -v "${GOPATH}":"${GOPATH}" -e GOPATH="${GOPATH}" -e RACE=1 -w ${PWD} golang:1.25.0 bash -c "make linux" + docker run --rm -v "${GOPATH}":"${GOPATH}" -e GOPATH="${GOPATH}" -e RACE=1 -w ${PWD} golang:1.25.3 bash -c "make linux" push: docker push "$(IMAGE):$(TAG)$(CDP_TAG)" @@ -78,7 +78,7 @@ mocks: GO111MODULE=on go generate ./... tools: - GO111MODULE=on go get k8s.io/client-go@kubernetes-1.30.4 + 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 diff --git a/README.md b/README.md index 9493115de..fdf2518ba 100644 --- a/README.md +++ b/README.md @@ -57,12 +57,12 @@ production for over five years. | Release | Postgres versions | K8s versions | Golang | | :-------- | :---------------: | :---------------: | :-----: | +| v1.15.0 | 13 → 17 | 1.27+ | 1.25.3 | | v1.14.0 | 13 → 17 | 1.27+ | 1.23.4 | | v1.13.0 | 12 → 16 | 1.27+ | 1.22.5 | | v1.12.0 | 11 → 16 | 1.27+ | 1.22.3 | | v1.11.0 | 11 → 16 | 1.27+ | 1.21.7 | | v1.10.1 | 10 → 15 | 1.21+ | 1.19.8 | -| v1.9.0 | 10 → 15 | 1.21+ | 1.18.9 | ## Getting started diff --git a/charts/postgres-operator/crds/operatorconfigurations.yaml b/charts/postgres-operator/crds/operatorconfigurations.yaml index 058769acf..c6da405a5 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-p2" + default: "ghcr.io/zalando/spilo-17:4.0-p3" enable_crd_registration: type: boolean default: true diff --git a/charts/postgres-operator/values.yaml b/charts/postgres-operator/values.yaml index bf94b63d0..f45275a4c 100644 --- a/charts/postgres-operator/values.yaml +++ b/charts/postgres-operator/values.yaml @@ -38,7 +38,7 @@ configGeneral: # etcd connection string for Patroni. Empty uses K8s-native DCS. etcd_host: "" # Spilo docker image - docker_image: ghcr.io/zalando/spilo-17:4.0-p2 + docker_image: ghcr.io/zalando/spilo-17:4.0-p3 # key name for annotation to ignore globally configured instance limits # ignore_instance_limits_annotation_key: "" diff --git a/docker/build_operator.sh b/docker/build_operator.sh index 53afb581b..5abe56666 100644 --- a/docker/build_operator.sh +++ b/docker/build_operator.sh @@ -13,7 +13,7 @@ apt-get install -y wget ( cd /tmp - wget -q "https://storage.googleapis.com/golang/go1.25.0.linux-${arch}.tar.gz" -O go.tar.gz + wget -q "https://storage.googleapis.com/golang/go1.25.3.linux-${arch}.tar.gz" -O go.tar.gz tar -xf go.tar.gz mv go /usr/local ln -s /usr/local/go/bin/go /usr/bin/go diff --git a/e2e/tests/test_e2e.py b/e2e/tests/test_e2e.py index b9a2a27d4..4681508c5 100644 --- a/e2e/tests/test_e2e.py +++ b/e2e/tests/test_e2e.py @@ -14,7 +14,7 @@ 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-p2" +SPILO_FULL_IMAGE = "ghcr.io/zalando/spilo-17:4.0-p3" def to_selector(labels): return ",".join(["=".join(lbl) for lbl in labels.items()]) diff --git a/go.mod b/go.mod index 150074cf8..7eda62d18 100644 --- a/go.mod +++ b/go.mod @@ -1,75 +1,75 @@ module github.com/zalando/postgres-operator -go 1.25.0 +go 1.25.3 require ( github.com/Masterminds/semver v1.5.0 - github.com/aws/aws-sdk-go v1.53.8 + github.com/aws/aws-sdk-go v1.55.8 github.com/golang/mock v1.6.0 github.com/lib/pq v1.10.9 github.com/motomux/pretty v0.0.0-20161209205251-b2aad2c9a95d github.com/pkg/errors v0.9.1 github.com/r3labs/diff v1.1.0 github.com/sirupsen/logrus v1.9.3 - github.com/stretchr/testify v1.9.0 - golang.org/x/crypto v0.41.0 + github.com/stretchr/testify v1.11.1 + golang.org/x/crypto v0.43.0 gopkg.in/yaml.v2 v2.4.0 - k8s.io/api v0.30.4 + k8s.io/api v0.32.9 k8s.io/apiextensions-apiserver v0.25.9 - k8s.io/apimachinery v0.30.4 - k8s.io/client-go v0.30.4 + k8s.io/apimachinery v0.32.9 + k8s.io/client-go v0.32.9 k8s.io/code-generator v0.25.9 ) require ( - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect - github.com/evanphx/json-patch v4.12.0+incompatible // indirect - github.com/go-logr/logr v1.4.1 // indirect - github.com/go-openapi/jsonpointer v0.19.6 // 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.22.3 // indirect + github.com/go-openapi/swag v0.23.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect - github.com/google/gnostic-models v0.6.8 // indirect - github.com/google/go-cmp v0.6.0 // indirect + github.com/google/gnostic-models v0.6.9 // indirect + github.com/google/go-cmp v0.7.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/gorilla/websocket v1.5.0 // indirect - github.com/imdario/mergo v0.3.6 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // 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/moby/spdystream v0.2.0 // 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.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/mod v0.27.0 // indirect - golang.org/x/net v0.43.0 // indirect - golang.org/x/oauth2 v0.10.0 // indirect - golang.org/x/sync v0.16.0 // indirect - golang.org/x/sys v0.35.0 // indirect - golang.org/x/term v0.34.0 // indirect - golang.org/x/text v0.28.0 // indirect - golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.36.0 // indirect + github.com/x448/float16 v0.8.4 // indirect + golang.org/x/mod v0.28.0 // indirect + golang.org/x/net v0.45.0 // indirect + golang.org/x/oauth2 v0.27.0 // indirect + golang.org/x/sync v0.17.0 // indirect + golang.org/x/sys v0.37.0 // indirect + golang.org/x/term v0.36.0 // indirect + golang.org/x/text v0.30.0 // indirect + golang.org/x/time v0.9.0 // indirect + golang.org/x/tools v0.37.0 // indirect golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.33.0 // indirect + google.golang.org/protobuf v1.36.5 // indirect + gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/gengo v0.0.0-20220902162205-c0856e24416d // indirect - k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70 // indirect - k8s.io/klog/v2 v2.120.1 // indirect - k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect - k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect - sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect - sigs.k8s.io/yaml v1.3.0 // indirect + k8s.io/gengo/v2 v2.0.0-20240826214909-a7b603a56eb7 // 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 + 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 ) diff --git a/go.sum b/go.sum index cab88ce55..ffdf21612 100644 --- a/go.sum +++ b/go.sum @@ -2,55 +2,52 @@ github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3Q github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/aws/aws-sdk-go v1.53.8 h1:eoqGb1WOHIrCFKo1d51cMcnt1ralfLFaEqRkC5Zzv8k= -github.com/aws/aws-sdk-go v1.53.8/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= +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/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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +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/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= -github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/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.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= -github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +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= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= -github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= -github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/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.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/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-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= -github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +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/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= @@ -73,8 +70,8 @@ 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/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= -github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +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= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -86,18 +83,19 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY= -github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM= -github.com/onsi/gomega v1.31.0 h1:54UJxxj6cPInHS3a35wm6BK/F9nHYueZ1NVujHDrnXE= -github.com/onsi/gomega v1.31.0/go.mod h1:DW9aCi7U6Yi40wNVAvT6kzFnEVEI5n3DloYBiKiT6zk= +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/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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 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.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +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/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= @@ -111,37 +109,40 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= -golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= +golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= +golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= -golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= +golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= +golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= -golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= -golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= -golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= +golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -149,26 +150,25 @@ 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.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= -golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= +golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= -golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= +golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= +golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= +golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= +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= -golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= -golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= +golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= +golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= golang.org/x/tools/go/expect v0.1.0-deprecated h1:jY2C5HGYR5lqex3gEniOQL0r7Dq5+VGVgY1nudX5lXY= golang.org/x/tools/go/expect v0.1.0-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM= @@ -177,14 +177,14 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +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= +gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -194,31 +194,34 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.30.4 h1:XASIELmW8w8q0i1Y4124LqPoWMycLjyQti/fdYHYjCs= -k8s.io/api v0.30.4/go.mod h1:ZqniWRKu7WIeLijbbzetF4U9qZ03cg5IRwl8YVs8mX0= +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/apimachinery v0.30.4 h1:5QHQI2tInzr8LsT4kU/2+fSeibH1eIHswNx480cqIoY= -k8s.io/apimachinery v0.30.4/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc= -k8s.io/client-go v0.30.4 h1:eculUe+HPQoPbixfwmaSZGsKcOf7D288tH6hDAdd+wY= -k8s.io/client-go v0.30.4/go.mod h1:IBS0R/Mt0LHkNHF4E6n+SUDPG7+m2po6RZU7YHeOpzc= +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-20240228010128-51d4e06bde70 h1:NGrVE502P0s0/1hudf8zjgwki1X/TByhmAoILTarmzo= -k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70/go.mod h1:VH3AT8AaQOqiGjMF9p0/IM1Dj+82ZwjfxUP1IxaHE+8= +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/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= -k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= -k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= -k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= -k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +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= +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.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= -sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/kubectl-pg/go.mod b/kubectl-pg/go.mod index 96a183fa9..2e3ed3cbe 100644 --- a/kubectl-pg/go.mod +++ b/kubectl-pg/go.mod @@ -5,7 +5,7 @@ go 1.25 require ( github.com/spf13/cobra v1.8.1 github.com/spf13/viper v1.19.0 - github.com/zalando/postgres-operator v1.13.0 + github.com/zalando/postgres-operator v1.14.0 k8s.io/api v0.30.4 k8s.io/apiextensions-apiserver v0.25.9 k8s.io/apimachinery v0.30.4 diff --git a/kubectl-pg/go.sum b/kubectl-pg/go.sum index 2237a9e03..65d7acbee 100644 --- a/kubectl-pg/go.sum +++ b/kubectl-pg/go.sum @@ -129,8 +129,8 @@ github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSW github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/zalando/postgres-operator v1.13.0 h1:T9Mb+ZRQyTxXbagIK66GLVGCwM3661aX2lOkNpax4s8= -github.com/zalando/postgres-operator v1.13.0/go.mod h1:WiMEKzUny2lJHYle+7+D/5BhlvPn8prl76rEDYLsQAg= +github.com/zalando/postgres-operator v1.14.0 h1:C8+n26C8v6fPB1SNW+Y8X6oQoEHufzGJXJzYPlix+zw= +github.com/zalando/postgres-operator v1.14.0/go.mod h1:ZTHY3sVfHgLLRpTgyR/44JcumbACeJBjztr3o1yHBdc= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= diff --git a/manifests/complete-postgres-manifest.yaml b/manifests/complete-postgres-manifest.yaml index 44d317123..7677dca62 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-p2 + dockerImage: ghcr.io/zalando/spilo-17:4.0-p3 teamId: "acid" numberOfInstances: 2 users: # Application/Robot users diff --git a/manifests/configmap.yaml b/manifests/configmap.yaml index 9473ef5ec..b757c5392 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-p2 + docker_image: ghcr.io/zalando/spilo-17:4.0-p3 # downscaler_annotations: "deployment-time,downscaler/*" enable_admin_role_for_users: "true" enable_crd_registration: "true" diff --git a/manifests/operatorconfiguration.crd.yaml b/manifests/operatorconfiguration.crd.yaml index ded2477d7..ac8b01d22 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-p2" + default: "ghcr.io/zalando/spilo-17:4.0-p3" enable_crd_registration: type: boolean default: true diff --git a/manifests/postgresql-operator-default-configuration.yaml b/manifests/postgresql-operator-default-configuration.yaml index 570ebd338..72e5b7c62 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-p2 + docker_image: ghcr.io/zalando/spilo-17:4.0-p3 # enable_crd_registration: true # crd_categories: # - all diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index b06386f39..9cd750e84 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -1788,7 +1788,7 @@ func (c *Cluster) GetSwitchoverSchedule() string { } } - if (schedule == time.Time{}) || possibleSwitchover.Before(schedule) { + if (schedule.Equal(time.Time{})) || possibleSwitchover.Before(schedule) { schedule = possibleSwitchover } } diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index 93d992c4b..e05a54553 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -523,13 +523,14 @@ func (c *Cluster) nodeAffinity(nodeReadinessLabel map[string]string, nodeAffinit }, } } else { - if c.OpConfig.NodeReadinessLabelMerge == "OR" { + switch c.OpConfig.NodeReadinessLabelMerge { + case "OR": manifestTerms := nodeAffinityCopy.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms manifestTerms = append(manifestTerms, nodeReadinessSelectorTerm) nodeAffinityCopy.RequiredDuringSchedulingIgnoredDuringExecution = &v1.NodeSelector{ NodeSelectorTerms: manifestTerms, } - } else if c.OpConfig.NodeReadinessLabelMerge == "AND" { + case "AND": for i, nodeSelectorTerm := range nodeAffinityCopy.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms { manifestExpressions := nodeSelectorTerm.MatchExpressions manifestExpressions = append(manifestExpressions, matchExpressions...) @@ -1297,7 +1298,7 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef return nil, fmt.Errorf("could not generate resource requirements: %v", err) } - if spec.InitContainers != nil && len(spec.InitContainers) > 0 { + if len(spec.InitContainers) > 0 { if c.OpConfig.EnableInitContainers != nil && !(*c.OpConfig.EnableInitContainers) { c.logger.Warningf("initContainers specified but disabled in configuration - next statefulset creation would fail") } @@ -1400,7 +1401,7 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef // generate container specs for sidecars specified in the cluster manifest clusterSpecificSidecars := []v1.Container{} - if spec.Sidecars != nil && len(spec.Sidecars) > 0 { + if len(spec.Sidecars) > 0 { // warn if sidecars are defined, but globally disabled (does not apply to globally defined sidecars) if c.OpConfig.EnableSidecars != nil && !(*c.OpConfig.EnableSidecars) { c.logger.Warningf("sidecars specified but disabled in configuration - next statefulset creation would fail") @@ -1504,11 +1505,12 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef updateStrategy := appsv1.StatefulSetUpdateStrategy{Type: appsv1.OnDeleteStatefulSetStrategyType} var podManagementPolicy appsv1.PodManagementPolicyType - if c.OpConfig.PodManagementPolicy == "ordered_ready" { + switch c.OpConfig.PodManagementPolicy { + case "ordered_ready": podManagementPolicy = appsv1.OrderedReadyPodManagement - } else if c.OpConfig.PodManagementPolicy == "parallel" { + case "parallel": podManagementPolicy = appsv1.ParallelPodManagement - } else { + default: return nil, fmt.Errorf("could not set the pod management policy to the unknown value: %v", c.OpConfig.PodManagementPolicy) } diff --git a/pkg/cluster/majorversionupgrade.go b/pkg/cluster/majorversionupgrade.go index 675004982..8b671023f 100644 --- a/pkg/cluster/majorversionupgrade.go +++ b/pkg/cluster/majorversionupgrade.go @@ -237,7 +237,7 @@ func (c *Cluster) majorVersionUpgrade() error { isUpgradeSuccess := true numberOfPods := len(pods) - if allRunning && masterPod != nil { + if allRunning { c.logger.Infof("healthy cluster ready to upgrade, current: %d desired: %d", c.currentMajorVersion, desiredVersion) if c.currentMajorVersion < desiredVersion { defer func() error { diff --git a/pkg/cluster/pod.go b/pkg/cluster/pod.go index b28e03078..12a18b9b3 100644 --- a/pkg/cluster/pod.go +++ b/pkg/cluster/pod.go @@ -432,9 +432,10 @@ func (c *Cluster) recreatePods(pods []v1.Pod, switchoverCandidates []spec.Namesp } newRole := PostgresRole(newPod.Labels[c.OpConfig.PodRoleLabel]) - if newRole == Replica { + switch newRole { + case Replica: replicas = append(replicas, util.NameFromMeta(pod.ObjectMeta)) - } else if newRole == Master { + case Master: newMasterPod = newPod } } diff --git a/pkg/cluster/resources.go b/pkg/cluster/resources.go index 2c87efe47..ed3eb3d75 100644 --- a/pkg/cluster/resources.go +++ b/pkg/cluster/resources.go @@ -94,12 +94,12 @@ func (c *Cluster) listResources() error { func (c *Cluster) createStatefulSet() (*appsv1.StatefulSet, error) { c.setProcessName("creating statefulset") // check if it's allowed that spec contains initContainers - if c.Spec.InitContainers != nil && len(c.Spec.InitContainers) > 0 && + if len(c.Spec.InitContainers) > 0 && c.OpConfig.EnableInitContainers != nil && !(*c.OpConfig.EnableInitContainers) { return nil, fmt.Errorf("initContainers specified but disabled in configuration") } // check if it's allowed that spec contains sidecars - if c.Spec.Sidecars != nil && len(c.Spec.Sidecars) > 0 && + if len(c.Spec.Sidecars) > 0 && c.OpConfig.EnableSidecars != nil && !(*c.OpConfig.EnableSidecars) { return nil, fmt.Errorf("sidecar containers specified but disabled in configuration") } diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index c632e598c..6bf8c06b4 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -1122,13 +1122,14 @@ func (c *Cluster) updateSecret( // fetch user map to update later var userMap map[string]spec.PgUser var userKey string - if secretUsername == c.systemUsers[constants.SuperuserKeyName].Name { + switch secretUsername { + case c.systemUsers[constants.SuperuserKeyName].Name: userKey = constants.SuperuserKeyName userMap = c.systemUsers - } else if secretUsername == c.systemUsers[constants.ReplicationUserKeyName].Name { + case c.systemUsers[constants.ReplicationUserKeyName].Name: userKey = constants.ReplicationUserKeyName userMap = c.systemUsers - } else { + default: userKey = secretUsername userMap = c.pgUsers } diff --git a/pkg/controller/operator_config.go b/pkg/controller/operator_config.go index 5739f6314..3081a3d01 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-p2") + result.DockerImage = util.Coalesce(fromCRD.DockerImage, "ghcr.io/zalando/spilo-17:4.0-p3") result.Workers = util.CoalesceUInt32(fromCRD.Workers, 8) result.MinInstances = fromCRD.MinInstances result.MaxInstances = fromCRD.MaxInstances diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index 30b967beb..886da7173 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -175,7 +175,7 @@ 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-p2"` + DockerImage string `name:"docker_image" default:"ghcr.io/zalando/spilo-17:4.0-p3"` 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 1af4c50ed098a26527e3229ddb8ce69d6c4a524d Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Tue, 21 Oct 2025 11:56:33 +0200 Subject: [PATCH 12/58] bump to v1.15.0 (#2965) * bump to v1.15.0 * more linter hints * update dependencies of kubectl-pg plugin --- LICENSE | 2 +- README.md | 8 +- charts/postgres-operator-ui/Chart.yaml | 4 +- charts/postgres-operator-ui/index.yaml | 58 ++--- .../postgres-operator-ui-1.15.0.tgz | Bin 0 -> 5089 bytes .../postgres-operator-ui-1.9.0.tgz | Bin 4926 -> 0 bytes charts/postgres-operator-ui/values.yaml | 2 +- charts/postgres-operator/Chart.yaml | 4 +- charts/postgres-operator/index.yaml | 56 ++--- .../postgres-operator-1.15.0.tgz | Bin 0 -> 18284 bytes .../postgres-operator-1.9.0.tgz | Bin 17544 -> 0 bytes charts/postgres-operator/values.yaml | 4 +- e2e/Dockerfile | 2 +- kubectl-pg/cmd/connect.go | 3 +- kubectl-pg/cmd/version.go | 2 +- kubectl-pg/go.mod | 75 ++++--- kubectl-pg/go.sum | 201 ++++++++---------- manifests/configmap.yaml | 2 +- manifests/operatorconfiguration.crd.yaml | 2 +- manifests/postgres-operator.yaml | 2 +- ...gresql-operator-default-configuration.yaml | 2 +- pkg/cluster/majorversionupgrade.go | 1 - pkg/controller/operator_config.go | 2 +- pkg/util/config/config.go | 2 +- pkg/util/volumes/ebs.go | 9 +- ui/app/package.json | 2 +- ui/manifests/deployment.yaml | 2 +- 27 files changed, 211 insertions(+), 236 deletions(-) create mode 100644 charts/postgres-operator-ui/postgres-operator-ui-1.15.0.tgz delete mode 100644 charts/postgres-operator-ui/postgres-operator-ui-1.9.0.tgz create mode 100644 charts/postgres-operator/postgres-operator-1.15.0.tgz delete mode 100644 charts/postgres-operator/postgres-operator-1.9.0.tgz diff --git a/LICENSE b/LICENSE index b21099078..2141e8bcb 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2024 Zalando SE +Copyright (c) 2025 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/README.md b/README.md index fdf2518ba..8820f04a5 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ pipelines with no access to Kubernetes API directly, promoting infrastructure as * Live volume resize without pod restarts (AWS EBS, PVC) * Database connection pooling with PGBouncer * Support fast in place major version upgrade. Supports global upgrade of all clusters. +* Pod protection during boostrap phase and configurable maintenance windows * Restore and cloning Postgres clusters on AWS, GCS and Azure * Additionally logical backups to S3 or GCS bucket can be configured * Standby cluster from S3 or GCS WAL archive @@ -32,7 +33,7 @@ pipelines with no access to Kubernetes API directly, promoting infrastructure as * Streaming replication cluster via Patroni * Point-In-Time-Recovery with [pg_basebackup](https://www.postgresql.org/docs/17/app-pgbasebackup.html) / -[WAL-E](https://github.com/wal-e/wal-e) via [Spilo](https://github.com/zalando/spilo) +[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), [pgextwlist](https://github.com/dimitri/pgextwlist), @@ -41,12 +42,17 @@ pipelines with no access to Kubernetes API directly, promoting infrastructure as [decoderbufs](https://github.com/debezium/postgres-decoderbufs), [hypopg](https://github.com/HypoPG/hypopg), [pg_cron](https://github.com/citusdata/pg_cron), +[pg_repack](https://github.com/reorg/pg_repack), [pg_partman](https://github.com/pgpartman/pg_partman), [pg_stat_kcache](https://github.com/powa-team/pg_stat_kcache), +[pg_audit](https://github.com/pgaudit/pgaudit), +[pgfaceting](https://github.com/cybertec-postgresql/pgfaceting), [pgq](https://github.com/pgq/pgq), [pgvector](https://github.com/pgvector/pgvector), [plpgsql_check](https://github.com/okbob/plpgsql_check), +[plproxy](https://github.com/plproxy/plproxy), [postgis](https://postgis.net/), +[roaringbitmap](https://github.com/ChenHuajun/pg_roaringbitmap), [set_user](https://github.com/pgaudit/set_user) and [timescaledb](https://github.com/timescale/timescaledb) diff --git a/charts/postgres-operator-ui/Chart.yaml b/charts/postgres-operator-ui/Chart.yaml index f4e2adf95..c9e28e56e 100644 --- a/charts/postgres-operator-ui/Chart.yaml +++ b/charts/postgres-operator-ui/Chart.yaml @@ -1,7 +1,7 @@ apiVersion: v2 name: postgres-operator-ui -version: 1.14.0 -appVersion: 1.14.0 +version: 1.15.0 +appVersion: 1.15.0 home: https://github.com/zalando/postgres-operator description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience keywords: diff --git a/charts/postgres-operator-ui/index.yaml b/charts/postgres-operator-ui/index.yaml index dab9594e9..3ad4ac84f 100644 --- a/charts/postgres-operator-ui/index.yaml +++ b/charts/postgres-operator-ui/index.yaml @@ -1,9 +1,32 @@ apiVersion: v1 entries: postgres-operator-ui: + - apiVersion: v2 + appVersion: 1.15.0 + created: "2025-10-16T11:34:57.912432565+02:00" + description: Postgres Operator UI provides a graphical interface for a convenient + database-as-a-service user experience + digest: d82b5fb7c3d4fd8b106343b2f9472cba5e6050315ab3c520a79366f2b2f20c7a + home: https://github.com/zalando/postgres-operator + keywords: + - postgres + - operator + - ui + - cloud-native + - patroni + - spilo + maintainers: + - email: opensource@zalando.de + name: Zalando + name: postgres-operator-ui + sources: + - https://github.com/zalando/postgres-operator + urls: + - postgres-operator-ui-1.15.0.tgz + version: 1.15.0 - apiVersion: v2 appVersion: 1.14.0 - created: "2024-12-23T11:26:07.721761867+01:00" + created: "2025-10-16T11:34:57.906677165+02:00" description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience digest: e87ed898079a852957a67a4caf3fbd27b9098e413f5d961b7a771a6ae8b3e17c @@ -26,7 +49,7 @@ entries: version: 1.14.0 - apiVersion: v2 appVersion: 1.13.0 - created: "2024-12-23T11:26:07.719409282+01:00" + created: "2025-10-16T11:34:57.904106882+02:00" description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience digest: e0444e516b50f82002d1a733527813c51759a627cefdd1005cea73659f824ea8 @@ -49,7 +72,7 @@ entries: version: 1.13.0 - apiVersion: v2 appVersion: 1.12.2 - created: "2024-12-23T11:26:07.717202918+01:00" + created: "2025-10-16T11:34:57.901526106+02:00" description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience digest: cbcef400c23ccece27d97369ad629278265c013e0a45c0b7f33e7568a082fedd @@ -72,7 +95,7 @@ entries: version: 1.12.2 - apiVersion: v2 appVersion: 1.11.0 - created: "2024-12-23T11:26:07.714792146+01:00" + created: "2025-10-16T11:34:57.898843691+02:00" description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience digest: a45f2284045c2a9a79750a36997386444f39b01ac722b17c84b431457577a3a2 @@ -95,7 +118,7 @@ entries: version: 1.11.0 - apiVersion: v2 appVersion: 1.10.1 - created: "2024-12-23T11:26:07.712194397+01:00" + created: "2025-10-16T11:34:57.896283083+02:00" description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience digest: 2e5e7a82aebee519ec57c6243eb8735124aa4585a3a19c66ffd69638fbeb11ce @@ -116,27 +139,4 @@ entries: urls: - postgres-operator-ui-1.10.1.tgz version: 1.10.1 - - apiVersion: v2 - appVersion: 1.9.0 - created: "2024-12-23T11:26:07.723891496+01:00" - description: Postgres Operator UI provides a graphical interface for a convenient - database-as-a-service user experience - digest: df434af6c8b697fe0631017ecc25e3c79e125361ae6622347cea41a545153bdc - home: https://github.com/zalando/postgres-operator - keywords: - - postgres - - operator - - ui - - cloud-native - - patroni - - spilo - maintainers: - - email: opensource@zalando.de - name: Zalando - name: postgres-operator-ui - sources: - - https://github.com/zalando/postgres-operator - urls: - - postgres-operator-ui-1.9.0.tgz - version: 1.9.0 -generated: "2024-12-23T11:26:07.709192608+01:00" +generated: "2025-10-16T11:34:57.893034861+02:00" diff --git a/charts/postgres-operator-ui/postgres-operator-ui-1.15.0.tgz b/charts/postgres-operator-ui/postgres-operator-ui-1.15.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..95fe866f6e03a19fcdd5161e845db9a1e3565643 GIT binary patch literal 5089 zcmV<76CUgziwG0|00000|0w_~VMtOiV@ORlOnEsqVl!4SWK%V1T2nbTPgYhoO;>Dc zVQyr3R8em|NM&qo0PKBjbK^FWa6a=_^wPWaB(ot!QZJNLo2|$3I4M^q9+zJxx3{&W zg2<+Xm;xC9)QmIZ`|Vc%NQu;oCC@mX*<0a*MG}oh188(N8Vxwlgq&~`PL?AlGUHA` zTPIV(rF%za=~qvYJkRrbosRzXJa6^a>-9Qc`CTvU`0bw8>we|=-FD!81>Td?z9SVv z68@F<$#tzZ?%yPloW4OWXvT&x4=j@Bi&x+EyRK)&C?ZaCslQ#6>;kV#c7dBS$ayxW zF$y3s;Uu5Zh@?Q7M4pfcA;~xph%z=uMv+MiASRNGi9m-4hd2WHoJI(RKo0mWM^2GN zXic*j4q+-~E{3hvgvx0#cB5?8`kkbN#aU~Od97Q#dzc50A6C05{@t@oRV>h0%Qhbgq#S0v62ZH z++_s^iL*Q<5`}A79P^Y$EbW(7Hf&!h930nH^&;sB%p+fSzAxx$b zcWI`-<(Z%|Q(w2vBQ*s9B$*6hUd|+dyhziFETz%i5bU$$Jd+n31u|(XI$mqBN7D2T zM2-9 zm@Ea|Z81j9P<0N?T5%SMR+O;_bEzLn!PL@~i`G#|7-bVje^MLBnuVGiKKRtgVEM#+G`uXhVi`SRe$LH5W_+?5{T>o%fbe@{Unw9Jqw)!Dyn!SZN#kZ7ATAGh&##HM{ ztU}FdlBUa9EwrfN6O`xGryT(r3RCv^j9^|*2N@R*kC0!VDX+-^u7>*!uj4{|m4Zd!&8n!%(|DOP4 z^QAnujvf+Il89T;qU=|Uq@!#+hvZteE3#WLCX}IrY)*N`X2_&d;dK^rt@UsX924Zq zIILwlB&V&n8NW?45<81=ZRJ_)%>CBUrV3R^mv_?v-Y3=(oA>yPKg>lYXnM3*+lEl8M#FejO1dP zNzH|zTB`(AsY?o~geohg*YPh`8eJ*d;p*b-)$7q!d-VPB$xk;Iql?SaAI|08pV!(xf&gxoSa@=jea`)$LQ>3y~GbZn+?kk07l*Xgm;&iEWcKk@!zTrTFL=a z0NzqLRrP?(Zjt@HR`M3_woy>0@9OmA^7Og}^jT=!Ohui}m#04*-@LjWU7r4U_WJy@ z5V?VbZ4C1L&B;%v*G&fbn`pzhh;C6T{Tjpgt|8g~zNVH+Jy6*Ksx|U1S3V*Q+dK(Jr;ya>bn49s5WjHV=aw>-KPrGF+F(*^?Vgw#IB?B9@ z1$AU9D&}3_FIm|!=x3xtZyiC-X~wC%1KlpYcBxtWN`YAwNRxc)2#%A4GF8^Bcw!(z z(1}(P))8Dj!XnqhX>^>yp-gT^F+}T#l(jg7#SUSTI@K4edKi|Bp|~4TWv+;iqp1+c zok>AsY^ll}fhk6sEK9v#>DP=`^<8Hv8nQpKzAb&A6uH#xm5Ul$*7Dhm|L0!#9+FNQ z{XdJ-^ov@W0(IoSo8meo`3NFJWIOHV2+$7N}UtA?)lM5P<4@1s*x9L zXj!0jMkG^koW+9YyZ7(Gy~GrWKzFg`f%|)ypu!sqa8j^Ip1Vy^qA;k!71M#e(fy>? zZQ#QFz5TiS?Eg~QG5<$XOmpPImHEfJf_KdSpyRFjf5XrV4)gyx();(V7ci%@p>~rc zG(}~5e>YRz5>0UkFIxBamV&ZQ-{pkG3+H8JpKDK&Yn5;gm}UNhv)EaJYS*)CHXN1I z0QZgAd{B_D=g2vY5k4qWm_^WQ>qk1fDw2f01KU|5Ll*02L+puh_K`&$Ns*>^@LNGr zvs55?uAAXnzo5ac;ialWHA%o2BT@(ikjcc7Hz>S17NJ_7VD7|sU9jR7xX{y#WdZ9%G5KVkZ?SB1Sc`ujULz6|< zR+)?LtzfnS?A8CNSYrCKZp-ky{fD?l=b~E-mWEbY(%Sg?XaaLinM}a`i*WuT>=kUL z-p4TAw&xXQtk$xm^j1G+NSjXe^V;ApI{9nLqO^#2Gll!jVi1~W^_ajzji|Azk_P48 zq+-*$U%)`=#mv6#(?#s9161c9^IUFJ!w2A)r^?u{|7wFhvL7{apIZQb=Cniqm;P;@ zrTFpN!8`Q7-|cmq`ake`fq&5d&yn80uWJTACXrk2lilB2_23YlMG861IQ?C(|h& zm3FQL${LiS+R`;~g37FZRU4lt9HEctsj7yOx*8Lylr{)`Qc^#LSRFj^-Z7yxRgX90 z?rMFp8g@~Ymf1hZECm-n0HZpl68cU2rOwjPKILGW$e&eZ_-Puns;*|-twsPZ0oP-L zD+G%MZNu>ab~Y1W8MVCZtt$A)E*oJla!PuJVAptGe{!qXZ@9m2ZOE*Gz~TOWxRx!+ z1n%$Gs=25IfUlKVMu*p#%S0{J+G5(XXr&1Q>U({^0RfQnOlDD*4#B=Yxu}!Ek;Igt z5EncfV-xdK%KS%^!&PKNPKRK(rbtpbwO4aD(?A)Ol%y{)C3jaCWh@p$=r%wkoncnU zMXui@WK|U|zMKhu(CkHI^VL2qdE)ruVsvx)YPC^iELv@tkj;f_B8i*0 zYsg+-oL(MZzrGwDUz|MxYcu*7KEQ89CUJKyC$Da(6xMYZ^@Nc?sS_J)94DW4J{H&nQ-;VS)W z2Up#kjZR*l|8Vx>{{8ZP195hoMPigvc54sqDzs}&x423i$tfyer8|+e^m0J9>`mQa z$4rb&F!@MGPT9m(!rQxd-~0DlN|Xlx#b|DL&oq zF`4FtG_alU0pKX(Xu!H*cRQFloMHg|PJ0Vz!VxPX+n(ob2Wrsh2bYffitPJ4B_!3&Te*X+Kv zQ{VDm0)41d`F6#{v-WvC7+MEH)xjHx#btZ$wZJEZtDPmc60)N>{A~3oonL z5Jz0ob6X46I_h&T^*5#+{+}vt{F9CX@9_V$J3)Ib{wp~6|DPqTt`W=eE)Nn9oxcVeT8D^qs5jdM8Kgnh};RyEMzw=#x;CME;CnP6h zno>zoxIYzREL~Ubk5pFCAkNCZ1w&b$ma6SnEW&}dRM&aZ>c%LE-wZ*j7r}QDnN~PW zSR*xzGw#fc%jW%*<>2h?XFB-t)}1{S?~_%J&-x*o&7Pjl5^*_r#>2B^j;)@nF{Rf} z+tj@h4-~CcyQM;zmJO0-;1`Re?U2>ko|;Q&QAU5S8wSqm+G(3VsD9{26Njm$BLZjr^SPNLfByJp%ZJeo;+kv3SMteB0FoAQ-t-((?H;%UG0{YTn=e$Vf%*?<1Q{(F|xxc{lz`YUzoVc+jT zd!d>ePiQYZ)9zwz`@$)A3Tj41!!Tbiv$Ydef5V+Vz(>nN?ak1QFxjwmA+avv$jlm~kcXRxGL!)#jsRhtyP(Ay}|MI-TN2-Ax z^4|-6ZzcZ24|<35U(b@7@qa6aK{iJMuBwSD@at(~{SK?xJ*3m8$G~lz|E}r*I$;^d zr{J+&^S>Lc+JD_{aG3wkl8)elNQs>3tA@;&o6Cg81x;hhCXkcpmP}AAZU7WTt}O@< zQ%qBsq}kZS9#J;=7C5H*UQNATt$imfwvK?|#9W^Bb*>k&7#pMNPv5xknx%J;Flxq_H-VYw43Tl%jd-*U&l^OpWweV9&K>QD75 z=B%|uq!y$_uFt*->xC=c=GF^$Om3|guAJr8i~q5X;0@t4D+HXqJQbFk^XylQq~+2W zNz1_T>{rX3izth+^>4I<+&KT0efsXX?*H{YKU|yt?OyjV|DPkRD0XC$Q{!XX#2MB3 zn#P^MrQ)jJ8{E-%YgV^6#zU|JFX%Xa-~{b!-ya6Op%=Pg;D27_)$dLijVBxxt(I0(lBZ_vk%-%h&mpy!W6 z5{5qM5$uoK=!cQ9B_4l=F_>$+%6p8fMOt~6v3YI4>N}0iL*rg!sGOAgZe#Oi?l%S< zEJ~X}c{}ep23Wo8c==_^Wvc*of6ddkep|_vY8Y4ff*KFz|wb-~TfCH5x~lbUZH#+EIiN24Or1+r0sa zd%++G`$4Z8d43x`(&vW@l3`jT#yB=xx27|EO?{zwzq&*mWK8eC082e$=?~FTbztau7 zVMy8}IPmK;@vFZ@zWRI1SI_nL;@7|*blQV2)P4=Hiy`R;1jmS7e}GXpjCw(*jpKgU z?)pK8kWRPXC2islqFyga;@+Uw?k8g$`+kY+z^{Kizk05}kzWlL?!m8vJ(pYU{=n}H zzEJTRC!KbOB)zCV?hV2Oyjgo(6GlinKIwM4ez!f2 z56i7*V%NcD>8f~e@qZMyaiBNw2SLwU7^I)u`*BEzbV!HvpPT+S00960_JA*Y0B`^R DDc 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 diff --git a/charts/postgres-operator-ui/values.yaml b/charts/postgres-operator-ui/values.yaml index 9923ff023..2e729ce76 100644 --- a/charts/postgres-operator-ui/values.yaml +++ b/charts/postgres-operator-ui/values.yaml @@ -8,7 +8,7 @@ replicaCount: 1 image: registry: ghcr.io repository: zalando/postgres-operator-ui - tag: v1.14.0 + tag: v1.15.0 pullPolicy: "IfNotPresent" # Optionally specify an array of imagePullSecrets. diff --git a/charts/postgres-operator/Chart.yaml b/charts/postgres-operator/Chart.yaml index 35852c488..40ffe22bc 100644 --- a/charts/postgres-operator/Chart.yaml +++ b/charts/postgres-operator/Chart.yaml @@ -1,7 +1,7 @@ apiVersion: v2 name: postgres-operator -version: 1.14.0 -appVersion: 1.14.0 +version: 1.15.0 +appVersion: 1.15.0 home: https://github.com/zalando/postgres-operator description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes keywords: diff --git a/charts/postgres-operator/index.yaml b/charts/postgres-operator/index.yaml index 4da98d70a..025ad2b9d 100644 --- a/charts/postgres-operator/index.yaml +++ b/charts/postgres-operator/index.yaml @@ -1,9 +1,31 @@ apiVersion: v1 entries: postgres-operator: + - apiVersion: v2 + appVersion: 1.15.0 + created: "2025-10-16T11:35:38.533627038+02:00" + description: Postgres Operator creates and manages PostgreSQL clusters running + in Kubernetes + digest: 002dd47647bf51fbba023bd1762d807be478cf37de7a44b80cd01ac1f20bd94a + home: https://github.com/zalando/postgres-operator + keywords: + - postgres + - operator + - cloud-native + - patroni + - spilo + maintainers: + - email: opensource@zalando.de + name: Zalando + name: postgres-operator + sources: + - https://github.com/zalando/postgres-operator + urls: + - postgres-operator-1.15.0.tgz + version: 1.15.0 - apiVersion: v2 appVersion: 1.14.0 - created: "2024-12-23T11:25:32.596716566+01:00" + created: "2025-10-16T11:35:38.52489216+02:00" description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes digest: 36e1571f3f455b213f16cdda7b1158648e8e84deb804ba47ed6b9b6d19263ba8 @@ -25,7 +47,7 @@ entries: version: 1.14.0 - apiVersion: v2 appVersion: 1.13.0 - created: "2024-12-23T11:25:32.591136261+01:00" + created: "2025-10-16T11:35:38.517347652+02:00" description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes digest: a839601689aea0a7e6bc0712a5244d435683cf3314c95794097ff08540e1dfef @@ -47,7 +69,7 @@ entries: version: 1.13.0 - apiVersion: v2 appVersion: 1.12.2 - created: "2024-12-23T11:25:32.585419709+01:00" + created: "2025-10-16T11:35:38.510819005+02:00" description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes digest: 65858d14a40d7fd90c32bd9fc60021acc9555c161079f43a365c70171eaf21d8 @@ -69,7 +91,7 @@ entries: version: 1.12.2 - apiVersion: v2 appVersion: 1.11.0 - created: "2024-12-23T11:25:32.580077286+01:00" + created: "2025-10-16T11:35:38.503781253+02:00" description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes digest: 3914b5e117bda0834f05c9207f007e2ac372864cf6e86dcc2e1362bbe46c14d9 @@ -91,7 +113,7 @@ entries: version: 1.11.0 - apiVersion: v2 appVersion: 1.10.1 - created: "2024-12-23T11:25:32.574641578+01:00" + created: "2025-10-16T11:35:38.494366224+02:00" description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes digest: cc3baa41753da92466223d0b334df27e79c882296577b404a8e9071411fcf19c @@ -111,26 +133,4 @@ entries: urls: - postgres-operator-1.10.1.tgz version: 1.10.1 - - apiVersion: v2 - appVersion: 1.9.0 - created: "2024-12-23T11:25:32.604748814+01:00" - description: Postgres Operator creates and manages PostgreSQL clusters running - in Kubernetes - digest: 64df90c898ca591eb3a330328173ffaadfbf9ddd474d8c42ed143edc9e3f4276 - home: https://github.com/zalando/postgres-operator - keywords: - - postgres - - operator - - cloud-native - - patroni - - spilo - maintainers: - - email: opensource@zalando.de - name: Zalando - name: postgres-operator - sources: - - https://github.com/zalando/postgres-operator - urls: - - postgres-operator-1.9.0.tgz - version: 1.9.0 -generated: "2024-12-23T11:25:32.568598763+01:00" +generated: "2025-10-16T11:35:38.487472753+02:00" diff --git a/charts/postgres-operator/postgres-operator-1.15.0.tgz b/charts/postgres-operator/postgres-operator-1.15.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..e029732ae3484e9588268943211611527ae8f253 GIT binary patch literal 18284 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 diff --git a/charts/postgres-operator/values.yaml b/charts/postgres-operator/values.yaml index f45275a4c..d66aa5608 100644 --- a/charts/postgres-operator/values.yaml +++ b/charts/postgres-operator/values.yaml @@ -1,7 +1,7 @@ image: registry: ghcr.io repository: zalando/postgres-operator - tag: v1.14.0 + tag: v1.15.0 pullPolicy: "IfNotPresent" # Optionally specify an array of imagePullSecrets. @@ -364,7 +364,7 @@ configLogicalBackup: # logical_backup_memory_request: "" # image for pods of the logical backup job (example runs pg_dumpall) - logical_backup_docker_image: "ghcr.io/zalando/postgres-operator/logical-backup:v1.14.0" + logical_backup_docker_image: "ghcr.io/zalando/postgres-operator/logical-backup:v1.15.0" # path of google cloud service account json file # logical_backup_google_application_credentials: "" diff --git a/e2e/Dockerfile b/e2e/Dockerfile index cfbc9eff7..4ad55c136 100644 --- a/e2e/Dockerfile +++ b/e2e/Dockerfile @@ -15,7 +15,7 @@ RUN apt-get update \ curl \ vim \ && pip3 install --no-cache-dir -r requirements.txt \ - && curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.24.3/bin/linux/amd64/kubectl \ + && curl -LO https://dl.k8s.io/release/v1.32.9/bin/linux/amd64/kubectl \ && chmod +x ./kubectl \ && mv ./kubectl /usr/local/bin/kubectl \ && apt-get clean \ diff --git a/kubectl-pg/cmd/connect.go b/kubectl-pg/cmd/connect.go index 2c6d87835..a7643ca05 100644 --- a/kubectl-pg/cmd/connect.go +++ b/kubectl-pg/cmd/connect.go @@ -23,6 +23,7 @@ THE SOFTWARE. package cmd import ( + "context" "log" "os" user "os/user" @@ -121,7 +122,7 @@ func connect(clusterName string, master bool, replica string, psql bool, user st log.Fatal(err) } - err = exec.Stream(remotecommand.StreamOptions{ + err = exec.StreamWithContext(context.TODO(), remotecommand.StreamOptions{ Stdin: os.Stdin, Stdout: os.Stdout, Stderr: os.Stderr, diff --git a/kubectl-pg/cmd/version.go b/kubectl-pg/cmd/version.go index e9a1e8056..23cc55422 100644 --- a/kubectl-pg/cmd/version.go +++ b/kubectl-pg/cmd/version.go @@ -65,7 +65,7 @@ func version(namespace string) { operatorDeployment := getPostgresOperator(client) if operatorDeployment.Name == "" { - log.Fatal("make sure zalando's postgres operator is running") + log.Fatalf("make sure zalando's postgres operator is running in namespace %s", namespace) } operatorImage := operatorDeployment.Spec.Template.Spec.Containers[0].Image imageDetails := strings.Split(operatorImage, ":") diff --git a/kubectl-pg/go.mod b/kubectl-pg/go.mod index 2e3ed3cbe..9a2faed99 100644 --- a/kubectl-pg/go.mod +++ b/kubectl-pg/go.mod @@ -3,72 +3,69 @@ module github.com/zalando/postgres-operator/kubectl-pg go 1.25 require ( - github.com/spf13/cobra v1.8.1 - github.com/spf13/viper v1.19.0 + github.com/spf13/cobra v1.10.1 + github.com/spf13/viper v1.21.0 github.com/zalando/postgres-operator v1.14.0 - k8s.io/api v0.30.4 + k8s.io/api v0.32.9 k8s.io/apiextensions-apiserver v0.25.9 - k8s.io/apimachinery v0.30.4 - k8s.io/client-go v0.30.4 + k8s.io/apimachinery v0.32.9 + k8s.io/client-go v0.32.9 ) 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/fsnotify/fsnotify v1.7.0 // indirect - github.com/go-logr/logr v1.4.1 // indirect - github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/fsnotify/fsnotify v1.9.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.22.3 // indirect + github.com/go-openapi/swag v0.23.0 // indirect + github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/uuid v1.4.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect - github.com/imdario/mergo v0.3.6 // indirect github.com/inconshreveable/mousetrap v1.1.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/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/moby/spdystream v0.2.0 // 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/motomux/pretty v0.0.0-20161209205251-b2aad2c9a95d // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect - github.com/pelletier/go-toml/v2 v2.2.2 // indirect - github.com/sagikazarmark/locafero v0.4.0 // indirect - github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/sagikazarmark/locafero v0.11.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect - github.com/sourcegraph/conc v0.3.0 // indirect - github.com/spf13/afero v1.11.0 // indirect - github.com/spf13/cast v1.6.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect + github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect + github.com/spf13/afero v1.15.0 // indirect + github.com/spf13/cast v1.10.0 // indirect + github.com/spf13/pflag v1.0.10 // indirect github.com/subosito/gotenv v1.6.0 // indirect - go.uber.org/multierr v1.11.0 // indirect + github.com/x448/float16 v0.8.4 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/crypto v0.31.0 // indirect - golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect - golang.org/x/net v0.25.0 // indirect - golang.org/x/oauth2 v0.18.0 // indirect - golang.org/x/sys v0.28.0 // indirect + golang.org/x/net v0.30.0 // indirect + golang.org/x/oauth2 v0.23.0 // indirect + golang.org/x/sys v0.29.0 // indirect golang.org/x/term v0.27.0 // indirect - golang.org/x/text v0.21.0 // indirect - golang.org/x/time v0.5.0 // indirect - google.golang.org/appengine v1.6.8 // indirect - google.golang.org/protobuf v1.33.0 // indirect + golang.org/x/text v0.28.0 // indirect + golang.org/x/time v0.7.0 // indirect + google.golang.org/protobuf v1.35.1 // indirect + gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/klog/v2 v2.120.1 // indirect - k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect - k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect - sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect - sigs.k8s.io/yaml v1.3.0 // indirect + k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect + k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect + sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/kubectl-pg/go.sum b/kubectl-pg/go.sum index 65d7acbee..2bb48c85a 100644 --- a/kubectl-pg/go.sum +++ b/kubectl-pg/go.sum @@ -1,6 +1,6 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +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= @@ -10,44 +10,42 @@ 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/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= -github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fxamacker/cbor/v2 v2.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= +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= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= -github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= +github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= -github.com/google/go-cmp v0.5.5/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.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= -github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= -github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -63,14 +61,10 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= -github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= -github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +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= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -82,151 +76,128 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY= -github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM= -github.com/onsi/gomega v1.31.0 h1:54UJxxj6cPInHS3a35wm6BK/F9nHYueZ1NVujHDrnXE= -github.com/onsi/gomega v1.31.0/go.mod h1:DW9aCi7U6Yi40wNVAvT6kzFnEVEI5n3DloYBiKiT6zk= -github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= -github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +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/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +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= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +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/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= -github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= -github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= -github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= +github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= -github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= -github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= -github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= -github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= -github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= -github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= +github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= +github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= +github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= +github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= +github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= +github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= +github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= +github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= +github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zalando/postgres-operator v1.14.0 h1:C8+n26C8v6fPB1SNW+Y8X6oQoEHufzGJXJzYPlix+zw= github.com/zalando/postgres-operator v1.14.0/go.mod h1:ZTHY3sVfHgLLRpTgyR/44JcumbACeJBjztr3o1yHBdc= -go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= -go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o= -golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= -golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= +golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= -golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= +golang.org/x/time v0.7.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-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.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= +golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= -google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/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-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= +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/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.30.4 h1:XASIELmW8w8q0i1Y4124LqPoWMycLjyQti/fdYHYjCs= -k8s.io/api v0.30.4/go.mod h1:ZqniWRKu7WIeLijbbzetF4U9qZ03cg5IRwl8YVs8mX0= +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/apimachinery v0.30.4 h1:5QHQI2tInzr8LsT4kU/2+fSeibH1eIHswNx480cqIoY= -k8s.io/apimachinery v0.30.4/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc= -k8s.io/client-go v0.30.4 h1:eculUe+HPQoPbixfwmaSZGsKcOf7D288tH6hDAdd+wY= -k8s.io/client-go v0.30.4/go.mod h1:IBS0R/Mt0LHkNHF4E6n+SUDPG7+m2po6RZU7YHeOpzc= -k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= -k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= -k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= -k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= -k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= -sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= -sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= +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/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-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y= +k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4= +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/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/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA= +sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= +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/manifests/configmap.yaml b/manifests/configmap.yaml index b757c5392..2c0ba9151 100644 --- a/manifests/configmap.yaml +++ b/manifests/configmap.yaml @@ -86,7 +86,7 @@ data: # logical_backup_cpu_limit: "" # logical_backup_cpu_request: "" logical_backup_cronjob_environment_secret: "" - logical_backup_docker_image: "ghcr.io/zalando/postgres-operator/logical-backup:v1.14.0" + logical_backup_docker_image: "ghcr.io/zalando/postgres-operator/logical-backup:v1.15.0" # logical_backup_google_application_credentials: "" logical_backup_job_prefix: "logical-backup-" # logical_backup_memory_limit: "" diff --git a/manifests/operatorconfiguration.crd.yaml b/manifests/operatorconfiguration.crd.yaml index ac8b01d22..6556b333c 100644 --- a/manifests/operatorconfiguration.crd.yaml +++ b/manifests/operatorconfiguration.crd.yaml @@ -508,7 +508,7 @@ spec: pattern: '^(\d+m|\d+(\.\d{1,3})?)$' logical_backup_docker_image: type: string - default: "ghcr.io/zalando/postgres-operator/logical-backup:v1.14.0" + default: "ghcr.io/zalando/postgres-operator/logical-backup:v1.15.0" logical_backup_google_application_credentials: type: string logical_backup_job_prefix: diff --git a/manifests/postgres-operator.yaml b/manifests/postgres-operator.yaml index e3f77657e..364cbb6dc 100644 --- a/manifests/postgres-operator.yaml +++ b/manifests/postgres-operator.yaml @@ -19,7 +19,7 @@ spec: serviceAccountName: postgres-operator containers: - name: postgres-operator - image: ghcr.io/zalando/postgres-operator:v1.14.0 + image: ghcr.io/zalando/postgres-operator:v1.15.0 imagePullPolicy: IfNotPresent resources: requests: diff --git a/manifests/postgresql-operator-default-configuration.yaml b/manifests/postgresql-operator-default-configuration.yaml index 72e5b7c62..389d9325a 100644 --- a/manifests/postgresql-operator-default-configuration.yaml +++ b/manifests/postgresql-operator-default-configuration.yaml @@ -168,7 +168,7 @@ configuration: # logical_backup_cpu_request: "" # logical_backup_memory_limit: "" # logical_backup_memory_request: "" - logical_backup_docker_image: "ghcr.io/zalando/postgres-operator/logical-backup:v1.14.0" + logical_backup_docker_image: "ghcr.io/zalando/postgres-operator/logical-backup:v1.15.0" # logical_backup_google_application_credentials: "" logical_backup_job_prefix: "logical-backup-" logical_backup_provider: "s3" diff --git a/pkg/cluster/majorversionupgrade.go b/pkg/cluster/majorversionupgrade.go index 8b671023f..b80cbaa09 100644 --- a/pkg/cluster/majorversionupgrade.go +++ b/pkg/cluster/majorversionupgrade.go @@ -16,7 +16,6 @@ import ( // VersionMap Map of version numbers var VersionMap = map[string]int{ - "12": 120000, "13": 130000, "14": 140000, "15": 150000, diff --git a/pkg/controller/operator_config.go b/pkg/controller/operator_config.go index 3081a3d01..5cf1d7e40 100644 --- a/pkg/controller/operator_config.go +++ b/pkg/controller/operator_config.go @@ -180,7 +180,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur // logical backup config result.LogicalBackupSchedule = util.Coalesce(fromCRD.LogicalBackup.Schedule, "30 00 * * *") - result.LogicalBackupDockerImage = util.Coalesce(fromCRD.LogicalBackup.DockerImage, "ghcr.io/zalando/postgres-operator/logical-backup:v1.14.0") + result.LogicalBackupDockerImage = util.Coalesce(fromCRD.LogicalBackup.DockerImage, "ghcr.io/zalando/postgres-operator/logical-backup:v1.15.0") result.LogicalBackupProvider = util.Coalesce(fromCRD.LogicalBackup.BackupProvider, "s3") result.LogicalBackupAzureStorageAccountName = fromCRD.LogicalBackup.AzureStorageAccountName result.LogicalBackupAzureStorageAccountKey = fromCRD.LogicalBackup.AzureStorageAccountKey diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index 886da7173..9fadd6a5b 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -127,7 +127,7 @@ type Scalyr struct { // LogicalBackup defines configuration for logical backup type LogicalBackup struct { LogicalBackupSchedule string `name:"logical_backup_schedule" default:"30 00 * * *"` - LogicalBackupDockerImage string `name:"logical_backup_docker_image" default:"ghcr.io/zalando/postgres-operator/logical-backup:v1.14.0"` + LogicalBackupDockerImage string `name:"logical_backup_docker_image" default:"ghcr.io/zalando/postgres-operator/logical-backup:v1.15.0"` LogicalBackupProvider string `name:"logical_backup_provider" default:"s3"` LogicalBackupAzureStorageAccountName string `name:"logical_backup_azure_storage_account_name" default:""` LogicalBackupAzureStorageContainer string `name:"logical_backup_azure_storage_container" default:""` diff --git a/pkg/util/volumes/ebs.go b/pkg/util/volumes/ebs.go index cb8f8e97f..45850d55f 100644 --- a/pkg/util/volumes/ebs.go +++ b/pkg/util/volumes/ebs.go @@ -88,12 +88,13 @@ func (r *EBSVolumeResizer) DescribeVolumes(volumeIds []string) ([]VolumeProperti } for _, v := range volumeOutput.Volumes { - if *v.VolumeType == "gp3" { + switch *v.VolumeType { + case "gp3": p = append(p, VolumeProperties{VolumeID: *v.VolumeId, Size: *v.Size, VolumeType: *v.VolumeType, Iops: *v.Iops, Throughput: *v.Throughput}) - } else if *v.VolumeType == "gp2" { + case "gp2": p = append(p, VolumeProperties{VolumeID: *v.VolumeId, Size: *v.Size, VolumeType: *v.VolumeType}) - } else { - return nil, fmt.Errorf("Discovered unexpected volume type %s %s", *v.VolumeId, *v.VolumeType) + default: + return nil, fmt.Errorf("discovered unexpected volume type %s %s", *v.VolumeId, *v.VolumeType) } } diff --git a/ui/app/package.json b/ui/app/package.json index ef24834ca..ab4c65c7f 100644 --- a/ui/app/package.json +++ b/ui/app/package.json @@ -1,6 +1,6 @@ { "name": "postgres-operator-ui", - "version": "1.14.0", + "version": "1.15.0", "description": "PostgreSQL Operator UI", "main": "src/app.js", "config": { diff --git a/ui/manifests/deployment.yaml b/ui/manifests/deployment.yaml index 3b3097416..41b940891 100644 --- a/ui/manifests/deployment.yaml +++ b/ui/manifests/deployment.yaml @@ -18,7 +18,7 @@ spec: serviceAccountName: postgres-operator-ui containers: - name: "service" - image: ghcr.io/zalando/postgres-operator-ui:v1.14.0 + image: ghcr.io/zalando/postgres-operator-ui:v1.15.0 ports: - containerPort: 8081 protocol: "TCP" From 2c57498e43f2742c6da936b1d02af1839f70c5e3 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Wed, 5 Nov 2025 16:28:37 +0100 Subject: [PATCH 13/58] skip db user actions when its secret failed to sync on update (#2969) * skip db user actions when its secret failed to sync on update * need to add new pgUser field to e2e test * lets collect errors of syncSecret so we still get status updateFailed --- e2e/tests/test_e2e.py | 3 +- pkg/cluster/sync.go | 61 +++++++++++++++++++++++++++++----------- pkg/cluster/sync_test.go | 20 +++++++++++-- pkg/spec/types.go | 1 + pkg/util/users/users.go | 4 +++ 5 files changed, 69 insertions(+), 20 deletions(-) diff --git a/e2e/tests/test_e2e.py b/e2e/tests/test_e2e.py index 4681508c5..f473b5cc4 100644 --- a/e2e/tests/test_e2e.py +++ b/e2e/tests/test_e2e.py @@ -1003,7 +1003,8 @@ class EndToEndTestCase(unittest.TestCase): "Origin": 2, "IsDbOwner": False, "Deleted": False, - "Rotated": False + "Rotated": False, + "Degraded": False, }) return True except: diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index 6bf8c06b4..a210790b3 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -1059,40 +1059,52 @@ func (c *Cluster) syncStandbyClusterConfiguration() error { func (c *Cluster) syncSecrets() error { c.logger.Debug("syncing secrets") c.setProcessName("syncing secrets") + errors := make([]string, 0) generatedSecrets := c.generateUserSecrets() retentionUsers := make([]string, 0) currentTime := time.Now() for secretUsername, generatedSecret := range generatedSecrets { - secret, err := c.KubeClient.Secrets(generatedSecret.Namespace).Create(context.TODO(), generatedSecret, metav1.CreateOptions{}) + pgUserDegraded := false + createdSecret, err := c.KubeClient.Secrets(generatedSecret.Namespace).Create(context.TODO(), generatedSecret, metav1.CreateOptions{}) if err == nil { - c.Secrets[secret.UID] = secret - c.logger.Infof("created new secret %s, namespace: %s, uid: %s", util.NameFromMeta(secret.ObjectMeta), generatedSecret.Namespace, secret.UID) + c.Secrets[createdSecret.UID] = createdSecret + c.logger.Infof("created new secret %s, namespace: %s, uid: %s", util.NameFromMeta(createdSecret.ObjectMeta), generatedSecret.Namespace, createdSecret.UID) continue } if k8sutil.ResourceAlreadyExists(err) { - if err = c.updateSecret(secretUsername, generatedSecret, &retentionUsers, currentTime); err != nil { - c.logger.Warningf("syncing secret %s failed: %v", util.NameFromMeta(secret.ObjectMeta), err) + updatedSecret, err := c.updateSecret(secretUsername, generatedSecret, &retentionUsers, currentTime) + if err == nil { + c.Secrets[updatedSecret.UID] = updatedSecret + continue } + errors = append(errors, fmt.Sprintf("syncing secret %s failed: %v", util.NameFromMeta(updatedSecret.ObjectMeta), err)) + pgUserDegraded = true } else { - return fmt.Errorf("could not create secret for user %s: in namespace %s: %v", secretUsername, generatedSecret.Namespace, err) + errors = append(errors, fmt.Sprintf("could not create secret for user %s: in namespace %s: %v", secretUsername, generatedSecret.Namespace, err)) + pgUserDegraded = true } + c.updatePgUser(secretUsername, pgUserDegraded) } // remove rotation users that exceed the retention interval if len(retentionUsers) > 0 { err := c.initDbConn() if err != nil { - return fmt.Errorf("could not init db connection: %v", err) + errors = append(errors, fmt.Sprintf("could not init db connection: %v", err)) } if err = c.cleanupRotatedUsers(retentionUsers, c.pgDb); err != nil { - return fmt.Errorf("error removing users exceeding configured retention interval: %v", err) + errors = append(errors, fmt.Sprintf("error removing users exceeding configured retention interval: %v", err)) } if err := c.closeDbConn(); err != nil { - c.logger.Errorf("could not close database connection after removing users exceeding configured retention interval: %v", err) + errors = append(errors, fmt.Sprintf("could not close database connection after removing users exceeding configured retention interval: %v", err)) } } + if len(errors) > 0 { + return fmt.Errorf("%v", strings.Join(errors, `', '`)) + } + return nil } @@ -1105,7 +1117,7 @@ func (c *Cluster) updateSecret( secretUsername string, generatedSecret *v1.Secret, retentionUsers *[]string, - currentTime time.Time) error { + currentTime time.Time) (*v1.Secret, error) { var ( secret *v1.Secret err error @@ -1115,7 +1127,7 @@ func (c *Cluster) updateSecret( // get the secret first if secret, err = c.KubeClient.Secrets(generatedSecret.Namespace).Get(context.TODO(), generatedSecret.Name, metav1.GetOptions{}); err != nil { - return fmt.Errorf("could not get current secret: %v", err) + return generatedSecret, fmt.Errorf("could not get current secret: %v", err) } c.Secrets[secret.UID] = secret @@ -1211,24 +1223,22 @@ func (c *Cluster) updateSecret( if updateSecret { c.logger.Infof("%s", updateSecretMsg) if secret, err = c.KubeClient.Secrets(secret.Namespace).Update(context.TODO(), secret, metav1.UpdateOptions{}); err != nil { - return fmt.Errorf("could not update secret %s: %v", secretName, err) + return secret, fmt.Errorf("could not update secret %s: %v", secretName, err) } - c.Secrets[secret.UID] = secret } if changed, _ := c.compareAnnotations(secret.Annotations, generatedSecret.Annotations, nil); changed { patchData, err := metaAnnotationsPatch(generatedSecret.Annotations) if err != nil { - return fmt.Errorf("could not form patch for secret %q annotations: %v", secret.Name, err) + return secret, fmt.Errorf("could not form patch for secret %q annotations: %v", secret.Name, err) } secret, err = c.KubeClient.Secrets(secret.Namespace).Patch(context.TODO(), secret.Name, types.MergePatchType, []byte(patchData), metav1.PatchOptions{}) if err != nil { - return fmt.Errorf("could not patch annotations for secret %q: %v", secret.Name, err) + return secret, fmt.Errorf("could not patch annotations for secret %q: %v", secret.Name, err) } - c.Secrets[secret.UID] = secret } - return nil + return secret, nil } func (c *Cluster) rotatePasswordInSecret( @@ -1334,6 +1344,23 @@ func (c *Cluster) rotatePasswordInSecret( return updateSecretMsg, nil } +func (c *Cluster) updatePgUser(secretUsername string, degraded bool) { + for key, pgUser := range c.pgUsers { + if pgUser.Name == secretUsername { + pgUser.Degraded = degraded + c.pgUsers[key] = pgUser + return + } + } + for key, pgUser := range c.systemUsers { + if pgUser.Name == secretUsername { + pgUser.Degraded = degraded + c.systemUsers[key] = pgUser + return + } + } +} + func (c *Cluster) syncRoles() (err error) { c.setProcessName("syncing roles") diff --git a/pkg/cluster/sync_test.go b/pkg/cluster/sync_test.go index e670daa70..d5bad341c 100644 --- a/pkg/cluster/sync_test.go +++ b/pkg/cluster/sync_test.go @@ -12,9 +12,12 @@ import ( v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" + k8stesting "k8s.io/client-go/testing" "github.com/golang/mock/gomock" + "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "github.com/zalando/postgres-operator/mocks" @@ -50,6 +53,16 @@ func newFakeK8sSyncClient() (k8sutil.KubernetesClient, *fake.Clientset) { } func newFakeK8sSyncSecretsClient() (k8sutil.KubernetesClient, *fake.Clientset) { + // add a reactor that checks namespace existence before creating secrets + clientSet.PrependReactor("create", "secrets", func(action k8stesting.Action) (bool, runtime.Object, error) { + createAction := action.(k8stesting.CreateAction) + secret := createAction.GetObject().(*v1.Secret) + if secret.Namespace != "default" { + return true, nil, errors.New("namespace does not exist") + } + return false, nil, nil + }) + return k8sutil.KubernetesClient{ SecretsGetter: clientSet.CoreV1(), }, clientSet @@ -810,7 +823,7 @@ func TestUpdateSecret(t *testing.T) { }, Spec: acidv1.PostgresSpec{ Databases: map[string]string{dbname: dbowner}, - Users: map[string]acidv1.UserFlags{appUser: {}, "bar": {}, dbowner: {}}, + Users: map[string]acidv1.UserFlags{appUser: {}, "bar": {}, dbowner: {}, "not-exist.test_user": {}}, UsersIgnoringSecretRotation: []string{"bar"}, UsersWithInPlaceSecretRotation: []string{dbowner}, Streams: []acidv1.Stream{ @@ -842,6 +855,7 @@ func TestUpdateSecret(t *testing.T) { PasswordRotationInterval: 1, PasswordRotationUserRetention: 3, }, + EnableCrossNamespaceSecret: true, Resources: config.Resources{ ClusterLabels: map[string]string{"application": "spilo"}, ClusterNameLabel: "cluster-name", @@ -864,7 +878,9 @@ func TestUpdateSecret(t *testing.T) { allUsers := make(map[string]spec.PgUser) for _, pgUser := range cluster.pgUsers { - allUsers[pgUser.Name] = pgUser + if !pgUser.Degraded { + allUsers[pgUser.Name] = pgUser + } } for _, systemUser := range cluster.systemUsers { allUsers[systemUser.Name] = systemUser diff --git a/pkg/spec/types.go b/pkg/spec/types.go index d727aee42..c08cc5c61 100644 --- a/pkg/spec/types.go +++ b/pkg/spec/types.go @@ -58,6 +58,7 @@ type PgUser struct { IsDbOwner bool `yaml:"is_db_owner"` Deleted bool `yaml:"deleted"` Rotated bool `yaml:"rotated"` + Degraded bool `yaml:"degraded"` } func (user *PgUser) Valid() bool { diff --git a/pkg/util/users/users.go b/pkg/util/users/users.go index 924d8390e..b3b60df04 100644 --- a/pkg/util/users/users.go +++ b/pkg/util/users/users.go @@ -48,6 +48,10 @@ func (strategy DefaultUserSyncStrategy) ProduceSyncRequests(dbUsers spec.PgUserM if newUser.Deleted { continue } + // when the secret of the user could not be created or updated skip any database actions + if newUser.Degraded { + continue + } dbUser, exists := dbUsers[name] if !exists { reqs = append(reqs, spec.PgSyncUserRequest{Kind: spec.PGSyncUserAdd, User: newUser}) From 8e629c5d6d84450f4d73eb4e2f6a45d954d9f504 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Wed, 3 Dec 2025 11:00:59 +0100 Subject: [PATCH 14/58] add Mikkel (#3002) --- CODEOWNERS | 2 +- MAINTAINERS | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index daca96b42..96213da15 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1,2 +1,2 @@ # global owners -* @sdudoladov @Jan-M @FxKu @jopadi @idanovinda @hughcapet +* @sdudoladov @Jan-M @FxKu @jopadi @idanovinda @hughcapet @mikkeloscar diff --git a/MAINTAINERS b/MAINTAINERS index 7c4aa6675..6ff2aa62e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4,3 +4,4 @@ Jan Mussler Jociele Padilha Ida Novindasari Polina Bungina +Mikkel Larsen From 744372b09bad91ca11b8f2bb9b4799f7be8f7e49 Mon Sep 17 00:00:00 2001 From: Aleksandr Vinokurov Date: Tue, 9 Dec 2025 09:36:32 +0100 Subject: [PATCH 15/58] Fix typo in README.md regarding bootstrap phase (#2978) Co-authored-by: Felix Kunde --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8820f04a5..cc2bdb17e 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ pipelines with no access to Kubernetes API directly, promoting infrastructure as * Live volume resize without pod restarts (AWS EBS, PVC) * Database connection pooling with PGBouncer * Support fast in place major version upgrade. Supports global upgrade of all clusters. -* Pod protection during boostrap phase and configurable maintenance windows +* Pod protection during bootstrap phase and configurable maintenance windows * Restore and cloning Postgres clusters on AWS, GCS and Azure * Additionally logical backups to S3 or GCS bucket can be configured * Standby cluster from S3 or GCS WAL archive From 42bbead4c95df2054e9fe12dd71ec867e27074a7 Mon Sep 17 00:00:00 2001 From: ovnozdrach <135333325+ovnozdrach@users.noreply.github.com> Date: Tue, 9 Dec 2025 11:37:11 +0300 Subject: [PATCH 16/58] Fix Sidecar without image specification issue (#2977) Co-authored-by: Oleg Nozdrachev Co-authored-by: Felix Kunde --- pkg/cluster/k8sres.go | 19 ++++ pkg/cluster/k8sres_test.go | 201 +++++++++++++++++++++++++++++++++++-- 2 files changed, 212 insertions(+), 8 deletions(-) diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index e05a54553..9bc39a9db 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -1303,6 +1303,9 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef c.logger.Warningf("initContainers specified but disabled in configuration - next statefulset creation would fail") } initContainers = spec.InitContainers + if err := c.validateContainers(initContainers); err != nil { + return nil, fmt.Errorf("invalid init containers: %v", err) + } } // backward compatible check for InitContainers @@ -1455,6 +1458,10 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef sidecarContainers = patchSidecarContainers(sidecarContainers, volumeMounts, c.OpConfig.SuperUsername, c.credentialSecretName(c.OpConfig.SuperUsername)) + if err := c.validateContainers(sidecarContainers); err != nil { + return nil, fmt.Errorf("invalid sidecar containers: %v", err) + } + tolerationSpec := tolerations(&spec.Tolerations, c.OpConfig.PodToleration) effectivePodPriorityClassName := util.Coalesce(spec.PodPriorityClassName, c.OpConfig.PodPriorityClassName) @@ -2592,3 +2599,15 @@ func ensurePath(file string, defaultDir string, defaultFile string) string { } return file } + +func (c *Cluster) validateContainers(containers []v1.Container) error { + for i, container := range containers { + if container.Name == "" { + return fmt.Errorf("container[%d]: name is required", i) + } + if container.Image == "" { + return fmt.Errorf("container '%v': image is required", container.Name) + } + } + return nil +} diff --git a/pkg/cluster/k8sres_test.go b/pkg/cluster/k8sres_test.go index 137c24081..6bd87366d 100644 --- a/pkg/cluster/k8sres_test.go +++ b/pkg/cluster/k8sres_test.go @@ -1935,7 +1935,8 @@ func TestAdditionalVolume(t *testing.T) { AdditionalVolumes: additionalVolumes, Sidecars: []acidv1.Sidecar{ { - Name: sidecarName, + Name: sidecarName, + DockerImage: "test-image", }, }, }, @@ -2163,10 +2164,12 @@ func TestSidecars(t *testing.T) { }, Sidecars: []acidv1.Sidecar{ { - Name: "cluster-specific-sidecar", + Name: "cluster-specific-sidecar", + DockerImage: "test-image", }, { - Name: "cluster-specific-sidecar-with-resources", + Name: "cluster-specific-sidecar-with-resources", + DockerImage: "test-image", Resources: &acidv1.Resources{ ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("210m"), Memory: k8sutil.StringToPointer("0.8Gi")}, ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("510m"), Memory: k8sutil.StringToPointer("1.4Gi")}, @@ -2201,7 +2204,8 @@ func TestSidecars(t *testing.T) { }, SidecarContainers: []v1.Container{ { - Name: "global-sidecar", + Name: "global-sidecar", + Image: "test-image", }, // will be replaced by a cluster specific sidecar with the same name { @@ -2271,6 +2275,7 @@ func TestSidecars(t *testing.T) { // cluster specific sidecar assert.Contains(t, s.Spec.Template.Spec.Containers, v1.Container{ Name: "cluster-specific-sidecar", + Image: "test-image", Env: env, Resources: generateKubernetesResources("200m", "500m", "0.7Gi", "1.3Gi"), ImagePullPolicy: v1.PullIfNotPresent, @@ -2297,6 +2302,7 @@ func TestSidecars(t *testing.T) { // global sidecar assert.Contains(t, s.Spec.Template.Spec.Containers, v1.Container{ Name: "global-sidecar", + Image: "test-image", Env: env, VolumeMounts: mounts, }) @@ -2325,6 +2331,180 @@ func TestSidecars(t *testing.T) { } +func TestContainerValidation(t *testing.T) { + testCases := []struct { + name string + spec acidv1.PostgresSpec + clusterConfig Config + expectedError string + }{ + { + name: "init container without image", + spec: acidv1.PostgresSpec{ + PostgresqlParam: acidv1.PostgresqlParam{ + PgVersion: "17", + }, + TeamID: "myapp", + NumberOfInstances: 1, + Volume: acidv1.Volume{ + Size: "1G", + }, + InitContainers: []v1.Container{ + { + Name: "invalid-initcontainer", + }, + }, + }, + clusterConfig: Config{ + OpConfig: config.Config{ + PodManagementPolicy: "ordered_ready", + ProtectedRoles: []string{"admin"}, + Auth: config.Auth{ + SuperUsername: superUserName, + ReplicationUsername: replicationUserName, + }, + }, + }, + expectedError: "image is required", + }, + { + name: "sidecar without name", + spec: acidv1.PostgresSpec{ + PostgresqlParam: acidv1.PostgresqlParam{ + PgVersion: "17", + }, + TeamID: "myapp", + NumberOfInstances: 1, + Volume: acidv1.Volume{ + Size: "1G", + }, + }, + clusterConfig: Config{ + OpConfig: config.Config{ + PodManagementPolicy: "ordered_ready", + ProtectedRoles: []string{"admin"}, + Auth: config.Auth{ + SuperUsername: superUserName, + ReplicationUsername: replicationUserName, + }, + SidecarContainers: []v1.Container{ + { + Image: "test-image", + }, + }, + }, + }, + expectedError: "name is required", + }, + { + name: "sidecar without image", + spec: acidv1.PostgresSpec{ + PostgresqlParam: acidv1.PostgresqlParam{ + PgVersion: "17", + }, + TeamID: "myapp", + NumberOfInstances: 1, + Volume: acidv1.Volume{ + Size: "1G", + }, + Sidecars: []acidv1.Sidecar{ + { + Name: "invalid-sidecar", + }, + }, + }, + clusterConfig: Config{ + OpConfig: config.Config{ + PodManagementPolicy: "ordered_ready", + ProtectedRoles: []string{"admin"}, + Auth: config.Auth{ + SuperUsername: superUserName, + ReplicationUsername: replicationUserName, + }, + }, + }, + expectedError: "image is required", + }, + { + name: "valid containers pass validation", + spec: acidv1.PostgresSpec{ + PostgresqlParam: acidv1.PostgresqlParam{ + PgVersion: "17", + }, + TeamID: "myapp", + NumberOfInstances: 1, + Volume: acidv1.Volume{ + Size: "1G", + }, + Sidecars: []acidv1.Sidecar{ + { + Name: "valid-sidecar", + DockerImage: "busybox:latest", + }, + }, + InitContainers: []v1.Container{ + { + Name: "valid-initcontainer", + Image: "alpine:latest", + }, + }, + }, + clusterConfig: Config{ + OpConfig: config.Config{ + PodManagementPolicy: "ordered_ready", + ProtectedRoles: []string{"admin"}, + Auth: config.Auth{ + SuperUsername: superUserName, + ReplicationUsername: replicationUserName, + }, + }, + }, + expectedError: "", + }, + { + name: "multiple invalid sidecars", + spec: acidv1.PostgresSpec{ + Sidecars: []acidv1.Sidecar{ + { + Name: "sidecar1", + }, + { + Name: "sidecar2", + }, + }, + }, + expectedError: "image is required", + }, + { + name: "empty container name and image", + spec: acidv1.PostgresSpec{ + InitContainers: []v1.Container{ + { + Name: "", + Image: "", + }, + }, + }, + expectedError: "name is required", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + cluster := New(tc.clusterConfig, k8sutil.KubernetesClient{}, acidv1.Postgresql{}, logger, eventRecorder) + + _, err := cluster.generateStatefulSet(&tc.spec) + + if tc.expectedError != "" { + assert.Error(t, err) + assert.Contains(t, err.Error(), tc.expectedError) + } else { + assert.NoError(t, err) + } + }) + } +} + func TestGeneratePodDisruptionBudget(t *testing.T) { testName := "Test PodDisruptionBudget spec generation" @@ -2618,7 +2798,8 @@ func TestGenerateService(t *testing.T) { Name: "cluster-specific-sidecar", }, { - Name: "cluster-specific-sidecar-with-resources", + Name: "cluster-specific-sidecar-with-resources", + DockerImage: "test-image", Resources: &acidv1.Resources{ ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("210m"), Memory: k8sutil.StringToPointer("0.8Gi")}, ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("510m"), Memory: k8sutil.StringToPointer("1.4Gi")}, @@ -2928,6 +3109,7 @@ func TestGenerateResourceRequirements(t *testing.T) { namespace := "default" clusterNameLabel := "cluster-name" sidecarName := "postgres-exporter" + dockerImage := "test-image" // enforceMinResourceLimits will be called 2 times emitting 4 events (2x cpu, 2x memory raise) // enforceMaxResourceRequests will be called 4 times emitting 6 events (2x cpu, 4x memory cap) @@ -2993,7 +3175,8 @@ func TestGenerateResourceRequirements(t *testing.T) { Spec: acidv1.PostgresSpec{ Sidecars: []acidv1.Sidecar{ { - Name: sidecarName, + Name: sidecarName, + DockerImage: dockerImage, }, }, TeamID: "acid", @@ -3232,7 +3415,8 @@ func TestGenerateResourceRequirements(t *testing.T) { Spec: acidv1.PostgresSpec{ Sidecars: []acidv1.Sidecar{ { - Name: sidecarName, + Name: sidecarName, + DockerImage: dockerImage, Resources: &acidv1.Resources{ ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("10m"), Memory: k8sutil.StringToPointer("10Mi")}, ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("100m"), Memory: k8sutil.StringToPointer("100Mi")}, @@ -3321,7 +3505,8 @@ func TestGenerateResourceRequirements(t *testing.T) { Spec: acidv1.PostgresSpec{ Sidecars: []acidv1.Sidecar{ { - Name: sidecarName, + Name: sidecarName, + DockerImage: dockerImage, Resources: &acidv1.Resources{ ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("10m"), Memory: k8sutil.StringToPointer("10Mi")}, ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("100m"), Memory: k8sutil.StringToPointer("100Mi")}, From 04ad66f7013b1a032c93edbf8375a9201ef27762 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Wed, 10 Dec 2025 10:01:07 +0100 Subject: [PATCH 17/58] stop retention user cleanup early again when DB connection attempt fails (#2999) * stop retention user cleanup early again when DB connection attempt fails * add unit test and new returned error from updateSecret --- pkg/cluster/database.go | 20 ++++++++++++--- pkg/cluster/sync.go | 26 +++++++++---------- pkg/cluster/sync_test.go | 54 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 17 deletions(-) diff --git a/pkg/cluster/database.go b/pkg/cluster/database.go index aac877bcf..56b5f3638 100644 --- a/pkg/cluster/database.go +++ b/pkg/cluster/database.go @@ -281,9 +281,23 @@ func findUsersFromRotation(rotatedUsers []string, db *sql.DB) (map[string]string return extraUsers, nil } -func (c *Cluster) cleanupRotatedUsers(rotatedUsers []string, db *sql.DB) error { +func (c *Cluster) cleanupRotatedUsers(rotatedUsers []string) error { c.setProcessName("checking for rotated users to remove from the database due to configured retention") - extraUsers, err := findUsersFromRotation(rotatedUsers, db) + + err := c.initDbConn() + if err != nil { + return fmt.Errorf("could not init db connection: %v", err) + } + defer func() { + if c.connectionIsClosed() { + return + } + if err := c.closeDbConn(); err != nil { + c.logger.Errorf("could not close database connection after removing users exceeding configured retention interval: %v", err) + } + }() + + extraUsers, err := findUsersFromRotation(rotatedUsers, c.pgDb) if err != nil { return fmt.Errorf("error when querying for deprecated users from password rotation: %v", err) } @@ -304,7 +318,7 @@ func (c *Cluster) cleanupRotatedUsers(rotatedUsers []string, db *sql.DB) error { } if retentionDate.After(userCreationDate) { c.logger.Infof("dropping user %q due to configured days in password_rotation_user_retention", rotatedUser) - if err = users.DropPgUser(rotatedUser, db); err != nil { + if err = users.DropPgUser(rotatedUser, c.pgDb); err != nil { c.logger.Errorf("could not drop role %q: %v", rotatedUser, err) continue } diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index a210790b3..ecf692702 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -1078,7 +1078,7 @@ func (c *Cluster) syncSecrets() error { c.Secrets[updatedSecret.UID] = updatedSecret continue } - errors = append(errors, fmt.Sprintf("syncing secret %s failed: %v", util.NameFromMeta(updatedSecret.ObjectMeta), err)) + errors = append(errors, fmt.Sprintf("syncing secret %s failed: %v", util.NameFromMeta(generatedSecret.ObjectMeta), err)) pgUserDegraded = true } else { errors = append(errors, fmt.Sprintf("could not create secret for user %s: in namespace %s: %v", secretUsername, generatedSecret.Namespace, err)) @@ -1089,16 +1089,9 @@ func (c *Cluster) syncSecrets() error { // remove rotation users that exceed the retention interval if len(retentionUsers) > 0 { - err := c.initDbConn() - if err != nil { - errors = append(errors, fmt.Sprintf("could not init db connection: %v", err)) - } - if err = c.cleanupRotatedUsers(retentionUsers, c.pgDb); err != nil { + if err := c.cleanupRotatedUsers(retentionUsers); err != nil { errors = append(errors, fmt.Sprintf("error removing users exceeding configured retention interval: %v", err)) } - if err := c.closeDbConn(); err != nil { - errors = append(errors, fmt.Sprintf("could not close database connection after removing users exceeding configured retention interval: %v", err)) - } } if len(errors) > 0 { @@ -1187,13 +1180,18 @@ func (c *Cluster) updateSecret( } } else { // username might not match if password rotation has been disabled again - if secretUsername != string(secret.Data["username"]) { + 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 %s does not contain the role %s - updating username and resetting password", secretName, secretUsername) + updateSecretMsg = fmt.Sprintf("secret does not contain the role %s - updating username and resetting password", secretUsername) } } @@ -1223,18 +1221,18 @@ func (c *Cluster) updateSecret( if updateSecret { c.logger.Infof("%s", updateSecretMsg) if secret, err = c.KubeClient.Secrets(secret.Namespace).Update(context.TODO(), secret, metav1.UpdateOptions{}); err != nil { - return secret, fmt.Errorf("could not update secret %s: %v", secretName, err) + return nil, fmt.Errorf("could not update secret: %v", err) } } if changed, _ := c.compareAnnotations(secret.Annotations, generatedSecret.Annotations, nil); changed { patchData, err := metaAnnotationsPatch(generatedSecret.Annotations) if err != nil { - return secret, fmt.Errorf("could not form patch for secret %q annotations: %v", secret.Name, err) + return nil, fmt.Errorf("could not form patch for secret annotations: %v", err) } secret, err = c.KubeClient.Secrets(secret.Namespace).Patch(context.TODO(), secret.Name, types.MergePatchType, []byte(patchData), metav1.PatchOptions{}) if err != nil { - return secret, fmt.Errorf("could not patch annotations for secret %q: %v", secret.Name, err) + return nil, fmt.Errorf("could not patch annotations for secret: %v", err) } } diff --git a/pkg/cluster/sync_test.go b/pkg/cluster/sync_test.go index d5bad341c..87e9dc8a5 100644 --- a/pkg/cluster/sync_test.go +++ b/pkg/cluster/sync_test.go @@ -964,3 +964,57 @@ func TestUpdateSecret(t *testing.T) { t.Errorf("%s: updated secret does not contain expected username: expected %s, got %s", testName, appUser, currentUsername) } } + +func TestUpdateSecretNameConflict(t *testing.T) { + client, _ := newFakeK8sSyncSecretsClient() + + clusterName := "acid-test-cluster" + namespace := "default" + secretTemplate := config.StringTemplate("{username}.{cluster}.credentials") + + // define manifest user that has the same name as a prepared database owner user except for dashes vs underscores + // because of this the operator cannot create both secrets because underscores are not allowed in k8s secret names + pg := acidv1.Postgresql{ + ObjectMeta: metav1.ObjectMeta{ + Name: clusterName, + Namespace: namespace, + }, + Spec: acidv1.PostgresSpec{ + PreparedDatabases: map[string]acidv1.PreparedDatabase{"prepared": {DefaultUsers: true}}, + Users: map[string]acidv1.UserFlags{"prepared-owner-user": {}}, + Volume: acidv1.Volume{ + Size: "1Gi", + }, + }, + } + + var cluster = New( + Config{ + OpConfig: config.Config{ + Auth: config.Auth{ + SuperUsername: "postgres", + ReplicationUsername: "standby", + SecretNameTemplate: secretTemplate, + }, + Resources: config.Resources{ + ClusterLabels: map[string]string{"application": "spilo"}, + ClusterNameLabel: "cluster-name", + }, + }, + }, client, pg, logger, eventRecorder) + + cluster.Name = clusterName + cluster.Namespace = namespace + cluster.pgUsers = map[string]spec.PgUser{} + + // init all users + cluster.initUsers() + // create secrets and fail because of user name mismatch + // prepared-owner-user from manifest vs prepared_owner_user from prepared database + err := cluster.syncSecrets() + 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") + assert.Contains(t, err.Error(), expectedError) +} From 88d6192064edcb6804eea848e80f7f283faa320f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Dec 2025 15:31:10 +0100 Subject: [PATCH 18/58] Bump js-yaml from 4.1.0 to 4.1.1 in /ui/app (#2989) Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 4.1.0 to 4.1.1. - [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md) - [Commits](https://github.com/nodeca/js-yaml/compare/4.1.0...4.1.1) --- updated-dependencies: - dependency-name: js-yaml dependency-version: 4.1.1 dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Felix Kunde --- ui/app/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/app/package.json b/ui/app/package.json index ab4c65c7f..03a9643c1 100644 --- a/ui/app/package.json +++ b/ui/app/package.json @@ -38,7 +38,7 @@ "brfs": "^2.0.2", "dedent-js": "1.0.1", "eslint": "^8.32.0", - "js-yaml": "4.1.0", + "js-yaml": "4.1.1", "pug": "^3.0.2", "rimraf": "^4.1.2", "riot": "^3.13.2", From 842c6eded3f72b7a4b9335a368afd6fb56b5cdf7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Dec 2025 15:34:42 +0100 Subject: [PATCH 19/58] Bump golang.org/x/crypto from 0.31.0 to 0.45.0 in /kubectl-pg (#2996) Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.31.0 to 0.45.0. - [Commits](https://github.com/golang/crypto/compare/v0.31.0...v0.45.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-version: 0.45.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Felix Kunde --- kubectl-pg/go.mod | 10 +++++----- kubectl-pg/go.sum | 24 ++++++++++++------------ 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/kubectl-pg/go.mod b/kubectl-pg/go.mod index 9a2faed99..fcd4aec43 100644 --- a/kubectl-pg/go.mod +++ b/kubectl-pg/go.mod @@ -51,12 +51,12 @@ require ( github.com/subosito/gotenv v1.6.0 // indirect github.com/x448/float16 v0.8.4 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/crypto v0.31.0 // indirect - golang.org/x/net v0.30.0 // indirect + golang.org/x/crypto v0.45.0 // indirect + golang.org/x/net v0.47.0 // indirect golang.org/x/oauth2 v0.23.0 // indirect - golang.org/x/sys v0.29.0 // indirect - golang.org/x/term v0.27.0 // indirect - golang.org/x/text v0.28.0 // indirect + golang.org/x/sys v0.38.0 // indirect + golang.org/x/term v0.37.0 // indirect + golang.org/x/text v0.31.0 // indirect golang.org/x/time v0.7.0 // indirect google.golang.org/protobuf v1.35.1 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect diff --git a/kubectl-pg/go.sum b/kubectl-pg/go.sum index 2bb48c85a..e905ac7e7 100644 --- a/kubectl-pg/go.sum +++ b/kubectl-pg/go.sum @@ -130,16 +130,16 @@ go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= +golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= -golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -149,22 +149,22 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= -golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +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.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= +golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= golang.org/x/time v0.7.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-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.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= -golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= +golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= +golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 1b4ffb08752426cd66e778cec442174b332b11dc Mon Sep 17 00:00:00 2001 From: Ida Novindasari Date: Wed, 10 Dec 2025 16:45:42 +0100 Subject: [PATCH 20/58] Separate pipeline for internal/external build and support multi arch (#2991) * separate pipeline for internal/external build and support multi arch * remove distutils and use node:lts-alpine in both * change base image arguments * fix local naming and base image * address feedback --------- Co-authored-by: Felix Kunde --- .github/workflows/publish_ghcr_image.yaml | 2 + Makefile | 13 +++-- delivery.yaml | 62 ++++++++++++++++------- docker/Dockerfile | 2 +- ui/Dockerfile | 2 +- ui/Makefile | 21 ++++++-- 6 files changed, 70 insertions(+), 32 deletions(-) diff --git a/.github/workflows/publish_ghcr_image.yaml b/.github/workflows/publish_ghcr_image.yaml index 78815783a..7bfb17a51 100644 --- a/.github/workflows/publish_ghcr_image.yaml +++ b/.github/workflows/publish_ghcr_image.yaml @@ -7,6 +7,8 @@ env: on: push: + branches: + - master tags: - '*' diff --git a/Makefile b/Makefile index 50d070b9d..385a03d09 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,8 @@ LOCAL_BUILD_FLAGS ?= $(BUILD_FLAGS) LDFLAGS ?= -X=main.version=$(VERSION) DOCKERDIR = docker -IMAGE ?= registry.opensource.zalan.do/acid/$(BINARY) +BASE_IMAGE ?= alpine:latest +IMAGE ?= $(BINARY) TAG ?= $(VERSION) GITHEAD = $(shell git rev-parse --short HEAD) GITURL = $(shell git config --get remote.origin.url) @@ -42,8 +43,9 @@ ifndef GOPATH GOPATH := $(HOME)/go endif -PATH := $(GOPATH)/bin:$(PATH) -SHELL := env PATH="$(PATH)" $(SHELL) +PATH := $(GOPATH)/bin:$(PATH) +SHELL := env PATH="$(PATH)" $(SHELL) +IMAGE_TAG := $(IMAGE):$(TAG)$(CDP_TAG)$(DEBUG_FRESH)$(DEBUG_POSTFIX) default: local @@ -66,14 +68,11 @@ docker: ${DOCKERDIR}/${DOCKERFILE} echo "Version ${VERSION}" echo "CDP tag ${CDP_TAG}" echo "git describe $(shell git describe --tags --always --dirty)" - docker build --rm -t "$(IMAGE):$(TAG)$(CDP_TAG)$(DEBUG_FRESH)$(DEBUG_POSTFIX)" -f "${DOCKERDIR}/${DOCKERFILE}" --build-arg VERSION="${VERSION}" . + docker build --rm -t "$(IMAGE_TAG)" -f "${DOCKERDIR}/${DOCKERFILE}" --build-arg VERSION="${VERSION}" --build-arg BASE_IMAGE="${BASE_IMAGE}" . indocker-race: docker run --rm -v "${GOPATH}":"${GOPATH}" -e GOPATH="${GOPATH}" -e RACE=1 -w ${PWD} golang:1.25.3 bash -c "make linux" -push: - docker push "$(IMAGE):$(TAG)$(CDP_TAG)" - mocks: GO111MODULE=on go generate ./... diff --git a/delivery.yaml b/delivery.yaml index 7eacd769b..9ab8b5a4c 100644 --- a/delivery.yaml +++ b/delivery.yaml @@ -1,6 +1,15 @@ version: "2017-09-20" +allow_concurrent_steps: true + +build_env: &BUILD_ENV + PYTHON_BASE_IMAGE: container-registry.zalando.net/library/python-3.11-slim + ALPINE_BASE_IMAGE: container-registry.zalando.net/library/alpine-3 + MULTI_ARCH_REGISTRY: container-registry-test.zalando.net/acid + pipeline: - id: build-postgres-operator + env: + <<: *BUILD_ENV type: script vm_config: type: linux @@ -17,17 +26,26 @@ pipeline: - desc: Build Docker image cmd: | - IS_PR_BUILD=${CDP_PULL_REQUEST_NUMBER+"true"} - if [[ ${CDP_TARGET_BRANCH} == "master" && ${IS_PR_BUILD} != "true" ]] - then - IMAGE=registry-write.opensource.zalan.do/acid/postgres-operator + if [ -z ${CDP_SOURCE_BRANCH} ]; then + IMAGE=${MULTI_ARCH_REGISTRY}/postgres-operator else - IMAGE=registry-write.opensource.zalan.do/acid/postgres-operator-test + IMAGE=${MULTI_ARCH_REGISTRY}/postgres-operator-test + fi + + docker buildx create --config /etc/cdp-buildkitd.toml --driver-opt network=host --bootstrap --use + docker buildx build --platform "linux/amd64,linux/arm64" \ + --build-arg BASE_IMAGE="${ALPINE_BASE_IMAGE}" \ + -t "${IMAGE}:${CDP_BUILD_VERSION}" \ + -f docker/Dockerfile \ + --push . + + if [ -z ${CDP_SOURCE_BRANCH} ]; then + cdp-promote-image ${IMAGE}:${CDP_BUILD_VERSION} fi - export IMAGE - make docker push - id: build-operator-ui + env: + <<: *BUILD_ENV type: script vm_config: type: linux @@ -46,18 +64,21 @@ pipeline: - desc: 'Build and push Docker image' cmd: | cd ui - IS_PR_BUILD=${CDP_PULL_REQUEST_NUMBER+"true"} - if [[ ${CDP_TARGET_BRANCH} == "master" && ${IS_PR_BUILD} != "true" ]] - then - IMAGE=registry-write.opensource.zalan.do/acid/postgres-operator-ui + if [ -z ${CDP_SOURCE_BRANCH} ]; then + IMAGE=${MULTI_ARCH_REGISTRY}/postgres-operator-ui else - IMAGE=registry-write.opensource.zalan.do/acid/postgres-operator-ui-test + IMAGE=${MULTI_ARCH_REGISTRY}/postgres-operator-ui-test + fi + + IMAGE_TAG=$(make docker-push IMAGE=${IMAGE} BASE_IMAGE=${PYTHON_BASE_IMAGE}) + + if [ -z ${CDP_SOURCE_BRANCH} ]; then + cdp-promote-image ${IMAGE_TAG} fi - export IMAGE - make docker - make push - id: build-logical-backup + env: + <<: *BUILD_ENV type: script vm_config: type: linux @@ -67,6 +88,11 @@ pipeline: cmd: | cd logical-backup export TAG=$(git describe --tags --always --dirty) - IMAGE="registry-write.opensource.zalan.do/acid/logical-backup" - docker build --rm -t "$IMAGE:$TAG$CDP_TAG" . - docker push "$IMAGE:$TAG$CDP_TAG" + docker buildx create --config /etc/cdp-buildkitd.toml --driver-opt network=host --bootstrap --use + docker buildx build --platform linux/amd64,linux/arm64 \ + -t ${MULTI_ARCH_REGISTRY}/postgres-operator-logical-backup:${TAG} \ + --push . + + if [ -z ${CDP_SOURCE_BRANCH} ]; then + cdp-promote-image ${MULTI_ARCH_REGISTRY}/postgres-operator-logical-backup:${TAG} + fi diff --git a/docker/Dockerfile b/docker/Dockerfile index e50380003..9eef4e68c 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,4 +1,4 @@ -ARG BASE_IMAGE=registry.opensource.zalan.do/library/alpine-3:latest +ARG BASE_IMAGE=alpine:latest FROM golang:1.25-alpine AS builder ARG VERSION=latest diff --git a/ui/Dockerfile b/ui/Dockerfile index 51f1d7744..8ed70c2c0 100644 --- a/ui/Dockerfile +++ b/ui/Dockerfile @@ -1,4 +1,4 @@ -ARG BASE_IMAGE=registry.opensource.zalan.do/library/python-3.11-slim:latest +ARG BASE_IMAGE=python:3.11-slim ARG NODE_IMAGE=node:lts-alpine FROM $NODE_IMAGE AS build diff --git a/ui/Makefile b/ui/Makefile index 8f88982ab..70c2018db 100644 --- a/ui/Makefile +++ b/ui/Makefile @@ -1,6 +1,7 @@ .PHONY: clean test appjs docker push mock -IMAGE ?= registry.opensource.zalan.do/acid/postgres-operator-ui +IMAGE ?= postgres-operator-ui +BASE_IMAGE ?= python:3.11-slim VERSION ?= $(shell git describe --tags --always --dirty) TAG ?= $(VERSION) GITHEAD = $(shell git rev-parse --short HEAD) @@ -30,10 +31,20 @@ docker: appjs echo "Version ${VERSION}" echo "CDP tag ${CDP_TAG}" echo "git describe $(shell git describe --tags --always --dirty)" - docker build --rm -t "$(IMAGE):$(TAG)$(CDP_TAG)" -f Dockerfile . - -push: - docker push "$(IMAGE):$(TAG)$(CDP_TAG)" + docker build --rm -t "$(IMAGE):$(TAG)$(CDP_TAG)" -f Dockerfile --build-arg BASE_IMAGE="${BASE_IMAGE}" . +docker-push: appjs + echo "Tag ${TAG}" + echo "Version ${VERSION}" + echo "CDP tag ${CDP_TAG}" + echo "git describe $(shell git describe --tags --always --dirty)" + docker buildx create --config /etc/cdp-buildkitd.toml --driver-opt network=host --bootstrap --use + docker buildx build --platform linux/amd64,linux/arm64 \ + --build-arg BASE_IMAGE="${BASE_IMAGE}" \ + -f Dockerfile \ + -t "$(IMAGE):$(TAG)$(CDP_TAG)" \ + --push . + echo "$(IMAGE):$(TAG)$(CDP_TAG)" + mock: docker run -it -p 8081:8081 "$(IMAGE):$(TAG)" --mock From f826f75769c6f99a6e7662f2abed5567c9f60531 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Dec 2025 17:07:20 +0100 Subject: [PATCH 21/58] Bump golang.org/x/crypto from 0.43.0 to 0.45.0 (#2997) Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.43.0 to 0.45.0. - [Commits](https://github.com/golang/crypto/compare/v0.43.0...v0.45.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-version: 0.45.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 16 ++++++++-------- go.sum | 32 ++++++++++++++++---------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/go.mod b/go.mod index 7eda62d18..df7c94b5b 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/r3labs/diff v1.1.0 github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.11.1 - golang.org/x/crypto v0.43.0 + 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 @@ -49,15 +49,15 @@ require ( github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/x448/float16 v0.8.4 // indirect - golang.org/x/mod v0.28.0 // indirect - golang.org/x/net v0.45.0 // indirect + golang.org/x/mod v0.29.0 // indirect + golang.org/x/net v0.47.0 // indirect golang.org/x/oauth2 v0.27.0 // indirect - golang.org/x/sync v0.17.0 // indirect - golang.org/x/sys v0.37.0 // indirect - golang.org/x/term v0.36.0 // indirect - golang.org/x/text v0.30.0 // indirect + golang.org/x/sync v0.18.0 // indirect + golang.org/x/sys v0.38.0 // indirect + golang.org/x/term v0.37.0 // indirect + golang.org/x/text v0.31.0 // indirect golang.org/x/time v0.9.0 // indirect - golang.org/x/tools v0.37.0 // indirect + golang.org/x/tools v0.38.0 // indirect golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated // indirect google.golang.org/protobuf v1.36.5 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect diff --git a/go.sum b/go.sum index ffdf21612..581054d7c 100644 --- a/go.sum +++ b/go.sum @@ -121,28 +121,28 @@ go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= -golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= +golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= +golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= -golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= +golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= +golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= -golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= -golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= +golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -150,15 +150,15 @@ 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.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= -golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +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= -golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= -golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= +golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= +golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= -golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= 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= @@ -167,8 +167,8 @@ golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roY 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= -golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= -golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= +golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= +golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= golang.org/x/tools/go/expect v0.1.0-deprecated h1:jY2C5HGYR5lqex3gEniOQL0r7Dq5+VGVgY1nudX5lXY= golang.org/x/tools/go/expect v0.1.0-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM= From 543acd5731a7fda227a66f500ef9a03a2233d0d9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Dec 2025 17:08:01 +0100 Subject: [PATCH 22/58] Bump werkzeug from 3.0.6 to 3.1.4 in /ui (#3001) Bumps [werkzeug](https://github.com/pallets/werkzeug) from 3.0.6 to 3.1.4. - [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.0.6...3.1.4) --- updated-dependencies: - dependency-name: werkzeug dependency-version: 3.1.4 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 75bcc1952..eaeafe3c1 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.0.6 +werkzeug==3.1.4 From fe340cb4295ced5f19b53d6205fcdb4a73d240f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Dec 2025 17:11:22 +0100 Subject: [PATCH 23/58] Bump golang.org/x/oauth2 from 0.23.0 to 0.27.0 in /kubectl-pg (#3008) Bumps [golang.org/x/oauth2](https://github.com/golang/oauth2) from 0.23.0 to 0.27.0. - [Commits](https://github.com/golang/oauth2/compare/v0.23.0...v0.27.0) --- updated-dependencies: - dependency-name: golang.org/x/oauth2 dependency-version: 0.27.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- kubectl-pg/go.mod | 2 +- kubectl-pg/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/kubectl-pg/go.mod b/kubectl-pg/go.mod index fcd4aec43..57fca863e 100644 --- a/kubectl-pg/go.mod +++ b/kubectl-pg/go.mod @@ -53,7 +53,7 @@ require ( go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/crypto v0.45.0 // indirect golang.org/x/net v0.47.0 // indirect - golang.org/x/oauth2 v0.23.0 // indirect + golang.org/x/oauth2 v0.27.0 // indirect golang.org/x/sys v0.38.0 // indirect golang.org/x/term v0.37.0 // indirect golang.org/x/text v0.31.0 // indirect diff --git a/kubectl-pg/go.sum b/kubectl-pg/go.sum index e905ac7e7..37ed34ec8 100644 --- a/kubectl-pg/go.sum +++ b/kubectl-pg/go.sum @@ -140,8 +140,8 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= -golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= -golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= +golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= From 5aa8f961ece9200b8304a3087ec886b21cbe04ff Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Wed, 10 Dec 2025 17:11:53 +0100 Subject: [PATCH 24/58] only trigger ghcr build on tag event (#3010) --- .github/workflows/publish_ghcr_image.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/publish_ghcr_image.yaml b/.github/workflows/publish_ghcr_image.yaml index 7bfb17a51..78815783a 100644 --- a/.github/workflows/publish_ghcr_image.yaml +++ b/.github/workflows/publish_ghcr_image.yaml @@ -7,8 +7,6 @@ env: on: push: - branches: - - master tags: - '*' From cd05682482304000654ff1d6c6feaae882fbfe39 Mon Sep 17 00:00:00 2001 From: Steven Berler <1030627+berler@users.noreply.github.com> Date: Thu, 11 Dec 2025 01:22:40 -0800 Subject: [PATCH 25/58] fix switchover schedule tests (#2995) * fix switchover schedule tests Previously the tests would fail depending on the local time zone and the time of day the test was being run. --------- Co-authored-by: Felix Kunde Co-authored-by: Mikkel Oscar Lyderik Larsen --- pkg/cluster/cluster.go | 6 +++++- pkg/cluster/cluster_test.go | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index 9cd750e84..b6a4e24a8 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -1767,9 +1767,13 @@ func (c *Cluster) GetStatus() *ClusterStatus { } func (c *Cluster) GetSwitchoverSchedule() string { + now := time.Now().UTC() + return c.getSwitchoverScheduleAtTime(now) +} + +func (c *Cluster) getSwitchoverScheduleAtTime(now time.Time) string { var possibleSwitchover, schedule time.Time - now := time.Now().UTC() for _, window := range c.Spec.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) diff --git a/pkg/cluster/cluster_test.go b/pkg/cluster/cluster_test.go index 25f61db98..d78d4c92e 100644 --- a/pkg/cluster/cluster_test.go +++ b/pkg/cluster/cluster_test.go @@ -2116,7 +2116,7 @@ func TestCompareVolumeMounts(t *testing.T) { } func TestGetSwitchoverSchedule(t *testing.T) { - now := time.Now() + now, _ := time.Parse(time.RFC3339, "2025-11-11T12:35:00Z") futureTimeStart := now.Add(1 * time.Hour) futureWindowTimeStart := futureTimeStart.Format("15:04") @@ -2195,7 +2195,7 @@ func TestGetSwitchoverSchedule(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { cluster.Spec.MaintenanceWindows = tt.windows - schedule := cluster.GetSwitchoverSchedule() + schedule := cluster.getSwitchoverScheduleAtTime(now) if schedule != tt.expected { t.Errorf("Expected GetSwitchoverSchedule to return %s, returned: %s", tt.expected, schedule) } From a27727f8d04cde3f45b48dd0364639d4e0ac54e2 Mon Sep 17 00:00:00 2001 From: Ida Novindasari Date: Fri, 12 Dec 2025 16:49:58 +0100 Subject: [PATCH 26/58] [ui] internal pipeline fix to build ui image (#3013) * fix ui pipeline * adjust logical backup image tagging --- delivery.yaml | 167 +++++++++++++++++++++++++++----------------------- ui/Makefile | 13 ---- 2 files changed, 89 insertions(+), 91 deletions(-) diff --git a/delivery.yaml b/delivery.yaml index 9ab8b5a4c..e582b982b 100644 --- a/delivery.yaml +++ b/delivery.yaml @@ -7,92 +7,103 @@ build_env: &BUILD_ENV MULTI_ARCH_REGISTRY: container-registry-test.zalando.net/acid pipeline: - - id: build-postgres-operator - env: - <<: *BUILD_ENV - type: script - vm_config: - type: linux - size: large - image: cdp-runtime/go - cache: - paths: - - /go/pkg/mod # pkg cache for Go modules - - ~/.cache/go-build # Go build cache - commands: - - desc: Run unit tests - cmd: | - make deps mocks test + - id: build-postgres-operator + env: + <<: *BUILD_ENV + type: script + vm_config: + type: linux + size: large + image: cdp-runtime/go + cache: + paths: + - /go/pkg/mod # pkg cache for Go modules + - ~/.cache/go-build # Go build cache + commands: + - desc: Run unit tests + cmd: | + make deps mocks test - - desc: Build Docker image - cmd: | - if [ -z ${CDP_SOURCE_BRANCH} ]; then - IMAGE=${MULTI_ARCH_REGISTRY}/postgres-operator - else - IMAGE=${MULTI_ARCH_REGISTRY}/postgres-operator-test - fi + - desc: Build Docker image + cmd: | + if [ -z ${CDP_SOURCE_BRANCH} ]; then + IMAGE=${MULTI_ARCH_REGISTRY}/postgres-operator + else + IMAGE=${MULTI_ARCH_REGISTRY}/postgres-operator-test + fi - docker buildx create --config /etc/cdp-buildkitd.toml --driver-opt network=host --bootstrap --use - docker buildx build --platform "linux/amd64,linux/arm64" \ - --build-arg BASE_IMAGE="${ALPINE_BASE_IMAGE}" \ - -t "${IMAGE}:${CDP_BUILD_VERSION}" \ - -f docker/Dockerfile \ - --push . + docker buildx create --config /etc/cdp-buildkitd.toml --driver-opt network=host --bootstrap --use + docker buildx build --platform "linux/amd64,linux/arm64" \ + --build-arg BASE_IMAGE="${ALPINE_BASE_IMAGE}" \ + -t "${IMAGE}:${CDP_BUILD_VERSION}" \ + -f docker/Dockerfile \ + --push . - if [ -z ${CDP_SOURCE_BRANCH} ]; then - cdp-promote-image ${IMAGE}:${CDP_BUILD_VERSION} - fi + if [ -z ${CDP_SOURCE_BRANCH} ]; then + cdp-promote-image ${IMAGE}:${CDP_BUILD_VERSION} + fi - - id: build-operator-ui - env: - <<: *BUILD_ENV - type: script - vm_config: - type: linux + - id: build-operator-ui + env: + <<: *BUILD_ENV + type: script + vm_config: + type: linux - commands: - - desc: 'Prepare environment' - cmd: | - apt-get update - apt-get install -y build-essential + commands: + - desc: 'Prepare environment' + cmd: | + apt-get update + apt-get install -y build-essential - - desc: 'Compile JavaScript app' - cmd: | - cd ui - make appjs + - desc: 'Compile JavaScript app' + cmd: | + cd ui + make appjs - - desc: 'Build and push Docker image' - cmd: | - cd ui - if [ -z ${CDP_SOURCE_BRANCH} ]; then - IMAGE=${MULTI_ARCH_REGISTRY}/postgres-operator-ui - else - IMAGE=${MULTI_ARCH_REGISTRY}/postgres-operator-ui-test - fi - - IMAGE_TAG=$(make docker-push IMAGE=${IMAGE} BASE_IMAGE=${PYTHON_BASE_IMAGE}) + - desc: 'Build and push Docker image' + cmd: | + cd ui + if [ -z ${CDP_SOURCE_BRANCH} ]; then + IMAGE=${MULTI_ARCH_REGISTRY}/postgres-operator-ui + 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 \ + --build-arg BASE_IMAGE="${PYTHON_BASE_IMAGE}" \ + -t ${IMAGE}:${CDP_BUILD_VERSION} \ + --push . - if [ -z ${CDP_SOURCE_BRANCH} ]; then - cdp-promote-image ${IMAGE_TAG} - fi + if [ -z ${CDP_SOURCE_BRANCH} ]; then + cdp-promote-image ${IMAGE}:${CDP_BUILD_VERSION} + fi - - id: build-logical-backup - env: - <<: *BUILD_ENV - type: script - vm_config: - type: linux + - id: build-logical-backup + env: + <<: *BUILD_ENV + type: script + vm_config: + type: linux - commands: - - desc: Build image - cmd: | - cd logical-backup - export TAG=$(git describe --tags --always --dirty) - docker buildx create --config /etc/cdp-buildkitd.toml --driver-opt network=host --bootstrap --use - docker buildx build --platform linux/amd64,linux/arm64 \ - -t ${MULTI_ARCH_REGISTRY}/postgres-operator-logical-backup:${TAG} \ - --push . + commands: + - desc: Build image + cmd: | + cd logical-backup - if [ -z ${CDP_SOURCE_BRANCH} ]; then - cdp-promote-image ${MULTI_ARCH_REGISTRY}/postgres-operator-logical-backup:${TAG} - fi + if [ -z ${CDP_SOURCE_BRANCH} ]; then + IMAGE=${MULTI_ARCH_REGISTRY}/logical-backup + else + IMAGE=${MULTI_ARCH_REGISTRY}/logical-backup-test + fi + + docker buildx create --config /etc/cdp-buildkitd.toml --driver-opt network=host --bootstrap --use + docker buildx build --platform linux/amd64,linux/arm64 \ + -t ${IMAGE}:${CDP_BUILD_VERSION} \ + --push . + + if [ -z ${CDP_SOURCE_BRANCH} ]; then + cdp-promote-image ${IMAGE}:${CDP_BUILD_VERSION} + fi diff --git a/ui/Makefile b/ui/Makefile index 70c2018db..ff4cf94f6 100644 --- a/ui/Makefile +++ b/ui/Makefile @@ -33,18 +33,5 @@ docker: appjs echo "git describe $(shell git describe --tags --always --dirty)" docker build --rm -t "$(IMAGE):$(TAG)$(CDP_TAG)" -f Dockerfile --build-arg BASE_IMAGE="${BASE_IMAGE}" . -docker-push: appjs - echo "Tag ${TAG}" - echo "Version ${VERSION}" - echo "CDP tag ${CDP_TAG}" - echo "git describe $(shell git describe --tags --always --dirty)" - docker buildx create --config /etc/cdp-buildkitd.toml --driver-opt network=host --bootstrap --use - docker buildx build --platform linux/amd64,linux/arm64 \ - --build-arg BASE_IMAGE="${BASE_IMAGE}" \ - -f Dockerfile \ - -t "$(IMAGE):$(TAG)$(CDP_TAG)" \ - --push . - echo "$(IMAGE):$(TAG)$(CDP_TAG)" - mock: docker run -it -p 8081:8081 "$(IMAGE):$(TAG)" --mock From c4f10ceadc9dfd541f61318441920eca1c01b5d2 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Tue, 16 Dec 2025 19:25:12 +0100 Subject: [PATCH 27/58] bump to v1.15.1 (#3011) * bump to v1.15.1 --- .../postgres-operator-issue-template.md | 2 +- README.md | 2 +- charts/postgres-operator-ui/Chart.yaml | 4 +- charts/postgres-operator-ui/index.yaml | 22 +++++------ .../postgres-operator-ui-1.15.0.tgz | Bin 5089 -> 0 bytes .../postgres-operator-ui-1.15.1.tgz | Bin 0 -> 5088 bytes charts/postgres-operator-ui/values.yaml | 2 +- charts/postgres-operator/Chart.yaml | 4 +- charts/postgres-operator/index.yaml | 36 ++++++++++++++---- .../postgres-operator-1.15.1.tgz | Bin 0 -> 18278 bytes charts/postgres-operator/values.yaml | 4 +- delivery.yaml | 1 - kubectl-pg/go.mod | 19 ++++----- kubectl-pg/go.sum | 35 +++++++++-------- manifests/configmap.yaml | 2 +- manifests/operatorconfiguration.crd.yaml | 2 +- manifests/postgres-operator.yaml | 2 +- ...gresql-operator-default-configuration.yaml | 2 +- pkg/controller/operator_config.go | 2 +- pkg/util/config/config.go | 2 +- ui/app/package.json | 2 +- ui/manifests/deployment.yaml | 2 +- 22 files changed, 86 insertions(+), 61 deletions(-) delete mode 100644 charts/postgres-operator-ui/postgres-operator-ui-1.15.0.tgz create mode 100644 charts/postgres-operator-ui/postgres-operator-ui-1.15.1.tgz create mode 100644 charts/postgres-operator/postgres-operator-1.15.1.tgz diff --git a/.github/ISSUE_TEMPLATE/postgres-operator-issue-template.md b/.github/ISSUE_TEMPLATE/postgres-operator-issue-template.md index ee3a704ea..3b731eb72 100644 --- a/.github/ISSUE_TEMPLATE/postgres-operator-issue-template.md +++ b/.github/ISSUE_TEMPLATE/postgres-operator-issue-template.md @@ -9,7 +9,7 @@ assignees: '' Please, answer some short questions which should help us to understand your problem / question better? -- **Which image of the operator are you using?** e.g. ghcr.io/zalando/postgres-operator:v1.13.0 +- **Which image of the operator are you using?** e.g. ghcr.io/zalando/postgres-operator:v1.15.1 - **Where do you run it - cloud or metal? Kubernetes or OpenShift?** [AWS K8s | GCP ... | Bare Metal K8s] - **Are you running Postgres Operator in production?** [yes | no] - **Type of issue?** [Bug report, question, feature request, etc.] diff --git a/README.md b/README.md index cc2bdb17e..7d54e9fd9 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ production for over five years. | Release | Postgres versions | K8s versions | Golang | | :-------- | :---------------: | :---------------: | :-----: | -| v1.15.0 | 13 → 17 | 1.27+ | 1.25.3 | +| v1.15.1 | 13 → 17 | 1.27+ | 1.25.3 | | v1.14.0 | 13 → 17 | 1.27+ | 1.23.4 | | v1.13.0 | 12 → 16 | 1.27+ | 1.22.5 | | v1.12.0 | 11 → 16 | 1.27+ | 1.22.3 | diff --git a/charts/postgres-operator-ui/Chart.yaml b/charts/postgres-operator-ui/Chart.yaml index c9e28e56e..871640467 100644 --- a/charts/postgres-operator-ui/Chart.yaml +++ b/charts/postgres-operator-ui/Chart.yaml @@ -1,7 +1,7 @@ apiVersion: v2 name: postgres-operator-ui -version: 1.15.0 -appVersion: 1.15.0 +version: 1.15.1 +appVersion: 1.15.1 home: https://github.com/zalando/postgres-operator description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience keywords: diff --git a/charts/postgres-operator-ui/index.yaml b/charts/postgres-operator-ui/index.yaml index 3ad4ac84f..20408aeaf 100644 --- a/charts/postgres-operator-ui/index.yaml +++ b/charts/postgres-operator-ui/index.yaml @@ -2,11 +2,11 @@ apiVersion: v1 entries: postgres-operator-ui: - apiVersion: v2 - appVersion: 1.15.0 - created: "2025-10-16T11:34:57.912432565+02:00" + appVersion: 1.15.1 + created: "2025-12-11T12:44:25.470723322+01:00" description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience - digest: d82b5fb7c3d4fd8b106343b2f9472cba5e6050315ab3c520a79366f2b2f20c7a + digest: 4bbb750934366038d692711f924151182b7be131b6822d011f5a4e51cf609482 home: https://github.com/zalando/postgres-operator keywords: - postgres @@ -22,11 +22,11 @@ entries: sources: - https://github.com/zalando/postgres-operator urls: - - postgres-operator-ui-1.15.0.tgz - version: 1.15.0 + - postgres-operator-ui-1.15.1.tgz + version: 1.15.1 - apiVersion: v2 appVersion: 1.14.0 - created: "2025-10-16T11:34:57.906677165+02:00" + created: "2025-12-11T12:44:25.468680645+01:00" description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience digest: e87ed898079a852957a67a4caf3fbd27b9098e413f5d961b7a771a6ae8b3e17c @@ -49,7 +49,7 @@ entries: version: 1.14.0 - apiVersion: v2 appVersion: 1.13.0 - created: "2025-10-16T11:34:57.904106882+02:00" + created: "2025-12-11T12:44:25.466716836+01:00" description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience digest: e0444e516b50f82002d1a733527813c51759a627cefdd1005cea73659f824ea8 @@ -72,7 +72,7 @@ entries: version: 1.13.0 - apiVersion: v2 appVersion: 1.12.2 - created: "2025-10-16T11:34:57.901526106+02:00" + created: "2025-12-11T12:44:25.464739895+01:00" description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience digest: cbcef400c23ccece27d97369ad629278265c013e0a45c0b7f33e7568a082fedd @@ -95,7 +95,7 @@ entries: version: 1.12.2 - apiVersion: v2 appVersion: 1.11.0 - created: "2025-10-16T11:34:57.898843691+02:00" + created: "2025-12-11T12:44:25.462698399+01:00" description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience digest: a45f2284045c2a9a79750a36997386444f39b01ac722b17c84b431457577a3a2 @@ -118,7 +118,7 @@ entries: version: 1.11.0 - apiVersion: v2 appVersion: 1.10.1 - created: "2025-10-16T11:34:57.896283083+02:00" + created: "2025-12-11T12:44:25.460357063+01:00" description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience digest: 2e5e7a82aebee519ec57c6243eb8735124aa4585a3a19c66ffd69638fbeb11ce @@ -139,4 +139,4 @@ entries: urls: - postgres-operator-ui-1.10.1.tgz version: 1.10.1 -generated: "2025-10-16T11:34:57.893034861+02:00" +generated: "2025-12-11T12:44:25.45732896+01:00" diff --git a/charts/postgres-operator-ui/postgres-operator-ui-1.15.0.tgz b/charts/postgres-operator-ui/postgres-operator-ui-1.15.0.tgz deleted file mode 100644 index 95fe866f6e03a19fcdd5161e845db9a1e3565643..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5089 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 DDc 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 CAR(s! literal 0 HcmV?d00001 diff --git a/charts/postgres-operator-ui/values.yaml b/charts/postgres-operator-ui/values.yaml index 2e729ce76..c308335b2 100644 --- a/charts/postgres-operator-ui/values.yaml +++ b/charts/postgres-operator-ui/values.yaml @@ -8,7 +8,7 @@ replicaCount: 1 image: registry: ghcr.io repository: zalando/postgres-operator-ui - tag: v1.15.0 + tag: v1.15.1 pullPolicy: "IfNotPresent" # Optionally specify an array of imagePullSecrets. diff --git a/charts/postgres-operator/Chart.yaml b/charts/postgres-operator/Chart.yaml index 40ffe22bc..6f0d2e762 100644 --- a/charts/postgres-operator/Chart.yaml +++ b/charts/postgres-operator/Chart.yaml @@ -1,7 +1,7 @@ apiVersion: v2 name: postgres-operator -version: 1.15.0 -appVersion: 1.15.0 +version: 1.15.1 +appVersion: 1.15.1 home: https://github.com/zalando/postgres-operator description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes keywords: diff --git a/charts/postgres-operator/index.yaml b/charts/postgres-operator/index.yaml index 025ad2b9d..ad0a890b6 100644 --- a/charts/postgres-operator/index.yaml +++ b/charts/postgres-operator/index.yaml @@ -1,9 +1,31 @@ apiVersion: v1 entries: postgres-operator: + - apiVersion: v2 + appVersion: 1.15.1 + created: "2025-12-11T12:43:37.232803735+01:00" + description: Postgres Operator creates and manages PostgreSQL clusters running + in Kubernetes + digest: 64d9c1d7be7e5bc4661099806b77dc509cb547938c2de6c7105f9665e9ee0aab + home: https://github.com/zalando/postgres-operator + keywords: + - postgres + - operator + - cloud-native + - patroni + - spilo + maintainers: + - email: opensource@zalando.de + name: Zalando + name: postgres-operator + sources: + - https://github.com/zalando/postgres-operator + urls: + - postgres-operator-1.15.1.tgz + version: 1.15.1 - apiVersion: v2 appVersion: 1.15.0 - created: "2025-10-16T11:35:38.533627038+02:00" + created: "2025-12-11T12:43:37.226030229+01:00" description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes digest: 002dd47647bf51fbba023bd1762d807be478cf37de7a44b80cd01ac1f20bd94a @@ -25,7 +47,7 @@ entries: version: 1.15.0 - apiVersion: v2 appVersion: 1.14.0 - created: "2025-10-16T11:35:38.52489216+02:00" + created: "2025-12-11T12:43:37.217094172+01:00" description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes digest: 36e1571f3f455b213f16cdda7b1158648e8e84deb804ba47ed6b9b6d19263ba8 @@ -47,7 +69,7 @@ entries: version: 1.14.0 - apiVersion: v2 appVersion: 1.13.0 - created: "2025-10-16T11:35:38.517347652+02:00" + created: "2025-12-11T12:43:37.211148314+01:00" description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes digest: a839601689aea0a7e6bc0712a5244d435683cf3314c95794097ff08540e1dfef @@ -69,7 +91,7 @@ entries: version: 1.13.0 - apiVersion: v2 appVersion: 1.12.2 - created: "2025-10-16T11:35:38.510819005+02:00" + created: "2025-12-11T12:43:37.202520969+01:00" description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes digest: 65858d14a40d7fd90c32bd9fc60021acc9555c161079f43a365c70171eaf21d8 @@ -91,7 +113,7 @@ entries: version: 1.12.2 - apiVersion: v2 appVersion: 1.11.0 - created: "2025-10-16T11:35:38.503781253+02:00" + created: "2025-12-11T12:43:37.197036041+01:00" description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes digest: 3914b5e117bda0834f05c9207f007e2ac372864cf6e86dcc2e1362bbe46c14d9 @@ -113,7 +135,7 @@ entries: version: 1.11.0 - apiVersion: v2 appVersion: 1.10.1 - created: "2025-10-16T11:35:38.494366224+02:00" + created: "2025-12-11T12:43:37.191421669+01:00" description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes digest: cc3baa41753da92466223d0b334df27e79c882296577b404a8e9071411fcf19c @@ -133,4 +155,4 @@ entries: urls: - postgres-operator-1.10.1.tgz version: 1.10.1 -generated: "2025-10-16T11:35:38.487472753+02:00" +generated: "2025-12-11T12:43:37.184779748+01:00" diff --git a/charts/postgres-operator/postgres-operator-1.15.1.tgz b/charts/postgres-operator/postgres-operator-1.15.1.tgz new file mode 100644 index 0000000000000000000000000000000000000000..1e9373dbd081595911e65364fa86df6613a417c4 GIT binary patch literal 18278 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 pg7utrc(8jgJjmtSV> Date: Thu, 18 Dec 2025 12:12:53 +0100 Subject: [PATCH 28/58] fix docker build for UI and bumped some outdated versions in docs and config (#3017) * fix docker build for UI and bumped some outdated versions in docs and config * update helm chart image again because of wrong format field * switch to new registry ghcr.io for e2e test * update e2e test runner Dockerfile --- .../crds/operatorconfigurations.yaml | 2 +- .../postgres-operator/crds/postgresqls.yaml | 1 - charts/postgres-operator/index.yaml | 18 ++++++------- .../postgres-operator-1.15.1.tgz | Bin 18278 -> 18344 bytes docs/administrator.md | 2 +- docs/developer.md | 10 +++---- docs/reference/operator_parameters.md | 2 +- e2e/Dockerfile | 25 +++++++----------- e2e/Makefile | 4 +-- e2e/exec_into_env.sh | 4 +-- e2e/requirements.txt | 4 +-- e2e/run.sh | 8 +++--- manifests/postgresql.crd.yaml | 1 - pkg/apis/acid.zalan.do/v1/crds.go | 9 +++---- pkg/apis/acid.zalan.do/v1/util_test.go | 2 -- ui/Dockerfile | 2 +- 16 files changed, 41 insertions(+), 53 deletions(-) diff --git a/charts/postgres-operator/crds/operatorconfigurations.yaml b/charts/postgres-operator/crds/operatorconfigurations.yaml index c6da405a5..58e84bd2f 100644 --- a/charts/postgres-operator/crds/operatorconfigurations.yaml +++ b/charts/postgres-operator/crds/operatorconfigurations.yaml @@ -510,7 +510,7 @@ spec: pattern: '^(\d+m|\d+(\.\d{1,3})?)$' logical_backup_docker_image: type: string - default: "ghcr.io/zalando/postgres-operator/logical-backup:v1.13.0" + default: "ghcr.io/zalando/postgres-operator/logical-backup:v1.15.1" logical_backup_google_application_credentials: type: string logical_backup_job_prefix: diff --git a/charts/postgres-operator/crds/postgresqls.yaml b/charts/postgres-operator/crds/postgresqls.yaml index 8083e5e1d..667c58efa 100644 --- a/charts/postgres-operator/crds/postgresqls.yaml +++ b/charts/postgres-operator/crds/postgresqls.yaml @@ -278,7 +278,6 @@ spec: items: type: string weight: - format: int32 type: integer requiredDuringSchedulingIgnoredDuringExecution: type: object diff --git a/charts/postgres-operator/index.yaml b/charts/postgres-operator/index.yaml index ad0a890b6..7128b8eb9 100644 --- a/charts/postgres-operator/index.yaml +++ b/charts/postgres-operator/index.yaml @@ -3,10 +3,10 @@ entries: postgres-operator: - apiVersion: v2 appVersion: 1.15.1 - created: "2025-12-11T12:43:37.232803735+01:00" + created: "2025-12-17T14:48:33.832345061+01:00" description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes - digest: 64d9c1d7be7e5bc4661099806b77dc509cb547938c2de6c7105f9665e9ee0aab + digest: 9f3edc3d796105c02c04eaae28a78e58fb08c1847a9de012245fd6ac2c0d2c00 home: https://github.com/zalando/postgres-operator keywords: - postgres @@ -25,7 +25,7 @@ entries: version: 1.15.1 - apiVersion: v2 appVersion: 1.15.0 - created: "2025-12-11T12:43:37.226030229+01:00" + created: "2025-12-17T14:48:33.826117296+01:00" description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes digest: 002dd47647bf51fbba023bd1762d807be478cf37de7a44b80cd01ac1f20bd94a @@ -47,7 +47,7 @@ entries: version: 1.15.0 - apiVersion: v2 appVersion: 1.14.0 - created: "2025-12-11T12:43:37.217094172+01:00" + created: "2025-12-17T14:48:33.819729144+01:00" description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes digest: 36e1571f3f455b213f16cdda7b1158648e8e84deb804ba47ed6b9b6d19263ba8 @@ -69,7 +69,7 @@ entries: version: 1.14.0 - apiVersion: v2 appVersion: 1.13.0 - created: "2025-12-11T12:43:37.211148314+01:00" + created: "2025-12-17T14:48:33.81038602+01:00" description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes digest: a839601689aea0a7e6bc0712a5244d435683cf3314c95794097ff08540e1dfef @@ -91,7 +91,7 @@ entries: version: 1.13.0 - apiVersion: v2 appVersion: 1.12.2 - created: "2025-12-11T12:43:37.202520969+01:00" + created: "2025-12-17T14:48:33.803256825+01:00" description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes digest: 65858d14a40d7fd90c32bd9fc60021acc9555c161079f43a365c70171eaf21d8 @@ -113,7 +113,7 @@ entries: version: 1.12.2 - apiVersion: v2 appVersion: 1.11.0 - created: "2025-12-11T12:43:37.197036041+01:00" + created: "2025-12-17T14:48:33.797369053+01:00" description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes digest: 3914b5e117bda0834f05c9207f007e2ac372864cf6e86dcc2e1362bbe46c14d9 @@ -135,7 +135,7 @@ entries: version: 1.11.0 - apiVersion: v2 appVersion: 1.10.1 - created: "2025-12-11T12:43:37.191421669+01:00" + created: "2025-12-17T14:48:33.791368349+01:00" description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes digest: cc3baa41753da92466223d0b334df27e79c882296577b404a8e9071411fcf19c @@ -155,4 +155,4 @@ entries: urls: - postgres-operator-1.10.1.tgz version: 1.10.1 -generated: "2025-12-11T12:43:37.184779748+01:00" +generated: "2025-12-17T14:48:33.785159183+01:00" diff --git a/charts/postgres-operator/postgres-operator-1.15.1.tgz b/charts/postgres-operator/postgres-operator-1.15.1.tgz index 1e9373dbd081595911e65364fa86df6613a417c4..7dce6f87d253c4a707ecc14d855e8c164a039f47 100644 GIT binary patch literal 18344 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
<-Um&qb!-ikePjCTH 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 DAPda- literal 18278 zcmV)kK%l=LiwG0|00000|0w_~VMtOiV@ORlOnEsqVl!4SWK%V1T2nbTPgYhoO;>Dc zVQyr3R8em|NM&qo0PMZ}d)qeBDEj`azXDg;^K87@K>E1nU z4;LaK2@OfG0BKwKxPSY3FaSu9A}Lyu?d-c|pC^?`U}gXe1~Y@fAZ8+&aUvWR6OI$c zcaGtN-~Hc|j@?!Uf`K6wzpgXw*UI=X64NL=qx8a~6^@nkPvt z#ydMRn#|Kl&tu`vCmi6&XFE$A+WANpH;nsY+;Nck+Hp|!-9cW!Qs0SiLa&J|gcHsp z+HsJGX}~%mrcr`vL^!}8A*R7t){g{BxkvuH#@+Kt2cZZ{3jddS)lq*f6QybbygN1K zy42kdJpRk`{~8A=5!)01&GUb6*xx@W&Huf@?*7yKe~5>Mct*w@ggBW|k?_SB&E_8O zQ3mf~CTPOs+j}Kg0;fmWj?X-?eRDL&f9E*U+2)d3An<0ydjT**axB#7T!lcoLBR6fAnHK8(>62ZG4*GZGPw z1LWDjP>eYaiImeC`5RSCGR8CLTA#!LTZAM^keIVH@O9tsq_K|^0%eXbUyBYx3cc%b z-_=Zo)#8!xlmI|8?jBCajB!c?N+X|ebTg;k9C_Rq2Frw?fQp3p9VFkoc@;^UgSt2f zx&TxY!lGaS)QAKpWSRy-a?TA+<}6JR4MXBnoRDCFd?x!$7Ue7m=VZ3Iv4-l>8yvW@wyU7oU29|ux}iaU#3J(4xMteK z&75#G-&Moa2&07H@YF{!CsTTxbttStmdjRHd2y!zMK~k@nBMjgzHI#E|db% zLnmRJEKn+lKtCS}tzn?o$5%axE%AJJ&V-z_iYHYU_{{rAxax%t(to;2@)~KIX$a6a z&fs9Y+v_{=r~`05l7(ulBnD^F10@X6S;RO&vw%&MNYoP>q@qZ0i%s<;N9>w#kA)#kq!>3M%vE1qjl)ft z;)&u&;8+3@)F&S1YMqs1Y;(5aFI~j7nOvx41d&@D#sL|luKuN_fc_)FGtG4Q0CAQk zG?KXHBo_)I#y1h*k2&**5LpMwuJcmPNt6Uqs@*XDQ7ZZ9P_BLXDu)!_fwt=LEvp_X=LePxkNa`E1=|;k`8>u=#QSAFvDK{>>b)l{T;~LHZV?_{3<~TuEN;{TRU^PpFhT4i(hc z9wd;2sUSWygOfO*o&s!UP&P}|lr!I5Q+q5|jmV!Yioiu06E4drVXMa6=$MlQq7hB} zNe8Kd7PQO?)$H>Q|G~I6q(5lQ0Z?_>blM$Dp`sOvm#C|>+3r|MOgbeZK?$d`nH+lz zE742Tou&blpb-smVCfMo&?|wdk}w9M>JP?Uqb+D$ZpfqtV-RYs2|fw6(!wGu+l)D< z#79zVkbRu3yKJh}F<5G09gdI$i>OeI)AFJUJuJgG2-uA)ftY!LJ?duyADWj{&|rF)#uqSx*(0GW zBRS|AKFIwXsfjk`J2@Pl>wP4xT88>EjNm;hUOzOdTHPeyZtAAX%XQ#&} zT8`?)8NEKcLOu)Sn!u(oy>*kc8kGrFnz?DDwynyzs^&Ob76aztphOZ1zTnhXy)J7Ab(tyVF3lXQ#bJSeQWD16Wxb9Qm%? zgj@vzX^(wCNv;WB6x3<8-lig9A&i7oPp@-QUBhDpjuf1~n&?-P6*`^da!`pCXKI-M zRh5>jS2(1N6*cm^d2<@rU8da#b9_w@j+EMjIGGYoq-9joe;T7!;!Np6qb!`cVycko9=!PF80>7r z$;;gp%QZttWk(y`t`}e$x~61B6Qw{k2#wtL1W8Mi@>a? z3q|kN#hIGg)XTb1Rj(S(G=`10%$)&?X7aOPOx4>$ichr|7%U2eVM`@Sic89bX!F}w zDuxoZawgfns+UL*eoZCwlEmv-R(r)-LpSTDeFfWd{X2Zm0f!i3Wb%qqx;(}YQ}#CPO-29 zh3nP>xkhQzN1C>>=b|~`G+Q2Q?ZPY(nj~GWsud$_qqQ!BwQ|opnz22swKjOnq$;GzwF*|7Ra~f zM`wuOfAtSCEGuh!%xnJAJh>36AAFT z<XuP{d4Z1d-{o(W%_*q?L@PJJNomt)Hj5W`-Ol6g|s0^Nb#M3U!rm=W2aThy(H z88BA?Q-*_t=xT*%jJll2;p^s~K(Q-+d4|LdO}u&KQkpDcPznAS!dI} zG&@y-SiL^E1uq?o{wXOhEwpY1!&T~%D>5hrSNApHT&^z(i&?;C3sBFMAy}fX3X7?w zRqoJaWE6W-T2cw&A&r#9$+#y6G*}=!m8}HxNw1)~hqL1Dj7w{-&JmfsK$^5$HK5f2NeHIW5fzWkg_UQb!JFEHPUc zFSPWb0dsj8VIeV$(V#yle*lc~i{2Yl6r8?RZMu<~sDnY3lt|SoD%PZAx`gd!PQVia z61&t|t*uDtG(s__*EAqfbqke_s$k~t?M`h87usk!?P578g-~1C8UTiP1`;Jby&<6+ zK3TvPj1Dr02=N8%YEnO=O*yAC3SA;Kh^6R-gmF9rhYI~f5X3i-+2SR_%4`ng#8yTh z=46b>M7%`twf7RS=@fyhTn4OOt_nEQYl zwtLbL*)lHXl2wC+^5RrYkdQ|9unzb1NB~$(%leRuV>8^7-Pp-2et|d%u--}*EVLZ4 z+8=77z;yYsb~WfN)v8bdoXE9QDWn-R53A57m?A2QMVqZUDAvU>b-g+9K3aRZ15t2K zfaP|BX=37GEMEmDB9=rb^L-VH%qg+A0>iQbtaL+d?P;CNxb$3EH3vS>BUuYw3Rloe zLG95hkn)RY$jWlA2HS2Fiuf%5^6TQpGHg*}f1!}&V=xj+E+VB&L9m21k0nHar5sHb zu#ECiUlb`%7|_?;f`lO$H8tdtNq;^Rxo=%XqrmSa1{$>(-WY;HJum=q7PVrdK+j2U z)-E_c`otn) zZ+XK@P{^ST1xQl2w!*EfT2i1+AQX)Ft^_Gp-JT9~>3J-yau4G;4i?H0uWX(&MsUvx zqlVl2keM}9xfgk>Hy%G#ZBG_F?FKVALW*q#b5q-Ba`bYyq%q)^_P>l1iecY1aH{tL0tO&jE@>hiA9%p zb@|-6ajYomE;$*qX=E&Y+g!Frw%7&MlB$N&k3`8OnOlmVGo|rs@3WeFa_W6G#KfF< zu;fZw3ivB=r$W(*L$lp&_?gVGw`e9sR;#As4qajfysl5oC^t~z$TA!BOf?D1U6b+q z%D&MQRdrD3S!Os%aY%jSlS!&V+cl@>p(=?W!BmU#*Rt5SFt4r&pD;lb1Qf8D$ait9 zAQAZN1}02?X}dMUH%%i?X|CWXp5w&mgeW(*3+lD;0p+vOym3_xvr||IOvGS!$rhWS zeyO5do?N|`I+eDP2+!6qD04|fauie5150J~4^1JJ>J*pSwbaVw&oMge>oB`K@{&Xe z#N`_oj$RGtR2r=d1PnV#r&D5w+UT#Yt_;>g8E^pnufbYil-Cb}JQ&Xr%8`@&MI#s{ z7peo`;&KnFhf0^*#(sj3@f+|E|CmUebQu`%jOJxaTr9V;Z}0w?nvngoR>>^k#`8@C?~h**e( zgqi3!M%;7lxT(Pmk@jQM-I4fre9ARm*%h43bX2A_04G0Q8RwDYl^fxzVq-*7$CD$B z17}c(WXukiDIP+NTGY;;@H9{E0>QM!?BIZKh-!B`{$1Ien)%YZ!+NclWV+ldWS(_u|*C9QgRldKiDJ-)+)y# zW^$T(IQS8JA5$Y_Drprl@kBleCs08eJ!z0+qa#7N>XVBlm@=(;_gNo8X{?dSk9Nfk zg2Ew@Jlab3v951ukD31x(J3Nua;DxdSD0B!te!@0V?qw0_KnM>6UIElrk zXa+%8>J0SK=HDNJMX-!iHC@$ot-zIP8K%xoWqPXBcC8MU;QqlT*?FsOu?bGM;8Rz} z;iW_ltq!hEC$pW3eUallYS`m}n>{35J zwPj1Xnu)j!D-%ZH!co7Slm}&qy6cooYJijX7wk-AVDwterW#-+2(wb>?J@hsDCL3j zD?}{Odi>GfuPULKIyM}wj^sUCSyL+>dGFdFBu5es8*hk29$&=Taa93F$XRi8ay8uB zw|cff za}Y|-nWrJvhj8*^lMu~h#D#G_%lDaKp^k6(A&o%IPy!nQYn3WLn=79o9Fo$3gB}aP zby9Xm&>#?NXUDw7p&=5ki+<6+OU=nC0U5U=+_FP))Vh*HQi z2heEBY|rfu1p5tlptk(@H!bGi#SbW@5E~hUKkSe&4%mX2DA(elX6*a0(`j{We$)`m zbhWV$ahbBL)FkE;3v~#cP&(pU9oTBlXi2Ncj9x^fl{Y8W0K;si5hB=| zS8ZoyoSRiqqoIQEoQm&7GIX%X?TPw+Taf+6ujnpInYqt3$0PGlb zO)O$J4}3BQ3Du;!(^M+S+cC-;V(;Nj>CAQ@*&7Kkpk)TjAjeeoUXk&k=fDI^Z~~!g zW67t0R?e=a8EcjdvqZoobyFUwIW*qcnc|Pcflm&OX-{u8_r$f=D;_E|bA1eWm-XE78(fC9$k`fl8Nh(-zPAPc$ip{uG811taT8jvu9 zlvMykW}4kH2qg-O0E(#rmg^YPz@%sC0*7 zC80TNp6@g5Z}7~#Iy~rkDl-PgUR$ni4D}qZN3+ZS(#vTLaV5wuS&t{y=bjly? zaR2#GY)0Co&n*AhXd`MGIF`)+*lEruuFaLMyvY%=|7=Yju{@EkDs9zrtP& zr;O8NfxG|Ew+>j@-ClJQ6a0m*Ru6BW?bRMZ$vH-Mws z7gF8oo$T)mIFs2yaCVo<;STWPig+yY#Tf0W6FXGS zd;Mxq+Lt4@3CGn3H#*PIN&Q!l_Ylnt(ZspjHnH#8Fd{zcV#(Isdqtu0WZW#X+djEPHqYKx)@|>O5}=^ZIxlQp_pd@+L4S}IOk|Y?<~)! z^BnRwCEh$=$a`vDD0|r?xqJ5y_m`tf9aaPZMPn2$9Qaiz_~!iG(f@aGd47Cl7ygN} z(8MX_pw68M*7nt#c^jrH{Gd2w1saPsc$>ATbSTSA~SqsjS`9Z)ofvcS~@r#PRX z%R8hQ%+0JF1w%?HCt^O?d1g*@fp&qTggV)=ul(%x=dY5WGpXASlV|$iJ_$^MVTB`z(%pO?)+dAg4|O?ad?AcRKlRF_s)Iwc&)bARzqob-m7% zmqMqnEtN@4Uq9Qdusln$Cljgg1VOHMWCu_o*<_;5X)E`VI(Q?csfx?6)3CuI_cYQL zDHTOLU1_1^_F7JBcZ~j@{4eM9wZo=PGAG%N&;M&Jj=A5ftWp&l{{Xt)W2Y^Bdbac| zZ!wy&7CUF>?@z9J$!)SBj#c@;hx@}){@?xGe*Y=|??XKA8M-E1op{B`a$ajR$5|?t zmr_F=cRC2PF)s;_<7APAKYad-dY2?1SdiXXc82ilS2QDu+A?eDp;-8l(j!bLBMH%bM*V*c;$5B5s)|6q7<@HGD);`#h} z=R0&w!!f7`QyP$D5tARJ@`JrO8KduZzJBeo8eGLyZ=ma z0B#b$v~kFP~%)+Y)|VLi!dExf%mfib61GDY40CY=8!x+Q1|?_D^q?sth3%W+t( z$Ft}&KC?zP>*41#@`BV~!4AC_S@#!V!f2po@+(p?tZYKe{8rT419IJM-@%e>E4ExCN#G^}6|wqi8f%-)>EY-p?LwM3v?VoV}dEDCYfsuVar z+OEstf{xY~C$R7{{`TP1$Yx0fdP-~z$%ok64mn$~( zmO)hY^AYsFCjDP;!NFE%LHynEZlD$V|8V!9bpOwAxIfx`(*Gag`TW_|5BUUpJ?$g> z`n6+U4hElan#>uepHwEFkB4BuUk^HR?vL#~`%Xv_?BfKFv&}b&X=xFu(-E}1DrJeT z@Eic#cCG_}GABXU6Z0Kd8&+1>BN_7iVz-L_^{7Pxi;qK9>6Fr!l^>`I+4wU*}mhM!*TRUGIGB7dT8!czi^ zi-T}X-*A@3=IWiUbsk^8%d2+~oWV5jfv4c7ijlK^dQwgBbP%|MW)Rfwz#0KRV%cK& zmEF(dsBY-kBDfWOdG9m$^4zPLsAM_7)OZR^c`@{a=Y5jkQ=aJX{MmNWk5}=g;90W=l zsc;{YYX)+^Tc&b~C!BhK!60~W)g?dv#JGfF zE`2<{e63v>utTV>P^=h-nC7dqmKqhRBe1i1nWu2A7@BsPZOuKDWfh~;iA#M_?g=6P zP~%}D&Wbp*c0GEjfmO0Y2`uFc<9VkVTV83+pDV)5? zyl5?lv92AGxNqGINf{%-5$o;rf9* zB30%1Z$%QoXer4JJDB|Q1d&@RK!#oXax8l2*qoTXrWjr6jNeynM>tPpuacEO8v7D$ z6+KiMPCZE(N6%Z>Qp@RN^(>9Az3z+6KtRsg?h7L@mt`Z$yVUAPyC1EXJQfk?R(}!U zkb3Iazg!+@G+VEKn})r;HCo$zG7;Tk+u=l~R7F&pEI~i%v-DYRoAR_%_P$DPtdfvN zKJr=V_TkMrZ^=!tn$PN27@g#}lzfOrb~``=ppl&W5ISh3=V?#!4!I>>r7*E&mcAGV zQvOqQc%*z_)jCABxg+CFf(&Sc6E69fnJcZ;c9^KVF|3JV9LE(74g0M0o^$$x)@|>zG9+t?dbv-OoDX|%3jU+yKDAm)_|EK$OJvZQr{lCF*zZCyB z9PEwup8S6g@!XUD??=TiPj0|%xB*!}^l2p{Q5uG$gV3@!cEipWsmh)??l>BFai!UO zaIO0w>f*@%EA6^`J5QthH*SuNc7ImN|IzSZw;cby-+zk#c#ucyLFCVp2BZSeP`4Oh zW-kc2*f&M0rG1l3wL%Bm%YlF6r9gFV?iFf`64mQ?t4LCH>4gbvbs-YgT@kla5d_;S z1+<~cyV}-`2ZDB+Dh-j4m9A{`1<}YSQG$l+!}G!zcakK^|M9!*MKfYd%j8Q_$=FZFR5vRB1&_ zAfKAPMn#O#0Io^Zc{o%?A)I*gw^r|OtN+*OitN@_o$M&*4}e#^hRQm3($dZ_M>Ak+ zSfrsgMGH!!I^===(tEFI>HUZWfqOK%CDXP00`<@rB<6Uyw=dE#M`4EuY>c!rKB^4? zMp@3f$6Fj94+6`R+#SASk^>QZCP z#Dd>TcEtt`D`1rD0!@R$$z_Cnyv*t$lh0F@&<%oDJ(J)Qef_#q@iAtC!p%EhzmAta zCwK;_5ei5y(jd6d_i`=Oh_jD2aU{APSC5H`rTS07BCU$r)l92T&W?V3d*c4*)%ltG z_WaG8)3Z0$c`cnDO6{CfkJh?k!VqwLe)iMp8~5GOg?o1N?qnJHU1fKjh%g8*&ad9T zxjeaY&o53ckKUhOx{8Dkmq+hU&(GZRAOG{@`27~?sOYQ86PLLUzoL)cU!K2xdvfW* zr3iPUtawQD4%B6N%c>`J{<+1ZMyRlVwhS%b0nVTe`gZN5bbQM&N=51*m6};hoL3~c zoKnv`^CXGikYrpq+o-N<+JVbFKNY_)ttB;?h&^+;SMkhbWfngRRFTMDMe!oMQu4og zNQHtmhc7ETG!tc4I$kX+SpHHqh)cCwaaWdatpcL?Rtc$)JguU#{k;-^o%^h!zV*3g zowt|KrF@$inu;8z*3i{fd@{ZN>pe~Of4x3?Hyo?>e|F3He}=>1{?qxN2YFU0T<4kx zj%^+|zI?s39aKtjcs=N9KSCvhr>&y&h$gK<}9^KetO&9MSl#l}b#=)5b&HHLH3W>@oeS8E_8&0(8+T@oJLG^6g7 zF;{A>GgTV9JKry=>gDWa7umFu%3TYx>UO^>;QH?NRp87PR27Kg#n#Pm?A@m-SolV` zjJq>0UIEDLI#m;7U(mIZB5TK~3YxF(HEsj~n@pA6n_ZZ+qJQ;9P)&2BQ6YyuX#uKe zgH!=rx(H(h#PY^cC6k(aGMc$km0AYy@2%A~>3_DQa*VKaKv>>M3tSoh*)Q+^jE1|T zC;jgso;K@dbwH@T<&Z`*D-hHt!s9enHxT^wgCO}C8FR=?X=;7tO)nhU#FF?3*^ZQ0Go__100>1pw_kpJ41vHmi!LjL#nm-0XM z_xFcS^8X>8;`y&qV(4nczr0MCLqJ-FT@ZIsVjJPFk_|KXabDfVbIbF8zVcQgLm z?EmcUm-m16p7g(md1}vpR^DV<76lt5g|jDbJ(%pmS0`Z(+<#q&Ao{nO^=m1jW3_>! zt`64;Nt+`h}=hfxX<~C`R>eovgfwLd(}*4aZ1iUn)4o+X|5@`d19J% zm`3*8LkX;!Bw_y!2hNxEzn)|+I*S-5Tf(u*{@Yu+|7&mXbpHEc9>`0c5FSBp9yRPL zq1Pm(fls3uim~?*&xq)Co}u@1Dv(HHa8DsICqV!gfveLgG@89coCG+b*91aXt@k+c zJI_!gMI&VqdLDB!rMJXa7li*`FM8-a3KnoNd8|l6G2tkn5$Sb$udm#zgmKb&hK^Yn zvIzZhe1&|W4^DObq5LkF`=9H}yj9u&LUE9C9Mm;Soin1*EDbQPDWuBG8Bfk~ z@*KosRSN#}a6)E`)BH0If(}B$V=)<{%=e`ee+hHC&K&Hx9w;n?UlV@}_dBZ>DygOY zCY=-%CRIwRMR4Oc42SQ-;Nswd(A7AOm-e?Am; zzCf@kAS%JSzPphwi)Xcq3}W=$AS1^Rs&zaPQ)H8k4X8H2HybJF@VO@Y?fzEd{ za!#{^ls1);5oUx}k@=2BG)zMjrQw8dNpP0a5?1kY%F9(mhct3&fe1t1QHkTU=~seg zWRh}bQ5LJW3ISI4lsw^ha(Z%U6qpQMegMmTvE9)T7pDE?Ux6kXSv{;1~ zxnM24UKR^_PcJ4sp+wkFL4Fm^{#0;_dPtOp#iCAw;8Tw!`qxKNj{3Gfmm-jtQItBi zz9Rq{_uaCHTn33b$BU(JQ5pnz64)}(erQ6%3WyCofsiea>WHbVh|HaeW7M6^Jua6z zeF4A@*9;i8+x2=dVLZ zfIlrLj{$uQ9)eE*108Rf&=B132{14Gvssbh#c2W1QSa zPbCD0F7;h4#$l;bhTL|sC7$ojnW$~U%BfYJJIeIS))W`+Z5D2xslfe67I(r{do{vV zdm_ZQ`9fF+xwtUfkgbW>bq44M5{%O zr)zW5s2`RI;v~kDZwh#C39cZC8*SN#i8%pV%1NV-5xa>Tb?bX2${WT%lKMu}@qfnB4IF(~ z`lJtJ-Kk~uS`{jcqRVa~!d=+puPWI*C}{P-pr*k=tENr9USlC{m{fzb@NOcuZg!wm zGX%jg{1PS?tl?`MG@+~;?*htK{Z{zoc)Oe=BwE$bR#*pzRcLkIp$s9ITGd0eVuWm5 za#iprRg5CinD8xPGG3yk#k2DKVKt()=1wIBxHzO~q#D|kFAX7%QXDL=gLOzjHG^Bp z+<;bTPN>EF-DGltG{b>gAaCPt7%Z7VC<}SrHmZX%s`4q*Wx$(C+?YwTHpUYg(7NLH zJ8Z^n<+Bje%{x(p>jliK=3axZoP}x`U+p^GUh}vU@ip~voC(J<0!yo*Rk`zTTRrK( z)_4_4aL5A(RU}-iLi;cssv<9J)5^yQskX1y0tbD(HaK<@2@eN^x9zq2nRc0{h3TW` z;M~{eyzfn)(O9Q6!U6qMsYz}Z6OCx%E+TX zXq4Nl^#XA}qK-yWjzz*#FG)F3=4BzF#X54l0qCRJt_^E!_1;nOG52a_+j7-~r0MjL zRJTKwFqUL}sb(EjJvlM;W!hM3+Hq04Xx{;j)v%3WNv_!j96b$X6V*BBEdiADZ3|$6 zX`Q-#2ROyVzfFMZ#TJd`gwy2l#gZXowJCB>7BdE$K`yHyyC$yAh_kWfFkE(5Bb}bm zT2EJ6DGy^VR8i%IgjemWw(5uFZR-4SQ1|HUbyer+ujiLlAL}wPZ8}hSU~(4URE0kF zC$8od)jh@Iz10M4qBcl`U}a4i536<(*KDWrSsS%qo!FmLHnm2h&OD&5xO# z&!=A|B4IHL*le-3Pr+vOVi4n;iKNbdBk3T~HRUV{CBJ7o$ZOG1hPK*~&)p9SG= zjJz$%tuA{`%qlkes}Vy->uYEqqTvbQA&qcC+!@Cnsom(mBf8}cf0UV7YRPTZuZ9=TG{Qpm-+CLa77!Jtz5$f!<{GPR;N|G+i@&}2%a{NCm%pwc zKnGeq7704sYm43Roc!j0|Ke{MCEvgJn|}4{$;JCO?|%NBwkB(DsZ+(nwQ)%|4O>rst( z(->auy$e|yt=GUBSa6tcJ%Bd?j)-o-KT1-^BT>R};0ziT6EYF*EFQTbovrqRY(umz zA#|mECg`We6|~(MiGIK8ki^W3UCVvCR?mJ+C&Z;V1b6mY5CS%H#mJqc-p4h!cW!Xt z;!m#e*l7p*+Ms4)Q)1R{PWif>>Z)ta?;>aye@Zzi_u2ZpA&t4HErpL$^&49 zjq}P9rS^%D6n;bqDf?Wy-cIf^%ng>lI$)d3on@FL@?%EVT;G?$ZJ<>y1JlZGJ}%pW zE4H#KLvu~oSwgq7)awAJb(e_J^#AhPcQ2mrsA>P3_`dY&#pm6xm2GyoWmw<1Ih_O|MGu;s=ycA8~?%@P{1| z#sOOpQnP!fZ2Rp5%4zk#X5UV8cQ)K=JRbfSL(Ex1yoC69%O8P?73a7er0zhd{w9|- zx2^!RD%-Ra>2w>GjfpX@j3$Cq4w-=77#D!5M-)>vaII(BH*zo^Tww`LAWV6z)1_85 z5oDB`^38Whjrahl1^Izvy0g5WS-w8G8ufY(=$N{2HC#iRTDd#3o8^j+$!IXAB9Ue* ziIVEfi+4m<<6VX5e>tK7jkbfOR6uw*Snx4I&zf=)ZZIGU0kGP-ri=#hIBvDV=6q!w za8BK0_}hxX1J%(Un#A>;a2mk3w92F~ODI7m<9q*SD&U?%a@~XeCV{Iy^UE{==aCI*W~GG_tf0~W8Ijx@%8_!?*AF| z2ZQqc|AW1~r~7{%;<=ame{Ak~oJ)Ul3gBga(@*|k>3W~yt%3HPJ|$?&7x~0$q4^iQ z!sk7~VW&cVo5jOQ&@PK2PDIQiK_>yxclWHi*hLOqFCjQQ^$VDbx1+2=uJK&`&$p)M zYB1$v)w2s4tkzvaRk~>L?w#y18(FL6JvKmu(udEdrH1ee4PPsdP=!#Run7X-zE=>(a!RbxUKN<#Vi|BIC`T*3?ALi_-!@??97V zn0o!jbYvE7Mimmsu@)C3F6}==hu8!&A+neiv6rg14YGEI+5T{ZsQu~SsUA{ zDR1IRD>ZB4+7N_l!`k&#lJ(;i$$MAt>b6R)xnlFT1-bT^HpYQ1zn1fJ7wG>>cTOR zESkd<>ZPO@W%rh?MMz@Qt}QqNf9*T3e*gZ3^SlpS`(^NJe=z(#|NGa$@OLO98Fldc zi(lU-zdx71)Jw_Squ*bQp95>dyTTt|_WLiM|JKu3{`UO&uiraW^{wi4`)be7`#C|J z%*bu5?${;*Oa!T{Ikq(@Rt?O zSR$p}oC*Yj)l6EOpo*$#9rx=)hA2(x^49(ecF3L17u(+nREI-O*WMWm@$Ks$k2l@0 z`i8fkmUQ5I**|K?WrW&j2dmSEs(S@jX)%ooNW0A32zhbZ^tr{LX86{O$54+lq3?&3 zO=1s}3Lvfgr{UNVDeDi%27wQ21u&TK{71ObFkjeA0!ERov_6^>4r{C-5W$@#0)?qa z(1akLcmd|bM>K*39ir9IN3t+ZKnY6z-7B}aX^CkdS8X~Tw_N?I6*&dtQXT!@2B^2{ z)X9&H=X#p}yn~RCw+#C~uE-~|8j5Phrm%GHYzfSEXu6t*zpQa5F^R4p(z4zDeVr?B zC2DF++v}~r#YQ(dbN-bEFtFvrW&Q1#4RWsl;;QykUZbGj{4`#Or7|1I8T@w|`Rr!9 zzSlNRq2JJV&!4|z(U4Mmn)onxk{rK z((vtt^L*W!zoG9cx(aqQR0^-7rVL*9y~0P#Cr7n+Zf#&DX6f)>LpJ=YEE)c(y2nYq zKDi}cy6TL^Mu%)Ry2QxQ%^*>w?_CwSA#^sc!_Y?I&T%xUx~=s4wi-TCo|cnaDXB!g z8D2);qb;L7%>XZ3AU=5bKdWm0*mEFNQ3(Y76`o)uRxnUHCvfExEx4-hyu6-k;Q>`oy@^&lBTTfPPHIRSQKENaOUSQ8KEE3NNCo_Z=Fp z+kEY9VDpuG-pnUg`gW)Hgoou=0QIcq(j_>nd|=IEymDNh7TPCy^(3$K^R&=DG48(z zM3np?T}R(87^;F-%JI4Z=h z*S)SNp1Jc$*Huy2EgGSo6|pCSngo&R<8IT(y{3=*O&<@clRt4Bl4M1CvHM?N zVoqW#P1Xn51g~w+Diu#3P5n@xkF8CRb$(>+Tan!++To!I+DaHpn?bRWHVsVy;w1}K zDZ{HFs^HbL=9}#r2Z(RF4c}IRYee6|e%1Y4;nx)EwuMN##(1h$me@1&ns_wC0g7;v zavY#3Q!)BzvOwWW#5nTnOEkdsSyF(N#wcMZnR7xih(4XsM3kd+EGP}|-_NN)RGVVL#Xy1}l^T+WF*0E`2{keNxb>{>bI$qhqNBfrK0i4qB#zC;)FC3;U^qBry< zdPiTPpXs;Bu-56?s%NXgzU#B`oX+O!@G&fabzCgPT`WbDGkjN*9~ZS{=B@TqPT1hh zH$wpj2l)6{bENh+p-rqWV7DUB?uiGUn6sHMldhlN*#RTI0mZ-mmi~XGU{AQj{xXe@ z#D~hG08Ku_yzZOoTl7~}{^@F+nhB2l$##PSH$5pi6Pwl7ZGy{6Hv+IKYjF(#7LoJm zay@5-$I|MyJJ+?k?CyB2?popwUY)6ZgGk@Cvn*H_K`o`2FlEePm733qlonMc*Q#~7 zfP2kNB!nh7@#a^xH~+2Pjc8O4TC6o|=QGybx;AQaB*bIVQeNMOI;BDFvG&LCpEheu zcOQ{WloG|<-6@)KS2QjD0> zYn+gu$zp8~9^Rnp)%snKuz>JpJ7T+f!z$U(5R$cWTC57YYB8^xHiaddp8T2Os{25) z@3&MW)ox<&9j(76Pp70V#9}KgJ7X#cxn8WxGUh~EL!k>eLbIZ1&UaVDV^MuXW0R7V z3UdD&ZZ-FMZPCWAi-KC2-l`!;YJ=zp2Tc|+7J>)WeC(hf|8a42b#?jns^)vdRzWyB zUj+m59}<63^ZEGlmO?=SW*A%d<6tmpR*eNH56Q)zdcz&Ljw#$MFSKX%?YQ~ zI-7$|&;G~B<>~uc3Yu%5oj2DH*sS(ytGDNGPS0xbHq|^kZ>p>Jx09-9AXVb>d>sOn#Vz}84+hJ0mZvT@dgFG zSn#yTm^X#aShAvAyVDx?bY7>iqhYGNwesfI!&$-LYj43I*i5SPu&!*w7Ts}o-sG>^ zpw|iYA+mbCt&FDBx-04?bI#J)JihCZho=i$o}R5gEAD?$_ndBc15ES%Fa6#AXzBi! z-TnQi`(Ga7x!3z&6!$#N4KU^+gzSQo{0aiA(%&TLVOEl?mJOe zWiOv=dLSQZk$gEXA+T1`gc61(#A6}JFMG+zuDl$7J%?-F(g*DSZ@1TP=1dS2l5j$} zKsPi9RE+Y3Na~2M&ODH%OOftlK;9`T9%e{t*y>S-MCF0KnX?BPh{sk;o}LGJmi2!c z`Q&!*j{AKjs;aL16X;wMpdkv7 z-0CEXurCJGQcobBMp3?)(8xMFr_j(D5y=?!`$ON~J=otpm`wKu)5!$)hogxU^<%a?(I#6gVA)b@A*DHm<$Gchx@yS!K>l^Xo3%)nDD-saJPmDcUv>zs2vjy4o15N`+LJj>s|u1o!a) z*`Ij*gTWB*4R?3_-O=9u;mDhgMuT1N)!xCY-TteC>9l{ix7#NJe@doLTzFqxII7{o zQEM(7_J*yvuzxss)$i{;A{XxO9qt|agI&Da_YbE2tG+iHPW)F>Z@=Fk4zTCF+S}Xn z2K$5l!K>-+2#@ynyo3JWU_kJ6IPjlj;k|L;us5vaLd9EcxNy);7WPL6hlAnX=rLF1 zSA*Tj9vKV{CO+;Tj&`U0J@3`9e=zO$56BREql4k${;oIOCx`pK=MBkVv_G6o$nL&3 z*!5pMap8S&;h;tq4qD5?es9o%3wK}bj`sJ5!`(;ZLeCpbFy0*;?2UZ@~m3MeJ9KPD$J2;r^_IL3idDTA{><$J~Z#sD8J#pcEabdqVsN_P)TQd^L pg7utrc(8jgJjmtSV> +kind load docker-image ghcr.io/zalando/postgres-operator:${TAG} --name ``` Then create a new Postgres Operator deployment. @@ -276,10 +276,10 @@ Examples for fake K8s objects can be found in: The operator provides reference end-to-end (e2e) tests to ensure various infrastructure parts work smoothly together. The test code is available at `e2e/tests`. -The special `registry.opensource.zalan.do/acid/postgres-operator-e2e-tests-runner` image is used to run the tests. The container mounts the local `e2e/tests` directory at runtime, so whatever you modify in your local copy of the tests will be executed by a test runner. By maintaining a separate test runner image we avoid the need to re-build the e2e test image on every build. +The special `ghcr.io/zalando/postgres-operator-e2e-tests-runner` image is used to run the tests. The container mounts the local `e2e/tests` directory at runtime, so whatever you modify in your local copy of the tests will be executed by a test runner. By maintaining a separate test runner image we avoid the need to re-build the e2e test image on every build. -Each e2e execution tests a Postgres Operator image built from the current git branch. The test -runner creates a new local K8s cluster using [kind](https://kind.sigs.k8s.io/), +Each e2e execution tests a Postgres Operator image built from the current git branch. +The test runner creates a new local K8s cluster using [kind](https://kind.sigs.k8s.io/), utilizes provided manifest examples, and runs e2e tests contained in the `tests` folder. The K8s API client in the container connects to the `kind` cluster via the standard Docker `bridge` network. The kind cluster is deleted if tests diff --git a/docs/reference/operator_parameters.md b/docs/reference/operator_parameters.md index 7e7cbeaf0..5662d6b8e 100644 --- a/docs/reference/operator_parameters.md +++ b/docs/reference/operator_parameters.md @@ -824,7 +824,7 @@ grouped under the `logical_backup` key. runs `pg_dumpall` on a replica if possible and uploads compressed results to an S3 bucket under the key `////logical_backups`. The default image is the same image built with the Zalando-internal CI - pipeline. Default: "ghcr.io/zalando/postgres-operator/logical-backup:v1.13.0" + pipeline. Default: "ghcr.io/zalando/postgres-operator/logical-backup:v1.15.1" * **logical_backup_google_application_credentials** Specifies the path of the google cloud service account json file. Default is empty. diff --git a/e2e/Dockerfile b/e2e/Dockerfile index 4ad55c136..98bbf755a 100644 --- a/e2e/Dockerfile +++ b/e2e/Dockerfile @@ -1,27 +1,20 @@ # An image to run e2e tests. # The image does not include the tests; all necessary files are bind-mounted when a container starts. -FROM ubuntu:20.04 +FROM python:3.11-slim LABEL maintainer="Team ACID @ Zalando " ENV TERM xterm-256color -COPY requirements.txt ./ - -RUN apt-get update \ - && apt-get install --no-install-recommends -y \ - python3 \ - python3-setuptools \ - python3-pip \ - curl \ - vim \ - && pip3 install --no-cache-dir -r requirements.txt \ +RUN apt-get -qq -y update \ + # https://www.psycopg.org/docs/install.html#psycopg-vs-psycopg-binary + && apt-get -qq -y install --no-install-recommends curl vim python3-dev \ && curl -LO https://dl.k8s.io/release/v1.32.9/bin/linux/amd64/kubectl \ && chmod +x ./kubectl \ && mv ./kubectl /usr/local/bin/kubectl \ - && apt-get clean \ + && apt-get -qq -y clean \ && rm -rf /var/lib/apt/lists/* -# working line -# python3 -m unittest discover -v --failfast -k test_e2e.EndToEndTestCase.test_lazy_spilo_upgrade --start-directory tests -ENTRYPOINT ["python3", "-m", "unittest"] -CMD ["discover","-v","--failfast","--start-directory","/tests"] \ No newline at end of file +COPY requirements.txt ./ +RUN pip install -r ./requirements.txt + +CMD ["python", "-m", "unittest", "discover", "-v", "--failfast", "--start-directory", "/tests"] \ No newline at end of file diff --git a/e2e/Makefile b/e2e/Makefile index 52d24e9e5..5fa0de471 100644 --- a/e2e/Makefile +++ b/e2e/Makefile @@ -11,7 +11,7 @@ endif LOCAL_BUILD_FLAGS ?= $(BUILD_FLAGS) LDFLAGS ?= -X=main.version=$(VERSION) -IMAGE ?= registry.opensource.zalan.do/acid/$(BINARY) +IMAGE ?= ghcr.io/zalando/$(BINARY) VERSION ?= $(shell git describe --tags --always --dirty) TAG ?= $(VERSION) GITHEAD = $(shell git rev-parse --short HEAD) @@ -46,7 +46,7 @@ tools: # install pinned version of 'kind' # go install must run outside of a dir with a (module-based) Go project ! # otherwise go install updates project's dependencies and/or behaves differently - cd "/tmp" && GO111MODULE=on go install sigs.k8s.io/kind@v0.24.0 + cd "/tmp" && GO111MODULE=on go install sigs.k8s.io/kind@v0.27.0 e2etest: tools copy clean ./run.sh main diff --git a/e2e/exec_into_env.sh b/e2e/exec_into_env.sh index 59acbeeb4..a46efecbd 100755 --- a/e2e/exec_into_env.sh +++ b/e2e/exec_into_env.sh @@ -2,8 +2,8 @@ export cluster_name="postgres-operator-e2e-tests" export kubeconfig_path="/tmp/kind-config-${cluster_name}" -export operator_image="registry.opensource.zalan.do/acid/postgres-operator:latest" -export e2e_test_runner_image="registry.opensource.zalan.do/acid/postgres-operator-e2e-tests-runner:0.4" +export operator_image="ghcr.io/zalando/postgres-operator:latest" +export e2e_test_runner_image="ghcr.io/zalando/postgres-operator-e2e-tests-runner:latest" docker run -it --entrypoint /bin/bash --network=host -e "TERM=xterm-256color" \ --mount type=bind,source="$(readlink -f ${kubeconfig_path})",target=/root/.kube/config \ diff --git a/e2e/requirements.txt b/e2e/requirements.txt index d904585be..30b656552 100644 --- a/e2e/requirements.txt +++ b/e2e/requirements.txt @@ -1,3 +1,3 @@ -kubernetes==29.2.0 +kubernetes==31.0.0 timeout_decorator==0.5.0 -pyyaml==6.0.1 +pyyaml==6.0.3 diff --git a/e2e/run.sh b/e2e/run.sh index d289cb3f4..b0f13f92e 100755 --- a/e2e/run.sh +++ b/e2e/run.sh @@ -9,7 +9,7 @@ IFS=$'\n\t' readonly cluster_name="postgres-operator-e2e-tests" readonly kubeconfig_path="/tmp/kind-config-${cluster_name}" readonly spilo_image="registry.opensource.zalan.do/acid/spilo-17-e2e:0.3" -readonly e2e_test_runner_image="registry.opensource.zalan.do/acid/postgres-operator-e2e-tests-runner:0.4" +readonly e2e_test_runner_image="ghcr.io/zalando/postgres-operator-e2e-tests-runner:latest" export GOPATH=${GOPATH-~/go} export PATH=${GOPATH}/bin:$PATH @@ -19,11 +19,11 @@ echo "Kubeconfig path: ${kubeconfig_path}" function pull_images(){ operator_tag=$(git describe --tags --always --dirty) - if [[ -z $(docker images -q registry.opensource.zalan.do/acid/postgres-operator:${operator_tag}) ]] + if [[ -z $(docker images -q ghcr.io/zalando/postgres-operator:${operator_tag}) ]] then - docker pull registry.opensource.zalan.do/acid/postgres-operator:latest + docker pull ghcr.io/zalando/postgres-operator:latest fi - operator_image=$(docker images --filter=reference="registry.opensource.zalan.do/acid/postgres-operator" --format "{{.Repository}}:{{.Tag}}" | head -1) + operator_image=$(docker images --filter=reference="ghcr.io/zalando/postgres-operator" --format "{{.Repository}}:{{.Tag}}" | head -1) } function start_kind(){ diff --git a/manifests/postgresql.crd.yaml b/manifests/postgresql.crd.yaml index 39d751cef..7a1b21a4d 100644 --- a/manifests/postgresql.crd.yaml +++ b/manifests/postgresql.crd.yaml @@ -276,7 +276,6 @@ spec: items: type: string weight: - format: int32 type: integer requiredDuringSchedulingIgnoredDuringExecution: type: object diff --git a/pkg/apis/acid.zalan.do/v1/crds.go b/pkg/apis/acid.zalan.do/v1/crds.go index 3f6bf25d9..b89cb1448 100644 --- a/pkg/apis/acid.zalan.do/v1/crds.go +++ b/pkg/apis/acid.zalan.do/v1/crds.go @@ -436,8 +436,7 @@ var PostgresCRDResourceValidation = apiextv1.CustomResourceValidation{ }, }, "weight": { - Type: "integer", - Format: "int32", + Type: "integer", }, }, }, @@ -768,9 +767,9 @@ var PostgresCRDResourceValidation = apiextv1.CustomResourceValidation{ }, }, OneOf: []apiextv1.JSONSchemaProps{ - apiextv1.JSONSchemaProps{Required: []string{"s3_wal_path"}}, - apiextv1.JSONSchemaProps{Required: []string{"gs_wal_path"}}, - apiextv1.JSONSchemaProps{Required: []string{"standby_host"}}, + {Required: []string{"s3_wal_path"}}, + {Required: []string{"gs_wal_path"}}, + {Required: []string{"standby_host"}}, }, }, "streams": { diff --git a/pkg/apis/acid.zalan.do/v1/util_test.go b/pkg/apis/acid.zalan.do/v1/util_test.go index 5e4913ffe..9f3fe9bde 100644 --- a/pkg/apis/acid.zalan.do/v1/util_test.go +++ b/pkg/apis/acid.zalan.do/v1/util_test.go @@ -787,8 +787,6 @@ func TestPostgresListMeta(t *testing.T) { if a := tt.out.GetListMeta(); reflect.DeepEqual(a, tt.out.ListMeta) { t.Errorf("GetObjectMeta expected: %v, got: %v", tt.out.ListMeta, a) } - - return }) } } diff --git a/ui/Dockerfile b/ui/Dockerfile index 8ed70c2c0..63170a24b 100644 --- a/ui/Dockerfile +++ b/ui/Dockerfile @@ -17,7 +17,7 @@ WORKDIR /app RUN apt-get -qq -y update \ # https://www.psycopg.org/docs/install.html#psycopg-vs-psycopg-binary - && apt-get -qq -y install --no-install-recommends g++ libpq-dev python3-dev python3-distutils \ + && apt-get -qq -y install --no-install-recommends g++ libpq-dev python3-dev \ && apt-get -qq -y clean \ && rm -rf /var/lib/apt/lists/* From a06f8d796b0fc8bd3f3e49687fc15a72f2181d68 Mon Sep 17 00:00:00 2001 From: Ida Novindasari Date: Fri, 19 Dec 2025 09:53:38 +0100 Subject: [PATCH 29/58] 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 30/58] 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 31/58] 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 32/58] 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 33/58] 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 34/58] 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 35/58] 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 36/58] 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 37/58] 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 38/58] 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 39/58] 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 40/58] 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 41/58] 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 42/58] 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 43/58] 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 44/58] 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 45/58] 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 46/58] 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 47/58] 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 48/58] 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 49/58] 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) From e9478894a8aee185ab1f3cc34aefb8f87171a1f6 Mon Sep 17 00:00:00 2001 From: Polina Bungina <27892524+hughcapet@users.noreply.github.com> Date: Tue, 7 Apr 2026 12:16:55 +0200 Subject: [PATCH 50/58] Avoid rotating pods for PGVERSION change outside of maintenance window (#3065) * Avoid rotating pods for PGVERSION change outside of maintenance window * Update docs --- docs/administrator.md | 10 +++++++--- pkg/cluster/sync.go | 11 ++++++----- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/docs/administrator.md b/docs/administrator.md index 60db0f3f1..d18bb5349 100644 --- a/docs/administrator.md +++ b/docs/administrator.md @@ -65,7 +65,10 @@ the `PGVERSION` environment variable is set for the database pods. Since In-place major version upgrades can be configured to be executed by the operator with the `major_version_upgrade_mode` option. By default, it is enabled (mode: `manual`). In any case, altering the version in the manifest -will trigger a rolling update of pods to update the `PGVERSION` env variable. +will update the desired `PGVERSION`. If `maintenanceWindows` are configured, +major-version-related pod rotation is deferred until the next maintenance +window. Without maintenance windows, the operator will trigger a rolling +update of pods to apply the new `PGVERSION`. Spilo's [`configure_spilo`](https://github.com/zalando/spilo/blob/master/postgres-appliance/scripts/configure_spilo.py) script will notice the version mismatch but start the current version again. @@ -93,8 +96,9 @@ Thus, the `full` mode can create drift between desired and actual state. ### Upgrade during maintenance windows When `maintenanceWindows` are defined in the Postgres manifest the operator -will trigger a major version upgrade only during these periods. Make sure they -are at least twice as long as your configured `resync_period` to guarantee +will trigger major-version-related pod rotation and the major version upgrade +only during these periods. Make sure they are at least twice as long as your +configured `resync_period` to guarantee that operator actions can be triggered. ### Upgrade annotations diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index ffebd306c..3fa9e9783 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -41,6 +41,12 @@ func (c *Cluster) Sync(newSpec *acidv1.Postgresql) error { defer c.mu.Unlock() oldSpec := c.Postgresql + + if !c.isInMaintenanceWindow(newSpec.Spec.MaintenanceWindows) { + // do not apply any major version related changes yet + newSpec.Spec.PostgresqlParam.PgVersion = oldSpec.Spec.PostgresqlParam.PgVersion + } + c.setSpec(newSpec) defer func() { @@ -97,11 +103,6 @@ func (c *Cluster) Sync(newSpec *acidv1.Postgresql) error { } } - if !c.isInMaintenanceWindow(newSpec.Spec.MaintenanceWindows) { - // do not apply any major version related changes yet - newSpec.Spec.PostgresqlParam.PgVersion = oldSpec.Spec.PostgresqlParam.PgVersion - } - if err = c.syncStatefulSet(); err != nil { if !k8sutil.ResourceAlreadyExists(err) { err = fmt.Errorf("could not sync statefulsets: %v", err) From 39cc09ccaa1d5a91cca2464bcfb2c9988adff9d0 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Thu, 16 Apr 2026 17:13:18 +0200 Subject: [PATCH 51/58] feature toggle for using maintenance windows (#3074) * feature toggle for using maintenance windows --- .../crds/operatorconfigurations.yaml | 3 ++ charts/postgres-operator/values.yaml | 2 ++ docs/administrator.md | 10 +++--- docs/reference/cluster_manifest.md | 4 ++- docs/reference/operator_parameters.md | 3 ++ manifests/configmap.yaml | 1 + manifests/operatorconfiguration.crd.yaml | 3 ++ ...gresql-operator-default-configuration.yaml | 1 + pkg/apis/acid.zalan.do/v1/crds.go | 3 ++ .../v1/operator_configuration_type.go | 1 + .../acid.zalan.do/v1/zz_generated.deepcopy.go | 5 +++ pkg/cluster/util.go | 4 ++- pkg/cluster/util_test.go | 31 ++++++++++++++++++- pkg/controller/operator_config.go | 1 + pkg/util/config/config.go | 17 +++++----- 15 files changed, 73 insertions(+), 16 deletions(-) diff --git a/charts/postgres-operator/crds/operatorconfigurations.yaml b/charts/postgres-operator/crds/operatorconfigurations.yaml index 2c432a082..cb4b7a335 100644 --- a/charts/postgres-operator/crds/operatorconfigurations.yaml +++ b/charts/postgres-operator/crds/operatorconfigurations.yaml @@ -79,6 +79,9 @@ spec: enable_lazy_spilo_upgrade: type: boolean default: false + enable_maintenance_windows: + type: boolean + default: true enable_pgversion_env_var: type: boolean default: true diff --git a/charts/postgres-operator/values.yaml b/charts/postgres-operator/values.yaml index 6d0161bfb..dfec76b6b 100644 --- a/charts/postgres-operator/values.yaml +++ b/charts/postgres-operator/values.yaml @@ -27,6 +27,8 @@ configGeneral: - "all" # update only the statefulsets without immediately doing the rolling update enable_lazy_spilo_upgrade: false + # toogle to use maintenance windows feature + enable_maintenance_windows: true # set the PGVERSION env var instead of providing the version via postgresql.bin_dir in SPILO_CONFIGURATION enable_pgversion_env_var: true # start any new database pod without limitations on shm memory diff --git a/docs/administrator.md b/docs/administrator.md index d18bb5349..b7880b183 100644 --- a/docs/administrator.md +++ b/docs/administrator.md @@ -95,11 +95,11 @@ Thus, the `full` mode can create drift between desired and actual state. ### Upgrade during maintenance windows -When `maintenanceWindows` are defined in the Postgres manifest the operator -will trigger major-version-related pod rotation and the major version upgrade -only during these periods. Make sure they are at least twice as long as your -configured `resync_period` to guarantee -that operator actions can be triggered. +When `maintenanceWindows` are defined in the Postgres manifest or in the global +config the operator will trigger major-version-related pod rotation and the +major version upgrade only during these periods. Make sure they are at least +twice as long as your configured `resync_period` to guarantee that operator +actions can be triggered. ### Upgrade annotations diff --git a/docs/reference/cluster_manifest.md b/docs/reference/cluster_manifest.md index ae23dabb9..fd0660f57 100644 --- a/docs/reference/cluster_manifest.md +++ b/docs/reference/cluster_manifest.md @@ -118,7 +118,9 @@ These parameters are grouped directly under the `spec` key in the manifest. a list which defines specific time frames when certain maintenance operations 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. + "Sat:00:00-04:00" for specific days, with all times in UTC. Note, when the + global config option `enable_maintenance_windows` is false, the specified + windows will be ignored. * **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 a62f67dfb..6dd775069 100644 --- a/docs/reference/operator_parameters.md +++ b/docs/reference/operator_parameters.md @@ -173,6 +173,9 @@ 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. +* **enable_maintenance_windows** + toggle for using the maintenance windows feature. Default is `"true"`. + * **maintenance_windows** a list which defines specific time frames when certain maintenance operations such as automatic major upgrades or master pod migration are diff --git a/manifests/configmap.yaml b/manifests/configmap.yaml index 1cf455e57..571a4171b 100644 --- a/manifests/configmap.yaml +++ b/manifests/configmap.yaml @@ -46,6 +46,7 @@ data: enable_ebs_gp3_migration_max_size: "1000" enable_init_containers: "true" enable_lazy_spilo_upgrade: "false" + enable_maintenance_windows: "true" enable_master_load_balancer: "false" enable_master_pooler_load_balancer: "false" enable_password_rotation: "false" diff --git a/manifests/operatorconfiguration.crd.yaml b/manifests/operatorconfiguration.crd.yaml index 03534cefb..3be545b65 100644 --- a/manifests/operatorconfiguration.crd.yaml +++ b/manifests/operatorconfiguration.crd.yaml @@ -77,6 +77,9 @@ spec: enable_lazy_spilo_upgrade: type: boolean default: false + enable_maintenance_windows: + type: boolean + default: true enable_pgversion_env_var: type: boolean default: true diff --git a/manifests/postgresql-operator-default-configuration.yaml b/manifests/postgresql-operator-default-configuration.yaml index 9c54e4379..1c6a0e34a 100644 --- a/manifests/postgresql-operator-default-configuration.yaml +++ b/manifests/postgresql-operator-default-configuration.yaml @@ -8,6 +8,7 @@ configuration: # crd_categories: # - all # enable_lazy_spilo_upgrade: false + enable_maintenance_windows: true enable_pgversion_env_var: true # enable_shm_volume: true enable_spilo_wal_path_compat: false diff --git a/pkg/apis/acid.zalan.do/v1/crds.go b/pkg/apis/acid.zalan.do/v1/crds.go index 46739e46d..3175f152a 100644 --- a/pkg/apis/acid.zalan.do/v1/crds.go +++ b/pkg/apis/acid.zalan.do/v1/crds.go @@ -105,6 +105,9 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{ "enable_lazy_spilo_upgrade": { Type: "boolean", }, + "enable_maintenance_windows": { + Type: "boolean", + }, "enable_shm_volume": { 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 bfad24b0d..453d618d3 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"` + EnableMaintenanceWindows *bool `json:"enable_maintenance_windows,omitempty"` MaintenanceWindows []MaintenanceWindow `json:"maintenance_windows,omitempty"` SetMemoryRequestToLimit bool `json:"set_memory_request_to_limit,omitempty"` ShmVolume *bool `json:"enable_shm_volume,omitempty"` 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 159a87f35..0fa4b1037 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,11 @@ func (in *OperatorConfigurationData) DeepCopyInto(out *OperatorConfigurationData *out = make([]string, len(*in)) copy(*out, *in) } + if in.EnableMaintenanceWindows != nil { + in, out := &in.EnableMaintenanceWindows, &out.EnableMaintenanceWindows + *out = new(bool) + **out = **in + } if in.MaintenanceWindows != nil { in, out := &in.MaintenanceWindows, &out.MaintenanceWindows *out = make([]MaintenanceWindow, len(*in)) diff --git a/pkg/cluster/util.go b/pkg/cluster/util.go index 81e927d94..9c830129d 100644 --- a/pkg/cluster/util.go +++ b/pkg/cluster/util.go @@ -675,7 +675,9 @@ func isStandbyCluster(spec *acidv1.PostgresSpec) bool { } func (c *Cluster) isInMaintenanceWindow(specMaintenanceWindows []acidv1.MaintenanceWindow) bool { - if len(specMaintenanceWindows) == 0 && len(c.OpConfig.MaintenanceWindows) == 0 { + ignoreMaintenanceWindows := c.OpConfig.EnableMaintenanceWindows != nil && !*c.OpConfig.EnableMaintenanceWindows + noWindowsDefined := len(specMaintenanceWindows) == 0 && len(c.OpConfig.MaintenanceWindows) == 0 + if noWindowsDefined || ignoreMaintenanceWindows { return true } now := time.Now() diff --git a/pkg/cluster/util_test.go b/pkg/cluster/util_test.go index ea3e81e89..8413ca396 100644 --- a/pkg/cluster/util_test.go +++ b/pkg/cluster/util_test.go @@ -660,6 +660,7 @@ func TestIsInMaintenanceWindow(t *testing.T) { cluster := New( Config{ OpConfig: config.Config{ + EnableMaintenanceWindows: util.True(), Resources: config.Resources{ ClusterLabels: map[string]string{"application": "spilo"}, ClusterNameLabel: "cluster-name", @@ -683,12 +684,27 @@ func TestIsInMaintenanceWindow(t *testing.T) { name string windows []acidv1.MaintenanceWindow configWindows []string + windowsFlag bool expected bool }{ { name: "no maintenance windows", windows: nil, configWindows: nil, + windowsFlag: true, + expected: true, + }, + { + name: "maintenance windows diabled", + windows: []acidv1.MaintenanceWindow{ + { + Everyday: true, + StartTime: mustParseTime("00:00"), + EndTime: mustParseTime("23:59"), + }, + }, + configWindows: nil, + windowsFlag: false, expected: true, }, { @@ -701,6 +717,7 @@ func TestIsInMaintenanceWindow(t *testing.T) { }, }, configWindows: nil, + windowsFlag: true, expected: true, }, { @@ -713,6 +730,7 @@ func TestIsInMaintenanceWindow(t *testing.T) { }, }, configWindows: nil, + windowsFlag: true, expected: true, }, { @@ -724,24 +742,35 @@ func TestIsInMaintenanceWindow(t *testing.T) { EndTime: mustParseTime(futureTimeEndFormatted), }, }, - expected: false, + windowsFlag: true, + expected: false, }, { name: "global maintenance windows with future interval time", windows: nil, configWindows: []string{fmt.Sprintf("%s-%s", futureTimeStartFormatted, futureTimeEndFormatted)}, + windowsFlag: true, expected: false, }, { name: "global maintenance windows all day", windows: nil, configWindows: []string{"00:00-02:00", "02:00-23:59"}, + windowsFlag: true, + expected: true, + }, + { + name: "global maintenance windows ignored", + windows: nil, + configWindows: []string{"00:00-02:00", "02:00-23:59"}, + windowsFlag: false, expected: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + cluster.OpConfig.EnableMaintenanceWindows = &tt.windowsFlag cluster.OpConfig.MaintenanceWindows = tt.configWindows cluster.Spec.MaintenanceWindows = tt.windows if cluster.isInMaintenanceWindow(cluster.Spec.MaintenanceWindows) != tt.expected { diff --git a/pkg/controller/operator_config.go b/pkg/controller/operator_config.go index 7719a2939..0a458618b 100644 --- a/pkg/controller/operator_config.go +++ b/pkg/controller/operator_config.go @@ -51,6 +51,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur result.ShmVolume = util.CoalesceBool(fromCRD.ShmVolume, util.True()) result.SidecarImages = fromCRD.SidecarImages result.SidecarContainers = fromCRD.SidecarContainers + result.EnableMaintenanceWindows = util.CoalesceBool(fromCRD.EnableMaintenanceWindows, util.True()) if len(fromCRD.MaintenanceWindows) > 0 { result.MaintenanceWindows = make([]string, 0, len(fromCRD.MaintenanceWindows)) for _, window := range fromCRD.MaintenanceWindows { diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index 468cf9328..914d7a180 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -173,14 +173,15 @@ type Config struct { LogicalBackup ConnectionPooler - 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 - 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"` + 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 + EnableMaintenanceWindows *bool `name:"enable_maintenance_windows" default:"true"` + 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"` // value of this string must be valid JSON or YAML; see initPodServiceAccount PodServiceAccountDefinition string `name:"pod_service_account_definition" default:""` PodServiceAccountRoleBindingDefinition string `name:"pod_service_account_role_binding_definition" default:""` From 3af93363fd19408605f4ad5df9afb71d0cb0bdba Mon Sep 17 00:00:00 2001 From: Ali Algohary <71212887+alimelgohary@users.noreply.github.com> Date: Thu, 16 Apr 2026 18:23:01 +0200 Subject: [PATCH 52/58] Fix JSON value of OPERATOR_UI_CONFIG (#3070) Remove excess comma from OPERATOR_UI_CONFIG. --- ui/manifests/deployment.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/manifests/deployment.yaml b/ui/manifests/deployment.yaml index ad41c38c7..e19a850b8 100644 --- a/ui/manifests/deployment.yaml +++ b/ui/manifests/deployment.yaml @@ -77,7 +77,7 @@ spec: "17", "16", "15", - "14", + "14" ] } # Exemple of settings to make snapshot view working in the ui when using AWS From bbf94324138580ec5d500614ef19907388bd9a15 Mon Sep 17 00:00:00 2001 From: DDD <58938832+dandeandean@users.noreply.github.com> Date: Fri, 17 Apr 2026 02:59:08 -0400 Subject: [PATCH 53/58] Wasm target updates (#3068) * Updates Needed for WASM Target * switch to regular (instead of local) build flags * update codegen to match other scripts --------- Co-authored-by: Felix Kunde --- Makefile | 3 +++ go.mod | 4 ++-- go.sum | 4 ++++ hack/update-codegen.sh | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 02c9c73f5..b96d71939 100644 --- a/Makefile +++ b/Makefile @@ -78,6 +78,9 @@ $(GENERATED_CRDS): $(GENERATED) local: ${SOURCES} $(GENERATED_CRDS) CGO_ENABLED=${CGO_ENABLED} go build -o build/${BINARY} $(LOCAL_BUILD_FLAGS) -ldflags "$(LDFLAGS)" $(SOURCES) +wasm: ${SOURCES} $(GENERATED_CRDS) + GOOS=wasip1 GOARCH=wasm CGO_ENABLED=${CGO_ENABLED} go build -o build/${BINARY}.wasm ${BUILD_FLAGS} -ldflags "$(LDFLAGS)" $(SOURCES) + linux: ${SOURCES} $(GENERATED_CRDS) GOOS=linux GOARCH=amd64 CGO_ENABLED=${CGO_ENABLED} go build -o build/linux/${BINARY} ${BUILD_FLAGS} -ldflags "$(LDFLAGS)" $(SOURCES) diff --git a/go.mod b/go.mod index a25723a44..e0e0b1956 100644 --- a/go.mod +++ b/go.mod @@ -6,11 +6,11 @@ require ( github.com/Masterminds/semver v1.5.0 github.com/aws/aws-sdk-go v1.55.8 github.com/golang/mock v1.6.0 - github.com/lib/pq v1.10.9 + github.com/lib/pq v1.11.2 github.com/motomux/pretty v0.0.0-20161209205251-b2aad2c9a95d github.com/pkg/errors v0.9.1 github.com/r3labs/diff v1.1.0 - github.com/sirupsen/logrus v1.9.3 + github.com/sirupsen/logrus v1.9.4 github.com/stretchr/testify v1.11.1 golang.org/x/crypto v0.45.0 gopkg.in/yaml.v2 v2.4.0 diff --git a/go.sum b/go.sum index 463b37211..5b70c6899 100644 --- a/go.sum +++ b/go.sum @@ -73,6 +73,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.11.2 h1:x6gxUeu39V0BHZiugWe8LXZYZ+Utk7hSJGThs8sdzfs= +github.com/lib/pq v1.11.2/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA= 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= @@ -113,6 +115,8 @@ github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99 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/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= +github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= 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= diff --git a/hack/update-codegen.sh b/hack/update-codegen.sh index 9d43bc512..1363c2786 100755 --- a/hack/update-codegen.sh +++ b/hack/update-codegen.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright 2017 The Kubernetes Authors. # From 085a1a91e6c7f062b848078888d3f93b63914926 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Apr 2026 13:56:32 +0200 Subject: [PATCH 54/58] Bump werkzeug from 3.1.5 to 3.1.6 in /ui (#3076) Bumps [werkzeug](https://github.com/pallets/werkzeug) from 3.1.5 to 3.1.6. - [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.5...3.1.6) --- updated-dependencies: - dependency-name: werkzeug dependency-version: 3.1.6 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 2e43ccb0e..ace18641d 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.5 +werkzeug==3.1.6 From 0ba2147d733ed4c6cf4bc5d85232d1bddf0720d6 Mon Sep 17 00:00:00 2001 From: Zadkiel AHARONIAN Date: Thu, 23 Apr 2026 17:47:12 +0200 Subject: [PATCH 55/58] fix(logical-backup): wait for PG connectivity before running backup (#3069) * fix(logical-backup): wait for PG connectivity before running backup The backup script connects to the target PostgreSQL pod immediately after resolving its IP via the Kubernetes API. When NetworkPolicy is enforced via iptables, a newly-created pod's IP may not yet be present in the destination node's ingress allow lists, causing cross-node connections to be rejected until the next policy sync. This adds a pg_isready retry loop before the dump starts, with configurable retries and delay via LOGICAL_BACKUP_CONNECT_RETRIES (default: 10) and LOGICAL_BACKUP_CONNECT_RETRY_DELAY (default: 2s). Signed-off-by: Zadkiel AHARONIAN * docs: document LOGICAL_BACKUP_CONNECT_RETRIES and RETRY_DELAY env vars Document the new environment variables that control the pg_isready retry loop added in the previous commit. These are passed via the existing logical_backup_cronjob_environment_secret mechanism. Signed-off-by: Zadkiel AHARONIAN --------- Signed-off-by: Zadkiel AHARONIAN Co-authored-by: Ida Novindasari --- docs/reference/operator_parameters.md | 13 +++++++++++++ logical-backup/dump.sh | 21 +++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/docs/reference/operator_parameters.md b/docs/reference/operator_parameters.md index 6dd775069..83f693acc 100644 --- a/docs/reference/operator_parameters.md +++ b/docs/reference/operator_parameters.md @@ -900,6 +900,19 @@ grouped under the `logical_backup` key. * **logical_backup_cronjob_environment_secret** Reference to a Kubernetes secret, which keys will be added as environment variables to the cronjob. Default: "" +The following environment variables can be passed to the logical backup +cronjob via `logical_backup_cronjob_environment_secret` to control +connectivity checks before the backup starts: + +* **LOGICAL_BACKUP_CONNECT_RETRIES** + Number of times to retry connecting to the target PostgreSQL pod before + giving up. This is useful when NetworkPolicy enforcement introduces a + short delay before a newly-created pod's IP is allowed through ingress + rules on the destination node. Default: "10" + +* **LOGICAL_BACKUP_CONNECT_RETRY_DELAY** + Delay in seconds between connectivity retries. Default: "2" + ## Debugging the operator Options to aid debugging of the operator itself. Grouped under the `debug` key. diff --git a/logical-backup/dump.sh b/logical-backup/dump.sh index a250670a6..7833de399 100755 --- a/logical-backup/dump.sh +++ b/logical-backup/dump.sh @@ -183,6 +183,25 @@ function get_master_pod { get_pods "labelSelector=${CLUSTER_NAME_LABEL}%3D${SCOPE},spilo-role%3Dmaster" | tee | head -n 1 } +# Wait for TCP connectivity to the target PostgreSQL pod. +# When NetworkPolicy is enforced via iptables, a newly-created pod's IP may not +# yet be present in the destination node's ingress allow lists, causing +# cross-node connections to be rejected until the next policy sync. +function wait_for_pg { + local retries=${LOGICAL_BACKUP_CONNECT_RETRIES:-10} + local delay=${LOGICAL_BACKUP_CONNECT_RETRY_DELAY:-2} + local i + for (( i=1; i<=retries; i++ )); do + if "$PG_BIN"/pg_isready -h "$PGHOST" -p "${PGPORT:-5432}" -q 2>/dev/null; then + return 0 + fi + echo "waiting for $PGHOST:${PGPORT:-5432} to become reachable (attempt $i/$retries)..." + sleep "$delay" + done + echo "ERROR: $PGHOST:${PGPORT:-5432} not reachable after $((retries * delay))s" + return 1 +} + CURRENT_NODENAME=$(get_current_pod | jq .items[].spec.nodeName --raw-output) export CURRENT_NODENAME @@ -197,6 +216,8 @@ for search in "${search_strategy[@]}"; do done +wait_for_pg + set -x if [ "$LOGICAL_BACKUP_PROVIDER" == "az" ]; then dump | compress > /tmp/azure-backup.sql.gz From 030c24f64e0504f6050245879fec7ad13d5282d8 Mon Sep 17 00:00:00 2001 From: Sai Asish Y Date: Thu, 23 Apr 2026 08:47:51 -0700 Subject: [PATCH 56/58] ui: honor AWS_ENDPOINT in read_basebackups S3 list/get (#3079) read_stored_clusters and read_versions build their S3 clients with endpoint_url=AWS_ENDPOINT, but read_basebackups used a bare client('s3') for both the list_objects_v2 paginator and the per-key get_object call. On MinIO / S3-compatible backends the list+get requests go to the default AWS endpoint, so the Backups tab renders cluster/version prefixes (picked up by the correctly-configured read_stored_clusters) but then returns empty base backup details (silently no hits against the real backend) (#3078). Build s3_client once per call with endpoint_url=AWS_ENDPOINT and reuse it for both the paginator and get_object. No behaviour change when AWS_ENDPOINT is unset; boto3 defaults to the AWS endpoint either way. Fixes #3078 Signed-off-by: SAY-5 Co-authored-by: SAY-5 Co-authored-by: Ida Novindasari --- ui/operator_ui/spiloutils.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/ui/operator_ui/spiloutils.py b/ui/operator_ui/spiloutils.py index 6a2f03bb2..8d2b73967 100644 --- a/ui/operator_ui/spiloutils.py +++ b/ui/operator_ui/spiloutils.py @@ -321,11 +321,18 @@ def read_basebackups( suffix = '' if uid == 'base' else '/' + uid backups = [] + # Reuse a single S3 client configured with AWS_ENDPOINT so MinIO / + # other S3-compatible backends are hit for list+get calls too. The + # previous plain client('s3') fell back to the default AWS endpoint + # and returned empty data against a custom endpoint; read_stored_clusters + # and read_versions already pass endpoint_url=AWS_ENDPOINT (#3078). + s3_client = client('s3', endpoint_url=AWS_ENDPOINT) + for vp in postgresql_versions: backup_prefix = f'{prefix}{pg_cluster}{suffix}/wal/{vp}/basebackups_005/' logger.info(f"{bucket}/{backup_prefix}") - paginator = client('s3').get_paginator('list_objects_v2') + paginator = s3_client.get_paginator('list_objects_v2') pages = paginator.paginate(Bucket=bucket, Prefix=backup_prefix) for page in pages: @@ -334,7 +341,7 @@ def read_basebackups( if not key.endswith("backup_stop_sentinel.json"): continue - response = client('s3').get_object(Bucket=bucket, Key=key) + response = s3_client.get_object(Bucket=bucket, Key=key) backup_info = loads(response["Body"].read().decode("utf-8")) last_modified = response["LastModified"].astimezone(timezone.utc).isoformat() From 27c969d14bda464c7a5e4000fc3a0cd35e6ab9bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20M=C3=A5rtensson?= Date: Fri, 24 Apr 2026 11:06:30 +0200 Subject: [PATCH 57/58] Set securityContext for backup container (#2117) Co-authored-by: Felix Kunde --- pkg/cluster/k8sres.go | 45 ++++++++++++------------------------------- 1 file changed, 12 insertions(+), 33 deletions(-) diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index 724986dbc..7d51951ff 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -818,9 +818,6 @@ func (c *Cluster) generatePodTemplate( sidecarContainers []v1.Container, sharePgSocketWithSidecars *bool, tolerationsSpec *[]v1.Toleration, - spiloRunAsUser *int64, - spiloRunAsGroup *int64, - spiloFSGroup *int64, nodeAffinity *v1.Affinity, schedulerName *string, terminateGracePeriod int64, @@ -839,18 +836,22 @@ func (c *Cluster) generatePodTemplate( terminateGracePeriodSeconds := terminateGracePeriod containers := []v1.Container{*spiloContainer} containers = append(containers, sidecarContainers...) - securityContext := v1.PodSecurityContext{} - - if spiloRunAsUser != nil { - securityContext.RunAsUser = spiloRunAsUser + securityContext := v1.PodSecurityContext{ + RunAsUser: c.OpConfig.Resources.SpiloRunAsUser, + RunAsGroup: c.OpConfig.Resources.SpiloRunAsGroup, + FSGroup: c.OpConfig.Resources.SpiloFSGroup, } - if spiloRunAsGroup != nil { - securityContext.RunAsGroup = spiloRunAsGroup + if c.Spec.SpiloRunAsUser != nil { + securityContext.RunAsUser = c.Spec.SpiloRunAsUser } - if spiloFSGroup != nil { - securityContext.FSGroup = spiloFSGroup + if c.Spec.SpiloRunAsGroup != nil { + securityContext.RunAsGroup = c.Spec.SpiloRunAsGroup + } + + if c.Spec.SpiloFSGroup != nil { + securityContext.FSGroup = c.Spec.SpiloFSGroup } podSpec := v1.PodSpec{ @@ -1352,22 +1353,6 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef // pickup the docker image for the spilo container effectiveDockerImage := util.Coalesce(spec.DockerImage, c.OpConfig.DockerImage) - // determine the User, Group and FSGroup for the spilo pod - effectiveRunAsUser := c.OpConfig.Resources.SpiloRunAsUser - if spec.SpiloRunAsUser != nil { - effectiveRunAsUser = spec.SpiloRunAsUser - } - - effectiveRunAsGroup := c.OpConfig.Resources.SpiloRunAsGroup - if spec.SpiloRunAsGroup != nil { - effectiveRunAsGroup = spec.SpiloRunAsGroup - } - - effectiveFSGroup := c.OpConfig.Resources.SpiloFSGroup - if spec.SpiloFSGroup != nil { - effectiveFSGroup = spec.SpiloFSGroup - } - volumeMounts := generateVolumeMounts(spec.Volume) // configure TLS with a custom secret volume @@ -1485,9 +1470,6 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef sidecarContainers, c.OpConfig.SharePgSocketWithSidecars, &tolerationSpec, - effectiveRunAsUser, - effectiveRunAsGroup, - effectiveFSGroup, c.nodeAffinity(c.OpConfig.NodeReadinessLabel, spec.NodeAffinity), spec.SchedulerName, int64(c.OpConfig.PodTerminateGracePeriod.Seconds()), @@ -2379,9 +2361,6 @@ func (c *Cluster) generateLogicalBackupJob() (*batchv1.CronJob, error) { []v1.Container{}, util.False(), &tolerationsSpec, - nil, - nil, - nil, c.nodeAffinity(c.OpConfig.NodeReadinessLabel, nil), nil, int64(c.OpConfig.PodTerminateGracePeriod.Seconds()), From 0ac28e3aad33bf0ad0c0ed26e799e712d623f3ce Mon Sep 17 00:00:00 2001 From: Polina Bungina <27892524+hughcapet@users.noreply.github.com> Date: Fri, 24 Apr 2026 14:23:54 +0200 Subject: [PATCH 58/58] Do not set aws-load-balancer-connection-idle-timeout by default (#3054) Co-authored-by: Felix Kunde --- docs/administrator.md | 6 ++-- e2e/tests/test_e2e.py | 2 -- pkg/cluster/cluster_test.go | 46 ++++++++----------------------- pkg/cluster/connection_pooler.go | 4 --- pkg/cluster/k8sres.go | 5 ---- pkg/util/constants/annotations.go | 2 -- 6 files changed, 14 insertions(+), 51 deletions(-) diff --git a/docs/administrator.md b/docs/administrator.md index b7880b183..e854775ce 100644 --- a/docs/administrator.md +++ b/docs/administrator.md @@ -891,15 +891,13 @@ cluster manifest. In the case any of these variables are omitted from the manifest, the operator configuration settings `enable_master_load_balancer` and `enable_replica_load_balancer` apply. Note that the operator settings affect all Postgresql services running in all namespaces watched by the operator. -If load balancing is enabled two default annotations will be applied to its -services: +If load balancing is enabled the following default annotation will be applied to +its services: - `external-dns.alpha.kubernetes.io/hostname` with the value defined by the operator configs `master_dns_name_format` and `replica_dns_name_format`. This value can't be overwritten. If any changing in its value is needed, it MUST be done changing the DNS format operator config parameters; and -- `service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout` with - a default value of "3600". There are multiple options to specify service annotations that will be merged with each other and override in the following order (where latter take diff --git a/e2e/tests/test_e2e.py b/e2e/tests/test_e2e.py index 70145f3e4..8cadb98a7 100644 --- a/e2e/tests/test_e2e.py +++ b/e2e/tests/test_e2e.py @@ -724,14 +724,12 @@ class EndToEndTestCase(unittest.TestCase): master_annotations = { "external-dns.alpha.kubernetes.io/hostname": "acid-minimal-cluster-pooler.default.db.example.com", - "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", } self.eventuallyTrue(lambda: k8s.check_service_annotations( master_pooler_label+","+pooler_label, master_annotations), "Wrong annotations") replica_annotations = { "external-dns.alpha.kubernetes.io/hostname": "acid-minimal-cluster-pooler-repl.default.db.example.com", - "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", } self.eventuallyTrue(lambda: k8s.check_service_annotations( replica_pooler_label+","+pooler_label, replica_annotations), "Wrong annotations") diff --git a/pkg/cluster/cluster_test.go b/pkg/cluster/cluster_test.go index c7181dbbc..8046943d4 100644 --- a/pkg/cluster/cluster_test.go +++ b/pkg/cluster/cluster_test.go @@ -680,8 +680,7 @@ func TestServiceAnnotations(t *testing.T) { operatorAnnotations: make(map[string]string), serviceAnnotations: make(map[string]string), expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg.test.db.example.com,test-stg.acid.db.example.com", - "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", + "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg.test.db.example.com,test-stg.acid.db.example.com", }, }, { @@ -702,8 +701,7 @@ func TestServiceAnnotations(t *testing.T) { operatorAnnotations: make(map[string]string), serviceAnnotations: make(map[string]string), expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg.test.db.example.com,test-stg.acid.db.example.com", - "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", + "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg.test.db.example.com,test-stg.acid.db.example.com", }, }, { @@ -714,8 +712,7 @@ func TestServiceAnnotations(t *testing.T) { operatorAnnotations: make(map[string]string), serviceAnnotations: map[string]string{"foo": "bar"}, expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg.test.db.example.com,test-stg.acid.db.example.com", - "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", + "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg.test.db.example.com,test-stg.acid.db.example.com", "foo": "bar", }, }, @@ -737,8 +734,7 @@ func TestServiceAnnotations(t *testing.T) { operatorAnnotations: map[string]string{"foo": "bar"}, serviceAnnotations: make(map[string]string), expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg.test.db.example.com,test-stg.acid.db.example.com", - "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", + "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg.test.db.example.com,test-stg.acid.db.example.com", "foo": "bar", }, }, @@ -780,8 +776,7 @@ func TestServiceAnnotations(t *testing.T) { "external-dns.alpha.kubernetes.io/hostname": "wrong.external-dns-name.example.com", }, expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg.test.db.example.com,test-stg.acid.db.example.com", - "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", + "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg.test.db.example.com,test-stg.acid.db.example.com", }, }, { @@ -792,8 +787,7 @@ func TestServiceAnnotations(t *testing.T) { serviceAnnotations: make(map[string]string), operatorAnnotations: make(map[string]string), expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg.test.db.example.com,test-stg.acid.db.example.com", - "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", + "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg.test.db.example.com,test-stg.acid.db.example.com", }, }, { @@ -835,8 +829,7 @@ func TestServiceAnnotations(t *testing.T) { operatorAnnotations: make(map[string]string), serviceAnnotations: make(map[string]string), expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg-repl.test.db.example.com,test-stg-repl.acid.db.example.com", - "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", + "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg-repl.test.db.example.com,test-stg-repl.acid.db.example.com", }, }, { @@ -857,8 +850,7 @@ func TestServiceAnnotations(t *testing.T) { operatorAnnotations: make(map[string]string), serviceAnnotations: make(map[string]string), expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg-repl.test.db.example.com,test-stg-repl.acid.db.example.com", - "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", + "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg-repl.test.db.example.com,test-stg-repl.acid.db.example.com", }, }, { @@ -869,8 +861,7 @@ func TestServiceAnnotations(t *testing.T) { operatorAnnotations: make(map[string]string), serviceAnnotations: map[string]string{"foo": "bar"}, expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg-repl.test.db.example.com,test-stg-repl.acid.db.example.com", - "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", + "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg-repl.test.db.example.com,test-stg-repl.acid.db.example.com", "foo": "bar", }, }, @@ -892,8 +883,7 @@ func TestServiceAnnotations(t *testing.T) { operatorAnnotations: map[string]string{"foo": "bar"}, serviceAnnotations: make(map[string]string), expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg-repl.test.db.example.com,test-stg-repl.acid.db.example.com", - "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", + "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg-repl.test.db.example.com,test-stg-repl.acid.db.example.com", "foo": "bar", }, }, @@ -935,8 +925,7 @@ func TestServiceAnnotations(t *testing.T) { "external-dns.alpha.kubernetes.io/hostname": "wrong.external-dns-name.example.com", }, expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg-repl.test.db.example.com,test-stg-repl.acid.db.example.com", - "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", + "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg-repl.test.db.example.com,test-stg-repl.acid.db.example.com", }, }, { @@ -947,8 +936,7 @@ func TestServiceAnnotations(t *testing.T) { serviceAnnotations: make(map[string]string), operatorAnnotations: make(map[string]string), expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg-repl.test.db.example.com,test-stg-repl.acid.db.example.com", - "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", + "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg-repl.test.db.example.com,test-stg-repl.acid.db.example.com", }, }, { @@ -1377,7 +1365,6 @@ func TestCompareServices(t *testing.T) { serviceWithOwnerReference := newService( map[string]string{ constants.ZalandoDNSNameAnnotation: "clstr.acid.zalan.do", - constants.ElbTimeoutAnnotationName: constants.ElbTimeoutAnnotationValue, }, v1.ServiceTypeClusterIP, []string{"128.141.0.0/16", "137.138.0.0/16"}, @@ -1406,7 +1393,6 @@ func TestCompareServices(t *testing.T) { current: newService( map[string]string{ constants.ZalandoDNSNameAnnotation: "clstr.acid.zalan.do", - constants.ElbTimeoutAnnotationName: constants.ElbTimeoutAnnotationValue, }, v1.ServiceTypeClusterIP, []string{"128.141.0.0/16", "137.138.0.0/16"}, @@ -1414,7 +1400,6 @@ func TestCompareServices(t *testing.T) { new: newService( map[string]string{ constants.ZalandoDNSNameAnnotation: "clstr.acid.zalan.do", - constants.ElbTimeoutAnnotationName: constants.ElbTimeoutAnnotationValue, }, v1.ServiceTypeClusterIP, []string{"128.141.0.0/16", "137.138.0.0/16"}, @@ -1426,7 +1411,6 @@ func TestCompareServices(t *testing.T) { current: newService( map[string]string{ constants.ZalandoDNSNameAnnotation: "clstr.acid.zalan.do", - constants.ElbTimeoutAnnotationName: constants.ElbTimeoutAnnotationValue, }, v1.ServiceTypeClusterIP, []string{"128.141.0.0/16", "137.138.0.0/16"}, @@ -1434,7 +1418,6 @@ func TestCompareServices(t *testing.T) { new: newService( map[string]string{ constants.ZalandoDNSNameAnnotation: "clstr.acid.zalan.do", - constants.ElbTimeoutAnnotationName: constants.ElbTimeoutAnnotationValue, }, v1.ServiceTypeLoadBalancer, []string{"128.141.0.0/16", "137.138.0.0/16"}, @@ -1447,7 +1430,6 @@ func TestCompareServices(t *testing.T) { current: newService( map[string]string{ constants.ZalandoDNSNameAnnotation: "clstr.acid.zalan.do", - constants.ElbTimeoutAnnotationName: constants.ElbTimeoutAnnotationValue, }, v1.ServiceTypeLoadBalancer, []string{"128.141.0.0/16", "137.138.0.0/16"}, @@ -1455,7 +1437,6 @@ func TestCompareServices(t *testing.T) { new: newService( map[string]string{ constants.ZalandoDNSNameAnnotation: "clstr.acid.zalan.do", - constants.ElbTimeoutAnnotationName: constants.ElbTimeoutAnnotationValue, }, v1.ServiceTypeLoadBalancer, []string{"185.249.56.0/22"}, @@ -1468,7 +1449,6 @@ func TestCompareServices(t *testing.T) { current: newService( map[string]string{ constants.ZalandoDNSNameAnnotation: "clstr.acid.zalan.do", - constants.ElbTimeoutAnnotationName: constants.ElbTimeoutAnnotationValue, }, v1.ServiceTypeLoadBalancer, []string{"128.141.0.0/16", "137.138.0.0/16"}, @@ -1476,7 +1456,6 @@ func TestCompareServices(t *testing.T) { new: newService( map[string]string{ constants.ZalandoDNSNameAnnotation: "clstr.acid.zalan.do", - constants.ElbTimeoutAnnotationName: constants.ElbTimeoutAnnotationValue, }, v1.ServiceTypeLoadBalancer, []string{}, @@ -1489,7 +1468,6 @@ func TestCompareServices(t *testing.T) { current: newService( map[string]string{ constants.ZalandoDNSNameAnnotation: "clstr.acid.zalan.do", - constants.ElbTimeoutAnnotationName: constants.ElbTimeoutAnnotationValue, }, v1.ServiceTypeClusterIP, []string{"128.141.0.0/16", "137.138.0.0/16"}, diff --git a/pkg/cluster/connection_pooler.go b/pkg/cluster/connection_pooler.go index ac4ce67d8..e70eac56e 100644 --- a/pkg/cluster/connection_pooler.go +++ b/pkg/cluster/connection_pooler.go @@ -533,10 +533,6 @@ func (c *Cluster) generatePoolerServiceAnnotations(role PostgresRole, spec *acid annotations := c.getCustomServiceAnnotations(role, spec) if c.shouldCreateLoadBalancerForPoolerService(role, spec) { - // set ELB Timeout annotation with default value - if _, ok := annotations[constants.ElbTimeoutAnnotationName]; !ok { - annotations[constants.ElbTimeoutAnnotationName] = constants.ElbTimeoutAnnotationValue - } // -repl suffix will be added by replicaDNSName clusterNameWithPoolerSuffix := c.connectionPoolerName(Master) if role == Master { diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index 7d51951ff..2eb867f06 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -2029,11 +2029,6 @@ func (c *Cluster) generateServiceAnnotations(role PostgresRole, spec *acidv1.Pos if c.shouldCreateLoadBalancerForService(role, spec) { dnsName := c.dnsName(role) - // Just set ELB Timeout annotation with default value, if it does not - // have a custom value - if _, ok := annotations[constants.ElbTimeoutAnnotationName]; !ok { - annotations[constants.ElbTimeoutAnnotationName] = constants.ElbTimeoutAnnotationValue - } // External DNS name annotation is not customizable annotations[constants.ZalandoDNSNameAnnotation] = dnsName } diff --git a/pkg/util/constants/annotations.go b/pkg/util/constants/annotations.go index fc5a84fa5..0330ddcb8 100644 --- a/pkg/util/constants/annotations.go +++ b/pkg/util/constants/annotations.go @@ -3,8 +3,6 @@ package constants // Names and values in Kubernetes annotation for services, statefulsets and volumes const ( ZalandoDNSNameAnnotation = "external-dns.alpha.kubernetes.io/hostname" - ElbTimeoutAnnotationName = "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout" - ElbTimeoutAnnotationValue = "3600" KubeIAmAnnotation = "iam.amazonaws.com/role" VolumeStorateProvisionerAnnotation = "pv.kubernetes.io/provisioned-by" PostgresqlControllerAnnotationKey = "acid.zalan.do/controller"