Merge branch 'master' into feat/configurable-pooler-securitycontext

This commit is contained in:
Felix Kunde 2026-06-25 11:59:04 +02:00 committed by GitHub
commit 50d20bfd89
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
55 changed files with 4004 additions and 2640 deletions

View File

@ -19,11 +19,11 @@ jobs:
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v6
- uses: actions/setup-go@v2
- uses: actions/setup-go@v6
with:
go-version: "^1.25.3"
go-version: "^1.26.4"
- name: Run unit tests
run: make test
@ -53,20 +53,20 @@ jobs:
echo "BACKUP_IMAGE=$BACKUP_IMAGE" >> $GITHUB_OUTPUT
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
uses: docker/setup-qemu-action@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v4
- name: Login to GHCR
uses: docker/login-action@v2
uses: docker/login-action@v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push multiarch operator image to ghcr
uses: docker/build-push-action@v3
uses: docker/build-push-action@v7
with:
context: .
file: docker/Dockerfile
@ -76,7 +76,7 @@ jobs:
platforms: linux/amd64,linux/arm64
- name: Build and push multiarch pooler image to ghcr
uses: docker/build-push-action@v3
uses: docker/build-push-action@v7
with:
context: pooler
push: true
@ -85,7 +85,7 @@ jobs:
platforms: linux/amd64,linux/arm64
- name: Build and push multiarch ui image to ghcr
uses: docker/build-push-action@v3
uses: docker/build-push-action@v7
with:
context: ui
push: true
@ -94,7 +94,7 @@ jobs:
platforms: linux/amd64,linux/arm64
- name: Build and push multiarch logical-backup image to ghcr
uses: docker/build-push-action@v3
uses: docker/build-push-action@v7
with:
context: logical-backup
push: true

View File

@ -11,10 +11,10 @@ jobs:
name: End-2-End tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/setup-go@v2
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: "^1.25.3"
go-version: "^1.26.4"
- name: Make dependencies
run: make mocks
- name: Code generation

View File

@ -11,10 +11,10 @@ jobs:
name: Unit tests and coverage
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-go@v2
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: "^1.25.3"
go-version: "^1.26.4"
- name: Make dependencies
run: make mocks
- name: Compile

View File

@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2025 Zalando SE
Copyright (c) 2026 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

View File

@ -2,6 +2,7 @@
BINARY ?= postgres-operator
BUILD_FLAGS ?= -v
GOARCH ?= amd64
CGO_ENABLED ?= 0
ifeq ($(RACE),1)
BUILD_FLAGS += -race -a
@ -66,15 +67,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 and postgresql.crd.yaml for now
@rm manifests/acid.zalan.do_operatorconfigurations.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_operatorconfigurations.yaml manifests/operatorconfiguration.crd.yaml
@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
@cp manifests/operatorconfiguration.crd.yaml pkg/apis/acid.zalan.do/v1/operatorconfiguration.crd.yaml
local: ${SOURCES} $(GENERATED_CRDS)
CGO_ENABLED=${CGO_ENABLED} go build -o build/${BINARY} $(LOCAL_BUILD_FLAGS) -ldflags "$(LDFLAGS)" $(SOURCES)
@ -83,10 +84,10 @@ 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)
GOOS=linux GOARCH=${GOARCH} CGO_ENABLED=${CGO_ENABLED} go build -o build/linux/${BINARY} ${BUILD_FLAGS} -ldflags "$(LDFLAGS)" $(SOURCES)
macos: ${SOURCES} $(GENERATED_CRDS)
GOOS=darwin GOARCH=amd64 CGO_ENABLED=${CGO_ENABLED} go build -o build/macos/${BINARY} ${BUILD_FLAGS} -ldflags "$(LDFLAGS)" $(SOURCES)
GOOS=darwin GOARCH=${GOARCH} CGO_ENABLED=${CGO_ENABLED} go build -o build/macos/${BINARY} ${BUILD_FLAGS} -ldflags "$(LDFLAGS)" $(SOURCES)
docker: $(GENERATED_CRDS) ${DOCKERDIR}/${DOCKERFILE}
echo `(env)`
@ -100,10 +101,10 @@ pooler:
cd pooler; docker build --rm -t "$(POOLER_TAG)" --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"
docker run --rm -v "${GOPATH}":"${GOPATH}" -e GOPATH="${GOPATH}" -e RACE=1 -w ${PWD} golang:1.26.4 bash -c "make linux"
mocks:
GO111MODULE=on go generate ./...
go generate ./...
fmt:
@gofmt -l -w -s $(DIRS)
@ -113,7 +114,7 @@ vet:
@staticcheck $(PKG)
test: mocks $(GENERATED) $(GENERATED_CRDS)
GO111MODULE=on go test ./...
go test ./...
codegen: $(GENERATED)

View File

@ -63,6 +63,7 @@ production for over five years.
| Release | Postgres versions | K8s versions | Golang |
| :-------- | :---------------: | :---------------: | :-----: |
| next | 14 → 18 | 1.27+ | 1.26.4 |
| 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 |

File diff suppressed because it is too large Load Diff

View File

@ -415,6 +415,12 @@ configLogicalBackup:
logical_backup_schedule: "30 00 * * *"
# secret to be used as reference for env variables in cronjob
logical_backup_cronjob_environment_secret: ""
# number of successful backup jobs to keep in cronjob history
logical_backup_successful_jobs_history_limit: 3
# number of failed backup jobs to keep in cronjob history
logical_backup_failed_jobs_history_limit: 3
# TTL in seconds after which finished backup jobs are automatically deleted
logical_backup_ttl_seconds_after_finished: 86400
# automate creation of human users with teams API service
configTeamsApi:

View File

@ -1,4 +1,4 @@
FROM golang:1.25-alpine
FROM golang:1.26-alpine
LABEL maintainer="Team ACID @ Zalando <team-acid@zalando.de>"
# We need root certificates to deal with teams api over https

View File

@ -1,14 +1,19 @@
ARG BASE_IMAGE=alpine:latest
FROM golang:1.25-alpine AS builder
FROM --platform=$BUILDPLATFORM golang:1.26-alpine AS builder
ARG VERSION=latest
ARG TARGETOS
ARG TARGETARCH
COPY . /go/src/github.com/zalando/postgres-operator
WORKDIR /go/src/github.com/zalando/postgres-operator
RUN GO111MODULE=on go mod vendor \
&& CGO_ENABLED=0 go build -o build/postgres-operator -v -ldflags "-X=main.version=${VERSION}" cmd/main.go
RUN go clean -cache -modcache
RUN go mod vendor \
&& CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o build/postgres-operator -v -ldflags "-X=main.version=${VERSION}" cmd/main.go
FROM ${BASE_IMAGE}
ARG BASE_IMAGE
FROM --platform=$TARGETPLATFORM ${BASE_IMAGE}
LABEL maintainer="Team ACID @ Zalando <team-acid@zalando.de>"
LABEL org.opencontainers.image.source="https://github.com/zalando/postgres-operator"

View File

@ -13,7 +13,7 @@ apt-get install -y wget
(
cd /tmp
wget -q "https://storage.googleapis.com/golang/go1.25.3.linux-${arch}.tar.gz" -O go.tar.gz
wget -q "https://storage.googleapis.com/golang/go1.26.4.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
@ -26,5 +26,6 @@ export PATH="$PATH:$HOME/go/bin"
export GOPATH="$HOME/go"
mkdir -p build
GO111MODULE=on go mod vendor
go clean -cache -modcache
go mod vendor
CGO_ENABLED=0 go build -o build/postgres-operator -v -ldflags "$OPERATOR_LDFLAGS" cmd/main.go

View File

@ -79,11 +79,6 @@ Those are top-level keys, containing both leaf keys and groups.
Instruct the operator to create/update the CRDs. If disabled the operator will rely on the CRDs being managed separately.
The default is `true`.
* **enable_crd_validation**
*deprecated*: toggles if the operator will create or update CRDs with
[OpenAPI v3 schema validation](https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/#validation)
The default is `true`. `false` will be ignored, since `apiextensions.io/v1` requires a structural schema definition.
* **crd_categories**
The operator will register CRDs in the `all` category by default so that they will be returned by a `kubectl get all` call. You are free to change categories or leave them empty.
@ -904,6 +899,15 @@ 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: ""
* **logical_backup_successful_jobs_history_limit**
number of successful backup jobs to keep in cronjob history. The default is `3`.
* **logical_backup_failed_jobs_history_limit**
number of failed backup jobs to keep in cronjob history. The default is `3`.
* **logical_backup_ttl_seconds_after_finished**
TTL in seconds after which finished backup jobs are automatically deleted. The default is `86400`.
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:

View File

@ -8,7 +8,7 @@ ENV TERM xterm-256color
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 \
&& curl -LO https://dl.k8s.io/release/v1.36.1/bin/linux/amd64/kubectl \
&& chmod +x ./kubectl \
&& mv ./kubectl /usr/local/bin/kubectl \
&& apt-get -qq -y clean \

View File

@ -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.27.0
cd "/tmp" && go install sigs.k8s.io/kind@v0.27.0
e2etest: tools copy clean
./run.sh main

98
go.mod
View File

@ -1,10 +1,12 @@
module github.com/zalando/postgres-operator
go 1.25.3
go 1.26.4
require (
github.com/Masterminds/semver v1.5.0
github.com/aws/aws-sdk-go v1.55.8
github.com/Masterminds/semver/v3 v3.5.0
github.com/aws/aws-sdk-go-v2 v1.42.0
github.com/aws/aws-sdk-go-v2/config v1.32.24
github.com/aws/aws-sdk-go-v2/service/ec2 v1.305.3
github.com/golang/mock v1.6.0
github.com/lib/pq v1.12.3
github.com/motomux/pretty v0.0.0-20161209205251-b2aad2c9a95d
@ -12,72 +14,80 @@ require (
github.com/r3labs/diff v1.1.0
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
k8s.io/api v0.32.9
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
golang.org/x/crypto v0.51.0
gopkg.in/yaml.v3 v3.0.1
k8s.io/api v0.36.1
k8s.io/apiextensions-apiserver v0.36.1
k8s.io/apimachinery v0.36.1
k8s.io/client-go v0.36.1
sigs.k8s.io/yaml v1.6.0
)
require (
github.com/aws/aws-sdk-go-v2/credentials v1.19.23 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.29 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.29 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.29 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.30 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.12 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.29 // indirect
github.com/aws/aws-sdk-go-v2/service/signin v1.1.5 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.31.3 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.6 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.43.3 // indirect
github.com/aws/smithy-go v1.27.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/emicklei/go-restful/v3 v3.13.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/fxamacker/cbor/v2 v2.9.0 // indirect
github.com/go-logr/logr v1.4.3 // 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
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/gnostic-models v0.7.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/moby/spdystream v0.5.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/spf13/cobra v1.9.1 // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/spf13/cobra v1.10.2 // indirect
github.com/spf13/pflag v1.0.9 // 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
golang.org/x/oauth2 v0.27.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.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
go.yaml.in/yaml/v2 v2.4.3 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/mod v0.36.0 // indirect
golang.org/x/net v0.55.0 // indirect
golang.org/x/oauth2 v0.34.0 // indirect
golang.org/x/sync v0.21.0 // indirect
golang.org/x/sys v0.46.0 // indirect
golang.org/x/term v0.44.0 // indirect
golang.org/x/text v0.38.0 // indirect
golang.org/x/time v0.14.0 // indirect
golang.org/x/tools v0.45.0 // indirect
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af // indirect
gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // 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
gopkg.in/yaml.v2 v2.4.0 // indirect
k8s.io/code-generator v0.36.1 // indirect
k8s.io/gengo/v2 v2.0.0-20250922181213-ec3ebc5fd46b // indirect
k8s.io/klog/v2 v2.140.0 // indirect
k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a // indirect
k8s.io/streaming v0.36.1 // indirect
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 // indirect
sigs.k8s.io/controller-tools v0.17.3 // indirect
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.3.2 // indirect
)
tool (

219
go.sum
View File

@ -1,25 +1,53 @@
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Masterminds/semver/v3 v3.5.0 h1:kQceYJfbupGfZOKZQg0kou0DgAKhzDg2NZPAwZ/2OOE=
github.com/Masterminds/semver/v3 v3.5.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
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.55.8 h1:JRmEUbU52aJQZ2AjX4q4Wu7t4uZjOu71uyNmaWlUkJQ=
github.com/aws/aws-sdk-go v1.55.8/go.mod h1:ZkViS9AqA6otK+JBBNH2++sx1sgxrPKcSzPPvQkUtXk=
github.com/aws/aws-sdk-go-v2 v1.42.0 h1:XvXMJTkFQtpBKIWZnmr9ZEOc2InWM2yldjXEJ/bymhA=
github.com/aws/aws-sdk-go-v2 v1.42.0/go.mod h1:27+ACypSLljLAEKsCYOmrjKh83vuTRkuAe9Uv/3A4bg=
github.com/aws/aws-sdk-go-v2/config v1.32.24 h1:aEDEj533yGdVvEHfkCY0D/1FbDrjnZr4pIulxRjqpHs=
github.com/aws/aws-sdk-go-v2/config v1.32.24/go.mod h1:yZtrGKJGlqfEW+/m2uTsJK+Jz7xF5R0eZfgcIG9m1ss=
github.com/aws/aws-sdk-go-v2/credentials v1.19.23 h1:Zhu3GOpRCkNjtE/gJpuPDsytSnaCCTQk8neAGsgzG5Y=
github.com/aws/aws-sdk-go-v2/credentials v1.19.23/go.mod h1:VsJF2ropPB37gDr7M2rLSpCE8IQWdpl62uae7qxZmqU=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.29 h1:r6qZHbT+wxgWO/e9vYNUEtg7lv5+UN3pRqKhLXvnArg=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.29/go.mod h1:QRnaRcTVGKPGRy8w78HMQtKUGRYcnMZAANATkeVA6Mo=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.29 h1:f3vKqSo13fhTYb+JEcXwXefZQE26I1FB5eTSniU67ko=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.29/go.mod h1:MzoLFUArKGpGD+ukmPiTPG1X5x4o6M2kq4v2dr1FiEc=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.29 h1:RdwIf/CuUsvJX3RgJagbOyotl/cxoLY4xviKuE7p2GY=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.29/go.mod h1:71wt8W2EgswdZy9Mf9KNnzxZ3TiZlv4caKghPktDOkA=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.30 h1:VTGy885W5DKBxWRUJbym9hytNaYzsyaPkCHGRRMAOhU=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.30/go.mod h1:AS0HycUvJRFvTt613AYDOgO2jzw+00cVSMny8XB3yMY=
github.com/aws/aws-sdk-go-v2/service/ec2 v1.305.3 h1:rEay0b3E0qqyY+W0c3ox8wGRsVK+GjxXadj9B9vpg4c=
github.com/aws/aws-sdk-go-v2/service/ec2 v1.305.3/go.mod h1:8mrDF7OtbuL0QpwP4YCvLuoOE4/5lL7D33MXgp069/Y=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.12 h1:ZD2+BSw9vFsNlKYIasSNt3uDbjqqXIBcM13UJv/Lx2k=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.12/go.mod h1:Ms4zlcVBbXbiP7EVLhl+lgjvA/a7YphqQ3Ih3174EmI=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.29 h1:DRebniUGZ2MqiiIVmQJ04vIXr918hubdHMnarSLEWyU=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.29/go.mod h1:LfRkPCD8YHDM2E5eTkos2UpwYeZnBcVarTa8L59bJHA=
github.com/aws/aws-sdk-go-v2/service/signin v1.1.5 h1:6Xt6Ztjkwdia/7EtEaG7ki/qZUYlCcd7tGUotQed1QE=
github.com/aws/aws-sdk-go-v2/service/signin v1.1.5/go.mod h1:LxYujSTLPRlp2vTtcUO/+1ilrew8ytt6SvQyOgejzFQ=
github.com/aws/aws-sdk-go-v2/service/sso v1.31.3 h1:ey1XLTYXb9PcLt4535632o5kCGXNXEhNb620Dqwuylo=
github.com/aws/aws-sdk-go-v2/service/sso v1.31.3/go.mod h1:Lk7PlmoTYryQmyBG0EXqj5BcUbj3whXdU2s3yGI3EAc=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.6 h1:yLr03zQE/5Eu5l3QU0Si+xMbLMbSDF2YXsigqXngs6g=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.6/go.mod h1:Q5N6icH+KJZDLh+ESNwzdv6cZ6vLFF/egy3IOxWhmz4=
github.com/aws/aws-sdk-go-v2/service/sts v1.43.3 h1:VrIhKRCSK1umelSgB9RghvA9RTUYeQffyAS5ApXehNI=
github.com/aws/aws-sdk-go-v2/service/sts v1.43.3/go.mod h1:r8wkDOuLaaMFqFiYAb8dGY2A3gJCOujMc6CFOVC4Zhc=
github.com/aws/smithy-go v1.27.1 h1:4T340VFndXtADGF52gYa1POyL7s9E4Z1OeZ1hCscIw8=
github.com/aws/smithy-go v1.27.1/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
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=
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/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes=
github.com/emicklei/go-restful/v3 v3.13.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=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
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.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-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=
@ -28,42 +56,25 @@ github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
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/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=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
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.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=
github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
github.com/google/go-cmp v0.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.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=
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/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=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/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=
@ -80,25 +91,22 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
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/moby/spdystream v0.5.1 h1:9sNYeYZUcci9R6/w7KDaFWEWeV4LStVG78Mpyq/Zm/Y=
github.com/moby/spdystream v0.5.1/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=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/motomux/pretty v0.0.0-20161209205251-b2aad2c9a95d h1:LznySqW8MqVeFh+pW6rOkFdld9QQ7jRydBKKM6jyPVI=
github.com/motomux/pretty v0.0.0-20161209205251-b2aad2c9a95d/go.mod h1:u3hJ0kqCQu/cPpsu3RbCOPZ0d7V3IjPjv1adNRleM9I=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/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.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=
@ -108,18 +116,20 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/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/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
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=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
github.com/spf13/pflag v1.0.9/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=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@ -129,114 +139,105 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
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=
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
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.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/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI=
golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
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/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4=
golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ=
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.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/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
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.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
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-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-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/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
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/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
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.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/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
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.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
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 v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8=
golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0=
golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM=
golang.org/x/tools/go/expect v0.1.1-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=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af h1:+5/Sw3GsDNlEmu7TfklWKPdQ0Ykja5VEmq2i817+jbI=
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
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/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo=
gopkg.in/evanphx/json-patch.v4 v4.13.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=
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.32.9 h1:q/59kk8lnecgG0grJqzrmXC1Jcl2hPWp9ltz0FQuoLI=
k8s.io/api v0.32.9/go.mod h1:jIfT3rwW4EU1IXZm9qjzSk/2j91k4CJL5vUULrxqp3Y=
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.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=
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=
k8s.io/api v0.36.1 h1:XbL/EMj8K2aJpJtePmqUyQMsM0D4QI2pvl7YKJ20FTY=
k8s.io/api v0.36.1/go.mod h1:KOWo4ey3TINlXjeHVuwB3i+tXXnu+UcwFBHlI/9dvEo=
k8s.io/apiextensions-apiserver v0.36.1 h1:6JfYmPUsuUIHuN+3QxutXYWj492RqF5fBSx67GYK5Ks=
k8s.io/apiextensions-apiserver v0.36.1/go.mod h1:pLzZin90riwisdzKwv/GoTwENooytoIx5zWJb4Hkby8=
k8s.io/apimachinery v0.36.1 h1:G63Gjx2W+q0YD+72Vo8oY0nDnePVwnuzTmmy5ENrVSA=
k8s.io/apimachinery v0.36.1/go.mod h1:ibYOR00vW/I1kzvi5SF0dRuJ52BvKtfvRdOn35GPQ+8=
k8s.io/client-go v0.36.1 h1:FN/K8QIT2CEDt+2WB2HnWrUANZ50AP5GII43/SP2JR0=
k8s.io/client-go v0.36.1/go.mod h1:s6rAnCtTGYDQnpNjEhSaISV+2O8jwruZ6m3QOYBFbtU=
k8s.io/code-generator v0.36.1 h1:5bHQ7NbBcFFLHcoyo/hgU3m2tQV5RLz2nv4QNDlsbXc=
k8s.io/code-generator v0.36.1/go.mod h1:oCv8WmrW2RGdcMyvSk1aYbBfSs51ggtSFQr1YNeuAuo=
k8s.io/gengo/v2 v2.0.0-20250922181213-ec3ebc5fd46b h1:gMplByicHV/TJBizHd9aVEsTYoJBnnUAT5MHlTkbjhQ=
k8s.io/gengo/v2 v2.0.0-20250922181213-ec3ebc5fd46b/go.mod h1:CgujABENc3KuTrcsdpGmrrASjtQsWCT7R99mEV4U/fM=
k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc=
k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0=
k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a h1:xCeOEAOoGYl2jnJoHkC3hkbPJgdATINPMAxaynU2Ovg=
k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a/go.mod h1:uGBT7iTA6c6MvqUvSXIaYZo9ukscABYi2btjhvgKGZ0=
k8s.io/streaming v0.36.1 h1:L+K68n4Gg940BGNNYtUBvL1WTLL0YnKT3s+P1MNAmR4=
k8s.io/streaming v0.36.1/go.mod h1:z6fV3D+NVkoeqRMtWwlUZK6U17SY/LqNzOxWL6GyR/s=
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU=
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=
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=
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg=
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc=
sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.2 h1:kwVWMx5yS1CrnFWA/2QHyRVJ8jM6dBA80uLmm0wJkk8=
sigs.k8s.io/structured-merge-diff/v6 v6.3.2/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

View File

@ -34,7 +34,6 @@ GROUPS_WITH_VERSIONS="${CUSTOM_RESOURCE_NAME_ZAL}:${CUSTOM_RESOURCE_VERSION},${C
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}"

View File

@ -38,7 +38,6 @@ data:
# downscaler_annotations: "deployment-time,downscaler/*"
enable_admin_role_for_users: "true"
enable_crd_registration: "true"
enable_crd_validation: "true"
enable_cross_namespace_secret: "false"
enable_finalizers: "false"
enable_database_access: "true"

File diff suppressed because it is too large Load Diff

View File

@ -302,7 +302,9 @@ spec:
a Container.
properties:
name:
description: Name of the environment variable. Must be a C_IDENTIFIER.
description: |-
Name of the environment variable.
May consist of any printable ASCII characters except '='.
type: string
value:
description: |-
@ -360,6 +362,43 @@ spec:
- fieldPath
type: object
x-kubernetes-map-type: atomic
fileKeyRef:
description: |-
FileKeyRef selects a key of the env file.
Requires the EnvFiles feature gate to be enabled.
properties:
key:
description: |-
The key within the env file. An invalid key will prevent the pod from starting.
The keys defined within a source may consist of any printable ASCII characters except '='.
During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters.
type: string
optional:
default: false
description: |-
Specify whether the file or its key must be defined. If the file or key
does not exist, then the env var is not published.
If optional is set to true and the specified key does not exist,
the environment variable will not be set in the Pod's containers.
If optional is set to false and the specified key does not exist,
an error will be returned during Pod creation.
type: boolean
path:
description: |-
The path within the volume from which to select the file.
Must be relative and may not contain the '..' path or start with '..'.
type: string
volumeName:
description: The name of the volume mount containing
the env file.
type: string
required:
- key
- path
- volumeName
type: object
x-kubernetes-map-type: atomic
resourceFieldRef:
description: |-
Selects a resource of the container: only resources limits and requests
@ -456,8 +495,9 @@ spec:
in a Container.
properties:
name:
description: Name of the environment variable. Must be
a C_IDENTIFIER.
description: |-
Name of the environment variable.
May consist of any printable ASCII characters except '='.
type: string
value:
description: |-
@ -515,6 +555,43 @@ spec:
- fieldPath
type: object
x-kubernetes-map-type: atomic
fileKeyRef:
description: |-
FileKeyRef selects a key of the env file.
Requires the EnvFiles feature gate to be enabled.
properties:
key:
description: |-
The key within the env file. An invalid key will prevent the pod from starting.
The keys defined within a source may consist of any printable ASCII characters except '='.
During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters.
type: string
optional:
default: false
description: |-
Specify whether the file or its key must be defined. If the file or key
does not exist, then the env var is not published.
If optional is set to true and the specified key does not exist,
the environment variable will not be set in the Pod's containers.
If optional is set to false and the specified key does not exist,
an error will be returned during Pod creation.
type: boolean
path:
description: |-
The path within the volume from which to select the file.
Must be relative and may not contain the '..' path or start with '..'.
type: string
volumeName:
description: The name of the volume mount containing
the env file.
type: string
required:
- key
- path
- volumeName
type: object
x-kubernetes-map-type: atomic
resourceFieldRef:
description: |-
Selects a resource of the container: only resources limits and requests
@ -575,14 +652,14 @@ spec:
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
The keys defined within a source may consist of any printable ASCII characters except '='.
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
of ConfigMaps or Secrets
properties:
configMapRef:
description: The ConfigMap to select from
@ -603,8 +680,9 @@ spec:
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.
description: |-
Optional text to prepend to the name of each environment variable.
May consist of any printable ASCII characters except '='.
type: string
secretRef:
description: The Secret to select from
@ -867,6 +945,12 @@ spec:
- port
type: object
type: object
stopSignal:
description: |-
StopSignal defines which signal will be sent to a container when it is being stopped.
If not specified, the default is defined by the container runtime in use.
StopSignal can only be set for Pods with a non-empty .spec.os.name
type: string
type: object
livenessProbe:
description: |-
@ -1237,7 +1321,9 @@ spec:
type: integer
type: object
resizePolicy:
description: Resources resize policy for the container.
description: |-
Resources resize policy for the container.
This field cannot be set on ephemeral containers.
items:
description: ContainerResizePolicy represents resource resize
policy for the container.
@ -1269,7 +1355,7 @@ spec:
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
This field depends on the
DynamicResourceAllocation feature gate.
This field is immutable. It can only be set for containers.
@ -1323,10 +1409,10 @@ spec:
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,
This overrides the pod-level restart policy. 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:
Additionally, 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"
@ -1338,6 +1424,59 @@ spec:
init container is started, or after any startupProbe has successfully
completed.
type: string
restartPolicyRules:
description: |-
Represents a list of rules to be checked to determine if the
container should be restarted on exit. The rules are evaluated in
order. Once a rule matches a container exit condition, the remaining
rules are ignored. If no rule matches the container exit condition,
the Container-level restart policy determines the whether the container
is restarted or not. Constraints on the rules:
- At most 20 rules are allowed.
- Rules can have the same action.
- Identical rules are not forbidden in validations.
When rules are specified, container MUST set RestartPolicy explicitly
even it if matches the Pod's RestartPolicy.
items:
description: ContainerRestartRule describes how a container
exit is handled.
properties:
action:
description: |-
Specifies the action taken on a container exit if the requirements
are satisfied. The only possible value is "Restart" to restart the
container.
type: string
exitCodes:
description: Represents the exit codes to check on container
exits.
properties:
operator:
description: |-
Represents the relationship between the container exit code(s) and the
specified values. Possible values are:
- In: the requirement is satisfied if the container exit code is in the
set of specified values.
- NotIn: the requirement is satisfied if the container exit code is
not in the set of specified values.
type: string
values:
description: |-
Specifies the set of values to check for container exit codes.
At most 255 elements are allowed.
items:
format: int32
type: integer
type: array
x-kubernetes-list-type: set
required:
- operator
type: object
required:
- action
type: object
type: array
x-kubernetes-list-type: atomic
securityContext:
description: |-
SecurityContext defines the security options the container should be run with.
@ -1413,7 +1552,6 @@ spec:
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:
@ -1878,8 +2016,9 @@ spec:
in a Container.
properties:
name:
description: Name of the environment variable. Must be
a C_IDENTIFIER.
description: |-
Name of the environment variable.
May consist of any printable ASCII characters except '='.
type: string
value:
description: |-
@ -1937,6 +2076,43 @@ spec:
- fieldPath
type: object
x-kubernetes-map-type: atomic
fileKeyRef:
description: |-
FileKeyRef selects a key of the env file.
Requires the EnvFiles feature gate to be enabled.
properties:
key:
description: |-
The key within the env file. An invalid key will prevent the pod from starting.
The keys defined within a source may consist of any printable ASCII characters except '='.
During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters.
type: string
optional:
default: false
description: |-
Specify whether the file or its key must be defined. If the file or key
does not exist, then the env var is not published.
If optional is set to true and the specified key does not exist,
the environment variable will not be set in the Pod's containers.
If optional is set to false and the specified key does not exist,
an error will be returned during Pod creation.
type: boolean
path:
description: |-
The path within the volume from which to select the file.
Must be relative and may not contain the '..' path or start with '..'.
type: string
volumeName:
description: The name of the volume mount containing
the env file.
type: string
required:
- key
- path
- volumeName
type: object
x-kubernetes-map-type: atomic
resourceFieldRef:
description: |-
Selects a resource of the container: only resources limits and requests
@ -1997,14 +2173,14 @@ spec:
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
The keys defined within a source may consist of any printable ASCII characters except '='.
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
of ConfigMaps or Secrets
properties:
configMapRef:
description: The ConfigMap to select from
@ -2025,8 +2201,9 @@ spec:
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.
description: |-
Optional text to prepend to the name of each environment variable.
May consist of any printable ASCII characters except '='.
type: string
secretRef:
description: The Secret to select from
@ -2289,6 +2466,12 @@ spec:
- port
type: object
type: object
stopSignal:
description: |-
StopSignal defines which signal will be sent to a container when it is being stopped.
If not specified, the default is defined by the container runtime in use.
StopSignal can only be set for Pods with a non-empty .spec.os.name
type: string
type: object
livenessProbe:
description: |-
@ -2659,7 +2842,9 @@ spec:
type: integer
type: object
resizePolicy:
description: Resources resize policy for the container.
description: |-
Resources resize policy for the container.
This field cannot be set on ephemeral containers.
items:
description: ContainerResizePolicy represents resource resize
policy for the container.
@ -2691,7 +2876,7 @@ spec:
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
This field depends on the
DynamicResourceAllocation feature gate.
This field is immutable. It can only be set for containers.
@ -2745,10 +2930,10 @@ spec:
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,
This overrides the pod-level restart policy. 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:
Additionally, 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"
@ -2760,6 +2945,59 @@ spec:
init container is started, or after any startupProbe has successfully
completed.
type: string
restartPolicyRules:
description: |-
Represents a list of rules to be checked to determine if the
container should be restarted on exit. The rules are evaluated in
order. Once a rule matches a container exit condition, the remaining
rules are ignored. If no rule matches the container exit condition,
the Container-level restart policy determines the whether the container
is restarted or not. Constraints on the rules:
- At most 20 rules are allowed.
- Rules can have the same action.
- Identical rules are not forbidden in validations.
When rules are specified, container MUST set RestartPolicy explicitly
even it if matches the Pod's RestartPolicy.
items:
description: ContainerRestartRule describes how a container
exit is handled.
properties:
action:
description: |-
Specifies the action taken on a container exit if the requirements
are satisfied. The only possible value is "Restart" to restart the
container.
type: string
exitCodes:
description: Represents the exit codes to check on container
exits.
properties:
operator:
description: |-
Represents the relationship between the container exit code(s) and the
specified values. Possible values are:
- In: the requirement is satisfied if the container exit code is in the
set of specified values.
- NotIn: the requirement is satisfied if the container exit code is
not in the set of specified values.
type: string
values:
description: |-
Specifies the set of values to check for container exit codes.
At most 255 elements are allowed.
items:
format: int32
type: integer
type: array
x-kubernetes-list-type: set
required:
- operator
type: object
required:
- action
type: object
type: array
x-kubernetes-list-type: atomic
securityContext:
description: |-
SecurityContext defines the security options the container should be run with.
@ -2835,7 +3073,6 @@ spec:
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:
@ -3849,8 +4086,9 @@ spec:
in a Container.
properties:
name:
description: Name of the environment variable. Must be
a C_IDENTIFIER.
description: |-
Name of the environment variable.
May consist of any printable ASCII characters except '='.
type: string
value:
description: |-
@ -3908,6 +4146,43 @@ spec:
- fieldPath
type: object
x-kubernetes-map-type: atomic
fileKeyRef:
description: |-
FileKeyRef selects a key of the env file.
Requires the EnvFiles feature gate to be enabled.
properties:
key:
description: |-
The key within the env file. An invalid key will prevent the pod from starting.
The keys defined within a source may consist of any printable ASCII characters except '='.
During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters.
type: string
optional:
default: false
description: |-
Specify whether the file or its key must be defined. If the file or key
does not exist, then the env var is not published.
If optional is set to true and the specified key does not exist,
the environment variable will not be set in the Pod's containers.
If optional is set to false and the specified key does not exist,
an error will be returned during Pod creation.
type: boolean
path:
description: |-
The path within the volume from which to select the file.
Must be relative and may not contain the '..' path or start with '..'.
type: string
volumeName:
description: The name of the volume mount containing
the env file.
type: string
required:
- key
- path
- volumeName
type: object
x-kubernetes-map-type: atomic
resourceFieldRef:
description: |-
Selects a resource of the container: only resources limits and requests
@ -4214,9 +4489,10 @@ spec:
operator:
description: |-
Operator represents a key's relationship to the value.
Valid operators are Exists and Equal. Defaults to Equal.
Valid operators are Exists, Equal, Lt, and Gt. Defaults to Equal.
Exists is equivalent to wildcard for value, so that a pod can
tolerate all taints of a particular category.
Lt and Gt perform numeric comparisons (requires feature gate TaintTolerationComparisonOperators).
type: string
tolerationSeconds:
description: |-
@ -4355,7 +4631,6 @@ spec:
- Ignore: nodeAffinity/nodeSelector are ignored. All nodes are included in the calculations.
If this value is nil, the behavior is equivalent to the Honor policy.
This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag.
type: string
nodeTaintsPolicy:
description: |-
@ -4366,7 +4641,6 @@ spec:
- Ignore: node taints are ignored. All nodes are included.
If this value is nil, the behavior is equivalent to the Ignore policy.
This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag.
type: string
topologyKey:
description: |-

File diff suppressed because it is too large Load Diff

View File

@ -13,11 +13,17 @@ import (
)
// +genclient
// +genclient:onlyVerbs=get
// +genclient:noStatus
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// OperatorConfiguration defines the specification for the OperatorConfiguration.
// +k8s:deepcopy-gen=true
// +kubebuilder:resource:categories=all,shortName=opconfig,scope=Namespaced
// +kubebuilder:printcolumn:name="Image",type=string,JSONPath=`.configuration.docker_image`,description="Spilo image to be used for Pods"
// +kubebuilder:printcolumn:name="Cluster-Label",type=string,JSONPath=`.configuration.kubernetes.cluster_name_label`,description="Label for K8s resources created by operator"
// +kubebuilder:printcolumn:name="Service-Account",type=string,JSONPath=`.configuration.kubernetes.pod_service_account_name`,description="Name of service account to be used"
// +kubebuilder:printcolumn:name="Min-Instances",type=integer,JSONPath=`.configuration.min_instances`,description="Minimum number of instances per Postgres cluster"
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`,description="Age of the OperatorConfiguration resource"
// +kubebuilder:subresource:status
type OperatorConfiguration struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata"`
@ -37,102 +43,170 @@ type OperatorConfigurationList struct {
// PostgresUsersConfiguration defines the system users of Postgres.
type PostgresUsersConfiguration struct {
SuperUsername string `json:"super_username,omitempty"`
ReplicationUsername string `json:"replication_username,omitempty"`
AdditionalOwnerRoles []string `json:"additional_owner_roles,omitempty"`
EnablePasswordRotation bool `json:"enable_password_rotation,omitempty"`
PasswordRotationInterval uint32 `json:"password_rotation_interval,omitempty"`
PasswordRotationUserRetention uint32 `json:"password_rotation_user_retention,omitempty"`
// +kubebuilder:default=postgres
SuperUsername string `json:"super_username,omitempty"`
// +kubebuilder:default=standby
ReplicationUsername string `json:"replication_username,omitempty"`
AdditionalOwnerRoles []string `json:"additional_owner_roles,omitempty"`
EnablePasswordRotation bool `json:"enable_password_rotation,omitempty"`
// +kubebuilder:default=90
PasswordRotationInterval uint32 `json:"password_rotation_interval,omitempty"`
// +kubebuilder:default=120
PasswordRotationUserRetention uint32 `json:"password_rotation_user_retention,omitempty"`
}
// MajorVersionUpgradeConfiguration defines how to execute major version upgrades of Postgres.
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
// +kubebuilder:validation:Enum=off;manual;full
// +kubebuilder:default=manual
MajorVersionUpgradeMode string `json:"major_version_upgrade_mode,omitempty"` // 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:"14"`
TargetMajorVersion string `json:"target_major_version" default:"18"`
// +kubebuilder:default="14"
MinimalMajorVersion string `json:"minimal_major_version,omitempty"`
// +kubebuilder:default="18"
TargetMajorVersion string `json:"target_major_version,omitempty"`
}
// KubernetesMetaConfiguration defines k8s conf required for all Postgres clusters and the operator itself
type KubernetesMetaConfiguration struct {
EnableOwnerReferences *bool `json:"enable_owner_references,omitempty"`
EnableOwnerReferences *bool `json:"enable_owner_references,omitempty"`
// +kubebuilder:default=postgres-pod
PodServiceAccountName string `json:"pod_service_account_name,omitempty"`
// TODO: change it to the proper json
PodServiceAccountDefinition string `json:"pod_service_account_definition,omitempty"`
PodServiceAccountRoleBindingDefinition string `json:"pod_service_account_role_binding_definition,omitempty"`
PodTerminateGracePeriod Duration `json:"pod_terminate_grace_period,omitempty"`
LivenessProbe *v1.Probe `json:"liveness_probe"`
SpiloPrivileged bool `json:"spilo_privileged,omitempty"`
SpiloAllowPrivilegeEscalation *bool `json:"spilo_allow_privilege_escalation,omitempty"`
SpiloRunAsUser *int64 `json:"spilo_runasuser,omitempty"`
SpiloRunAsGroup *int64 `json:"spilo_runasgroup,omitempty"`
SpiloFSGroup *int64 `json:"spilo_fsgroup,omitempty"`
AdditionalPodCapabilities []string `json:"additional_pod_capabilities,omitempty"`
WatchedNamespace string `json:"watched_namespace,omitempty"`
PDBNameFormat config.StringTemplate `json:"pdb_name_format,omitempty"`
PDBMasterLabelSelector *bool `json:"pdb_master_label_selector,omitempty"`
EnablePodDisruptionBudget *bool `json:"enable_pod_disruption_budget,omitempty"`
StorageResizeMode string `json:"storage_resize_mode,omitempty"`
EnableInitContainers *bool `json:"enable_init_containers,omitempty"`
EnableSidecars *bool `json:"enable_sidecars,omitempty"`
SharePgSocketWithSidecars *bool `json:"share_pgsocket_with_sidecars,omitempty"`
SecretNameTemplate config.StringTemplate `json:"secret_name_template,omitempty"`
ClusterDomain string `json:"cluster_domain,omitempty"`
OAuthTokenSecretName spec.NamespacedName `json:"oauth_token_secret_name,omitempty"`
InfrastructureRolesSecretName spec.NamespacedName `json:"infrastructure_roles_secret_name,omitempty"`
InfrastructureRolesDefs []*config.InfrastructureRole `json:"infrastructure_roles_secrets,omitempty"`
PodRoleLabel string `json:"pod_role_label,omitempty"`
ClusterLabels map[string]string `json:"cluster_labels,omitempty"`
InheritedLabels []string `json:"inherited_labels,omitempty"`
InheritedAnnotations []string `json:"inherited_annotations,omitempty"`
DownscalerAnnotations []string `json:"downscaler_annotations,omitempty"`
IgnoredAnnotations []string `json:"ignored_annotations,omitempty"`
ClusterNameLabel string `json:"cluster_name_label,omitempty"`
DeleteAnnotationDateKey string `json:"delete_annotation_date_key,omitempty"`
DeleteAnnotationNameKey string `json:"delete_annotation_name_key,omitempty"`
NodeReadinessLabel map[string]string `json:"node_readiness_label,omitempty"`
NodeReadinessLabelMerge string `json:"node_readiness_label_merge,omitempty"`
CustomPodAnnotations map[string]string `json:"custom_pod_annotations,omitempty"`
PodServiceAccountDefinition string `json:"pod_service_account_definition,omitempty"`
PodServiceAccountRoleBindingDefinition string `json:"pod_service_account_role_binding_definition,omitempty"`
// +kubebuilder:default="5m"
// Postgres pods are terminated forcefully after this timeout
PodTerminateGracePeriod Duration `json:"pod_terminate_grace_period,omitempty"`
// +optional
LivenessProbe *v1.Probe `json:"liveness_probe"`
SpiloPrivileged bool `json:"spilo_privileged,omitempty"`
// +kubebuilder:default=true
SpiloAllowPrivilegeEscalation *bool `json:"spilo_allow_privilege_escalation,omitempty"`
SpiloRunAsUser *int64 `json:"spilo_runasuser,omitempty"`
SpiloRunAsGroup *int64 `json:"spilo_runasgroup,omitempty"`
SpiloFSGroup *int64 `json:"spilo_fsgroup,omitempty"`
AdditionalPodCapabilities []string `json:"additional_pod_capabilities,omitempty"`
WatchedNamespace string `json:"watched_namespace,omitempty"`
// +kubebuilder:default="postgres-{cluster}-pdb"
// defines the template for PDB names
PDBNameFormat config.StringTemplate `json:"pdb_name_format,omitempty"`
// +kubebuilder:default=true
PDBMasterLabelSelector *bool `json:"pdb_master_label_selector,omitempty"`
// +kubebuilder:default=true
EnablePodDisruptionBudget *bool `json:"enable_pod_disruption_budget,omitempty"`
// +kubebuilder:validation:Enum=ebs;mixed;pvc;off
// +kubebuilder:default=pvc
StorageResizeMode string `json:"storage_resize_mode,omitempty"`
// +kubebuilder:default=true
EnableInitContainers *bool `json:"enable_init_containers,omitempty"`
// +kubebuilder:default=true
EnableSidecars *bool `json:"enable_sidecars,omitempty"`
SharePgSocketWithSidecars *bool `json:"share_pgsocket_with_sidecars,omitempty"`
// +kubebuilder:default="{username}.{cluster}.credentials.{tprkind}.{tprgroup}"
// template for database user secrets generated by the operator,
// here username contains the namespace in the format namespace.username
// if the user is in different namespace than cluster and cross namespace secrets
// are enabled via `enable_cross_namespace_secret` flag in the configuration.
SecretNameTemplate config.StringTemplate `json:"secret_name_template,omitempty"`
// +kubebuilder:default="cluster.local"
ClusterDomain string `json:"cluster_domain,omitempty"`
// +kubebuilder:default=postgres-operator
// namespaced name of the secret containing the OAuth2 token to pass to the teams API
OAuthTokenSecretName spec.NamespacedName `json:"oauth_token_secret_name,omitempty"`
InfrastructureRolesSecretName spec.NamespacedName `json:"infrastructure_roles_secret_name,omitempty"`
// +kubebuilder:validation:Type=array
// namespaced name of the secret containing infrastructure roles names and passwords
InfrastructureRolesDefs []*config.InfrastructureRole `json:"infrastructure_roles_secrets,omitempty"`
// +kubebuilder:default=spilo-role
PodRoleLabel string `json:"pod_role_label,omitempty"`
// +kubebuilder:default={application: spilo}
ClusterLabels map[string]string `json:"cluster_labels,omitempty"`
InheritedLabels []string `json:"inherited_labels,omitempty"`
InheritedAnnotations []string `json:"inherited_annotations,omitempty"`
DownscalerAnnotations []string `json:"downscaler_annotations,omitempty"`
IgnoredAnnotations []string `json:"ignored_annotations,omitempty"`
// +kubebuilder:default=cluster-name
ClusterNameLabel string `json:"cluster_name_label,omitempty"`
DeleteAnnotationDateKey string `json:"delete_annotation_date_key,omitempty"`
DeleteAnnotationNameKey string `json:"delete_annotation_name_key,omitempty"`
NodeReadinessLabel map[string]string `json:"node_readiness_label,omitempty"`
// +kubebuilder:validation:Enum=AND;OR
NodeReadinessLabelMerge string `json:"node_readiness_label_merge,omitempty"`
CustomPodAnnotations map[string]string `json:"custom_pod_annotations,omitempty"`
// TODO: use a proper toleration structure?
PodToleration map[string]string `json:"toleration,omitempty"`
PodEnvironmentConfigMap spec.NamespacedName `json:"pod_environment_configmap,omitempty"`
PodEnvironmentSecret string `json:"pod_environment_secret,omitempty"`
PodPriorityClassName string `json:"pod_priority_class_name,omitempty"`
MasterPodMoveTimeout Duration `json:"master_pod_move_timeout,omitempty"`
EnablePodAntiAffinity bool `json:"enable_pod_antiaffinity,omitempty"`
PodAntiAffinityPreferredDuringScheduling bool `json:"pod_antiaffinity_preferred_during_scheduling,omitempty"`
PodAntiAffinityTopologyKey string `json:"pod_antiaffinity_topology_key,omitempty"`
PodManagementPolicy string `json:"pod_management_policy,omitempty"`
PersistentVolumeClaimRetentionPolicy map[string]string `json:"persistent_volume_claim_retention_policy,omitempty"`
EnableSecretsDeletion *bool `json:"enable_secrets_deletion,omitempty"`
EnablePersistentVolumeClaimDeletion *bool `json:"enable_persistent_volume_claim_deletion,omitempty"`
EnableReadinessProbe bool `json:"enable_readiness_probe,omitempty"`
EnableCrossNamespaceSecret bool `json:"enable_cross_namespace_secret,omitempty"`
EnableFinalizers *bool `json:"enable_finalizers,omitempty"`
PodToleration map[string]string `json:"toleration,omitempty"`
// namespaced name of the ConfigMap with environment variables to populate on every pod
PodEnvironmentConfigMap spec.NamespacedName `json:"pod_environment_configmap,omitempty"`
PodEnvironmentSecret string `json:"pod_environment_secret,omitempty"`
PodPriorityClassName string `json:"pod_priority_class_name,omitempty"`
// +kubebuilder:default="20m"
// timeout for successful migration of master pods from unschedulable node
MasterPodMoveTimeout Duration `json:"master_pod_move_timeout,omitempty"`
EnablePodAntiAffinity bool `json:"enable_pod_antiaffinity,omitempty"`
PodAntiAffinityPreferredDuringScheduling bool `json:"pod_antiaffinity_preferred_during_scheduling,omitempty"`
// +kubebuilder:default="kubernetes.io/hostname"
PodAntiAffinityTopologyKey string `json:"pod_antiaffinity_topology_key,omitempty"`
// +kubebuilder:validation:Enum=ordered_ready;parallel
// +kubebuilder:default=ordered_ready
PodManagementPolicy string `json:"pod_management_policy,omitempty"`
PersistentVolumeClaimRetentionPolicy map[string]string `json:"persistent_volume_claim_retention_policy,omitempty"`
// +kubebuilder:default=true
EnableSecretsDeletion *bool `json:"enable_secrets_deletion,omitempty"`
// +kubebuilder:default=true
EnablePersistentVolumeClaimDeletion *bool `json:"enable_persistent_volume_claim_deletion,omitempty"`
EnableReadinessProbe bool `json:"enable_readiness_probe,omitempty"`
EnableCrossNamespaceSecret bool `json:"enable_cross_namespace_secret,omitempty"`
EnableFinalizers *bool `json:"enable_finalizers,omitempty"`
}
// PostgresPodResourcesDefaults defines the spec of default resources
type PostgresPodResourcesDefaults struct {
DefaultCPURequest string `json:"default_cpu_request,omitempty"`
// +kubebuilder:validation:Pattern=`^(\d+m|\d+(\.\d{1,3})?)$`
DefaultCPURequest string `json:"default_cpu_request,omitempty"`
// +kubebuilder:validation:Pattern=`^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$`
DefaultMemoryRequest string `json:"default_memory_request,omitempty"`
DefaultCPULimit string `json:"default_cpu_limit,omitempty"`
DefaultMemoryLimit string `json:"default_memory_limit,omitempty"`
MinCPULimit string `json:"min_cpu_limit,omitempty"`
MinMemoryLimit string `json:"min_memory_limit,omitempty"`
MaxCPURequest string `json:"max_cpu_request,omitempty"`
MaxMemoryRequest string `json:"max_memory_request,omitempty"`
// +kubebuilder:validation:Pattern=`^(\d+m|\d+(\.\d{1,3})?)$`
DefaultCPULimit string `json:"default_cpu_limit,omitempty"`
// +kubebuilder:validation:Pattern=`^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$`
DefaultMemoryLimit string `json:"default_memory_limit,omitempty"`
// +kubebuilder:validation:Pattern=`^(\d+m|\d+(\.\d{1,3})?)$`
MinCPULimit string `json:"min_cpu_limit,omitempty"`
// +kubebuilder:validation:Pattern=`^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$`
MinMemoryLimit string `json:"min_memory_limit,omitempty"`
// +kubebuilder:validation:Pattern=`^(\d+m|\d+(\.\d{1,3})?)$`
MaxCPURequest string `json:"max_cpu_request,omitempty"`
// +kubebuilder:validation:Pattern=`^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$`
MaxMemoryRequest string `json:"max_memory_request,omitempty"`
}
// OperatorTimeouts defines the timeout of ResourceCheck, PodWait, ReadyWait
type OperatorTimeouts struct {
ResourceCheckInterval Duration `json:"resource_check_interval,omitempty"`
ResourceCheckTimeout Duration `json:"resource_check_timeout,omitempty"`
PodLabelWaitTimeout Duration `json:"pod_label_wait_timeout,omitempty"`
PodDeletionWaitTimeout Duration `json:"pod_deletion_wait_timeout,omitempty"`
ReadyWaitInterval Duration `json:"ready_wait_interval,omitempty"`
ReadyWaitTimeout Duration `json:"ready_wait_timeout,omitempty"`
// +kubebuilder:default="3s"
// interval to wait between consecutive attempts to check for some K8s resources
ResourceCheckInterval Duration `json:"resource_check_interval,omitempty"`
// +kubebuilder:default="10m"
// timeout when waiting for the presence of a certain K8s resource
ResourceCheckTimeout Duration `json:"resource_check_timeout,omitempty"`
// +kubebuilder:default="10m"
// timeout when waiting for pod role and cluster labels
PodLabelWaitTimeout Duration `json:"pod_label_wait_timeout,omitempty"`
// +kubebuilder:default="10m"
// timeout when waiting for the Postgres pods to be deleted
PodDeletionWaitTimeout Duration `json:"pod_deletion_wait_timeout,omitempty"`
// +kubebuilder:default="4s"
// interval between consecutive attempts waiting for postgresql CRD to be created
ReadyWaitInterval Duration `json:"ready_wait_interval,omitempty"`
// +kubebuilder:default="30s"
// timeout for the complete postgres CRD creation
ReadyWaitTimeout Duration `json:"ready_wait_timeout,omitempty"`
// +kubebuilder:default="1s"
// interval between consecutive attempts of operator calling the Patroni API
PatroniAPICheckInterval Duration `json:"patroni_api_check_interval,omitempty"`
PatroniAPICheckTimeout Duration `json:"patroni_api_check_timeout,omitempty"`
// +kubebuilder:default="5s"
// timeout when waiting for successful response from Patroni API
PatroniAPICheckTimeout Duration `json:"patroni_api_check_timeout,omitempty"`
}
// LoadBalancerConfiguration defines the LB configuration
@ -143,24 +217,36 @@ type LoadBalancerConfiguration struct {
EnableReplicaLoadBalancer bool `json:"enable_replica_load_balancer,omitempty"`
EnableReplicaPoolerLoadBalancer bool `json:"enable_replica_pooler_load_balancer,omitempty"`
// kept in LoadBalancerConfiguration because all the other parameters apply here too
// NodePort flags kept in LoadBalancerConfiguration because all the other parameters apply here too
EnableMasterNodePort bool `json:"enable_master_node_port,omitempty"`
EnableMasterPoolerNodePort bool `json:"enable_master_pooler_node_port,omitempty"`
EnableReplicaNodePort bool `json:"enable_replica_node_port,omitempty"`
EnableReplicaPoolerNodePort bool `json:"enable_replica_pooler_node_port,omitempty"`
CustomServiceAnnotations map[string]string `json:"custom_service_annotations,omitempty"`
MasterDNSNameFormat config.StringTemplate `json:"master_dns_name_format,omitempty"`
MasterLegacyDNSNameFormat config.StringTemplate `json:"master_legacy_dns_name_format,omitempty"`
ReplicaDNSNameFormat config.StringTemplate `json:"replica_dns_name_format,omitempty"`
CustomServiceAnnotations map[string]string `json:"custom_service_annotations,omitempty"`
// +kubebuilder:default="{cluster}.{namespace}.{hostedzone}"
// defines the DNS name string template for the master load balancer cluster
MasterDNSNameFormat config.StringTemplate `json:"master_dns_name_format,omitempty"`
// +kubebuilder:default="{cluster}.{team}.{hostedzone}"
// deprecated DNS template for master load balancer using team name
MasterLegacyDNSNameFormat config.StringTemplate `json:"master_legacy_dns_name_format,omitempty"`
// +kubebuilder:default="{cluster}-repl.{namespace}.{hostedzone}"
// defines the DNS name string template for the replica load balancer cluster
ReplicaDNSNameFormat config.StringTemplate `json:"replica_dns_name_format,omitempty"`
// +kubebuilder:default="{cluster}-repl.{team}.{hostedzone}"
// deprecated DNS template for replica load balancer using team name
ReplicaLegacyDNSNameFormat config.StringTemplate `json:"replica_legacy_dns_name_format,omitempty"`
ExternalTrafficPolicy string `json:"external_traffic_policy" default:"Cluster"`
// +kubebuilder:validation:Enum=Cluster;Local
// +kubebuilder:default=Cluster
ExternalTrafficPolicy string `json:"external_traffic_policy,omitempty"`
}
// AWSGCPConfiguration defines the configuration for AWS
// TODO complete Google Cloud Platform (GCP) configuration
type AWSGCPConfiguration struct {
WALES3Bucket string `json:"wal_s3_bucket,omitempty"`
WALES3Bucket string `json:"wal_s3_bucket,omitempty"`
// +kubebuilder:default=eu-central-1
AWSRegion string `json:"aws_region,omitempty"`
WALGSBucket string `json:"wal_gs_bucket,omitempty"`
GCPCredentials string `json:"gcp_credentials,omitempty"`
@ -169,75 +255,117 @@ type AWSGCPConfiguration struct {
KubeIAMRole string `json:"kube_iam_role,omitempty"`
AdditionalSecretMount string `json:"additional_secret_mount,omitempty"`
AdditionalSecretMountPath string `json:"additional_secret_mount_path,omitempty"`
EnableEBSGp3Migration bool `json:"enable_ebs_gp3_migration" default:"false"`
EnableEBSGp3MigrationMaxSize int64 `json:"enable_ebs_gp3_migration_max_size" default:"1000"`
EnableEBSGp3Migration bool `json:"enable_ebs_gp3_migration,omitempty"`
EnableEBSGp3MigrationMaxSize int64 `json:"enable_ebs_gp3_migration_max_size,omitempty"`
}
// OperatorDebugConfiguration defines options for the debug mode
type OperatorDebugConfiguration struct {
DebugLogging *bool `json:"debug_logging,omitempty"`
// +kubebuilder:default=true
DebugLogging *bool `json:"debug_logging,omitempty"`
// +kubebuilder:default=true
EnableDBAccess *bool `json:"enable_database_access,omitempty"`
}
// TeamsAPIConfiguration defines the configuration of TeamsAPI
type TeamsAPIConfiguration struct {
EnableTeamsAPI bool `json:"enable_teams_api,omitempty"`
TeamsAPIUrl string `json:"teams_api_url,omitempty"`
TeamAPIRoleConfiguration map[string]string `json:"team_api_role_configuration,omitempty"`
EnableTeamSuperuser bool `json:"enable_team_superuser,omitempty"`
EnableAdminRoleForUsers bool `json:"enable_admin_role_for_users,omitempty"`
TeamAdminRole string `json:"team_admin_role,omitempty"`
PamRoleName string `json:"pam_role_name,omitempty"`
PamConfiguration string `json:"pam_configuration,omitempty"`
ProtectedRoles []string `json:"protected_role_names,omitempty"`
PostgresSuperuserTeams []string `json:"postgres_superuser_teams,omitempty"`
EnablePostgresTeamCRD bool `json:"enable_postgres_team_crd,omitempty"`
EnablePostgresTeamCRDSuperusers bool `json:"enable_postgres_team_crd_superusers,omitempty"`
EnableTeamMemberDeprecation bool `json:"enable_team_member_deprecation,omitempty"`
RoleDeletionSuffix string `json:"role_deletion_suffix,omitempty"`
EnableTeamsAPI bool `json:"enable_teams_api,omitempty"`
// +kubebuilder:default="https://teams.example.com/api/"
TeamsAPIUrl string `json:"teams_api_url,omitempty"`
// +kubebuilder:default={log_statement: all}
TeamAPIRoleConfiguration map[string]string `json:"team_api_role_configuration,omitempty"`
EnableTeamSuperuser bool `json:"enable_team_superuser,omitempty"`
// +kubebuilder:default=true
EnableAdminRoleForUsers bool `json:"enable_admin_role_for_users,omitempty"`
// +kubebuilder:default=admin
TeamAdminRole string `json:"team_admin_role,omitempty"`
// +kubebuilder:default=zalandos
PamRoleName string `json:"pam_role_name,omitempty"`
// +kubebuilder:default="https://info.example.com/oauth2/tokeninfo?access_token= uid realm=/employees"
PamConfiguration string `json:"pam_configuration,omitempty"`
// +kubebuilder:default="[\"admin\", \"cron_admin\"]"
ProtectedRoles []string `json:"protected_role_names,omitempty"`
PostgresSuperuserTeams []string `json:"postgres_superuser_teams,omitempty"`
// +kubebuilder:default=true
EnablePostgresTeamCRD bool `json:"enable_postgres_team_crd,omitempty"`
EnablePostgresTeamCRDSuperusers bool `json:"enable_postgres_team_crd_superusers,omitempty"`
EnableTeamMemberDeprecation bool `json:"enable_team_member_deprecation,omitempty"`
// +kubebuilder:default=_deleted
RoleDeletionSuffix string `json:"role_deletion_suffix,omitempty"`
}
// LoggingRESTAPIConfiguration defines Logging API conf
type LoggingRESTAPIConfiguration struct {
APIPort int `json:"api_port,omitempty"`
RingLogLines int `json:"ring_log_lines,omitempty"`
// +kubebuilder:default=8080
APIPort int `json:"api_port,omitempty"`
// +kubebuilder:default=100
RingLogLines int `json:"ring_log_lines,omitempty"`
// +kubebuilder:default=1000
ClusterHistoryEntries int `json:"cluster_history_entries,omitempty"`
}
// ScalyrConfiguration defines the configuration for ScalyrAPI
type ScalyrConfiguration struct {
ScalyrAPIKey string `json:"scalyr_api_key,omitempty"`
ScalyrImage string `json:"scalyr_image,omitempty"`
ScalyrServerURL string `json:"scalyr_server_url,omitempty"`
ScalyrCPURequest string `json:"scalyr_cpu_request,omitempty"`
ScalyrAPIKey string `json:"scalyr_api_key,omitempty"`
ScalyrImage string `json:"scalyr_image,omitempty"`
// +kubebuilder:default="https://upload.eu.scalyr.com"
ScalyrServerURL string `json:"scalyr_server_url,omitempty"`
// +kubebuilder:validation:Pattern=`^(\d+m|\d+(\.\d{1,3})?)$`
// +kubebuilder:default="100m"
ScalyrCPURequest string `json:"scalyr_cpu_request,omitempty"`
// +kubebuilder:validation:Pattern=`^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$`
// +kubebuilder:default="50Mi"
ScalyrMemoryRequest string `json:"scalyr_memory_request,omitempty"`
ScalyrCPULimit string `json:"scalyr_cpu_limit,omitempty"`
ScalyrMemoryLimit string `json:"scalyr_memory_limit,omitempty"`
// +kubebuilder:validation:Pattern=`^(\d+m|\d+(\.\d{1,3})?)$`
// +kubebuilder:default="1"
ScalyrCPULimit string `json:"scalyr_cpu_limit,omitempty"`
// +kubebuilder:validation:Pattern=`^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$`
// +kubebuilder:default="500Mi"
ScalyrMemoryLimit string `json:"scalyr_memory_limit,omitempty"`
}
// ConnectionPoolerConfiguration defines default configuration for connection pooler
type ConnectionPoolerConfiguration struct {
NumberOfInstances *int32 `json:"connection_pooler_number_of_instances,omitempty"`
Schema string `json:"connection_pooler_schema,omitempty"`
User string `json:"connection_pooler_user,omitempty"`
Image string `json:"connection_pooler_image,omitempty"`
Mode string `json:"connection_pooler_mode,omitempty"`
MaxDBConnections *int32 `json:"connection_pooler_max_db_connections,omitempty"`
DefaultCPURequest string `json:"connection_pooler_default_cpu_request,omitempty"`
// +kubebuilder:validation:Minimum=1
// +kubebuilder:default=2
NumberOfInstances *int32 `json:"connection_pooler_number_of_instances,omitempty"`
// +kubebuilder:default=pooler
Schema string `json:"connection_pooler_schema,omitempty"`
// +kubebuilder:default=pooler
User string `json:"connection_pooler_user,omitempty"`
// +kubebuilder:default="ghcr.io/zalando/postgres-operator/pgbouncer:latest"
Image string `json:"connection_pooler_image,omitempty"`
// +kubebuilder:validation:Enum=session;transaction
// +kubebuilder:default=transaction
Mode string `json:"connection_pooler_mode,omitempty"`
MaxDBConnections *int32 `json:"connection_pooler_max_db_connections,omitempty"`
// +kubebuilder:validation:Pattern=`^(\d+m|\d+(\.\d{1,3})?)$`
DefaultCPURequest string `json:"connection_pooler_default_cpu_request,omitempty"`
// +kubebuilder:validation:Pattern=`^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$`
DefaultMemoryRequest string `json:"connection_pooler_default_memory_request,omitempty"`
DefaultCPULimit string `json:"connection_pooler_default_cpu_limit,omitempty"`
DefaultMemoryLimit string `json:"connection_pooler_default_memory_limit,omitempty"`
// PodSecurityContext and SecurityContext allow overriding the pooler pod- and
// container-level security contexts (e.g. to match a hardened image's UID/GID or to
// satisfy restricted Pod Security Standards). When unset the operator keeps its defaults.
// +kubebuilder:validation:Pattern=`^(\d+m|\d+(\.\d{1,3})?)$`
DefaultCPULimit string `json:"connection_pooler_default_cpu_limit,omitempty"`
// +kubebuilder:validation:Pattern=`^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$`
DefaultMemoryLimit string `json:"connection_pooler_default_memory_limit,omitempty"`
// +kubebuilder:validation:XPreserveUnknownFields
// +kubebuilder:validation:Type=object
// +kubebuilder:validation:Schemaless
PodSecurityContext *v1.PodSecurityContext `json:"connection_pooler_pod_security_context,omitempty"`
// +kubebuilder:validation:XPreserveUnknownFields
// +kubebuilder:validation:Type=object
// +kubebuilder:validation:Schemaless
SecurityContext *v1.SecurityContext `json:"connection_pooler_security_context,omitempty"`
}
// OperatorLogicalBackupConfiguration defines configuration for logical backup
type OperatorLogicalBackupConfiguration struct {
Schedule string `json:"logical_backup_schedule,omitempty"`
DockerImage string `json:"logical_backup_docker_image,omitempty"`
// +kubebuilder:validation:Pattern=`^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$`
// +kubebuilder:default="30 00 * * *"
Schedule string `json:"logical_backup_schedule,omitempty"`
// +kubebuilder:default="ghcr.io/zalando/postgres-operator/logical-backup:v1.15.1"
DockerImage string `json:"logical_backup_docker_image,omitempty"`
// +kubebuilder:validation:Enum=az;gcs;s3
// +kubebuilder:default=s3
BackupProvider string `json:"logical_backup_provider,omitempty"`
AzureStorageAccountName string `json:"logical_backup_azure_storage_account_name,omitempty"`
AzureStorageContainer string `json:"logical_backup_azure_storage_container,omitempty"`
@ -251,12 +379,26 @@ type OperatorLogicalBackupConfiguration struct {
S3SSE string `json:"logical_backup_s3_sse,omitempty"`
RetentionTime string `json:"logical_backup_s3_retention_time,omitempty"`
GoogleApplicationCredentials string `json:"logical_backup_google_application_credentials,omitempty"`
JobPrefix string `json:"logical_backup_job_prefix,omitempty"`
CronjobEnvironmentSecret string `json:"logical_backup_cronjob_environment_secret,omitempty"`
CPURequest string `json:"logical_backup_cpu_request,omitempty"`
MemoryRequest string `json:"logical_backup_memory_request,omitempty"`
CPULimit string `json:"logical_backup_cpu_limit,omitempty"`
MemoryLimit string `json:"logical_backup_memory_limit,omitempty"`
// +kubebuilder:default=logical-backup-
JobPrefix string `json:"logical_backup_job_prefix,omitempty"`
CronjobEnvironmentSecret string `json:"logical_backup_cronjob_environment_secret,omitempty"`
// +kubebuilder:validation:Pattern=`^(\d+m|\d+(\.\d{1,3})?)$`
CPURequest string `json:"logical_backup_cpu_request,omitempty"`
// +kubebuilder:validation:Pattern=`^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$`
MemoryRequest string `json:"logical_backup_memory_request,omitempty"`
// +kubebuilder:validation:Pattern=`^(\d+m|\d+(\.\d{1,3})?)$`
CPULimit string `json:"logical_backup_cpu_limit,omitempty"`
// +kubebuilder:validation:Pattern=`^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$`
MemoryLimit string `json:"logical_backup_memory_limit,omitempty"`
// +kubebuilder:validation:Minimum=0
// +kubebuilder:default=3
SuccessfulJobsHistoryLimit *int32 `json:"logical_backup_successful_jobs_history_limit,omitempty"`
// +kubebuilder:validation:Minimum=0
// +kubebuilder:default=3
FailedJobsHistoryLimit *int32 `json:"logical_backup_failed_jobs_history_limit,omitempty"`
// +kubebuilder:validation:Minimum=0
// +kubebuilder:default=86400
TTLSecondsAfterFinished *int32 `json:"logical_backup_ttl_seconds_after_finished,omitempty"`
}
// PatroniConfiguration defines configuration for Patroni
@ -266,42 +408,80 @@ type PatroniConfiguration struct {
// OperatorConfigurationData defines the operation config
type OperatorConfigurationData struct {
EnableCRDRegistration *bool `json:"enable_crd_registration,omitempty"`
EnableCRDValidation *bool `json:"enable_crd_validation,omitempty"`
CRDCategories []string `json:"crd_categories,omitempty"`
EnableLazySpiloUpgrade bool `json:"enable_lazy_spilo_upgrade,omitempty"`
EnablePgVersionEnvVar bool `json:"enable_pgversion_env_var,omitempty"`
EnableSpiloWalPathCompat bool `json:"enable_spilo_wal_path_compat,omitempty"`
EnableTeamIdClusternamePrefix bool `json:"enable_team_id_clustername_prefix,omitempty"`
EtcdHost string `json:"etcd_host,omitempty"`
KubernetesUseConfigMaps bool `json:"kubernetes_use_configmaps,omitempty"`
DockerImage string `json:"docker_image,omitempty"`
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"`
SidecarImages map[string]string `json:"sidecar_docker_images,omitempty"` // deprecated in favour of SidecarContainers
SidecarContainers []v1.Container `json:"sidecars,omitempty"`
PostgresUsersConfiguration PostgresUsersConfiguration `json:"users"`
MajorVersionUpgrade MajorVersionUpgradeConfiguration `json:"major_version_upgrade"`
Kubernetes KubernetesMetaConfiguration `json:"kubernetes"`
PostgresPodResources PostgresPodResourcesDefaults `json:"postgres_pod_resources"`
Timeouts OperatorTimeouts `json:"timeouts"`
LoadBalancer LoadBalancerConfiguration `json:"load_balancer"`
AWSGCP AWSGCPConfiguration `json:"aws_or_gcp"`
OperatorDebug OperatorDebugConfiguration `json:"debug"`
TeamsAPI TeamsAPIConfiguration `json:"teams_api"`
LoggingRESTAPI LoggingRESTAPIConfiguration `json:"logging_rest_api"`
Scalyr ScalyrConfiguration `json:"scalyr"`
LogicalBackup OperatorLogicalBackupConfiguration `json:"logical_backup"`
ConnectionPooler ConnectionPoolerConfiguration `json:"connection_pooler"`
Patroni PatroniConfiguration `json:"patroni"`
// +kubebuilder:default=true
EnableCRDRegistration *bool `json:"enable_crd_registration,omitempty"`
CRDCategories []string `json:"crd_categories,omitempty"`
EnableLazySpiloUpgrade bool `json:"enable_lazy_spilo_upgrade,omitempty"`
// +kubebuilder:default=true
EnablePgVersionEnvVar bool `json:"enable_pgversion_env_var,omitempty"`
EnableSpiloWalPathCompat bool `json:"enable_spilo_wal_path_compat,omitempty"`
EnableTeamIdClusternamePrefix bool `json:"enable_team_id_clustername_prefix,omitempty"`
// +kubebuilder:default=""
EtcdHost string `json:"etcd_host,omitempty"`
// +kubebuilder:default=true
KubernetesUseConfigMaps bool `json:"kubernetes_use_configmaps,omitempty"`
// +kubebuilder:default="ghcr.io/zalando/spilo-18:4.1-p1"
DockerImage string `json:"docker_image,omitempty"`
// +kubebuilder:validation:Minimum=1
// +kubebuilder:default=8
Workers uint32 `json:"workers,omitempty"`
// +kubebuilder:default="30m"
// period between consecutive sync requests
ResyncPeriod Duration `json:"resync_period,omitempty"`
// +kubebuilder:default="5m"
// period between consecutive repair requests
RepairPeriod Duration `json:"repair_period,omitempty"`
// +kubebuilder:default=true
EnableMaintenanceWindows *bool `json:"enable_maintenance_windows,omitempty"`
// +kubebuilder:validation:Schemaless
// +kubebuilder:validation:Type=array
MaintenanceWindows []MaintenanceWindow `json:"maintenance_windows,omitempty"`
SetMemoryRequestToLimit bool `json:"set_memory_request_to_limit,omitempty"`
// +kubebuilder:default=true
ShmVolume *bool `json:"enable_shm_volume,omitempty"`
SidecarImages map[string]string `json:"sidecar_docker_images,omitempty"` // deprecated in favour of SidecarContainers
// +kubebuilder:validation:XPreserveUnknownFields
// +kubebuilder:validation:Type=object
// +kubebuilder:validation:Schemaless
SidecarContainers []v1.Container `json:"sidecars,omitempty"`
// +optional
PostgresUsersConfiguration PostgresUsersConfiguration `json:"users"`
// +optional
MajorVersionUpgrade MajorVersionUpgradeConfiguration `json:"major_version_upgrade"`
// +optional
Kubernetes KubernetesMetaConfiguration `json:"kubernetes"`
// +optional
PostgresPodResources PostgresPodResourcesDefaults `json:"postgres_pod_resources"`
// +optional
Timeouts OperatorTimeouts `json:"timeouts"`
// +optional
LoadBalancer LoadBalancerConfiguration `json:"load_balancer"`
// +optional
AWSGCP AWSGCPConfiguration `json:"aws_or_gcp"`
// +optional
OperatorDebug OperatorDebugConfiguration `json:"debug"`
// +optional
TeamsAPI TeamsAPIConfiguration `json:"teams_api"`
// +optional
LoggingRESTAPI LoggingRESTAPIConfiguration `json:"logging_rest_api"`
// +optional
Scalyr ScalyrConfiguration `json:"scalyr"`
// +optional
LogicalBackup OperatorLogicalBackupConfiguration `json:"logical_backup"`
// +optional
ConnectionPooler ConnectionPoolerConfiguration `json:"connection_pooler"`
// +optional
Patroni PatroniConfiguration `json:"patroni"`
// +kubebuilder:validation:Minimum=-1
// +kubebuilder:default=-1
// -1 = disabled
MinInstances int32 `json:"min_instances,omitempty"`
// +kubebuilder:validation:Minimum=-1
// +kubebuilder:default=-1
// -1 = disabled
MaxInstances int32 `json:"max_instances,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"`
}

View File

@ -0,0 +1,971 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.17.3
name: operatorconfigurations.acid.zalan.do
spec:
group: acid.zalan.do
names:
categories:
- all
kind: OperatorConfiguration
listKind: OperatorConfigurationList
plural: operatorconfigurations
shortNames:
- opconfig
singular: operatorconfiguration
scope: Namespaced
versions:
- additionalPrinterColumns:
- description: Spilo image to be used for Pods
jsonPath: .configuration.docker_image
name: Image
type: string
- description: Label for K8s resources created by operator
jsonPath: .configuration.kubernetes.cluster_name_label
name: Cluster-Label
type: string
- description: Name of service account to be used
jsonPath: .configuration.kubernetes.pod_service_account_name
name: Service-Account
type: string
- description: Minimum number of instances per Postgres cluster
jsonPath: .configuration.min_instances
name: Min-Instances
type: integer
- description: Age of the OperatorConfiguration resource
jsonPath: .metadata.creationTimestamp
name: Age
type: date
name: v1
schema:
openAPIV3Schema:
description: OperatorConfiguration defines the specification for the OperatorConfiguration.
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
configuration:
description: OperatorConfigurationData defines the operation config
properties:
aws_or_gcp:
description: AWSGCPConfiguration defines the configuration for AWS
properties:
additional_secret_mount:
type: string
additional_secret_mount_path:
type: string
aws_region:
default: eu-central-1
type: string
enable_ebs_gp3_migration:
type: boolean
enable_ebs_gp3_migration_max_size:
format: int64
type: integer
gcp_credentials:
type: string
kube_iam_role:
type: string
log_s3_bucket:
type: string
wal_az_storage_account:
type: string
wal_gs_bucket:
type: string
wal_s3_bucket:
type: string
type: object
connection_pooler:
description: ConnectionPoolerConfiguration defines default configuration
for connection pooler
properties:
connection_pooler_default_cpu_limit:
pattern: ^(\d+m|\d+(\.\d{1,3})?)$
type: string
connection_pooler_default_cpu_request:
pattern: ^(\d+m|\d+(\.\d{1,3})?)$
type: string
connection_pooler_default_memory_limit:
pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$
type: string
connection_pooler_default_memory_request:
pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$
type: string
connection_pooler_image:
default: ghcr.io/zalando/postgres-operator/pgbouncer:latest
type: string
connection_pooler_max_db_connections:
format: int32
type: integer
connection_pooler_mode:
default: transaction
enum:
- session
- transaction
type: string
connection_pooler_number_of_instances:
default: 2
format: int32
minimum: 1
type: integer
connection_pooler_schema:
default: pooler
type: string
connection_pooler_user:
default: pooler
type: string
type: object
crd_categories:
items:
type: string
type: array
debug:
description: OperatorDebugConfiguration defines options for the debug
mode
properties:
debug_logging:
default: true
type: boolean
enable_database_access:
default: true
type: boolean
type: object
docker_image:
default: ghcr.io/zalando/spilo-18:4.1-p1
type: string
enable_crd_registration:
default: true
type: boolean
enable_lazy_spilo_upgrade:
type: boolean
enable_maintenance_windows:
default: true
type: boolean
enable_pgversion_env_var:
default: true
type: boolean
enable_shm_volume:
default: true
type: boolean
enable_spilo_wal_path_compat:
type: boolean
enable_team_id_clustername_prefix:
type: boolean
etcd_host:
default: ""
type: string
ignore_instance_limits_annotation_key:
type: string
ignore_resources_limits_annotation_key:
type: string
kubernetes:
description: KubernetesMetaConfiguration defines k8s conf required
for all Postgres clusters and the operator itself
properties:
additional_pod_capabilities:
items:
type: string
type: array
cluster_domain:
default: cluster.local
type: string
cluster_labels:
additionalProperties:
type: string
default:
application: spilo
type: object
cluster_name_label:
default: cluster-name
type: string
custom_pod_annotations:
additionalProperties:
type: string
type: object
delete_annotation_date_key:
type: string
delete_annotation_name_key:
type: string
downscaler_annotations:
items:
type: string
type: array
enable_cross_namespace_secret:
type: boolean
enable_finalizers:
type: boolean
enable_init_containers:
default: true
type: boolean
enable_owner_references:
type: boolean
enable_persistent_volume_claim_deletion:
default: true
type: boolean
enable_pod_antiaffinity:
type: boolean
enable_pod_disruption_budget:
default: true
type: boolean
enable_readiness_probe:
type: boolean
enable_secrets_deletion:
default: true
type: boolean
enable_sidecars:
default: true
type: boolean
ignored_annotations:
items:
type: string
type: array
infrastructure_roles_secret_name:
description: |-
NamespacedName comprises a resource name, with a mandatory namespace,
rendered as "<namespace>/<name>". 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
properties:
name:
type: string
namespace:
type: string
required:
- name
type: object
infrastructure_roles_secrets:
description: namespaced name of the secret containing infrastructure
roles names and passwords
items:
properties:
defaultrolevalue:
type: string
defaultuservalue:
type: string
details:
description: This field point out the detailed yaml definition
of the role, if exists
type: string
passwordkey:
type: string
rolekey:
type: string
secretname:
description: |-
Name of a secret which describes the role, and optionally name of a
configmap with an extra information
properties:
name:
type: string
namespace:
type: string
required:
- name
type: object
template:
type: boolean
userkey:
type: string
type: object
type: array
inherited_annotations:
items:
type: string
type: array
inherited_labels:
items:
type: string
type: array
liveness_probe:
description: |-
Probe describes a health check to be performed against a container to determine whether it is
alive or ready to receive traffic.
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
master_pod_move_timeout:
default: 20m
description: timeout for successful migration of master pods from
unschedulable node
format: int64
type: integer
node_readiness_label:
additionalProperties:
type: string
type: object
node_readiness_label_merge:
enum:
- AND
- OR
type: string
oauth_token_secret_name:
default: postgres-operator
description: namespaced name of the secret containing the OAuth2
token to pass to the teams API
properties:
name:
type: string
namespace:
type: string
required:
- name
type: object
pdb_master_label_selector:
default: true
type: boolean
pdb_name_format:
default: postgres-{cluster}-pdb
description: defines the template for PDB names
type: string
persistent_volume_claim_retention_policy:
additionalProperties:
type: string
type: object
pod_antiaffinity_preferred_during_scheduling:
type: boolean
pod_antiaffinity_topology_key:
default: kubernetes.io/hostname
type: string
pod_environment_configmap:
description: namespaced name of the ConfigMap with environment
variables to populate on every pod
properties:
name:
type: string
namespace:
type: string
required:
- name
type: object
pod_environment_secret:
type: string
pod_management_policy:
default: ordered_ready
enum:
- ordered_ready
- parallel
type: string
pod_priority_class_name:
type: string
pod_role_label:
default: spilo-role
type: string
pod_service_account_definition:
type: string
pod_service_account_name:
default: postgres-pod
type: string
pod_service_account_role_binding_definition:
type: string
pod_terminate_grace_period:
default: 5m
description: Postgres pods are terminated forcefully after this
timeout
format: int64
type: integer
secret_name_template:
default: '{username}.{cluster}.credentials.{tprkind}.{tprgroup}'
description: |-
template for database user secrets generated by the operator,
here username contains the namespace in the format namespace.username
if the user is in different namespace than cluster and cross namespace secrets
are enabled via `enable_cross_namespace_secret` flag in the configuration.
type: string
share_pgsocket_with_sidecars:
type: boolean
spilo_allow_privilege_escalation:
default: true
type: boolean
spilo_fsgroup:
format: int64
type: integer
spilo_privileged:
type: boolean
spilo_runasgroup:
format: int64
type: integer
spilo_runasuser:
format: int64
type: integer
storage_resize_mode:
default: pvc
enum:
- ebs
- mixed
- pvc
- "off"
type: string
toleration:
additionalProperties:
type: string
type: object
watched_namespace:
type: string
type: object
kubernetes_use_configmaps:
default: true
type: boolean
load_balancer:
description: LoadBalancerConfiguration defines the LB configuration
properties:
custom_service_annotations:
additionalProperties:
type: string
type: object
db_hosted_zone:
type: string
enable_master_load_balancer:
type: boolean
enable_master_node_port:
type: boolean
enable_master_pooler_load_balancer:
type: boolean
enable_master_pooler_node_port:
type: boolean
enable_replica_load_balancer:
type: boolean
enable_replica_node_port:
type: boolean
enable_replica_pooler_load_balancer:
type: boolean
enable_replica_pooler_node_port:
type: boolean
external_traffic_policy:
default: Cluster
enum:
- Cluster
- Local
type: string
master_dns_name_format:
default: '{cluster}.{namespace}.{hostedzone}'
description: defines the DNS name string template for the master
load balancer cluster
type: string
master_legacy_dns_name_format:
default: '{cluster}.{team}.{hostedzone}'
description: deprecated DNS template for master load balancer
using team name
type: string
replica_dns_name_format:
default: '{cluster}-repl.{namespace}.{hostedzone}'
description: defines the DNS name string template for the replica
load balancer cluster
type: string
replica_legacy_dns_name_format:
default: '{cluster}-repl.{team}.{hostedzone}'
description: deprecated DNS template for replica load balancer
using team name
type: string
type: object
logging_rest_api:
description: LoggingRESTAPIConfiguration defines Logging API conf
properties:
api_port:
default: 8080
type: integer
cluster_history_entries:
default: 1000
type: integer
ring_log_lines:
default: 100
type: integer
type: object
logical_backup:
description: OperatorLogicalBackupConfiguration defines configuration
for logical backup
properties:
logical_backup_azure_storage_account_key:
type: string
logical_backup_azure_storage_account_name:
type: string
logical_backup_azure_storage_container:
type: string
logical_backup_cpu_limit:
pattern: ^(\d+m|\d+(\.\d{1,3})?)$
type: string
logical_backup_cpu_request:
pattern: ^(\d+m|\d+(\.\d{1,3})?)$
type: string
logical_backup_cronjob_environment_secret:
type: string
logical_backup_docker_image:
default: ghcr.io/zalando/postgres-operator/logical-backup:v1.15.1
type: string
logical_backup_failed_jobs_history_limit:
default: 3
format: int32
minimum: 0
type: integer
logical_backup_google_application_credentials:
type: string
logical_backup_job_prefix:
default: logical-backup-
type: string
logical_backup_memory_limit:
pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$
type: string
logical_backup_memory_request:
pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$
type: string
logical_backup_provider:
default: s3
enum:
- az
- gcs
- s3
type: string
logical_backup_s3_access_key_id:
type: string
logical_backup_s3_bucket:
type: string
logical_backup_s3_bucket_prefix:
type: string
logical_backup_s3_endpoint:
type: string
logical_backup_s3_region:
type: string
logical_backup_s3_retention_time:
type: string
logical_backup_s3_secret_access_key:
type: string
logical_backup_s3_sse:
type: string
logical_backup_schedule:
default: 30 00 * * *
pattern: ^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$
type: string
logical_backup_successful_jobs_history_limit:
default: 3
format: int32
minimum: 0
type: integer
logical_backup_ttl_seconds_after_finished:
default: 86400
format: int32
minimum: 0
type: integer
type: object
maintenance_windows:
type: array
major_version_upgrade:
description: MajorVersionUpgradeConfiguration defines how to execute
major version upgrades of Postgres.
properties:
major_version_upgrade_mode:
default: manual
enum:
- "off"
- manual
- full
type: string
major_version_upgrade_team_allow_list:
items:
type: string
type: array
minimal_major_version:
default: "14"
type: string
target_major_version:
default: "18"
type: string
type: object
max_instances:
default: -1
description: -1 = disabled
format: int32
minimum: -1
type: integer
min_instances:
default: -1
description: -1 = disabled
format: int32
minimum: -1
type: integer
patroni:
description: PatroniConfiguration defines configuration for Patroni
properties:
enable_patroni_failsafe_mode:
type: boolean
type: object
postgres_pod_resources:
description: PostgresPodResourcesDefaults defines the spec of default
resources
properties:
default_cpu_limit:
pattern: ^(\d+m|\d+(\.\d{1,3})?)$
type: string
default_cpu_request:
pattern: ^(\d+m|\d+(\.\d{1,3})?)$
type: string
default_memory_limit:
pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$
type: string
default_memory_request:
pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$
type: string
max_cpu_request:
pattern: ^(\d+m|\d+(\.\d{1,3})?)$
type: string
max_memory_request:
pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$
type: string
min_cpu_limit:
pattern: ^(\d+m|\d+(\.\d{1,3})?)$
type: string
min_memory_limit:
pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$
type: string
type: object
repair_period:
default: 5m
description: period between consecutive repair requests
format: int64
type: integer
resync_period:
default: 30m
description: period between consecutive sync requests
format: int64
type: integer
scalyr:
description: ScalyrConfiguration defines the configuration for ScalyrAPI
properties:
scalyr_api_key:
type: string
scalyr_cpu_limit:
default: "1"
pattern: ^(\d+m|\d+(\.\d{1,3})?)$
type: string
scalyr_cpu_request:
default: 100m
pattern: ^(\d+m|\d+(\.\d{1,3})?)$
type: string
scalyr_image:
type: string
scalyr_memory_limit:
default: 500Mi
pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$
type: string
scalyr_memory_request:
default: 50Mi
pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$
type: string
scalyr_server_url:
default: https://upload.eu.scalyr.com
type: string
type: object
set_memory_request_to_limit:
type: boolean
sidecar_docker_images:
additionalProperties:
type: string
type: object
sidecars:
type: object
x-kubernetes-preserve-unknown-fields: true
teams_api:
description: TeamsAPIConfiguration defines the configuration of TeamsAPI
properties:
enable_admin_role_for_users:
default: true
type: boolean
enable_postgres_team_crd:
default: true
type: boolean
enable_postgres_team_crd_superusers:
type: boolean
enable_team_member_deprecation:
type: boolean
enable_team_superuser:
type: boolean
enable_teams_api:
type: boolean
pam_configuration:
default: https://info.example.com/oauth2/tokeninfo?access_token=
uid realm=/employees
type: string
pam_role_name:
default: zalandos
type: string
postgres_superuser_teams:
items:
type: string
type: array
protected_role_names:
default: '["admin", "cron_admin"]'
items:
type: string
type: array
role_deletion_suffix:
default: _deleted
type: string
team_admin_role:
default: admin
type: string
team_api_role_configuration:
additionalProperties:
type: string
default:
log_statement: all
type: object
teams_api_url:
default: https://teams.example.com/api/
type: string
type: object
timeouts:
description: OperatorTimeouts defines the timeout of ResourceCheck,
PodWait, ReadyWait
properties:
patroni_api_check_interval:
default: 1s
description: interval between consecutive attempts of operator
calling the Patroni API
format: int64
type: integer
patroni_api_check_timeout:
default: 5s
description: timeout when waiting for successful response from
Patroni API
format: int64
type: integer
pod_deletion_wait_timeout:
default: 10m
description: timeout when waiting for the Postgres pods to be
deleted
format: int64
type: integer
pod_label_wait_timeout:
default: 10m
description: timeout when waiting for pod role and cluster labels
format: int64
type: integer
ready_wait_interval:
default: 4s
description: interval between consecutive attempts waiting for
postgresql CRD to be created
format: int64
type: integer
ready_wait_timeout:
default: 30s
description: timeout for the complete postgres CRD creation
format: int64
type: integer
resource_check_interval:
default: 3s
description: interval to wait between consecutive attempts to
check for some K8s resources
format: int64
type: integer
resource_check_timeout:
default: 10m
description: timeout when waiting for the presence of a certain
K8s resource
format: int64
type: integer
type: object
users:
description: PostgresUsersConfiguration defines the system users of
Postgres.
properties:
additional_owner_roles:
items:
type: string
type: array
enable_password_rotation:
type: boolean
password_rotation_interval:
default: 90
format: int32
type: integer
password_rotation_user_retention:
default: 120
format: int32
type: integer
replication_username:
default: standby
type: string
super_username:
default: postgres
type: string
type: object
workers:
default: 8
format: int32
minimum: 1
type: integer
type: object
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:
- configuration
- metadata
type: object
served: true
storage: true
subresources:
status: {}

View File

@ -302,7 +302,9 @@ spec:
a Container.
properties:
name:
description: Name of the environment variable. Must be a C_IDENTIFIER.
description: |-
Name of the environment variable.
May consist of any printable ASCII characters except '='.
type: string
value:
description: |-
@ -360,6 +362,43 @@ spec:
- fieldPath
type: object
x-kubernetes-map-type: atomic
fileKeyRef:
description: |-
FileKeyRef selects a key of the env file.
Requires the EnvFiles feature gate to be enabled.
properties:
key:
description: |-
The key within the env file. An invalid key will prevent the pod from starting.
The keys defined within a source may consist of any printable ASCII characters except '='.
During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters.
type: string
optional:
default: false
description: |-
Specify whether the file or its key must be defined. If the file or key
does not exist, then the env var is not published.
If optional is set to true and the specified key does not exist,
the environment variable will not be set in the Pod's containers.
If optional is set to false and the specified key does not exist,
an error will be returned during Pod creation.
type: boolean
path:
description: |-
The path within the volume from which to select the file.
Must be relative and may not contain the '..' path or start with '..'.
type: string
volumeName:
description: The name of the volume mount containing
the env file.
type: string
required:
- key
- path
- volumeName
type: object
x-kubernetes-map-type: atomic
resourceFieldRef:
description: |-
Selects a resource of the container: only resources limits and requests
@ -456,8 +495,9 @@ spec:
in a Container.
properties:
name:
description: Name of the environment variable. Must be
a C_IDENTIFIER.
description: |-
Name of the environment variable.
May consist of any printable ASCII characters except '='.
type: string
value:
description: |-
@ -515,6 +555,43 @@ spec:
- fieldPath
type: object
x-kubernetes-map-type: atomic
fileKeyRef:
description: |-
FileKeyRef selects a key of the env file.
Requires the EnvFiles feature gate to be enabled.
properties:
key:
description: |-
The key within the env file. An invalid key will prevent the pod from starting.
The keys defined within a source may consist of any printable ASCII characters except '='.
During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters.
type: string
optional:
default: false
description: |-
Specify whether the file or its key must be defined. If the file or key
does not exist, then the env var is not published.
If optional is set to true and the specified key does not exist,
the environment variable will not be set in the Pod's containers.
If optional is set to false and the specified key does not exist,
an error will be returned during Pod creation.
type: boolean
path:
description: |-
The path within the volume from which to select the file.
Must be relative and may not contain the '..' path or start with '..'.
type: string
volumeName:
description: The name of the volume mount containing
the env file.
type: string
required:
- key
- path
- volumeName
type: object
x-kubernetes-map-type: atomic
resourceFieldRef:
description: |-
Selects a resource of the container: only resources limits and requests
@ -575,14 +652,14 @@ spec:
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
The keys defined within a source may consist of any printable ASCII characters except '='.
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
of ConfigMaps or Secrets
properties:
configMapRef:
description: The ConfigMap to select from
@ -603,8 +680,9 @@ spec:
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.
description: |-
Optional text to prepend to the name of each environment variable.
May consist of any printable ASCII characters except '='.
type: string
secretRef:
description: The Secret to select from
@ -867,6 +945,12 @@ spec:
- port
type: object
type: object
stopSignal:
description: |-
StopSignal defines which signal will be sent to a container when it is being stopped.
If not specified, the default is defined by the container runtime in use.
StopSignal can only be set for Pods with a non-empty .spec.os.name
type: string
type: object
livenessProbe:
description: |-
@ -1237,7 +1321,9 @@ spec:
type: integer
type: object
resizePolicy:
description: Resources resize policy for the container.
description: |-
Resources resize policy for the container.
This field cannot be set on ephemeral containers.
items:
description: ContainerResizePolicy represents resource resize
policy for the container.
@ -1269,7 +1355,7 @@ spec:
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
This field depends on the
DynamicResourceAllocation feature gate.
This field is immutable. It can only be set for containers.
@ -1323,10 +1409,10 @@ spec:
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,
This overrides the pod-level restart policy. 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:
Additionally, 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"
@ -1338,6 +1424,59 @@ spec:
init container is started, or after any startupProbe has successfully
completed.
type: string
restartPolicyRules:
description: |-
Represents a list of rules to be checked to determine if the
container should be restarted on exit. The rules are evaluated in
order. Once a rule matches a container exit condition, the remaining
rules are ignored. If no rule matches the container exit condition,
the Container-level restart policy determines the whether the container
is restarted or not. Constraints on the rules:
- At most 20 rules are allowed.
- Rules can have the same action.
- Identical rules are not forbidden in validations.
When rules are specified, container MUST set RestartPolicy explicitly
even it if matches the Pod's RestartPolicy.
items:
description: ContainerRestartRule describes how a container
exit is handled.
properties:
action:
description: |-
Specifies the action taken on a container exit if the requirements
are satisfied. The only possible value is "Restart" to restart the
container.
type: string
exitCodes:
description: Represents the exit codes to check on container
exits.
properties:
operator:
description: |-
Represents the relationship between the container exit code(s) and the
specified values. Possible values are:
- In: the requirement is satisfied if the container exit code is in the
set of specified values.
- NotIn: the requirement is satisfied if the container exit code is
not in the set of specified values.
type: string
values:
description: |-
Specifies the set of values to check for container exit codes.
At most 255 elements are allowed.
items:
format: int32
type: integer
type: array
x-kubernetes-list-type: set
required:
- operator
type: object
required:
- action
type: object
type: array
x-kubernetes-list-type: atomic
securityContext:
description: |-
SecurityContext defines the security options the container should be run with.
@ -1413,7 +1552,6 @@ spec:
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:
@ -1878,8 +2016,9 @@ spec:
in a Container.
properties:
name:
description: Name of the environment variable. Must be
a C_IDENTIFIER.
description: |-
Name of the environment variable.
May consist of any printable ASCII characters except '='.
type: string
value:
description: |-
@ -1937,6 +2076,43 @@ spec:
- fieldPath
type: object
x-kubernetes-map-type: atomic
fileKeyRef:
description: |-
FileKeyRef selects a key of the env file.
Requires the EnvFiles feature gate to be enabled.
properties:
key:
description: |-
The key within the env file. An invalid key will prevent the pod from starting.
The keys defined within a source may consist of any printable ASCII characters except '='.
During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters.
type: string
optional:
default: false
description: |-
Specify whether the file or its key must be defined. If the file or key
does not exist, then the env var is not published.
If optional is set to true and the specified key does not exist,
the environment variable will not be set in the Pod's containers.
If optional is set to false and the specified key does not exist,
an error will be returned during Pod creation.
type: boolean
path:
description: |-
The path within the volume from which to select the file.
Must be relative and may not contain the '..' path or start with '..'.
type: string
volumeName:
description: The name of the volume mount containing
the env file.
type: string
required:
- key
- path
- volumeName
type: object
x-kubernetes-map-type: atomic
resourceFieldRef:
description: |-
Selects a resource of the container: only resources limits and requests
@ -1997,14 +2173,14 @@ spec:
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
The keys defined within a source may consist of any printable ASCII characters except '='.
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
of ConfigMaps or Secrets
properties:
configMapRef:
description: The ConfigMap to select from
@ -2025,8 +2201,9 @@ spec:
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.
description: |-
Optional text to prepend to the name of each environment variable.
May consist of any printable ASCII characters except '='.
type: string
secretRef:
description: The Secret to select from
@ -2289,6 +2466,12 @@ spec:
- port
type: object
type: object
stopSignal:
description: |-
StopSignal defines which signal will be sent to a container when it is being stopped.
If not specified, the default is defined by the container runtime in use.
StopSignal can only be set for Pods with a non-empty .spec.os.name
type: string
type: object
livenessProbe:
description: |-
@ -2659,7 +2842,9 @@ spec:
type: integer
type: object
resizePolicy:
description: Resources resize policy for the container.
description: |-
Resources resize policy for the container.
This field cannot be set on ephemeral containers.
items:
description: ContainerResizePolicy represents resource resize
policy for the container.
@ -2691,7 +2876,7 @@ spec:
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
This field depends on the
DynamicResourceAllocation feature gate.
This field is immutable. It can only be set for containers.
@ -2745,10 +2930,10 @@ spec:
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,
This overrides the pod-level restart policy. 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:
Additionally, 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"
@ -2760,6 +2945,59 @@ spec:
init container is started, or after any startupProbe has successfully
completed.
type: string
restartPolicyRules:
description: |-
Represents a list of rules to be checked to determine if the
container should be restarted on exit. The rules are evaluated in
order. Once a rule matches a container exit condition, the remaining
rules are ignored. If no rule matches the container exit condition,
the Container-level restart policy determines the whether the container
is restarted or not. Constraints on the rules:
- At most 20 rules are allowed.
- Rules can have the same action.
- Identical rules are not forbidden in validations.
When rules are specified, container MUST set RestartPolicy explicitly
even it if matches the Pod's RestartPolicy.
items:
description: ContainerRestartRule describes how a container
exit is handled.
properties:
action:
description: |-
Specifies the action taken on a container exit if the requirements
are satisfied. The only possible value is "Restart" to restart the
container.
type: string
exitCodes:
description: Represents the exit codes to check on container
exits.
properties:
operator:
description: |-
Represents the relationship between the container exit code(s) and the
specified values. Possible values are:
- In: the requirement is satisfied if the container exit code is in the
set of specified values.
- NotIn: the requirement is satisfied if the container exit code is
not in the set of specified values.
type: string
values:
description: |-
Specifies the set of values to check for container exit codes.
At most 255 elements are allowed.
items:
format: int32
type: integer
type: array
x-kubernetes-list-type: set
required:
- operator
type: object
required:
- action
type: object
type: array
x-kubernetes-list-type: atomic
securityContext:
description: |-
SecurityContext defines the security options the container should be run with.
@ -2835,7 +3073,6 @@ spec:
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:
@ -3849,8 +4086,9 @@ spec:
in a Container.
properties:
name:
description: Name of the environment variable. Must be
a C_IDENTIFIER.
description: |-
Name of the environment variable.
May consist of any printable ASCII characters except '='.
type: string
value:
description: |-
@ -3908,6 +4146,43 @@ spec:
- fieldPath
type: object
x-kubernetes-map-type: atomic
fileKeyRef:
description: |-
FileKeyRef selects a key of the env file.
Requires the EnvFiles feature gate to be enabled.
properties:
key:
description: |-
The key within the env file. An invalid key will prevent the pod from starting.
The keys defined within a source may consist of any printable ASCII characters except '='.
During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters.
type: string
optional:
default: false
description: |-
Specify whether the file or its key must be defined. If the file or key
does not exist, then the env var is not published.
If optional is set to true and the specified key does not exist,
the environment variable will not be set in the Pod's containers.
If optional is set to false and the specified key does not exist,
an error will be returned during Pod creation.
type: boolean
path:
description: |-
The path within the volume from which to select the file.
Must be relative and may not contain the '..' path or start with '..'.
type: string
volumeName:
description: The name of the volume mount containing
the env file.
type: string
required:
- key
- path
- volumeName
type: object
x-kubernetes-map-type: atomic
resourceFieldRef:
description: |-
Selects a resource of the container: only resources limits and requests
@ -4214,9 +4489,10 @@ spec:
operator:
description: |-
Operator represents a key's relationship to the value.
Valid operators are Exists and Equal. Defaults to Equal.
Valid operators are Exists, Equal, Lt, and Gt. Defaults to Equal.
Exists is equivalent to wildcard for value, so that a pod can
tolerate all taints of a particular category.
Lt and Gt perform numeric comparisons (requires feature gate TaintTolerationComparisonOperators).
type: string
tolerationSeconds:
description: |-
@ -4355,7 +4631,6 @@ spec:
- Ignore: nodeAffinity/nodeSelector are ignored. All nodes are included in the calculations.
If this value is nil, the behavior is equivalent to the Honor policy.
This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag.
type: string
nodeTaintsPolicy:
description: |-
@ -4366,7 +4641,6 @@ spec:
- Ignore: node taints are ignored. All nodes are included.
If this value is nil, the behavior is equivalent to the Ignore policy.
This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag.
type: string
topologyKey:
description: |-

View File

@ -438,11 +438,6 @@ func (in *OperatorConfigurationData) DeepCopyInto(out *OperatorConfigurationData
*out = new(bool)
**out = **in
}
if in.EnableCRDValidation != nil {
in, out := &in.EnableCRDValidation, &out.EnableCRDValidation
*out = new(bool)
**out = **in
}
if in.CRDCategories != nil {
in, out := &in.CRDCategories, &out.CRDCategories
*out = make([]string, len(*in))
@ -490,7 +485,7 @@ func (in *OperatorConfigurationData) DeepCopyInto(out *OperatorConfigurationData
in.TeamsAPI.DeepCopyInto(&out.TeamsAPI)
out.LoggingRESTAPI = in.LoggingRESTAPI
out.Scalyr = in.Scalyr
out.LogicalBackup = in.LogicalBackup
in.LogicalBackup.DeepCopyInto(&out.LogicalBackup)
in.ConnectionPooler.DeepCopyInto(&out.ConnectionPooler)
in.Patroni.DeepCopyInto(&out.Patroni)
return
@ -568,6 +563,21 @@ func (in *OperatorDebugConfiguration) DeepCopy() *OperatorDebugConfiguration {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OperatorLogicalBackupConfiguration) DeepCopyInto(out *OperatorLogicalBackupConfiguration) {
*out = *in
if in.SuccessfulJobsHistoryLimit != nil {
in, out := &in.SuccessfulJobsHistoryLimit, &out.SuccessfulJobsHistoryLimit
*out = new(int32)
**out = **in
}
if in.FailedJobsHistoryLimit != nil {
in, out := &in.FailedJobsHistoryLimit, &out.FailedJobsHistoryLimit
*out = new(int32)
**out = **in
}
if in.TTLSecondsAfterFinished != nil {
in, out := &in.TTLSecondsAfterFinished, &out.TTLSecondsAfterFinished
*out = new(int32)
**out = **in
}
return
}

View File

@ -92,6 +92,7 @@ type Cluster struct {
mu sync.Mutex
userSyncStrategy spec.UserSyncer
deleteOptions metav1.DeleteOptions
podEventsStore cache.Store
podEventsQueue *cache.FIFO
replicationSlots map[string]interface{}
@ -125,14 +126,17 @@ type compareLogicalBackupJobResult struct {
func New(cfg Config, kubeClient k8sutil.KubernetesClient, pgSpec acidv1.Postgresql, logger *logrus.Entry, eventRecorder record.EventRecorder) *Cluster {
deletePropagationPolicy := metav1.DeletePropagationOrphan
podEventsQueue := cache.NewFIFO(func(obj interface{}) (string, error) {
keyFn := func(obj interface{}) (string, error) {
e, ok := obj.(PodEvent)
if !ok {
return "", fmt.Errorf("could not cast to PodEvent")
return "", fmt.Errorf("could not cast to pod event")
}
return fmt.Sprintf("%s-%s", e.PodName, e.ResourceVersion), nil
})
}
podEventsStore := cache.NewStore(keyFn)
podEventsQueue := cache.NewFIFO(keyFn)
passwordEncryption, ok := pgSpec.Spec.PostgresqlParam.Parameters["password_encryption"]
if !ok {
passwordEncryption = "scram-sha-256"
@ -159,6 +163,7 @@ func New(cfg Config, kubeClient k8sutil.KubernetesClient, pgSpec acidv1.Postgres
AdditionalOwnerRoles: cfg.OpConfig.AdditionalOwnerRoles,
},
deleteOptions: metav1.DeleteOptions{PropagationPolicy: &deletePropagationPolicy},
podEventsStore: podEventsStore,
podEventsQueue: podEventsQueue,
KubeClient: kubeClient,
currentMajorVersion: 0,
@ -919,6 +924,21 @@ func (c *Cluster) compareLogicalBackupJob(cur, new *batchv1.CronJob) *compareLog
reasons = append(reasons, fmt.Sprintf("logical backup container specs do not match: %v", strings.Join(contReasons, `', '`)))
}
if !reflect.DeepEqual(cur.Spec.SuccessfulJobsHistoryLimit, new.Spec.SuccessfulJobsHistoryLimit) {
match = false
reasons = append(reasons, fmt.Sprintf("new job's successfulJobsHistoryLimit %v does not match the current one %v", new.Spec.SuccessfulJobsHistoryLimit, cur.Spec.SuccessfulJobsHistoryLimit))
}
if !reflect.DeepEqual(cur.Spec.FailedJobsHistoryLimit, new.Spec.FailedJobsHistoryLimit) {
match = false
reasons = append(reasons, fmt.Sprintf("new job's failedJobsHistoryLimit %v does not match the current one %v", new.Spec.FailedJobsHistoryLimit, cur.Spec.FailedJobsHistoryLimit))
}
if !reflect.DeepEqual(cur.Spec.JobTemplate.Spec.TTLSecondsAfterFinished, new.Spec.JobTemplate.Spec.TTLSecondsAfterFinished) {
match = false
reasons = append(reasons, fmt.Sprintf("new job's TTLSecondsAfterFinished %v does not match the current one %v", new.Spec.JobTemplate.Spec.TTLSecondsAfterFinished, cur.Spec.JobTemplate.Spec.TTLSecondsAfterFinished))
}
return &compareLogicalBackupJobResult{match: match, reasons: reasons, deletedPodAnnotations: deletedPodAnnotations}
}
@ -1341,6 +1361,9 @@ func (c *Cluster) NeedsRepair() (bool, acidv1.PostgresStatus) {
// ReceivePodEvent is called back by the controller in order to add the cluster's pod event to the queue.
func (c *Cluster) ReceivePodEvent(event PodEvent) {
if err := c.podEventsStore.Add(event); err != nil {
c.logger.Errorf("error when receiving pod event for lookup: %v", err)
}
if err := c.podEventsQueue.Add(event); err != nil {
c.logger.Errorf("error when receiving pod events: %v", err)
}
@ -1381,7 +1404,19 @@ func (c *Cluster) processPodEventQueue(stopCh <-chan struct{}) {
case <-stopCh:
return
default:
if _, err := c.podEventsQueue.Pop(cache.PopProcessFunc(c.processPodEvent)); err != nil {
_, err := c.podEventsQueue.Pop(cache.PopProcessFunc(func(obj interface{}, isInInitialList bool) error {
event, ok := obj.(PodEvent)
if !ok {
c.logger.Errorf("could not cast to pod event")
return nil // skip event to keep processing
}
c.processPodEvent(event, isInInitialList)
if err := c.podEventsStore.Delete(obj); err != nil {
c.logger.Errorf("failed to delete key from lookup store: %v", err)
}
return nil
}))
if err != nil {
c.logger.Errorf("error when processing pod event queue %v", err)
}
}

View File

@ -1567,12 +1567,21 @@ func TestCompareServices(t *testing.T) {
}
}
var (
defaultSuccessfulJobsHistoryLimit = int32(3)
defaultFailedJobsHistoryLimit = int32(3)
defaultTTLSecondsAfterFinished = int32(86400)
)
func newCronJob(image, schedule string, vars []v1.EnvVar, mounts []v1.VolumeMount) *batchv1.CronJob {
cron := &batchv1.CronJob{
Spec: batchv1.CronJobSpec{
Schedule: schedule,
Schedule: schedule,
SuccessfulJobsHistoryLimit: &defaultSuccessfulJobsHistoryLimit,
FailedJobsHistoryLimit: &defaultFailedJobsHistoryLimit,
JobTemplate: batchv1.JobTemplateSpec{
Spec: batchv1.JobSpec{
TTLSecondsAfterFinished: &defaultTTLSecondsAfterFinished,
Template: v1.PodTemplateSpec{
Spec: v1.PodSpec{
Containers: []v1.Container{

View File

@ -171,7 +171,7 @@ func (c *Cluster) enforceMinResourceLimits(resources *v1.ResourceRequirements) e
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("%s", msg)
c.eventRecorder.Eventf(c.GetReference(), v1.EventTypeWarning, "ResourceLimits", msg)
c.eventRecorder.Event(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
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("%s", msg)
c.eventRecorder.Eventf(c.GetReference(), v1.EventTypeWarning, "ResourceLimits", msg)
c.eventRecorder.Event(c.GetReference(), v1.EventTypeWarning, "ResourceLimits", msg)
resources.Limits[v1.ResourceMemory], _ = resource.ParseQuantity(minMemoryLimit)
}
}
@ -1469,7 +1469,7 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef
}
sidecarContainers, conflicts := mergeContainers(clusterSpecificSidecars, c.Config.OpConfig.SidecarContainers, globalSidecarContainersByDockerImage, scalyrSidecars)
for containerName := range conflicts {
for _, containerName := range conflicts {
c.logger.Warningf("a sidecar is specified twice. Ignoring sidecar %q in favor of %q with high a precedence",
containerName, containerName)
}
@ -2452,7 +2452,13 @@ func (c *Cluster) generateLogicalBackupJob() (*batchv1.CronJob, error) {
// configure a batch job
jobSpec := batchv1.JobSpec{
Template: *podTemplate,
Template: *podTemplate,
TTLSecondsAfterFinished: c.OpConfig.LogicalBackup.LogicalBackupTTLSecondsAfterFinished,
}
if jobSpec.TTLSecondsAfterFinished == nil {
defaultTTL := int32(86400)
jobSpec.TTLSecondsAfterFinished = &defaultTTL
}
// configure a cron job
@ -2470,6 +2476,18 @@ func (c *Cluster) generateLogicalBackupJob() (*batchv1.CronJob, error) {
schedule = c.OpConfig.LogicalBackupSchedule
}
successfulJobsHistoryLimit := c.OpConfig.LogicalBackup.LogicalBackupSuccessfulJobsHistoryLimit
if successfulJobsHistoryLimit == nil {
defaultLimit := int32(3)
successfulJobsHistoryLimit = &defaultLimit
}
failedJobsHistoryLimit := c.OpConfig.LogicalBackup.LogicalBackupFailedJobsHistoryLimit
if failedJobsHistoryLimit == nil {
defaultLimit := int32(3)
failedJobsHistoryLimit = &defaultLimit
}
cronJob := &batchv1.CronJob{
ObjectMeta: metav1.ObjectMeta{
Name: c.getLogicalBackupJobName(),
@ -2479,9 +2497,11 @@ func (c *Cluster) generateLogicalBackupJob() (*batchv1.CronJob, error) {
OwnerReferences: c.ownerReferences(),
},
Spec: batchv1.CronJobSpec{
Schedule: schedule,
JobTemplate: jobTemplateSpec,
ConcurrencyPolicy: batchv1.ForbidConcurrent,
Schedule: schedule,
JobTemplate: jobTemplateSpec,
ConcurrencyPolicy: batchv1.ForbidConcurrent,
SuccessfulJobsHistoryLimit: successfulJobsHistoryLimit,
FailedJobsHistoryLimit: failedJobsHistoryLimit,
},
}

View File

@ -4229,6 +4229,32 @@ func TestGenerateLogicalBackupJob(t *testing.T) {
if !reflect.DeepEqual(tt.expectedResources, clusterResources) {
t.Errorf("%s - %s: expected resources %#v, got %#v", t.Name(), tt.subTest, tt.expectedResources, clusterResources)
}
expectedSuccessfulJobsHistoryLimit := int32(3)
if cluster.OpConfig.LogicalBackup.LogicalBackupSuccessfulJobsHistoryLimit != nil {
expectedSuccessfulJobsHistoryLimit = *cluster.OpConfig.LogicalBackup.LogicalBackupSuccessfulJobsHistoryLimit
}
if *cronJob.Spec.SuccessfulJobsHistoryLimit != expectedSuccessfulJobsHistoryLimit {
t.Errorf("%s - %s: expected successfulJobsHistoryLimit %d, got %d", t.Name(), tt.subTest, expectedSuccessfulJobsHistoryLimit, *cronJob.Spec.SuccessfulJobsHistoryLimit)
}
expectedFailedJobsHistoryLimit := int32(3)
if cluster.OpConfig.LogicalBackup.LogicalBackupFailedJobsHistoryLimit != nil {
expectedFailedJobsHistoryLimit = *cluster.OpConfig.LogicalBackup.LogicalBackupFailedJobsHistoryLimit
}
if *cronJob.Spec.FailedJobsHistoryLimit != expectedFailedJobsHistoryLimit {
t.Errorf("%s - %s: expected failedJobsHistoryLimit %d, got %d", t.Name(), tt.subTest, expectedFailedJobsHistoryLimit, *cronJob.Spec.FailedJobsHistoryLimit)
}
expectedTTL := int32(86400)
if cluster.OpConfig.LogicalBackup.LogicalBackupTTLSecondsAfterFinished != nil {
expectedTTL = *cluster.OpConfig.LogicalBackup.LogicalBackupTTLSecondsAfterFinished
}
if cronJob.Spec.JobTemplate.Spec.TTLSecondsAfterFinished == nil {
t.Errorf("%s - %s: expected TTLSecondsAfterFinished to be set", t.Name(), tt.subTest)
} else if *cronJob.Spec.JobTemplate.Spec.TTLSecondsAfterFinished != expectedTTL {
t.Errorf("%s - %s: expected TTLSecondsAfterFinished %d, got %d", t.Name(), tt.subTest, expectedTTL, *cronJob.Spec.JobTemplate.Spec.TTLSecondsAfterFinished)
}
}
}

View File

@ -7,7 +7,7 @@ import (
"strconv"
"strings"
"github.com/Masterminds/semver"
"github.com/Masterminds/semver/v3"
"github.com/zalando/postgres-operator/pkg/spec"
"github.com/zalando/postgres-operator/pkg/util"
v1 "k8s.io/api/core/v1"

View File

@ -11,7 +11,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/zalando/postgres-operator/pkg/spec"
"github.com/zalando/postgres-operator/pkg/util/constants"
"github.com/zalando/postgres-operator/pkg/util/filesystems"
@ -91,18 +91,18 @@ func (c *Cluster) syncUnderlyingEBSVolume() error {
var modifyType *string
if targetValue.Iops != nil && *targetValue.Iops >= int64(3000) {
if volume.Iops != *targetValue.Iops {
if volume.Iops != int64(*targetValue.Iops) {
modifyIops = targetValue.Iops
}
}
if targetValue.Throughput != nil && *targetValue.Throughput >= int64(125) {
if volume.Throughput != *targetValue.Throughput {
if volume.Throughput != int64(*targetValue.Throughput) {
modifyThroughput = targetValue.Throughput
}
}
if targetSize > volume.Size {
if targetSize > int64(volume.Size) {
modifySize = &targetSize
}

View File

@ -11,7 +11,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"

View File

@ -65,6 +65,7 @@ type Controller struct {
nodesInformer cache.SharedIndexInformer
podCh chan cluster.PodEvent
clusterEventStores []cache.Store // [workerID]Store
clusterEventQueues []*cache.FIFO // [workerID]Queue
lastClusterSyncTime int64
lastClusterRepairTime int64
@ -356,17 +357,19 @@ func (c *Controller) initController() {
c.config.InfrastructureRoles = infraRoles
}
c.clusterEventStores = make([]cache.Store, c.opConfig.Workers)
c.clusterEventQueues = make([]*cache.FIFO, c.opConfig.Workers)
c.workerLogs = make(map[uint32]ringlog.RingLogger, c.opConfig.Workers)
for i := range c.clusterEventQueues {
c.clusterEventQueues[i] = cache.NewFIFO(func(obj interface{}) (string, error) {
keyFn := func(obj interface{}) (string, error) {
e, ok := obj.(ClusterEvent)
if !ok {
return "", fmt.Errorf("could not cast to ClusterEvent")
return "", fmt.Errorf("could not cast to cluster event")
}
return queueClusterKey(e.EventType, e.UID), nil
})
}
c.clusterEventStores[i] = cache.NewStore(keyFn)
c.clusterEventQueues[i] = cache.NewFIFO(keyFn)
}
c.apiserver = apiserver.New(c, c.opConfig.APIPort, c.logger.Logger)

View File

@ -80,8 +80,8 @@ func (c *Controller) GetStatus() *spec.ControllerStatus {
c.clustersMu.RUnlock()
queueSizes := make(map[int]int, c.opConfig.Workers)
for workerID, queue := range c.clusterEventQueues {
queueSizes[workerID] = len(queue.ListKeys())
for workerID, store := range c.clusterEventStores {
queueSizes[workerID] = len(store.ListKeys())
}
return &spec.ControllerStatus{
@ -180,11 +180,11 @@ func (c *Controller) Fire(e *logrus.Entry) error {
// ListQueue dumps cluster event queue of the provided worker
func (c *Controller) ListQueue(workerID uint32) (*spec.QueueDump, error) {
if workerID >= uint32(len(c.clusterEventQueues)) {
if workerID >= uint32(len(c.clusterEventStores)) {
return nil, fmt.Errorf("could not find worker")
}
q := c.clusterEventQueues[workerID]
q := c.clusterEventStores[workerID]
return &spec.QueueDump{
Keys: q.ListKeys(),
List: q.List(),
@ -196,7 +196,7 @@ func (c *Controller) GetWorkersCnt() uint32 {
return c.opConfig.Workers
}
//WorkerStatus provides status of the worker
// WorkerStatus provides status of the worker
func (c *Controller) WorkerStatus(workerID uint32) (*cluster.WorkerStatus, error) {
obj, ok := c.curWorkerCluster.Load(workerID)
if !ok || obj == nil {

View File

@ -31,7 +31,6 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
// general config
result.EnableCRDRegistration = util.CoalesceBool(fromCRD.EnableCRDRegistration, util.True())
result.EnableCRDValidation = util.CoalesceBool(fromCRD.EnableCRDValidation, util.True())
result.CRDCategories = util.CoalesceStrArr(fromCRD.CRDCategories, []string{"all"})
result.EnableLazySpiloUpgrade = fromCRD.EnableLazySpiloUpgrade
result.EnablePgVersionEnvVar = fromCRD.EnablePgVersionEnvVar
@ -217,6 +216,9 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
result.LogicalBackupMemoryRequest = fromCRD.LogicalBackup.MemoryRequest
result.LogicalBackupCPULimit = fromCRD.LogicalBackup.CPULimit
result.LogicalBackupMemoryLimit = fromCRD.LogicalBackup.MemoryLimit
result.LogicalBackupSuccessfulJobsHistoryLimit = util.CoalesceInt32(fromCRD.LogicalBackup.SuccessfulJobsHistoryLimit, k8sutil.Int32ToPointer(3))
result.LogicalBackupFailedJobsHistoryLimit = util.CoalesceInt32(fromCRD.LogicalBackup.FailedJobsHistoryLimit, k8sutil.Int32ToPointer(3))
result.LogicalBackupTTLSecondsAfterFinished = fromCRD.LogicalBackup.TTLSecondsAfterFinished
// debug config
result.DebugLogging = *util.CoalesceBool(fromCRD.OperatorDebug.DebugLogging, util.True())

View File

@ -182,7 +182,7 @@ func (c *Controller) addCluster(lg *logrus.Entry, clusterName spec.NamespacedNam
return cl, nil
}
func (c *Controller) processEvent(event ClusterEvent) {
func (c *Controller) processEvent(event ClusterEvent, isInInitialList bool) {
var clusterName spec.NamespacedName
var clHistory ringlog.RingLogger
var err error
@ -371,11 +371,22 @@ func (c *Controller) processClusterEventsQueue(idx int, stopCh <-chan struct{},
go func() {
<-stopCh
c.clusterEventQueues[idx].Close()
(*c.clusterEventQueues[idx]).Close()
}()
for {
obj, err := c.clusterEventQueues[idx].Pop(cache.PopProcessFunc(func(interface{}, bool) error { return nil }))
_, err := (*c.clusterEventQueues[idx]).Pop(cache.PopProcessFunc(func(obj interface{}, isInitialList bool) error {
event, ok := obj.(ClusterEvent)
if !ok {
c.logger.Errorf("could not cast to cluster event")
return nil // skip event to keep processing
}
c.processEvent(event, isInitialList)
if err := c.clusterEventStores[idx].Delete(obj); err != nil {
c.logger.Errorf("failed to delete key from lookup store: %v", err)
}
return nil
}))
if err != nil {
if err == cache.ErrFIFOClosed {
return
@ -383,12 +394,6 @@ func (c *Controller) processClusterEventsQueue(idx int, stopCh <-chan struct{},
c.logger.Errorf("error when processing cluster events queue: %v", err)
continue
}
event, ok := obj.(ClusterEvent)
if !ok {
c.logger.Errorf("could not cast to ClusterEvent")
}
c.processEvent(event)
}
}
@ -523,7 +528,10 @@ func (c *Controller) queueClusterEvent(informerOldSpec, informerNewSpec *acidv1.
}
lg := c.logger.WithField("worker", workerID).WithField("cluster-name", clusterName)
if err := c.clusterEventQueues[workerID].Add(clusterEvent); err != nil {
if err := c.clusterEventStores[workerID].Add(clusterEvent); err != nil {
lg.Errorf("error while storing cluster event for lookup: %v", clusterEvent)
}
if err := (*c.clusterEventQueues[workerID]).Add(clusterEvent); err != nil {
lg.Errorf("error while queueing cluster event: %v", clusterEvent)
}
lg.Infof("%s event has been queued", eventType)
@ -533,9 +541,9 @@ func (c *Controller) queueClusterEvent(informerOldSpec, informerNewSpec *acidv1.
}
// A delete event discards all prior requests for that cluster.
for _, evType := range []EventType{EventAdd, EventSync, EventUpdate, EventRepair} {
obj, exists, err := c.clusterEventQueues[workerID].GetByKey(queueClusterKey(evType, uid))
obj, exists, err := c.clusterEventStores[workerID].GetByKey(queueClusterKey(evType, uid))
if err != nil {
lg.Warningf("could not get event from the queue: %v", err)
lg.Warningf("could not get event from the lookup store: %v", err)
continue
}
@ -543,12 +551,18 @@ func (c *Controller) queueClusterEvent(informerOldSpec, informerNewSpec *acidv1.
continue
}
err = c.clusterEventQueues[workerID].Delete(obj)
err = (*c.clusterEventQueues[workerID]).Delete(obj)
if err != nil {
lg.Warningf("could not delete event from the queue: %v", err)
} else {
lg.Debugf("event %s has been discarded for the cluster", evType)
}
err = c.clusterEventStores[workerID].Delete(obj)
if err != nil {
lg.Warningf("could not delete event from the lookup store: %v", err)
} else {
lg.Debugf("event %s has been deleted from the lookup store", evType)
}
}
}

View File

@ -16,7 +16,7 @@ import (
"github.com/zalando/postgres-operator/pkg/util"
"github.com/zalando/postgres-operator/pkg/util/config"
"github.com/zalando/postgres-operator/pkg/util/k8sutil"
"gopkg.in/yaml.v2"
"gopkg.in/yaml.v3"
)
func (c *Controller) makeClusterConfig() cluster.Config {
@ -103,7 +103,11 @@ func (c *Controller) createPostgresCRD() error {
}
func (c *Controller) createConfigurationCRD() error {
return c.createOperatorCRD(acidv1.ConfigurationCRD(c.opConfig.CRDCategories))
crd, err := acidv1.OperatorConfigurationCRD(c.opConfig.CRDCategories)
if err != nil {
return fmt.Errorf("could not create OperatorConfiguration CRD object: %v", err)
}
return c.createOperatorCRD(crd)
}
func readDecodedRole(s string) (*spec.PgUser, error) {

View File

@ -30,6 +30,7 @@ import (
fakeacidv1 "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake"
zalandov1 "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/zalando.org/v1"
fakezalandov1 "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/zalando.org/v1/fake"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/discovery"
@ -41,10 +42,6 @@ import (
// It's backed by a very simple object tracker that processes creates, updates and deletions as-is,
// 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 {
@ -57,9 +54,13 @@ func NewSimpleClientset(objects ...runtime.Object) *Clientset {
cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake}
cs.AddReactor("*", "*", testing.ObjectReaction(o))
cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) {
var opts metav1.ListOptions
if watchAction, ok := action.(testing.WatchActionImpl); ok {
opts = watchAction.ListOptions
}
gvr := action.GetResource()
ns := action.GetNamespace()
watch, err := o.Watch(gvr, ns)
watch, err := o.Watch(gvr, ns, opts)
if err != nil {
return false, nil, err
}
@ -86,6 +87,17 @@ func (c *Clientset) Tracker() testing.ObjectTracker {
return c.tracker
}
// IsWatchListSemanticsUnSupported informs the reflector that this client
// doesn't support WatchList semantics.
//
// This is a synthetic method whose sole purpose is to satisfy the optional
// interface check performed by the reflector.
// Returning true signals that WatchList can NOT be used.
// No additional logic is implemented here.
func (c *Clientset) IsWatchListSemanticsUnSupported() bool {
return true
}
var (
_ clientset.Interface = &Clientset{}
_ testing.FakeClient = &Clientset{}

View File

@ -61,9 +61,7 @@ func (c *AcidV1Client) Postgresqls(namespace string) PostgresqlInterface {
// where httpClient was generated with rest.HTTPClientFor(c).
func NewForConfig(c *rest.Config) (*AcidV1Client, error) {
config := *c
if err := setConfigDefaults(&config); err != nil {
return nil, err
}
setConfigDefaults(&config)
httpClient, err := rest.HTTPClientFor(&config)
if err != nil {
return nil, err
@ -75,9 +73,7 @@ func NewForConfig(c *rest.Config) (*AcidV1Client, error) {
// Note the http client provided takes precedence over the configured transport values.
func NewForConfigAndClient(c *rest.Config, h *http.Client) (*AcidV1Client, error) {
config := *c
if err := setConfigDefaults(&config); err != nil {
return nil, err
}
setConfigDefaults(&config)
client, err := rest.RESTClientForConfigAndClient(&config, h)
if err != nil {
return nil, err
@ -100,7 +96,7 @@ func New(c rest.Interface) *AcidV1Client {
return &AcidV1Client{c}
}
func setConfigDefaults(config *rest.Config) error {
func setConfigDefaults(config *rest.Config) {
gv := acidzalandov1.SchemeGroupVersion
config.GroupVersion = &gv
config.APIPath = "/apis"
@ -109,8 +105,6 @@ func setConfigDefaults(config *rest.Config) error {
if config.UserAgent == "" {
config.UserAgent = rest.DefaultKubernetesUserAgent()
}
return nil
}
// RESTClient returns a RESTClient that is used to communicate

View File

@ -32,18 +32,26 @@ import (
// fakeOperatorConfigurations implements OperatorConfigurationInterface
type fakeOperatorConfigurations struct {
*gentype.FakeClient[*v1.OperatorConfiguration]
*gentype.FakeClientWithList[*v1.OperatorConfiguration, *v1.OperatorConfigurationList]
Fake *FakeAcidV1
}
func newFakeOperatorConfigurations(fake *FakeAcidV1, namespace string) acidzalandov1.OperatorConfigurationInterface {
return &fakeOperatorConfigurations{
gentype.NewFakeClient[*v1.OperatorConfiguration](
gentype.NewFakeClientWithList[*v1.OperatorConfiguration, *v1.OperatorConfigurationList](
fake.Fake,
namespace,
v1.SchemeGroupVersion.WithResource("operatorconfigurations"),
v1.SchemeGroupVersion.WithKind("OperatorConfiguration"),
func() *v1.OperatorConfiguration { return &v1.OperatorConfiguration{} },
func() *v1.OperatorConfigurationList { return &v1.OperatorConfigurationList{} },
func(dst, src *v1.OperatorConfigurationList) { dst.ListMeta = src.ListMeta },
func(list *v1.OperatorConfigurationList) []*v1.OperatorConfiguration {
return gentype.ToPointerSlice(list.Items)
},
func(list *v1.OperatorConfigurationList, items []*v1.OperatorConfiguration) {
list.Items = gentype.FromPointerSlice(items)
},
),
fake,
}

View File

@ -30,6 +30,8 @@ import (
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"
gentype "k8s.io/client-go/gentype"
)
@ -41,24 +43,32 @@ type OperatorConfigurationsGetter interface {
// OperatorConfigurationInterface has methods to work with OperatorConfiguration resources.
type OperatorConfigurationInterface interface {
Create(ctx context.Context, operatorConfiguration *acidzalandov1.OperatorConfiguration, opts metav1.CreateOptions) (*acidzalandov1.OperatorConfiguration, error)
Update(ctx context.Context, operatorConfiguration *acidzalandov1.OperatorConfiguration, opts metav1.UpdateOptions) (*acidzalandov1.OperatorConfiguration, 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) (*acidzalandov1.OperatorConfiguration, error)
List(ctx context.Context, opts metav1.ListOptions) (*acidzalandov1.OperatorConfigurationList, 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 *acidzalandov1.OperatorConfiguration, err error)
OperatorConfigurationExpansion
}
// operatorConfigurations implements OperatorConfigurationInterface
type operatorConfigurations struct {
*gentype.Client[*acidzalandov1.OperatorConfiguration]
*gentype.ClientWithList[*acidzalandov1.OperatorConfiguration, *acidzalandov1.OperatorConfigurationList]
}
// newOperatorConfigurations returns a OperatorConfigurations
func newOperatorConfigurations(c *AcidV1Client, namespace string) *operatorConfigurations {
return &operatorConfigurations{
gentype.NewClient[*acidzalandov1.OperatorConfiguration](
gentype.NewClientWithList[*acidzalandov1.OperatorConfiguration, *acidzalandov1.OperatorConfigurationList](
"operatorconfigurations",
c.RESTClient(),
scheme.ParameterCodec,
namespace,
func() *acidzalandov1.OperatorConfiguration { return &acidzalandov1.OperatorConfiguration{} },
func() *acidzalandov1.OperatorConfigurationList { return &acidzalandov1.OperatorConfigurationList{} },
),
}
}

View File

@ -51,9 +51,7 @@ func (c *ZalandoV1Client) FabricEventStreams(namespace string) FabricEventStream
// where httpClient was generated with rest.HTTPClientFor(c).
func NewForConfig(c *rest.Config) (*ZalandoV1Client, error) {
config := *c
if err := setConfigDefaults(&config); err != nil {
return nil, err
}
setConfigDefaults(&config)
httpClient, err := rest.HTTPClientFor(&config)
if err != nil {
return nil, err
@ -65,9 +63,7 @@ func NewForConfig(c *rest.Config) (*ZalandoV1Client, error) {
// Note the http client provided takes precedence over the configured transport values.
func NewForConfigAndClient(c *rest.Config, h *http.Client) (*ZalandoV1Client, error) {
config := *c
if err := setConfigDefaults(&config); err != nil {
return nil, err
}
setConfigDefaults(&config)
client, err := rest.RESTClientForConfigAndClient(&config, h)
if err != nil {
return nil, err
@ -90,7 +86,7 @@ func New(c rest.Interface) *ZalandoV1Client {
return &ZalandoV1Client{c}
}
func setConfigDefaults(config *rest.Config) error {
func setConfigDefaults(config *rest.Config) {
gv := zalandoorgv1.SchemeGroupVersion
config.GroupVersion = &gv
config.APIPath = "/apis"
@ -99,8 +95,6 @@ func setConfigDefaults(config *rest.Config) error {
if config.UserAgent == "" {
config.UserAgent = rest.DefaultKubernetesUserAgent()
}
return nil
}
// RESTClient returns a RESTClient that is used to communicate

View File

@ -30,6 +30,8 @@ import (
// Interface provides access to all the informers in this group version.
type Interface interface {
// OperatorConfigurations returns a OperatorConfigurationInformer.
OperatorConfigurations() OperatorConfigurationInformer
// PostgresTeams returns a PostgresTeamInformer.
PostgresTeams() PostgresTeamInformer
// Postgresqls returns a PostgresqlInformer.
@ -47,6 +49,11 @@ func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakList
return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
}
// OperatorConfigurations returns a OperatorConfigurationInformer.
func (v *version) OperatorConfigurations() OperatorConfigurationInformer {
return &operatorConfigurationInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
}
// PostgresTeams returns a PostgresTeamInformer.
func (v *version) PostgresTeams() PostgresTeamInformer {
return &postgresTeamInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}

View File

@ -0,0 +1,122 @@
/*
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
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
// Code generated by informer-gen. DO NOT EDIT.
package v1
import (
context "context"
time "time"
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"
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"
schema "k8s.io/apimachinery/pkg/runtime/schema"
watch "k8s.io/apimachinery/pkg/watch"
cache "k8s.io/client-go/tools/cache"
)
// OperatorConfigurationInformer provides access to a shared informer and lister for
// OperatorConfigurations.
type OperatorConfigurationInformer interface {
Informer() cache.SharedIndexInformer
Lister() acidzalandov1.OperatorConfigurationLister
}
type operatorConfigurationInformer struct {
factory internalinterfaces.SharedInformerFactory
tweakListOptions internalinterfaces.TweakListOptionsFunc
namespace string
}
// NewOperatorConfigurationInformer constructs a new informer for OperatorConfiguration type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewOperatorConfigurationInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
return NewOperatorConfigurationInformerWithOptions(client, namespace, internalinterfaces.InformerOptions{ResyncPeriod: resyncPeriod, Indexers: indexers})
}
// NewFilteredOperatorConfigurationInformer constructs a new informer for OperatorConfiguration type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewFilteredOperatorConfigurationInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
return NewOperatorConfigurationInformerWithOptions(client, namespace, internalinterfaces.InformerOptions{ResyncPeriod: resyncPeriod, Indexers: indexers, TweakListOptions: tweakListOptions})
}
// NewOperatorConfigurationInformerWithOptions constructs a new informer for OperatorConfiguration type with additional options.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewOperatorConfigurationInformerWithOptions(client versioned.Interface, namespace string, options internalinterfaces.InformerOptions) cache.SharedIndexInformer {
gvr := schema.GroupVersionResource{Group: "acid.zalan.do", Version: "v1", Resource: "operatorconfigurations"}
identifier := options.InformerName.WithResource(gvr)
tweakListOptions := options.TweakListOptions
return cache.NewSharedIndexInformerWithOptions(
cache.ToListWatcherWithWatchListSemantics(&cache.ListWatch{
ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) {
if tweakListOptions != nil {
tweakListOptions(&opts)
}
return client.AcidV1().OperatorConfigurations(namespace).List(context.Background(), opts)
},
WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) {
if tweakListOptions != nil {
tweakListOptions(&opts)
}
return client.AcidV1().OperatorConfigurations(namespace).Watch(context.Background(), opts)
},
ListWithContextFunc: func(ctx context.Context, opts metav1.ListOptions) (runtime.Object, error) {
if tweakListOptions != nil {
tweakListOptions(&opts)
}
return client.AcidV1().OperatorConfigurations(namespace).List(ctx, opts)
},
WatchFuncWithContext: func(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
if tweakListOptions != nil {
tweakListOptions(&opts)
}
return client.AcidV1().OperatorConfigurations(namespace).Watch(ctx, opts)
},
}, client),
&apisacidzalandov1.OperatorConfiguration{},
cache.SharedIndexInformerOptions{
ResyncPeriod: options.ResyncPeriod,
Indexers: options.Indexers,
Identifier: identifier,
},
)
}
func (f *operatorConfigurationInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
return NewOperatorConfigurationInformerWithOptions(client, f.namespace, internalinterfaces.InformerOptions{ResyncPeriod: resyncPeriod, Indexers: cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, InformerName: f.factory.InformerName(), TweakListOptions: f.tweakListOptions})
}
func (f *operatorConfigurationInformer) Informer() cache.SharedIndexInformer {
return f.factory.InformerFor(&apisacidzalandov1.OperatorConfiguration{}, f.defaultInformer)
}
func (f *operatorConfigurationInformer) Lister() acidzalandov1.OperatorConfigurationLister {
return acidzalandov1.NewOperatorConfigurationLister(f.Informer().GetIndexer())
}

View File

@ -34,6 +34,7 @@ import (
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"
schema "k8s.io/apimachinery/pkg/runtime/schema"
watch "k8s.io/apimachinery/pkg/watch"
cache "k8s.io/client-go/tools/cache"
)
@ -55,36 +56,61 @@ type postgresqlInformer struct {
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewPostgresqlInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
return NewFilteredPostgresqlInformer(client, namespace, resyncPeriod, indexers, nil)
return NewPostgresqlInformerWithOptions(client, namespace, internalinterfaces.InformerOptions{ResyncPeriod: resyncPeriod, Indexers: indexers})
}
// NewFilteredPostgresqlInformer constructs a new informer for Postgresql type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewFilteredPostgresqlInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
return cache.NewSharedIndexInformer(
&cache.ListWatch{
ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
return NewPostgresqlInformerWithOptions(client, namespace, internalinterfaces.InformerOptions{ResyncPeriod: resyncPeriod, Indexers: indexers, TweakListOptions: tweakListOptions})
}
// NewPostgresqlInformerWithOptions constructs a new informer for Postgresql type with additional options.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewPostgresqlInformerWithOptions(client versioned.Interface, namespace string, options internalinterfaces.InformerOptions) cache.SharedIndexInformer {
gvr := schema.GroupVersionResource{Group: "acid.zalan.do", Version: "v1", Resource: "postgresqls"}
identifier := options.InformerName.WithResource(gvr)
tweakListOptions := options.TweakListOptions
return cache.NewSharedIndexInformerWithOptions(
cache.ToListWatcherWithWatchListSemantics(&cache.ListWatch{
ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
tweakListOptions(&opts)
}
return client.AcidV1().Postgresqls(namespace).List(context.TODO(), options)
return client.AcidV1().Postgresqls(namespace).List(context.Background(), opts)
},
WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
tweakListOptions(&opts)
}
return client.AcidV1().Postgresqls(namespace).Watch(context.TODO(), options)
return client.AcidV1().Postgresqls(namespace).Watch(context.Background(), opts)
},
},
ListWithContextFunc: func(ctx context.Context, opts metav1.ListOptions) (runtime.Object, error) {
if tweakListOptions != nil {
tweakListOptions(&opts)
}
return client.AcidV1().Postgresqls(namespace).List(ctx, opts)
},
WatchFuncWithContext: func(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
if tweakListOptions != nil {
tweakListOptions(&opts)
}
return client.AcidV1().Postgresqls(namespace).Watch(ctx, opts)
},
}, client),
&apisacidzalandov1.Postgresql{},
resyncPeriod,
indexers,
cache.SharedIndexInformerOptions{
ResyncPeriod: options.ResyncPeriod,
Indexers: options.Indexers,
Identifier: identifier,
},
)
}
func (f *postgresqlInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
return NewFilteredPostgresqlInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
return NewPostgresqlInformerWithOptions(client, f.namespace, internalinterfaces.InformerOptions{ResyncPeriod: resyncPeriod, Indexers: cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, InformerName: f.factory.InformerName(), TweakListOptions: f.tweakListOptions})
}
func (f *postgresqlInformer) Informer() cache.SharedIndexInformer {

View File

@ -34,6 +34,7 @@ import (
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"
schema "k8s.io/apimachinery/pkg/runtime/schema"
watch "k8s.io/apimachinery/pkg/watch"
cache "k8s.io/client-go/tools/cache"
)
@ -55,36 +56,61 @@ type postgresTeamInformer struct {
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewPostgresTeamInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
return NewFilteredPostgresTeamInformer(client, namespace, resyncPeriod, indexers, nil)
return NewPostgresTeamInformerWithOptions(client, namespace, internalinterfaces.InformerOptions{ResyncPeriod: resyncPeriod, Indexers: indexers})
}
// NewFilteredPostgresTeamInformer constructs a new informer for PostgresTeam type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewFilteredPostgresTeamInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
return cache.NewSharedIndexInformer(
&cache.ListWatch{
ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
return NewPostgresTeamInformerWithOptions(client, namespace, internalinterfaces.InformerOptions{ResyncPeriod: resyncPeriod, Indexers: indexers, TweakListOptions: tweakListOptions})
}
// NewPostgresTeamInformerWithOptions constructs a new informer for PostgresTeam type with additional options.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewPostgresTeamInformerWithOptions(client versioned.Interface, namespace string, options internalinterfaces.InformerOptions) cache.SharedIndexInformer {
gvr := schema.GroupVersionResource{Group: "acid.zalan.do", Version: "v1", Resource: "postgresteams"}
identifier := options.InformerName.WithResource(gvr)
tweakListOptions := options.TweakListOptions
return cache.NewSharedIndexInformerWithOptions(
cache.ToListWatcherWithWatchListSemantics(&cache.ListWatch{
ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
tweakListOptions(&opts)
}
return client.AcidV1().PostgresTeams(namespace).List(context.TODO(), options)
return client.AcidV1().PostgresTeams(namespace).List(context.Background(), opts)
},
WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
tweakListOptions(&opts)
}
return client.AcidV1().PostgresTeams(namespace).Watch(context.TODO(), options)
return client.AcidV1().PostgresTeams(namespace).Watch(context.Background(), opts)
},
},
ListWithContextFunc: func(ctx context.Context, opts metav1.ListOptions) (runtime.Object, error) {
if tweakListOptions != nil {
tweakListOptions(&opts)
}
return client.AcidV1().PostgresTeams(namespace).List(ctx, opts)
},
WatchFuncWithContext: func(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
if tweakListOptions != nil {
tweakListOptions(&opts)
}
return client.AcidV1().PostgresTeams(namespace).Watch(ctx, opts)
},
}, client),
&apisacidzalandov1.PostgresTeam{},
resyncPeriod,
indexers,
cache.SharedIndexInformerOptions{
ResyncPeriod: options.ResyncPeriod,
Indexers: options.Indexers,
Identifier: identifier,
},
)
}
func (f *postgresTeamInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
return NewFilteredPostgresTeamInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
return NewPostgresTeamInformerWithOptions(client, f.namespace, internalinterfaces.InformerOptions{ResyncPeriod: resyncPeriod, Indexers: cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, InformerName: f.factory.InformerName(), TweakListOptions: f.tweakListOptions})
}
func (f *postgresTeamInformer) Informer() cache.SharedIndexInformer {

View File

@ -25,6 +25,7 @@ SOFTWARE.
package externalversions
import (
context "context"
reflect "reflect"
sync "sync"
time "time"
@ -36,6 +37,7 @@ import (
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
schema "k8s.io/apimachinery/pkg/runtime/schema"
wait "k8s.io/apimachinery/pkg/util/wait"
cache "k8s.io/client-go/tools/cache"
)
@ -50,6 +52,7 @@ type sharedInformerFactory struct {
defaultResync time.Duration
customResync map[reflect.Type]time.Duration
transform cache.TransformFunc
informerName *cache.InformerName
informers map[reflect.Type]cache.SharedIndexInformer
// startedInformers is used for tracking which informers have been started.
@ -96,6 +99,21 @@ func WithTransform(transform cache.TransformFunc) SharedInformerOption {
}
}
// WithInformerName sets the InformerName for informer identity used in metrics.
// The InformerName must be created via cache.NewInformerName() at startup,
// which validates global uniqueness. Each informer type will register its
// GVR under this name.
func WithInformerName(informerName *cache.InformerName) SharedInformerOption {
return func(factory *sharedInformerFactory) *sharedInformerFactory {
factory.informerName = informerName
return factory
}
}
func (f *sharedInformerFactory) InformerName() *cache.InformerName {
return f.informerName
}
// NewSharedInformerFactory constructs a new instance of sharedInformerFactory for all namespaces.
func NewSharedInformerFactory(client versioned.Interface, defaultResync time.Duration) SharedInformerFactory {
return NewSharedInformerFactoryWithOptions(client, defaultResync)
@ -104,6 +122,7 @@ func NewSharedInformerFactory(client versioned.Interface, defaultResync time.Dur
// NewFilteredSharedInformerFactory constructs a new instance of sharedInformerFactory.
// Listers obtained via this SharedInformerFactory will be subject to the same filters
// as specified here.
//
// Deprecated: Please use NewSharedInformerFactoryWithOptions instead
func NewFilteredSharedInformerFactory(client versioned.Interface, defaultResync time.Duration, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerFactory {
return NewSharedInformerFactoryWithOptions(client, defaultResync, WithNamespace(namespace), WithTweakListOptions(tweakListOptions))
@ -129,6 +148,10 @@ func NewSharedInformerFactoryWithOptions(client versioned.Interface, defaultResy
}
func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) {
f.StartWithContext(wait.ContextForChannel(stopCh))
}
func (f *sharedInformerFactory) StartWithContext(ctx context.Context) {
f.lock.Lock()
defer f.lock.Unlock()
@ -138,15 +161,9 @@ func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) {
for informerType, informer := range f.informers {
if !f.startedInformers[informerType] {
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.wg.Go(func() {
informer.RunWithContext(ctx)
})
f.startedInformers[informerType] = true
}
}
@ -159,9 +176,15 @@ func (f *sharedInformerFactory) Shutdown() {
// Will return immediately if there is nothing to wait for.
f.wg.Wait()
f.informerName.Release()
}
func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool {
result := f.WaitForCacheSyncWithContext(wait.ContextForChannel(stopCh))
return result.Synced
}
func (f *sharedInformerFactory) WaitForCacheSyncWithContext(ctx context.Context) cache.SyncResult {
informers := func() map[reflect.Type]cache.SharedIndexInformer {
f.lock.Lock()
defer f.lock.Unlock()
@ -175,10 +198,31 @@ func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[ref
return informers
}()
res := map[reflect.Type]bool{}
for informType, informer := range informers {
res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced)
// Wait for informers to sync, without polling.
cacheSyncs := make([]cache.DoneChecker, 0, len(informers))
for _, informer := range informers {
cacheSyncs = append(cacheSyncs, informer.HasSyncedChecker())
}
cache.WaitFor(ctx, "" /* no logging */, cacheSyncs...)
res := cache.SyncResult{
Synced: make(map[reflect.Type]bool, len(informers)),
}
failed := false
for informType, informer := range informers {
hasSynced := informer.HasSynced()
if !hasSynced {
failed = true
}
res.Synced[informType] = hasSynced
}
if failed {
// context.Cause is more informative than ctx.Err().
// This must be non-nil, otherwise WaitFor wouldn't have stopped
// prematurely.
res.Err = context.Cause(ctx)
}
return res
}
@ -200,7 +244,9 @@ func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internal
}
informer = newFunc(f.client, resyncPeriod)
informer.SetTransform(f.transform)
if f.transform != nil {
informer.SetTransform(f.transform)
}
f.informers[informerType] = informer
return informer
@ -211,33 +257,52 @@ func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internal
//
// It is typically used like this:
//
// ctx, cancel := context.Background()
// ctx, cancel := context.WithCancel(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
// }
// handle, err := typeInformer.Informer().AddEventHandler(...)
// if err != nil {
// return fmt.Errorf("register event handler: %v", err)
// }
// defer typeInformer.Informer().RemoveEventHandler(handle) // Avoids leaking goroutines.
// factory.StartWithContext(ctx) // Start processing these informers.
// synced := factory.WaitForCacheSyncWithContext(ctx)
// if err := synced.AsError(); err != nil {
// return err
// }
// for v := range synced {
// // Only if desired log some information similar to this.
// fmt.Fprintf(os.Stdout, "cache synced: %s", v)
// }
//
// // Also make sure that all of the initial cache events have been delivered.
// if !WaitFor(ctx, "event handler sync", handle.HasSyncedChecker()) {
// // Must have failed because of context.
// return fmt.Errorf("sync event handler: %w", context.Cause(ctx))
// }
//
// // Creating informers can also be created after Start, but then
// // Start must be called again:
// anotherGenericInformer := factory.ForResource(resource)
// factory.Start(ctx.Done())
// factory.StartWithContext(ctx)
type SharedInformerFactory interface {
internalinterfaces.SharedInformerFactory
// 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.
//
// Contextual logging: StartWithContext should be used instead of Start in code which supports contextual logging.
Start(stopCh <-chan struct{})
// StartWithContext initializes all requested informers. They are handled in goroutines
// which run until the context gets canceled.
// Warning: StartWithContext does not block. When run in a go-routine, it will race with a later WaitForCacheSync.
StartWithContext(ctx context.Context)
// Shutdown marks a factory as shutting down. At that point no new
// informers can be started anymore and Start will return without
// doing anything.
@ -252,8 +317,14 @@ type SharedInformerFactory interface {
// WaitForCacheSync blocks until all started informers' caches were synced
// or the stop channel gets closed.
//
// Contextual logging: WaitForCacheSync should be used instead of WaitForCacheSync in code which supports contextual logging. It also returns a more useful result.
WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool
// WaitForCacheSyncWithContext blocks until all started informers' caches were synced
// or the context gets canceled.
WaitForCacheSyncWithContext(ctx context.Context) cache.SyncResult
// ForResource gives generic access to a shared informer of the matching type.
ForResource(resource schema.GroupVersionResource) (GenericInformer, error)

View File

@ -60,6 +60,8 @@ func (f *genericInformer) Lister() cache.GenericLister {
func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) {
switch resource {
// Group=acid.zalan.do, Version=v1
case v1.SchemeGroupVersion.WithResource("operatorconfigurations"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Acid().V1().OperatorConfigurations().Informer()}, nil
case v1.SchemeGroupVersion.WithResource("postgresteams"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Acid().V1().PostgresTeams().Informer()}, nil
case v1.SchemeGroupVersion.WithResource("postgresqls"):

View File

@ -40,7 +40,26 @@ type NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexI
type SharedInformerFactory interface {
Start(stopCh <-chan struct{})
InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer
InformerName() *cache.InformerName
}
// TweakListOptionsFunc is a function that transforms a v1.ListOptions.
type TweakListOptionsFunc func(*v1.ListOptions)
// InformerOptions holds the options for creating an informer.
type InformerOptions struct {
// ResyncPeriod is the resync period for this informer.
// If not set, defaults to 0 (no resync).
ResyncPeriod time.Duration
// Indexers are the indexers for this informer.
Indexers cache.Indexers
// InformerName is used to uniquely identify this informer for metrics.
// If not set, metrics will not be published for this informer.
// Use cache.NewInformerName() to create an InformerName at startup.
InformerName *cache.InformerName
// TweakListOptions is an optional function to modify the list options.
TweakListOptions TweakListOptionsFunc
}

View File

@ -34,6 +34,7 @@ import (
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"
schema "k8s.io/apimachinery/pkg/runtime/schema"
watch "k8s.io/apimachinery/pkg/watch"
cache "k8s.io/client-go/tools/cache"
)
@ -55,36 +56,61 @@ type fabricEventStreamInformer struct {
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewFabricEventStreamInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
return NewFilteredFabricEventStreamInformer(client, namespace, resyncPeriod, indexers, nil)
return NewFabricEventStreamInformerWithOptions(client, namespace, internalinterfaces.InformerOptions{ResyncPeriod: resyncPeriod, Indexers: indexers})
}
// NewFilteredFabricEventStreamInformer constructs a new informer for FabricEventStream type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewFilteredFabricEventStreamInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
return cache.NewSharedIndexInformer(
&cache.ListWatch{
ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
return NewFabricEventStreamInformerWithOptions(client, namespace, internalinterfaces.InformerOptions{ResyncPeriod: resyncPeriod, Indexers: indexers, TweakListOptions: tweakListOptions})
}
// NewFabricEventStreamInformerWithOptions constructs a new informer for FabricEventStream type with additional options.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewFabricEventStreamInformerWithOptions(client versioned.Interface, namespace string, options internalinterfaces.InformerOptions) cache.SharedIndexInformer {
gvr := schema.GroupVersionResource{Group: "zalando.org", Version: "v1", Resource: "fabriceventstreams"}
identifier := options.InformerName.WithResource(gvr)
tweakListOptions := options.TweakListOptions
return cache.NewSharedIndexInformerWithOptions(
cache.ToListWatcherWithWatchListSemantics(&cache.ListWatch{
ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
tweakListOptions(&opts)
}
return client.ZalandoV1().FabricEventStreams(namespace).List(context.TODO(), options)
return client.ZalandoV1().FabricEventStreams(namespace).List(context.Background(), opts)
},
WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
tweakListOptions(&opts)
}
return client.ZalandoV1().FabricEventStreams(namespace).Watch(context.TODO(), options)
return client.ZalandoV1().FabricEventStreams(namespace).Watch(context.Background(), opts)
},
},
ListWithContextFunc: func(ctx context.Context, opts metav1.ListOptions) (runtime.Object, error) {
if tweakListOptions != nil {
tweakListOptions(&opts)
}
return client.ZalandoV1().FabricEventStreams(namespace).List(ctx, opts)
},
WatchFuncWithContext: func(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
if tweakListOptions != nil {
tweakListOptions(&opts)
}
return client.ZalandoV1().FabricEventStreams(namespace).Watch(ctx, opts)
},
}, client),
&apiszalandoorgv1.FabricEventStream{},
resyncPeriod,
indexers,
cache.SharedIndexInformerOptions{
ResyncPeriod: options.ResyncPeriod,
Indexers: options.Indexers,
Identifier: identifier,
},
)
}
func (f *fabricEventStreamInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
return NewFilteredFabricEventStreamInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
return NewFabricEventStreamInformerWithOptions(client, f.namespace, internalinterfaces.InformerOptions{ResyncPeriod: resyncPeriod, Indexers: cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, InformerName: f.factory.InformerName(), TweakListOptions: f.tweakListOptions})
}
func (f *fabricEventStreamInformer) Informer() cache.SharedIndexInformer {

View File

@ -24,6 +24,14 @@ SOFTWARE.
package v1
// OperatorConfigurationListerExpansion allows custom methods to be added to
// OperatorConfigurationLister.
type OperatorConfigurationListerExpansion interface{}
// OperatorConfigurationNamespaceListerExpansion allows custom methods to be added to
// OperatorConfigurationNamespaceLister.
type OperatorConfigurationNamespaceListerExpansion interface{}
// PostgresTeamListerExpansion allows custom methods to be added to
// PostgresTeamLister.
type PostgresTeamListerExpansion interface{}

View File

@ -0,0 +1,76 @@
/*
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
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
// Code generated by lister-gen. DO NOT EDIT.
package v1
import (
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"
)
// OperatorConfigurationLister helps list OperatorConfigurations.
// All objects returned here must be treated as read-only.
type OperatorConfigurationLister interface {
// List lists all OperatorConfigurations in the indexer.
// Objects returned here must be treated as read-only.
List(selector labels.Selector) (ret []*acidzalandov1.OperatorConfiguration, err error)
// OperatorConfigurations returns an object that can list and get OperatorConfigurations.
OperatorConfigurations(namespace string) OperatorConfigurationNamespaceLister
OperatorConfigurationListerExpansion
}
// operatorConfigurationLister implements the OperatorConfigurationLister interface.
type operatorConfigurationLister struct {
listers.ResourceIndexer[*acidzalandov1.OperatorConfiguration]
}
// NewOperatorConfigurationLister returns a new OperatorConfigurationLister.
func NewOperatorConfigurationLister(indexer cache.Indexer) OperatorConfigurationLister {
return &operatorConfigurationLister{listers.New[*acidzalandov1.OperatorConfiguration](indexer, acidzalandov1.Resource("operatorconfiguration"))}
}
// OperatorConfigurations returns an object that can list and get OperatorConfigurations.
func (s *operatorConfigurationLister) OperatorConfigurations(namespace string) OperatorConfigurationNamespaceLister {
return operatorConfigurationNamespaceLister{listers.NewNamespaced[*acidzalandov1.OperatorConfiguration](s.ResourceIndexer, namespace)}
}
// OperatorConfigurationNamespaceLister helps list and get OperatorConfigurations.
// All objects returned here must be treated as read-only.
type OperatorConfigurationNamespaceLister interface {
// List lists all OperatorConfigurations in the indexer for a given namespace.
// Objects returned here must be treated as read-only.
List(selector labels.Selector) (ret []*acidzalandov1.OperatorConfiguration, err error)
// Get retrieves the OperatorConfiguration from the indexer for a given namespace and name.
// Objects returned here must be treated as read-only.
Get(name string) (*acidzalandov1.OperatorConfiguration, error)
OperatorConfigurationNamespaceListerExpansion
}
// operatorConfigurationNamespaceLister implements the OperatorConfigurationNamespaceLister
// interface.
type operatorConfigurationNamespaceLister struct {
listers.ResourceIndexer[*acidzalandov1.OperatorConfiguration]
}

View File

@ -19,7 +19,6 @@ type CRD struct {
ResyncPeriod time.Duration `name:"resync_period" default:"30m"`
RepairPeriod time.Duration `name:"repair_period" default:"5m"`
EnableCRDRegistration *bool `name:"enable_crd_registration" default:"true"`
EnableCRDValidation *bool `name:"enable_crd_validation" default:"true"`
CRDCategories []string `name:"crd_categories" default:"all"`
}
@ -149,6 +148,9 @@ type LogicalBackup struct {
LogicalBackupMemoryRequest string `name:"logical_backup_memory_request"`
LogicalBackupCPULimit string `name:"logical_backup_cpu_limit"`
LogicalBackupMemoryLimit string `name:"logical_backup_memory_limit"`
LogicalBackupSuccessfulJobsHistoryLimit *int32 `name:"logical_backup_successful_jobs_history_limit" default:"3"`
LogicalBackupFailedJobsHistoryLimit *int32 `name:"logical_backup_failed_jobs_history_limit" default:"3"`
LogicalBackupTTLSecondsAfterFinished *int32 `name:"logical_backup_ttl_seconds_after_finished" default:"86400"`
}
// Operator options for connection pooler

View File

@ -1,12 +1,13 @@
package volumes
import (
"context"
"fmt"
"strings"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/aws/aws-sdk-go-v2/service/ec2/types"
v1 "k8s.io/api/core/v1"
"github.com/zalando/postgres-operator/pkg/util/constants"
@ -15,17 +16,17 @@ import (
// EBSVolumeResizer implements volume resizing interface for AWS EBS volumes.
type EBSVolumeResizer struct {
connection *ec2.EC2
connection *ec2.Client
AWSRegion string
}
// ConnectToProvider connects to AWS.
func (r *EBSVolumeResizer) ConnectToProvider() error {
sess, err := session.NewSession(&aws.Config{Region: aws.String(r.AWSRegion)})
cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion(r.AWSRegion))
if err != nil {
return fmt.Errorf("could not establish AWS session: %v", err)
}
r.connection = ec2.New(sess)
r.connection = ec2.NewFromConfig(cfg)
return nil
}
@ -77,7 +78,7 @@ func (r *EBSVolumeResizer) DescribeVolumes(volumeIds []string) ([]VolumeProperti
}
}
volumeOutput, err := r.connection.DescribeVolumes(&ec2.DescribeVolumesInput{VolumeIds: aws.StringSlice((volumeIds))})
volumeOutput, err := r.connection.DescribeVolumes(context.TODO(), &ec2.DescribeVolumesInput{VolumeIds: volumeIds})
if err != nil {
return nil, err
}
@ -88,13 +89,13 @@ func (r *EBSVolumeResizer) DescribeVolumes(volumeIds []string) ([]VolumeProperti
}
for _, v := range volumeOutput.Volumes {
switch *v.VolumeType {
switch v.VolumeType {
case "gp3":
p = append(p, VolumeProperties{VolumeID: *v.VolumeId, Size: *v.Size, VolumeType: *v.VolumeType, Iops: *v.Iops, Throughput: *v.Throughput})
p = append(p, VolumeProperties{VolumeID: *v.VolumeId, Size: int64(*v.Size), VolumeType: string(v.VolumeType), Iops: int64(*v.Iops), Throughput: int64(*v.Throughput)})
case "gp2":
p = append(p, VolumeProperties{VolumeID: *v.VolumeId, Size: *v.Size, VolumeType: *v.VolumeType})
p = append(p, VolumeProperties{VolumeID: *v.VolumeId, Size: int64(*v.Size), VolumeType: string(v.VolumeType)})
default:
return nil, fmt.Errorf("discovered unexpected volume type %s %s", *v.VolumeId, *v.VolumeType)
return nil, fmt.Errorf("discovered unexpected volume type %s %s", *v.VolumeId, v.VolumeType)
}
}
@ -104,7 +105,7 @@ func (r *EBSVolumeResizer) DescribeVolumes(volumeIds []string) ([]VolumeProperti
// ResizeVolume actually calls AWS API to resize the EBS volume if necessary.
func (r *EBSVolumeResizer) ResizeVolume(volumeID string, newSize int64) error {
/* first check if the volume is already of a requested size */
volumeOutput, err := r.connection.DescribeVolumes(&ec2.DescribeVolumesInput{VolumeIds: []*string{&volumeID}})
volumeOutput, err := r.connection.DescribeVolumes(context.TODO(), &ec2.DescribeVolumesInput{VolumeIds: []string{volumeID}})
if err != nil {
return fmt.Errorf("could not get information about the volume: %v", err)
}
@ -112,17 +113,18 @@ func (r *EBSVolumeResizer) ResizeVolume(volumeID string, newSize int64) error {
if *vol.VolumeId != volumeID {
return fmt.Errorf("describe volume %q returned information about a non-matching volume %q", volumeID, *vol.VolumeId)
}
if *vol.Size == newSize {
sizeInt32 := int32(newSize)
if *vol.Size == sizeInt32 {
// nothing to do
return nil
}
input := ec2.ModifyVolumeInput{Size: &newSize, VolumeId: &volumeID}
output, err := r.connection.ModifyVolume(&input)
input := ec2.ModifyVolumeInput{Size: &sizeInt32, VolumeId: &volumeID}
output, err := r.connection.ModifyVolume(context.TODO(), &input)
if err != nil {
return fmt.Errorf("could not modify persistent volume: %v", err)
}
state := *output.VolumeModification.ModificationState
state := output.VolumeModification.ModificationState
if state == constants.EBSVolumeStateFailed {
return fmt.Errorf("could not modify persistent volume %q: modification state failed", volumeID)
}
@ -133,10 +135,10 @@ func (r *EBSVolumeResizer) ResizeVolume(volumeID string, newSize int64) error {
return nil
}
// wait until the volume reaches the "optimizing" or "completed" state
in := ec2.DescribeVolumesModificationsInput{VolumeIds: []*string{&volumeID}}
in := ec2.DescribeVolumesModificationsInput{VolumeIds: []string{volumeID}}
return retryutil.Retry(constants.EBSVolumeResizeWaitInterval, constants.EBSVolumeResizeWaitTimeout,
func() (bool, error) {
out, err := r.connection.DescribeVolumesModifications(&in)
out, err := r.connection.DescribeVolumesModifications(context.TODO(), &in)
if err != nil {
return false, fmt.Errorf("could not describe volume modification: %v", err)
}
@ -147,20 +149,35 @@ func (r *EBSVolumeResizer) ResizeVolume(volumeID string, newSize int64) error {
return false, fmt.Errorf("non-matching volume id when describing modifications: %q is different from %q",
*out.VolumesModifications[0].VolumeId, volumeID)
}
return *out.VolumesModifications[0].ModificationState != constants.EBSVolumeStateModifying, nil
return out.VolumesModifications[0].ModificationState != constants.EBSVolumeStateModifying, nil
})
}
// ModifyVolume Modify EBS volume
func (r *EBSVolumeResizer) ModifyVolume(volumeID string, newType *string, newSize *int64, iops *int64, throughput *int64) error {
/* first check if the volume is already of a requested size */
input := ec2.ModifyVolumeInput{Size: newSize, VolumeId: &volumeID, VolumeType: newType, Iops: iops, Throughput: throughput}
output, err := r.connection.ModifyVolume(&input)
var sizeInt32 *int32
var iopsInt32 *int32
var throughputInt32 *int32
if newSize != nil {
s := int32(*newSize)
sizeInt32 = &s
}
if iops != nil {
i := int32(*iops)
iopsInt32 = &i
}
if throughput != nil {
t := int32(*throughput)
throughputInt32 = &t
}
input := ec2.ModifyVolumeInput{Size: sizeInt32, VolumeId: &volumeID, VolumeType: types.VolumeType(*newType), Iops: iopsInt32, Throughput: throughputInt32}
output, err := r.connection.ModifyVolume(context.TODO(), &input)
if err != nil {
return fmt.Errorf("could not modify persistent volume: %v", err)
}
state := *output.VolumeModification.ModificationState
state := output.VolumeModification.ModificationState
if state == constants.EBSVolumeStateFailed {
return fmt.Errorf("could not modify persistent volume %q: modification state failed", volumeID)
}
@ -171,10 +188,10 @@ func (r *EBSVolumeResizer) ModifyVolume(volumeID string, newType *string, newSiz
return nil
}
// wait until the volume reaches the "optimizing" or "completed" state
in := ec2.DescribeVolumesModificationsInput{VolumeIds: []*string{&volumeID}}
in := ec2.DescribeVolumesModificationsInput{VolumeIds: []string{volumeID}}
return retryutil.Retry(constants.EBSVolumeResizeWaitInterval, constants.EBSVolumeResizeWaitTimeout,
func() (bool, error) {
out, err := r.connection.DescribeVolumesModifications(&in)
out, err := r.connection.DescribeVolumesModifications(context.TODO(), &in)
if err != nil {
return false, fmt.Errorf("could not describe volume modification: %v", err)
}
@ -185,7 +202,7 @@ func (r *EBSVolumeResizer) ModifyVolume(volumeID string, newType *string, newSiz
return false, fmt.Errorf("non-matching volume id when describing modifications: %q is different from %q",
*out.VolumesModifications[0].VolumeId, volumeID)
}
return *out.VolumesModifications[0].ModificationState != constants.EBSVolumeStateModifying, nil
return out.VolumesModifications[0].ModificationState != constants.EBSVolumeStateModifying, nil
})
}

View File

@ -38,7 +38,7 @@
"brfs": "^2.0.2",
"dedent-js": "1.0.1",
"eslint": "^8.32.0",
"js-yaml": "4.1.1",
"js-yaml": "4.2.0",
"pug": "^3.0.2",
"rimraf": "^4.1.2",
"riot": "^3.13.2",