Merge branch 'master' into observed-generation
This commit is contained in:
commit
f75e3f1b63
|
|
@ -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,10 +94,22 @@ 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
|
||||
build-args: BASE_IMAGE=ubuntu:22.04
|
||||
tags: "${{ steps.image_lb.outputs.BACKUP_IMAGE }}"
|
||||
platforms: linux/amd64,linux/arm64
|
||||
|
||||
- name: Build and push postgres-operator chart to ghcr
|
||||
run: |
|
||||
helm package charts/postgres-operator
|
||||
helm push postgres-operator-*.tgz oci://${{ env.REGISTRY }}/zalando/charts
|
||||
rm -rf postgres-operator-*.tgz
|
||||
|
||||
- name: Build and push postgres-operator-ui chart to ghcr
|
||||
run: |
|
||||
helm package charts/postgres-operator-ui
|
||||
helm push postgres-operator-ui-*.tgz oci://${{ env.REGISTRY }}/zalando/charts
|
||||
rm -rf postgres-operator-ui-*.tgz
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ _testmain.go
|
|||
*.test
|
||||
*.prof
|
||||
/vendor/
|
||||
/kubectl-pg/vendor/
|
||||
/build/
|
||||
/docker/build/
|
||||
/github.com/
|
||||
|
|
|
|||
2
LICENSE
2
LICENSE
|
|
@ -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
|
||||
|
|
|
|||
18
Makefile
18
Makefile
|
|
@ -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,18 @@ $(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/postgresql.crd.yaml charts/postgres-operator/crds/postgresqls.yaml
|
||||
@cp manifests/operatorconfiguration.crd.yaml pkg/apis/acid.zalan.do/v1/operatorconfiguration.crd.yaml
|
||||
@cp manifests/operatorconfiguration.crd.yaml charts/postgres-operator/crds/operatorconfigurations.yaml
|
||||
@cp manifests/postgresteam.crd.yaml charts/postgres-operator/crds/postgresteams.yaml
|
||||
|
||||
local: ${SOURCES} $(GENERATED_CRDS)
|
||||
CGO_ENABLED=${CGO_ENABLED} go build -o build/${BINARY} $(LOCAL_BUILD_FLAGS) -ldflags "$(LDFLAGS)" $(SOURCES)
|
||||
|
|
@ -83,10 +87,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 +104,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 +117,7 @@ vet:
|
|||
@staticcheck $(PKG)
|
||||
|
||||
test: mocks $(GENERATED) $(GENERATED_CRDS)
|
||||
GO111MODULE=on go test ./...
|
||||
go test ./...
|
||||
|
||||
codegen: $(GENERATED)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -1,9 +1,12 @@
|
|||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: postgresqls.acid.zalan.do
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.17.3
|
||||
labels:
|
||||
app.kubernetes.io/name: postgres-operator
|
||||
name: postgresqls.acid.zalan.do
|
||||
spec:
|
||||
group: acid.zalan.do
|
||||
names:
|
||||
|
|
@ -107,7 +110,7 @@ spec:
|
|||
description: load balancers' source ranges are the same for master
|
||||
and replica services
|
||||
items:
|
||||
pattern: ^(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\/(\d|[1-2]\d|3[0-2])$
|
||||
pattern: ^((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\/(\d|[1-2]\d|3[0-2])|(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))\/(12[0-8]|1[01][0-9]|[1-9]?[0-9]))$
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
|
|
@ -273,14 +276,26 @@ spec:
|
|||
vars that enable load balancers are pointers because it is important to know if any of them is omitted from the Postgres manifest
|
||||
in that case the var evaluates to nil and the value is taken from the operator config
|
||||
type: boolean
|
||||
enableMasterNodePort:
|
||||
description: |-
|
||||
vars to enable and configure nodeport services
|
||||
set ports to 0 or nil to let kubernetes decide which port to use
|
||||
overrides loadbalancer configuration
|
||||
type: boolean
|
||||
enableMasterPoolerLoadBalancer:
|
||||
type: boolean
|
||||
enableMasterPoolerNodePort:
|
||||
type: boolean
|
||||
enableReplicaConnectionPooler:
|
||||
type: boolean
|
||||
enableReplicaLoadBalancer:
|
||||
type: boolean
|
||||
enableReplicaNodePort:
|
||||
type: boolean
|
||||
enableReplicaPoolerLoadBalancer:
|
||||
type: boolean
|
||||
enableReplicaPoolerNodePort:
|
||||
type: boolean
|
||||
enableShmVolume:
|
||||
type: boolean
|
||||
env:
|
||||
|
|
@ -289,7 +304,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: |-
|
||||
|
|
@ -347,6 +364,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
|
||||
|
|
@ -443,8 +497,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: |-
|
||||
|
|
@ -502,6 +557,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
|
||||
|
|
@ -562,14 +654,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
|
||||
|
|
@ -590,8 +682,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
|
||||
|
|
@ -854,6 +947,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: |-
|
||||
|
|
@ -1224,7 +1323,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.
|
||||
|
|
@ -1256,7 +1357,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.
|
||||
|
|
@ -1310,10 +1411,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"
|
||||
|
|
@ -1325,6 +1426,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.
|
||||
|
|
@ -1400,7 +1554,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:
|
||||
|
|
@ -1865,8 +2018,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: |-
|
||||
|
|
@ -1924,6 +2078,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
|
||||
|
|
@ -1984,14 +2175,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
|
||||
|
|
@ -2012,8 +2203,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
|
||||
|
|
@ -2276,6 +2468,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: |-
|
||||
|
|
@ -2646,7 +2844,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.
|
||||
|
|
@ -2678,7 +2878,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.
|
||||
|
|
@ -2732,10 +2932,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"
|
||||
|
|
@ -2747,6 +2947,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.
|
||||
|
|
@ -2822,7 +3075,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:
|
||||
|
|
@ -3245,6 +3497,159 @@ spec:
|
|||
- name
|
||||
type: object
|
||||
type: array
|
||||
livenessProbe:
|
||||
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
|
||||
logicalBackupRetention:
|
||||
type: string
|
||||
logicalBackupSchedule:
|
||||
|
|
@ -3255,6 +3660,12 @@ spec:
|
|||
pattern: '^\ *((Mon|Tue|Wed|Thu|Fri|Sat|Sun):(2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))-((2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))\ *$'
|
||||
type: string
|
||||
type: array
|
||||
masterNodePort:
|
||||
format: int32
|
||||
type: integer
|
||||
masterPoolerNodePort:
|
||||
format: int32
|
||||
type: integer
|
||||
masterServiceAnnotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
|
|
@ -3559,6 +3970,12 @@ spec:
|
|||
replicaLoadBalancer:
|
||||
description: deprecated
|
||||
type: boolean
|
||||
replicaNodePort:
|
||||
format: int32
|
||||
type: integer
|
||||
replicaPoolerNodePort:
|
||||
format: int32
|
||||
type: integer
|
||||
replicaServiceAnnotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
|
|
@ -3671,8 +4088,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: |-
|
||||
|
|
@ -3730,6 +4148,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
|
||||
|
|
@ -4036,9 +4491,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: |-
|
||||
|
|
@ -4056,22 +4512,178 @@ spec:
|
|||
type: object
|
||||
type: array
|
||||
topologySpreadConstraints:
|
||||
type: array
|
||||
nullable: true
|
||||
items:
|
||||
type: object
|
||||
description: TopologySpreadConstraint specifies how to spread matching
|
||||
pods among the given topology.
|
||||
properties:
|
||||
maxskew:
|
||||
type: integer
|
||||
labelSelector:
|
||||
description: |-
|
||||
LabelSelector is used to find matching pods.
|
||||
Pods that match this label selector are counted to determine the number of pods
|
||||
in their corresponding topology domain.
|
||||
properties:
|
||||
matchExpressions:
|
||||
description: matchExpressions is a list of label selector
|
||||
requirements. The requirements are ANDed.
|
||||
items:
|
||||
description: |-
|
||||
A label selector requirement is a selector that contains values, a key, and an operator that
|
||||
relates the key and values.
|
||||
properties:
|
||||
key:
|
||||
description: key is the label key that the selector
|
||||
applies to.
|
||||
type: string
|
||||
operator:
|
||||
description: |-
|
||||
operator represents a key's relationship to a set of values.
|
||||
Valid operators are In, NotIn, Exists and DoesNotExist.
|
||||
type: string
|
||||
values:
|
||||
description: |-
|
||||
values is an array of string values. If the operator is In or NotIn,
|
||||
the values array must be non-empty. If the operator is Exists or DoesNotExist,
|
||||
the values array must be empty. This array is replaced during a strategic
|
||||
merge patch.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
x-kubernetes-list-type: atomic
|
||||
required:
|
||||
- key
|
||||
- operator
|
||||
type: object
|
||||
type: array
|
||||
x-kubernetes-list-type: atomic
|
||||
matchLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: |-
|
||||
matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
|
||||
map is equivalent to an element of matchExpressions, whose key field is "key", the
|
||||
operator is "In", and the values array contains only "value". The requirements are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
matchLabelKeys:
|
||||
description: |-
|
||||
MatchLabelKeys is a set of pod label keys to select the pods over which
|
||||
spreading will be calculated. The keys are used to lookup values from the
|
||||
incoming pod labels, those key-value labels are ANDed with labelSelector
|
||||
to select the group of existing pods over which spreading will be calculated
|
||||
for the incoming pod. The same key is forbidden to exist in both MatchLabelKeys and LabelSelector.
|
||||
MatchLabelKeys cannot be set when LabelSelector isn't set.
|
||||
Keys that don't exist in the incoming pod labels will
|
||||
be ignored. A null or empty list means only match against labelSelector.
|
||||
|
||||
This is a beta field and requires the MatchLabelKeysInPodTopologySpread feature gate to be enabled (enabled by default).
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
x-kubernetes-list-type: atomic
|
||||
maxSkew:
|
||||
description: |-
|
||||
MaxSkew describes the degree to which pods may be unevenly distributed.
|
||||
When `whenUnsatisfiable=DoNotSchedule`, it is the maximum permitted difference
|
||||
between the number of matching pods in the target topology and the global minimum.
|
||||
The global minimum is the minimum number of matching pods in an eligible domain
|
||||
or zero if the number of eligible domains is less than MinDomains.
|
||||
For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same
|
||||
labelSelector spread as 2/2/1:
|
||||
In this case, the global minimum is 1.
|
||||
| zone1 | zone2 | zone3 |
|
||||
| P P | P P | P |
|
||||
- if MaxSkew is 1, incoming pod can only be scheduled to zone3 to become 2/2/2;
|
||||
scheduling it onto zone1(zone2) would make the ActualSkew(3-1) on zone1(zone2)
|
||||
violate MaxSkew(1).
|
||||
- if MaxSkew is 2, incoming pod can be scheduled onto any zone.
|
||||
When `whenUnsatisfiable=ScheduleAnyway`, it is used to give higher precedence
|
||||
to topologies that satisfy it.
|
||||
It's a required field. Default value is 1 and 0 is not allowed.
|
||||
format: int32
|
||||
minimum: 1
|
||||
type: integer
|
||||
minDomains:
|
||||
description: |-
|
||||
MinDomains indicates a minimum number of eligible domains.
|
||||
When the number of eligible domains with matching topology keys is less than minDomains,
|
||||
Pod Topology Spread treats "global minimum" as 0, and then the calculation of Skew is performed.
|
||||
And when the number of eligible domains with matching topology keys equals or greater than minDomains,
|
||||
this value has no effect on scheduling.
|
||||
As a result, when the number of eligible domains is less than minDomains,
|
||||
scheduler won't schedule more than maxSkew Pods to those domains.
|
||||
If value is nil, the constraint behaves as if MinDomains is equal to 1.
|
||||
Valid values are integers greater than 0.
|
||||
When value is not nil, WhenUnsatisfiable must be DoNotSchedule.
|
||||
|
||||
For example, in a 3-zone cluster, MaxSkew is set to 2, MinDomains is set to 5 and pods with the same
|
||||
labelSelector spread as 2/2/2:
|
||||
| zone1 | zone2 | zone3 |
|
||||
| P P | P P | P P |
|
||||
The number of domains is less than 5(MinDomains), so "global minimum" is treated as 0.
|
||||
In this situation, new pod with the same labelSelector cannot be scheduled,
|
||||
because computed skew will be 3(3 - 0) if new Pod is scheduled to any of the three zones,
|
||||
it will violate MaxSkew.
|
||||
format: int32
|
||||
type: integer
|
||||
nodeAffinityPolicy:
|
||||
description: |-
|
||||
NodeAffinityPolicy indicates how we will treat Pod's nodeAffinity/nodeSelector
|
||||
when calculating pod topology spread skew. Options are:
|
||||
- Honor: only nodes matching nodeAffinity/nodeSelector are included in the calculations.
|
||||
- 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.
|
||||
type: string
|
||||
nodeTaintsPolicy:
|
||||
description: |-
|
||||
NodeTaintsPolicy indicates how we will treat node taints when calculating
|
||||
pod topology spread skew. Options are:
|
||||
- Honor: nodes without taints, along with tainted nodes for which the incoming pod
|
||||
has a toleration, are included.
|
||||
- Ignore: node taints are ignored. All nodes are included.
|
||||
|
||||
If this value is nil, the behavior is equivalent to the Ignore policy.
|
||||
type: string
|
||||
topologyKey:
|
||||
description: |-
|
||||
TopologyKey is the key of node labels. Nodes that have a label with this key
|
||||
and identical values are considered to be in the same topology.
|
||||
We consider each <key, value> as a "bucket", and try to put balanced number
|
||||
of pods into each bucket.
|
||||
We define a domain as a particular instance of a topology.
|
||||
Also, we define an eligible domain as a domain whose nodes meet the requirements of
|
||||
nodeAffinityPolicy and nodeTaintsPolicy.
|
||||
e.g. If TopologyKey is "kubernetes.io/hostname", each Node is a domain of that topology.
|
||||
And, if TopologyKey is "topology.kubernetes.io/zone", each zone is a domain of that topology.
|
||||
It's a required field.
|
||||
type: string
|
||||
whenUnsatisfiable:
|
||||
description: |-
|
||||
WhenUnsatisfiable indicates how to deal with a pod if it doesn't satisfy
|
||||
the spread constraint.
|
||||
- DoNotSchedule (default) tells the scheduler not to schedule it.
|
||||
- ScheduleAnyway tells the scheduler to schedule the pod in any location,
|
||||
but giving higher precedence to topologies that would help reduce the
|
||||
skew.
|
||||
A constraint is considered "Unsatisfiable" for an incoming pod
|
||||
if and only if every possible node assignment for that pod would violate
|
||||
"MaxSkew" on some topology.
|
||||
For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same
|
||||
labelSelector spread as 3/1/1:
|
||||
| zone1 | zone2 | zone3 |
|
||||
| P P P | P | P |
|
||||
If WhenUnsatisfiable is set to DoNotSchedule, incoming pod can only be scheduled
|
||||
to zone2(zone3) to become 3/2/1(3/1/2) as ActualSkew(2-1) on zone2(zone3) satisfies
|
||||
MaxSkew(1). In other words, the cluster can still be imbalanced, but scheduler
|
||||
won't make it *more* imbalanced.
|
||||
It's a required field.
|
||||
type: string
|
||||
enum:
|
||||
- DoNotSchedule
|
||||
- ScheduleAnyway
|
||||
required:
|
||||
- maxSkew
|
||||
- topologyKey
|
||||
- whenUnsatisfiable
|
||||
type: object
|
||||
type: array
|
||||
useLoadBalancer:
|
||||
description: |-
|
||||
deprecated load balancer settings maintained for backward compatibility
|
||||
|
|
|
|||
|
|
@ -1,70 +1,84 @@
|
|||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: postgresteams.acid.zalan.do
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.17.3
|
||||
labels:
|
||||
app.kubernetes.io/name: postgres-operator
|
||||
name: postgresteams.acid.zalan.do
|
||||
spec:
|
||||
group: acid.zalan.do
|
||||
names:
|
||||
categories:
|
||||
- all
|
||||
kind: PostgresTeam
|
||||
listKind: PostgresTeamList
|
||||
plural: postgresteams
|
||||
singular: postgresteam
|
||||
shortNames:
|
||||
- pgteam
|
||||
categories:
|
||||
- all
|
||||
singular: postgresteam
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: PostgresTeam defines Custom Resource Definition Object for team
|
||||
management.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
type: string
|
||||
kind:
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: PostgresTeamSpec defines the specification for the PostgresTeam
|
||||
TPR.
|
||||
properties:
|
||||
additionalMembers:
|
||||
additionalProperties:
|
||||
description: List of users who will also be added to the Postgres
|
||||
cluster.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
description: Map for teamId and associated additional users
|
||||
type: object
|
||||
additionalSuperuserTeams:
|
||||
additionalProperties:
|
||||
description: List of teams to become Postgres superusers
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
description: Map for teamId and associated additional superuser teams
|
||||
type: object
|
||||
additionalTeams:
|
||||
additionalProperties:
|
||||
description: List of teams whose members will also be added to the
|
||||
Postgres cluster.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
description: Map for teamId and associated additional teams
|
||||
type: object
|
||||
type: object
|
||||
required:
|
||||
- metadata
|
||||
- spec
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required:
|
||||
- kind
|
||||
- apiVersion
|
||||
- spec
|
||||
properties:
|
||||
kind:
|
||||
type: string
|
||||
enum:
|
||||
- PostgresTeam
|
||||
apiVersion:
|
||||
type: string
|
||||
enum:
|
||||
- acid.zalan.do/v1
|
||||
spec:
|
||||
type: object
|
||||
properties:
|
||||
additionalSuperuserTeams:
|
||||
type: object
|
||||
description: "Map for teamId and associated additional superuser teams"
|
||||
additionalProperties:
|
||||
type: array
|
||||
nullable: true
|
||||
description: "List of teams to become Postgres superusers"
|
||||
items:
|
||||
type: string
|
||||
additionalTeams:
|
||||
type: object
|
||||
description: "Map for teamId and associated additional teams"
|
||||
additionalProperties:
|
||||
type: array
|
||||
nullable: true
|
||||
description: "List of teams whose members will also be added to the Postgres cluster"
|
||||
items:
|
||||
type: string
|
||||
additionalMembers:
|
||||
type: object
|
||||
description: "Map for teamId and associated additional users"
|
||||
additionalProperties:
|
||||
type: array
|
||||
nullable: true
|
||||
description: "List of users who will also be added to the Postgres cluster"
|
||||
items:
|
||||
type: string
|
||||
|
|
|
|||
|
|
@ -231,6 +231,19 @@ configKubernetes:
|
|||
# whether the Spilo container should run with additional permissions other than parent.
|
||||
# required by cron which needs setuid
|
||||
spilo_allow_privilege_escalation: true
|
||||
|
||||
# liveness probe for the spilo pod
|
||||
# liveness_probe:
|
||||
# httpGet:
|
||||
# scheme: HTTP
|
||||
# path: /liveness
|
||||
# port: 8008
|
||||
# initialDelaySeconds: 10
|
||||
# periodSeconds: 10
|
||||
# timeoutSeconds: 5
|
||||
# successThreshold: 1
|
||||
# failureThreshold: 3
|
||||
|
||||
# storage resize strategy, available options are: ebs, pvc, off or mixed
|
||||
storage_resize_mode: pvc
|
||||
# pod toleration assigned to instances of every Postgres cluster
|
||||
|
|
@ -402,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:
|
||||
|
|
@ -499,7 +518,6 @@ podPriorityClassName:
|
|||
|
||||
resources:
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 500Mi
|
||||
requests:
|
||||
cpu: 100m
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -928,6 +928,41 @@ For the `external-dns.alpha.kubernetes.io/hostname` annotation the `-pooler`
|
|||
suffix will be appended to the cluster name used in the template which is
|
||||
defined in `master|replica_dns_name_format`.
|
||||
|
||||
## Node Ports
|
||||
|
||||
Alternatively to Load Balancers Node Ports can be used. Kubernetes services with type
|
||||
`NodePort` redirect traffic from a specified port on your kubernetes nodes to your service.
|
||||
To expose your services to an external network with NodePorts you can set `enableMasterNodePort` and/or `enableReplicaNodePort` to `true`
|
||||
in your cluster manifest. In the case any of these variables are omitted from the manifest, the operator configuration settings `enable_master_node_port` and `enable_replica_node_port` apply.
|
||||
Note that the operator settings affect all Postgresql services running in all namespaces watched
|
||||
by the operator.
|
||||
|
||||
**Enabling a NodePort configuration will override the corresponding LoadBalancer configuration.**
|
||||
|
||||
There are multiple options to specify service annotations that will be merged
|
||||
with each other and override in the following order (where latter take
|
||||
precedence):
|
||||
|
||||
1. Globally configured `custom_service_annotations`
|
||||
2. `serviceAnnotations` specified in the cluster manifest
|
||||
3. `masterServiceAnnotations` and `replicaServiceAnnotations` specified in the cluster manifest
|
||||
|
||||
Load-Balancer specific annotations are not applied.
|
||||
|
||||
Node port services can also be configured for the [connection pooler](user.md#connection-pooler) pods
|
||||
with the manifest flags `enableMasterPoolerNodePort` and/or `enableReplicaPoolerNodePort` or in the operator configuration with `enable_master_pooler_node_port`
|
||||
and/or `enable_replica_pooler_node_port`.
|
||||
|
||||
To configure which ports Kubernetes should use for your NodePort service you can configure ports in your cluster manifest
|
||||
for each type:
|
||||
|
||||
- masterNodePort
|
||||
- masterPoolerNodePort
|
||||
- replicaNodePort
|
||||
- replicaPoolerNodePort
|
||||
|
||||
When not defined or set to 0 kubernetes will choose a port for you from [your kubernetes cluster's configured range](https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport).
|
||||
|
||||
## Running periodic 'autorepair' scans of K8s objects
|
||||
|
||||
The Postgres Operator periodically scans all K8s objects belonging to each
|
||||
|
|
|
|||
|
|
@ -27,20 +27,11 @@ git clone https://github.com/zalando/postgres-operator.git
|
|||
|
||||
## Building the operator
|
||||
|
||||
We use [Go Modules](https://github.com/golang/go/wiki/Modules) for handling
|
||||
dependencies. When using Go below v1.13 you need to explicitly enable Go modules
|
||||
by setting the `GO111MODULE` environment variable to `on`. The make targets do
|
||||
this for you, so simply run
|
||||
We use [Go Modules](https://github.com/golang/go/wiki/Modules) for handling dependencies.
|
||||
Run `go mod vendor && go mod tidy` to install them.
|
||||
|
||||
```bash
|
||||
make
|
||||
```
|
||||
|
||||
Build the operator with the `make docker` command. You may define the TAG
|
||||
variable to assign an explicit tag to your Docker image and the IMAGE to set
|
||||
the image name. By default, the tag is computed with
|
||||
`git describe --tags --always --dirty` and the image is
|
||||
`ghcr.io/zalando/postgres-operator`
|
||||
Build the operator with the `make docker` command. You may define the TAG variable to assign an explicit tag to your Docker image and the IMAGE to set the image name. By default, the tag is computed with
|
||||
`git describe --tags --always --dirty` and the image is `ghcr.io/zalando/postgres-operator`. On macos search and replace `sed -i` commands with `sed -i ''` for the make commands to work.
|
||||
|
||||
```bash
|
||||
export TAG=$(git describe --tags --always --dirty)
|
||||
|
|
@ -296,8 +287,7 @@ Please run flake8 [before submitting a PR](http://flake8.pycqa.org/en/latest/use
|
|||
In the case you want to add functionality to the operator that shall be
|
||||
controlled via the operator configuration there are a few places that need to
|
||||
be updated. As explained [here](reference/operator_parameters.md), it's possible
|
||||
to configure the operator either with a ConfigMap or CRD, but currently we aim
|
||||
to synchronize parameters everywhere.
|
||||
to configure the operator either with a ConfigMap or CRD.
|
||||
|
||||
When choosing a parameter name for a new option in a Postgres cluster manifest,
|
||||
keep in mind the naming conventions there. We use `camelCase` for manifest
|
||||
|
|
@ -320,32 +310,28 @@ manifest files:
|
|||
|
||||
Postgres manifest parameters are defined in the [api package](https://github.com/zalando/postgres-operator/blob/master/pkg/apis/acid.zalan.do/v1/postgresql_type.go).
|
||||
The operator behavior has to be implemented at least in [k8sres.go](https://github.com/zalando/postgres-operator/blob/master/pkg/cluster/k8sres.go).
|
||||
Validation of CRD parameters is controlled in [crds.go](https://github.com/zalando/postgres-operator/blob/master/pkg/apis/acid.zalan.do/v1/crds.go).
|
||||
Please, reflect your changes in tests, for example in:
|
||||
|
||||
* [config_test.go](https://github.com/zalando/postgres-operator/blob/master/pkg/util/config/config_test.go)
|
||||
* [k8sres_test.go](https://github.com/zalando/postgres-operator/blob/master/pkg/cluster/k8sres_test.go)
|
||||
* [util_test.go](https://github.com/zalando/postgres-operator/blob/master/pkg/apis/acid.zalan.do/v1/util_test.go)
|
||||
|
||||
### Updating manifest files
|
||||
### Generating the CRDs
|
||||
|
||||
For the CRD-based configuration, please update the following files:
|
||||
|
||||
* the default [OperatorConfiguration](https://github.com/zalando/postgres-operator/blob/master/manifests/postgresql-operator-default-configuration.yaml)
|
||||
* the CRD's [validation](https://github.com/zalando/postgres-operator/blob/master/manifests/operatorconfiguration.crd.yaml)
|
||||
* the CRD's validation in the [Helm chart](https://github.com/zalando/postgres-operator/blob/master/charts/postgres-operator/crds/operatorconfigurations.yaml)
|
||||
|
||||
Add new options also to the Helm chart's [values file](https://github.com/zalando/postgres-operator/blob/master/charts/postgres-operator/values.yaml) file.
|
||||
It follows the OperatorConfiguration CRD layout. Nested values will be flattened for the ConfigMap.
|
||||
Last but no least, update the [ConfigMap](https://github.com/zalando/postgres-operator/blob/master/manifests/configmap.yaml) manifest example as well.
|
||||
The CRDs can be automatically generated from the go structs. Use the correct kubebuilder annotations for defining the validation, constraints or default values etc.. Run `make` to update the CRDs which are stored in three locations:
|
||||
- In the Go api package
|
||||
- The example manifests folder
|
||||
- The helm chart folder
|
||||
|
||||
### Updating documentation
|
||||
|
||||
Finally, add a section for each new configuration option and/or cluster manifest
|
||||
Config changes need to be reflected in the Helm chart's [values file](https://github.com/zalando/postgres-operator/blob/master/charts/postgres-operator/values.yaml), too. It follows the OperatorConfiguration CRD layout. Nested values will be flattened for the ConfigMap.
|
||||
|
||||
Add a section for each new configuration option and/or cluster manifest
|
||||
parameter in the reference documents:
|
||||
|
||||
* [config reference](reference/operator_parameters.md)
|
||||
* [manifest reference](reference/cluster_manifest.md)
|
||||
|
||||
It also helps users to explain new features with examples in the
|
||||
[administrator docs](administrator.md).
|
||||
It can also help other K8s admins to explain new features with examples in the
|
||||
[administrator docs](administrator.md) and also update the [OperatorConfiguration CRD](https://github.com/zalando/postgres-operator/blob/master/manifests/postgresql-operator-default-configuration.yaml) and [ConfigMap](https://github.com/zalando/postgres-operator/blob/master/manifests/configmap.yaml) manifest examples.
|
||||
|
|
|
|||
|
|
@ -85,6 +85,10 @@ These parameters are grouped directly under the `spec` key in the manifest.
|
|||
requires a custom Spilo image. Note the FSGroup of a Pod cannot be changed
|
||||
without recreating a new Pod. Optional.
|
||||
|
||||
* **livenessProbe**
|
||||
Allows for adding a liveness probe to the Spilo container to detect if it's
|
||||
running properly.
|
||||
|
||||
* **enableMasterLoadBalancer**
|
||||
boolean flag to override the operator defaults (set by the
|
||||
`enable_master_load_balancer` parameter) to define whether to enable the load
|
||||
|
|
@ -109,10 +113,53 @@ These parameters are grouped directly under the `spec` key in the manifest.
|
|||
|
||||
* **allowedSourceRanges**
|
||||
when one or more load balancers are enabled for the cluster, this parameter
|
||||
defines the comma-separated range of IP networks (in CIDR-notation). The
|
||||
corresponding load balancer is accessible only to the networks defined by
|
||||
this parameter. Optional, when empty the load balancer service becomes
|
||||
inaccessible from outside of the Kubernetes cluster.
|
||||
defines the comma-separated range of IP networks (in CIDR-notation). Both
|
||||
IPv4 (e.g. `192.168.1.0/24`) and IPv6 (e.g. `fd01::/48`) CIDR ranges are
|
||||
supported. The corresponding load balancer is accessible only to the networks
|
||||
defined by this parameter. Optional, when empty the load balancer service
|
||||
becomes inaccessible from outside of the Kubernetes cluster.
|
||||
|
||||
* **enableMasterNodePort**
|
||||
boolean flag to override the operator defaults (set by the
|
||||
`enable_master_node_port` parameter) to define whether to enable the node
|
||||
port pointing to the Postgres primary. Optional. Overrides `enableMasterLoadBalancer`.
|
||||
|
||||
* **enableMasterPoolerNodePort**
|
||||
boolean flag to override the operator defaults (set by the
|
||||
`enable_master_pooler_node_port` parameter) to define whether to enable
|
||||
the node port for master pooler pods pointing to the Postgres primary.
|
||||
Optional. Overrides `enableMasterPoolerLoadBalancer`.
|
||||
|
||||
* **enableReplicaNodePort**
|
||||
boolean flag to override the operator defaults (set by the
|
||||
`enable_replica_node_port` parameter) to define whether to enable the node
|
||||
port pointing to the Postgres standby instances. Optional. Overrides `enableReplicaLoadBalancer`.
|
||||
|
||||
* **enableReplicaPoolerNodePort**
|
||||
boolean flag to override the operator defaults (set by the
|
||||
`enable_replica_pooler_node_port` parameter) to define whether to enable
|
||||
the node port for replica pooler pods pointing to the Postgres standby
|
||||
instances. Optional. Overrides `enableReplicaPoolerLoadBalancer`.
|
||||
|
||||
* **masterNodePort**
|
||||
integer flag to specify a port number for the node port to the Postgres primary.
|
||||
Only used when `enableMasterNodePort` or `enable_master_node_port` are enabled.
|
||||
Optional. Kubernetes will provide a port number for you if not specified.
|
||||
|
||||
* **masterPoolerNodePort**
|
||||
integer flag to specify a port number for the node port for the master pooler pods pointing to the Postgres primary.
|
||||
Only used when `enableMasterPoolerNodePort` or `enable_master_pooler_node_port` are enabled.
|
||||
Optional. Kubernetes will provide a port number for you if not specified.
|
||||
|
||||
* **replicaNodePort**
|
||||
integer flag to specify a port number for the node port pointing to the Postgres standby instances.
|
||||
Only used when `enableReplicaNodePort` or `enable_replica_node_port` are enabled.
|
||||
Optional. Kubernetes will provide a port number for you if not specified.
|
||||
|
||||
* **replicaPoolerNodePort**
|
||||
integer flag to specify a port number for the node port for the replica pooler pods pointing to the Postgres standby instances
|
||||
Only used when `enableReplicaPoolerNodePort` or `enable_replica_pooler_node_port` are enabled.
|
||||
Optional. Kubernetes will provide a port number for you if not specified.
|
||||
|
||||
* **maintenanceWindows**
|
||||
a list which defines specific time frames when certain maintenance operations
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
@ -334,6 +329,10 @@ configuration they are grouped under the `kubernetes` key.
|
|||
Postgres pods are [terminated forcefully](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-termination)
|
||||
after this timeout. The default is `5m`.
|
||||
|
||||
* **liveness_probe**
|
||||
Allows for adding a liveness probe to the Spilo container to detect if it's
|
||||
running properly. Cannot be configured via ConfigMap. Default is empty.
|
||||
|
||||
* **custom_pod_annotations**
|
||||
This key/value map provides a list of annotations that get attached to each pod
|
||||
of a database created by the operator. If the annotation key is also provided
|
||||
|
|
@ -900,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:
|
||||
|
|
|
|||
|
|
@ -96,10 +96,7 @@ psql -U postgres -h localhost -p 6432
|
|||
|
||||
## Password encryption
|
||||
|
||||
Passwords are encrypted with `md5` hash generation by default. However, it is
|
||||
possible to use the more recent `scram-sha-256` method by changing the
|
||||
`password_encryption` parameter in the Postgres config. You can define it
|
||||
directly from the cluster manifest:
|
||||
Passwords are encrypted using the `scram-sha-256` hashing method by default. Other methods can be configured by changing the `password_encryption` parameter in the cluster manifest:
|
||||
|
||||
```yaml
|
||||
apiVersion: "acid.zalan.do/v1"
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -243,7 +243,7 @@ class K8s:
|
|||
time.sleep(self.RETRY_TIMEOUT_SEC)
|
||||
|
||||
def get_logical_backup_job(self, namespace='default'):
|
||||
return self.api.batch_v1.list_namespaced_cron_job(namespace, label_selector="application=spilo")
|
||||
return self.api.batch_v1.list_namespaced_cron_job(namespace, label_selector="application=spilo-logical-backup")
|
||||
|
||||
def wait_for_logical_backup_job(self, expected_num_of_jobs):
|
||||
while (len(self.get_logical_backup_job().items) != expected_num_of_jobs):
|
||||
|
|
|
|||
100
go.mod
100
go.mod
|
|
@ -1,83 +1,93 @@
|
|||
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.11.2
|
||||
github.com/lib/pq v1.12.3
|
||||
github.com/motomux/pretty v0.0.0-20161209205251-b2aad2c9a95d
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/r3labs/diff v1.1.0
|
||||
github.com/sirupsen/logrus v1.9.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 (
|
||||
|
|
|
|||
223
go.sum
223
go.sum
|
|
@ -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=
|
||||
|
|
@ -71,8 +82,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lib/pq v1.11.2 h1:x6gxUeu39V0BHZiugWe8LXZYZ+Utk7hSJGThs8sdzfs=
|
||||
github.com/lib/pq v1.11.2/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
|
||||
github.com/lib/pq v1.12.3 h1:tTWxr2YLKwIvK90ZXEw8GP7UFHtcbTtty8zsI+YjrfQ=
|
||||
github.com/lib/pq v1.12.3/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
|
|
@ -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=
|
||||
|
|
|
|||
|
|
@ -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}"
|
||||
|
|
|
|||
|
|
@ -1,137 +0,0 @@
|
|||
# Kubectl Plugin for Zalando's Postgres Operator
|
||||
|
||||
This plugin is a prototype developed as a part of **Google Summer of Code 2019** under the [Postgres Operator](https://summerofcode.withgoogle.com/archive/2019/organizations/6187982082539520/) organization.
|
||||
|
||||
## Installation of kubectl pg plugin
|
||||
|
||||
This project uses Go Modules for dependency management to build locally.
|
||||
Install go and enable go modules with ```export GO111MODULE=on```.
|
||||
From Go >=1.13 Go modules will be enabled by default.
|
||||
|
||||
```bash
|
||||
# Assumes you have a working KUBECONFIG
|
||||
$ GO111MODULE="on"
|
||||
$ GOPATH/src/github.com/zalando/postgres-operator/kubectl-pg go mod vendor
|
||||
# This generate a vendor directory with all dependencies needed by the plugin.
|
||||
$ $GOPATH/src/github.com/zalando/postgres-operator/kubectl-pg go install
|
||||
# This will place the kubectl-pg binary in your $GOPATH/bin
|
||||
```
|
||||
|
||||
## Before using the kubectl pg plugin make sure to set KUBECONFIG env variable
|
||||
|
||||
Ideally KUBECONFIG is found in $HOME/.kube/config else specify the KUBECONFIG path here.
|
||||
```export KUBECONFIG=$HOME/.kube/config```
|
||||
|
||||
## List all commands available in kubectl pg
|
||||
|
||||
```kubectl pg --help``` (or) ```kubectl pg```
|
||||
|
||||
## Check if Postgres Operator is installed and running
|
||||
|
||||
```kubectl pg check```
|
||||
|
||||
## Create a new cluster using manifest file
|
||||
|
||||
```kubectl pg create -f acid-minimal-cluster.yaml```
|
||||
|
||||
## List postgres resources
|
||||
|
||||
```kubectl pg list```
|
||||
|
||||
List clusters across namespaces
|
||||
```kubectl pg list all```
|
||||
|
||||
## Update existing cluster using manifest file
|
||||
|
||||
```kubectl pg update -f acid-minimal-cluster.yaml```
|
||||
|
||||
## Delete existing cluster
|
||||
|
||||
Using the manifest file:
|
||||
```kubectl pg delete -f acid-minimal-cluster.yaml```
|
||||
|
||||
Or by specifying the cluster name:
|
||||
```kubectl pg delete acid-minimal-cluster```
|
||||
|
||||
Use `--namespace` or `-n` flag if your cluster is in a different namespace to where your current context is pointing to:
|
||||
```kubectl pg delete acid-minimal-cluster -n namespace01```
|
||||
|
||||
## Adding manifest roles to an existing cluster
|
||||
|
||||
```kubectl pg add-user USER01 -p CREATEDB,LOGIN -c acid-minimal-cluster```
|
||||
|
||||
Privileges can only be [SUPERUSER, REPLICATION, INHERIT, LOGIN, NOLOGIN, CREATEROLE, CREATEDB, BYPASSRLS]
|
||||
Note: By default, a LOGIN user is created (unless NOLOGIN is specified).
|
||||
|
||||
## Adding databases to an existing cluster
|
||||
|
||||
You have to specify an owner of the new database and this role must already exist in the cluster:
|
||||
```kubectl pg add-db DB01 -o OWNER01 -c acid-minimal-cluster```
|
||||
|
||||
## Extend the volume of an existing pg cluster
|
||||
|
||||
```kubectl pg ext-volume 2Gi -c acid-minimal-cluster```
|
||||
|
||||
## Print the version of Postgres Operator and kubectl pg plugin
|
||||
|
||||
```kubectl pg version```
|
||||
|
||||
## Connect to the shell of a postgres pod
|
||||
|
||||
Connect to the master pod:
|
||||
```kubectl pg connect -c CLUSTER -m```
|
||||
|
||||
Connect to a random replica pod:
|
||||
```kubectl pg connect -c CLUSTER```
|
||||
|
||||
Connect to a certain replica pod:
|
||||
```kubectl pg connect -c CLUSTER -r 0```
|
||||
|
||||
## Connect to a database via psql
|
||||
|
||||
Adding the `-p` flag allows you to directly connect to a given database with the psql client.
|
||||
With `-u` you specify the user. If left out the name of the current OS user is taken.
|
||||
`-d` lets you specify the database. If no database is specified, it will be the same as the user name.
|
||||
|
||||
Connect to `app_db` database on the master with role `app_user`:
|
||||
```kubectl pg connect -c CLUSTER -m -p -u app_user -d app_db```
|
||||
|
||||
Connect to the `postgres` database on a random replica with role `postgres`:
|
||||
```kubectl pg connect -c CLUSTER -p -u postgres```
|
||||
|
||||
Connect to a certain replica assuming name of OS user, database role and name are all the same:
|
||||
```kubectl pg connect -c CLUSTER -r 0 -p```
|
||||
|
||||
|
||||
## Access Postgres Operator logs
|
||||
|
||||
```kubectl pg logs -o```
|
||||
|
||||
## Access Patroni logs of different database pods
|
||||
|
||||
Fetch logs of master:
|
||||
```kubectl pg logs -c CLUSTER -m```
|
||||
|
||||
Fetch logs of a random replica pod:
|
||||
```kubectl pg logs -c CLUSTER```
|
||||
|
||||
Fetch logs of specified replica
|
||||
```kubectl pg logs -c CLUSTER -r 2```
|
||||
|
||||
## Development
|
||||
|
||||
When making changes to the plugin make sure to change the (major/patch) version of plugin in `build.sh` script and run `./build.sh`.
|
||||
|
||||
## Google Summer of Code 2019
|
||||
|
||||
### GSoC Proposal
|
||||
|
||||
[kubectl pg proposal](https://docs.google.com/document/d/1-WMy9HkfZ1XnnMbzplMe9rCzKrRMGaMz4owLVXXPb7w/edit)
|
||||
|
||||
### Weekly Reports
|
||||
|
||||
https://github.com/VineethReddy02/GSoC-Kubectl-Plugin-for-Postgres-Operator-tracker
|
||||
|
||||
### Final Project Report
|
||||
|
||||
https://gist.github.com/VineethReddy02/159283bd368a710379eaf0f6bd60a40a
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
|
||||
VERSION=1.0
|
||||
sed -i "s/KubectlPgVersion string = \"[^\"]*\"/KubectlPgVersion string = \"${VERSION}\"/" cmd/version.go
|
||||
go install
|
||||
|
|
@ -1,114 +0,0 @@
|
|||
/*
|
||||
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
PostgresqlLister "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
// addDbCmd represents the addDb command
|
||||
var addDbCmd = &cobra.Command{
|
||||
Use: "add-db",
|
||||
Short: "Adds a DB and its owner to a Postgres cluster. The owner role is created if it does not exist",
|
||||
Long: `Adds a new DB to the Postgres cluster. Owner needs to be specified by the -o flag, cluster with -c flag.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(args) > 0 {
|
||||
dbName := args[0]
|
||||
dbOwner, _ := cmd.Flags().GetString("owner")
|
||||
clusterName, _ := cmd.Flags().GetString("cluster")
|
||||
addDb(dbName, dbOwner, clusterName)
|
||||
} else {
|
||||
fmt.Println("database name can't be empty. Use kubectl pg add-db [-h | --help] for more info")
|
||||
}
|
||||
|
||||
},
|
||||
Example: `
|
||||
kubectl pg add-db db01 -o owner01 -c cluster01
|
||||
`,
|
||||
}
|
||||
|
||||
// add db and it's owner to the cluster
|
||||
func addDb(dbName string, dbOwner string, clusterName string) {
|
||||
config := getConfig()
|
||||
postgresConfig, err := PostgresqlLister.NewForConfig(config)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
namespace := getCurrentNamespace()
|
||||
postgresql, err := postgresConfig.Postgresqls(namespace).Get(context.TODO(), clusterName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var dbOwnerExists bool
|
||||
dbUsers := postgresql.Spec.Users
|
||||
for key := range dbUsers {
|
||||
if key == dbOwner {
|
||||
dbOwnerExists = true
|
||||
}
|
||||
}
|
||||
var patch []byte
|
||||
// validating reserved DB names
|
||||
if dbOwnerExists && dbName != "postgres" && dbName != "template0" && dbName != "template1" {
|
||||
patch = dbPatch(dbName, dbOwner)
|
||||
} else if !dbOwnerExists {
|
||||
log.Fatal("The provided db-owner doesn't exist")
|
||||
} else {
|
||||
log.Fatal("The provided db-name is reserved by postgres")
|
||||
}
|
||||
|
||||
updatedPostgres, err := postgresConfig.Postgresqls(namespace).Patch(context.TODO(), postgresql.Name, types.MergePatchType, patch, metav1.PatchOptions{})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if updatedPostgres.ResourceVersion != postgresql.ResourceVersion {
|
||||
fmt.Printf("Created new database %s with owner %s in PostgreSQL cluster %s.\n", dbName, dbOwner, updatedPostgres.Name)
|
||||
} else {
|
||||
fmt.Printf("postgresql %s is unchanged.\n", updatedPostgres.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func dbPatch(dbname string, owner string) []byte {
|
||||
ins := map[string]map[string]map[string]string{"spec": {"databases": {dbname: owner}}}
|
||||
patchInstances, err := json.Marshal(ins)
|
||||
if err != nil {
|
||||
log.Fatal(err, "unable to parse patch for add-db")
|
||||
}
|
||||
return patchInstances
|
||||
}
|
||||
|
||||
func init() {
|
||||
addDbCmd.Flags().StringP("owner", "o", "", "provide owner of the database.")
|
||||
addDbCmd.Flags().StringP("cluster", "c", "", "provide a postgres cluster name.")
|
||||
rootCmd.AddCommand(addDbCmd)
|
||||
}
|
||||
|
|
@ -1,144 +0,0 @@
|
|||
/*
|
||||
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
PostgresqlLister "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
var allowedPrivileges = []string{"SUPERUSER", "REPLICATION", "INHERIT", "LOGIN", "NOLOGIN", "CREATEROLE", "CREATEDB", "BYPASSRLS"}
|
||||
|
||||
// addUserCmd represents the addUser command
|
||||
var addUserCmd = &cobra.Command{
|
||||
Use: "add-user",
|
||||
Short: "Adds a user to the postgres cluster with given privileges",
|
||||
Long: `Adds a user to the postgres cluster. You can add privileges as well with -p flag.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
clusterName, _ := cmd.Flags().GetString("cluster")
|
||||
privileges, _ := cmd.Flags().GetString("privileges")
|
||||
|
||||
if len(args) > 0 {
|
||||
user := args[0]
|
||||
var permissions []string
|
||||
var perms []string
|
||||
|
||||
if privileges != "" {
|
||||
parsedRoles := strings.Replace(privileges, ",", " ", -1)
|
||||
parsedRoles = strings.ToUpper(parsedRoles)
|
||||
permissions = strings.Fields(parsedRoles)
|
||||
var invalidPerms []string
|
||||
|
||||
for _, userPrivilege := range permissions {
|
||||
validPerm := false
|
||||
for _, privilege := range allowedPrivileges {
|
||||
if privilege == userPrivilege {
|
||||
perms = append(perms, userPrivilege)
|
||||
validPerm = true
|
||||
}
|
||||
}
|
||||
if !validPerm {
|
||||
invalidPerms = append(invalidPerms, userPrivilege)
|
||||
}
|
||||
}
|
||||
|
||||
if len(invalidPerms) > 0 {
|
||||
fmt.Printf("Invalid privileges %s\n", invalidPerms)
|
||||
return
|
||||
}
|
||||
}
|
||||
addUser(user, clusterName, perms)
|
||||
}
|
||||
},
|
||||
Example: `
|
||||
kubectl pg add-user user01 -p login,createdb -c cluster01
|
||||
`,
|
||||
}
|
||||
|
||||
// add user to the cluster with provided permissions
|
||||
func addUser(user string, clusterName string, permissions []string) {
|
||||
config := getConfig()
|
||||
postgresConfig, err := PostgresqlLister.NewForConfig(config)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
namespace := getCurrentNamespace()
|
||||
postgresql, err := postgresConfig.Postgresqls(namespace).Get(context.TODO(), clusterName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
setUsers := make(map[string]bool)
|
||||
for _, k := range permissions {
|
||||
setUsers[k] = true
|
||||
}
|
||||
|
||||
if existingRoles, key := postgresql.Spec.Users[user]; key {
|
||||
for _, k := range existingRoles {
|
||||
setUsers[k] = true
|
||||
}
|
||||
}
|
||||
|
||||
Privileges := []string{}
|
||||
for keys, values := range setUsers {
|
||||
if values {
|
||||
Privileges = append(Privileges, keys)
|
||||
}
|
||||
}
|
||||
|
||||
patch := applyUserPatch(user, Privileges)
|
||||
updatedPostgresql, err := postgresConfig.Postgresqls(namespace).Patch(context.TODO(), postgresql.Name, types.MergePatchType, patch, metav1.PatchOptions{})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if updatedPostgresql.ResourceVersion != postgresql.ResourceVersion {
|
||||
fmt.Printf("postgresql %s is updated with new user %s and with privileges %s.\n", updatedPostgresql.Name, user, permissions)
|
||||
} else {
|
||||
fmt.Printf("postgresql %s is unchanged.\n", updatedPostgresql.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func applyUserPatch(user string, value []string) []byte {
|
||||
ins := map[string]map[string]map[string][]string{"spec": {"users": {user: value}}}
|
||||
patchInstances, err := json.Marshal(ins)
|
||||
if err != nil {
|
||||
log.Fatal(err, "unable to parse number of instances json")
|
||||
}
|
||||
return patchInstances
|
||||
}
|
||||
|
||||
func init() {
|
||||
addUserCmd.Flags().StringP("cluster", "c", "", "add user to the provided cluster.")
|
||||
addUserCmd.Flags().StringP("privileges", "p", "", "add privileges separated by commas without spaces")
|
||||
rootCmd.AddCommand(addUserCmd)
|
||||
}
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
/*
|
||||
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
postgresConstants "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1"
|
||||
v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
apiextv1 "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// checkCmd represent kubectl pg check.
|
||||
var checkCmd = &cobra.Command{
|
||||
Use: "check",
|
||||
Short: "Checks the Postgres operator is installed in the k8s cluster",
|
||||
Long: `Checks that the Postgres CRD is registered in a k8s cluster.
|
||||
This means that the operator pod was able to start normally.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
check()
|
||||
},
|
||||
Example: `
|
||||
kubectl pg check
|
||||
`,
|
||||
}
|
||||
|
||||
// check validates postgresql CRD registered or not.
|
||||
func check() *v1.CustomResourceDefinition {
|
||||
config := getConfig()
|
||||
apiExtClient, err := apiextv1.NewForConfig(config)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
crdInfo, err := apiExtClient.CustomResourceDefinitions().Get(context.TODO(), postgresConstants.PostgresCRDResouceName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if crdInfo.Name == postgresConstants.PostgresCRDResouceName {
|
||||
fmt.Printf("Postgres Operator is installed in the k8s cluster.\n")
|
||||
} else {
|
||||
fmt.Printf("Postgres Operator is not installed in the k8s cluster.\n")
|
||||
}
|
||||
return crdInfo
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(checkCmd)
|
||||
}
|
||||
|
|
@ -1,144 +0,0 @@
|
|||
/*
|
||||
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"os"
|
||||
user "os/user"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/remotecommand"
|
||||
)
|
||||
|
||||
// connectCmd represents the kubectl pg connect command
|
||||
var connectCmd = &cobra.Command{
|
||||
Use: "connect",
|
||||
Short: "Connects to the shell prompt, psql prompt of postgres cluster",
|
||||
Long: `Connects to the shell prompt, psql prompt of postgres cluster and also to specified replica or master.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
clusterName, _ := cmd.Flags().GetString("cluster")
|
||||
master, _ := cmd.Flags().GetBool("master")
|
||||
replica, _ := cmd.Flags().GetString("replica")
|
||||
psql, _ := cmd.Flags().GetBool("psql")
|
||||
userName, _ := cmd.Flags().GetString("user")
|
||||
dbName, _ := cmd.Flags().GetString("database")
|
||||
|
||||
if psql {
|
||||
if userName == "" {
|
||||
userInfo, err := user.Current()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
userName = userInfo.Username
|
||||
}
|
||||
}
|
||||
if dbName == "" {
|
||||
dbName = userName
|
||||
}
|
||||
|
||||
connect(clusterName, master, replica, psql, userName, dbName)
|
||||
},
|
||||
Example: `
|
||||
#connects to the master of postgres cluster
|
||||
kubectl pg connect -c cluster -m
|
||||
|
||||
#connects to the random replica of postgres cluster
|
||||
kubectl pg connect -c cluster
|
||||
|
||||
#connects to the provided replica number of postgres cluster
|
||||
kubectl pg connect -c cluster -r 2
|
||||
|
||||
#connects to psql prompt of master for provided postgres cluster with current shell user
|
||||
kubectl pg connect -c cluster -p -m
|
||||
|
||||
#connects to psql prompt of random replica for provided postgres cluster with provided user and db
|
||||
kubectl pg connect -c cluster -p -u user01 -d db01
|
||||
`,
|
||||
}
|
||||
|
||||
func connect(clusterName string, master bool, replica string, psql bool, user string, dbName string) {
|
||||
config := getConfig()
|
||||
client, err := kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
podName := getPodName(clusterName, master, replica)
|
||||
var execRequest *rest.Request
|
||||
|
||||
if psql {
|
||||
execRequest = client.CoreV1().RESTClient().Post().Resource("pods").
|
||||
Name(podName).
|
||||
Namespace(getCurrentNamespace()).
|
||||
SubResource("exec").
|
||||
Param("container", "postgres").
|
||||
Param("command", "psql").
|
||||
Param("command", dbName).
|
||||
Param("command", user).
|
||||
Param("stdin", "true").
|
||||
Param("stdout", "true").
|
||||
Param("stderr", "true").
|
||||
Param("tty", "true")
|
||||
} else {
|
||||
execRequest = client.CoreV1().RESTClient().Post().Resource("pods").
|
||||
Name(podName).
|
||||
Namespace(getCurrentNamespace()).
|
||||
SubResource("exec").
|
||||
Param("container", "postgres").
|
||||
Param("command", "su").
|
||||
Param("command", "postgres").
|
||||
Param("stdin", "true").
|
||||
Param("stdout", "true").
|
||||
Param("stderr", "true").
|
||||
Param("tty", "true")
|
||||
}
|
||||
|
||||
exec, err := remotecommand.NewSPDYExecutor(config, "POST", execRequest.URL())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
err = exec.StreamWithContext(context.TODO(), remotecommand.StreamOptions{
|
||||
Stdin: os.Stdin,
|
||||
Stdout: os.Stdout,
|
||||
Stderr: os.Stderr,
|
||||
Tty: true,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
connectCmd.Flags().StringP("cluster", "c", "", "provide the cluster name.")
|
||||
connectCmd.Flags().BoolP("master", "m", false, "connect to master.")
|
||||
connectCmd.Flags().StringP("replica", "r", "", "connect to replica. Specify replica number.")
|
||||
connectCmd.Flags().BoolP("psql", "p", false, "connect to psql prompt.")
|
||||
connectCmd.Flags().StringP("user", "u", "", "provide user.")
|
||||
connectCmd.Flags().StringP("database", "d", "", "provide database name.")
|
||||
rootCmd.AddCommand(connectCmd)
|
||||
}
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
/*
|
||||
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
v1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1"
|
||||
PostgresqlLister "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1"
|
||||
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// createCmd kubectl pg create.
|
||||
var createCmd = &cobra.Command{
|
||||
Use: "create",
|
||||
Short: "Creates postgres object using manifest file",
|
||||
Long: `Creates postgres custom resource objects from a manifest file.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fileName, _ := cmd.Flags().GetString("file")
|
||||
create(fileName)
|
||||
},
|
||||
Example: `
|
||||
kubectl pg create -f cluster-manifest.yaml
|
||||
`,
|
||||
}
|
||||
|
||||
// Create postgresql resources.
|
||||
func create(fileName string) {
|
||||
config := getConfig()
|
||||
postgresConfig, err := PostgresqlLister.NewForConfig(config)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
ymlFile, err := os.ReadFile(fileName)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
decode := scheme.Codecs.UniversalDeserializer().Decode
|
||||
obj, _, err := decode([]byte(ymlFile), nil, &v1.Postgresql{})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
postgresSql := obj.(*v1.Postgresql)
|
||||
_, err = postgresConfig.Postgresqls(postgresSql.Namespace).Create(context.TODO(), postgresSql, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Printf("postgresql %s created.\n", postgresSql.Name)
|
||||
}
|
||||
|
||||
func init() {
|
||||
createCmd.Flags().StringP("file", "f", "", "manifest file with the cluster definition.")
|
||||
rootCmd.AddCommand(createCmd)
|
||||
}
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
/*
|
||||
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
v1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1"
|
||||
PostgresqlLister "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1"
|
||||
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// deleteCmd represents kubectl pg delete.
|
||||
var deleteCmd = &cobra.Command{
|
||||
Use: "delete",
|
||||
Short: "Deletes postgresql object by cluster-name/manifest file",
|
||||
Long: `Deletes the postgres objects identified by a manifest file or cluster-name.
|
||||
Deleting the manifest is sufficient to delete the cluster.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
namespace, _ := cmd.Flags().GetString("namespace")
|
||||
file, _ := cmd.Flags().GetString("file")
|
||||
|
||||
if file != "" {
|
||||
deleteByFile(file)
|
||||
} else if namespace != "" {
|
||||
if len(args) != 0 {
|
||||
clusterName := args[0]
|
||||
deleteByName(clusterName, namespace)
|
||||
} else {
|
||||
fmt.Println("cluster name can't be empty")
|
||||
}
|
||||
} else {
|
||||
fmt.Println("use the flag either -n or -f to delete a resource.")
|
||||
}
|
||||
},
|
||||
Example: `
|
||||
#Deleting the postgres cluster using manifest file
|
||||
kubectl pg delete -f cluster-manifest.yaml
|
||||
|
||||
#Deleting the postgres cluster using cluster name in current namespace.
|
||||
kubectl pg delete cluster01
|
||||
|
||||
#Deleting the postgres cluster using cluster name in provided namespace
|
||||
kubectl pg delete cluster01 -n namespace01
|
||||
`,
|
||||
}
|
||||
|
||||
func deleteByFile(file string) {
|
||||
config := getConfig()
|
||||
postgresConfig, err := PostgresqlLister.NewForConfig(config)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
ymlFile, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
decode := scheme.Codecs.UniversalDeserializer().Decode
|
||||
obj, _, err := decode([]byte(ymlFile), nil, &v1.Postgresql{})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
postgresSql := obj.(*v1.Postgresql)
|
||||
_, err = postgresConfig.Postgresqls(postgresSql.Namespace).Get(context.TODO(), postgresSql.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
fmt.Printf("Postgresql %s not found with the provided namespace %s : %s \n", postgresSql.Name, postgresSql.Namespace, err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("Are you sure you want to remove this PostgreSQL cluster? If so, please type (%s/%s) and hit Enter\n", postgresSql.Namespace, postgresSql.Name)
|
||||
|
||||
confirmAction(postgresSql.Name, postgresSql.Namespace)
|
||||
err = postgresConfig.Postgresqls(postgresSql.Namespace).Delete(context.TODO(), postgresSql.Name, metav1.DeleteOptions{})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Printf("Postgresql %s deleted from %s.\n", postgresSql.Name, postgresSql.Namespace)
|
||||
}
|
||||
|
||||
func deleteByName(clusterName string, namespace string) {
|
||||
config := getConfig()
|
||||
postgresConfig, err := PostgresqlLister.NewForConfig(config)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = postgresConfig.Postgresqls(namespace).Get(context.TODO(), clusterName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
fmt.Printf("Postgresql %s not found with the provided namespace %s : %s \n", clusterName, namespace, err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("Are you sure you want to remove this PostgreSQL cluster? If so, please type (%s/%s) and hit Enter\n", namespace, clusterName)
|
||||
|
||||
confirmAction(clusterName, namespace)
|
||||
err = postgresConfig.Postgresqls(namespace).Delete(context.TODO(), clusterName, metav1.DeleteOptions{})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Printf("Postgresql %s deleted from %s.\n", clusterName, namespace)
|
||||
}
|
||||
|
||||
func init() {
|
||||
namespace := getCurrentNamespace()
|
||||
deleteCmd.Flags().StringP("namespace", "n", namespace, "namespace of the cluster to be deleted.")
|
||||
deleteCmd.Flags().StringP("file", "f", "", "manifest file with the cluster definition.")
|
||||
rootCmd.AddCommand(deleteCmd)
|
||||
}
|
||||
|
|
@ -1,119 +0,0 @@
|
|||
/*
|
||||
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
PostgresqlLister "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
// extVolumeCmd represents the extVolume command
|
||||
var extVolumeCmd = &cobra.Command{
|
||||
Use: "ext-volume",
|
||||
Short: "Increases the volume size of a given Postgres cluster",
|
||||
Long: `Extends the volume of the postgres cluster. But volume cannot be shrinked.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
clusterName, _ := cmd.Flags().GetString("cluster")
|
||||
if len(args) > 0 {
|
||||
volume := args[0]
|
||||
extVolume(volume, clusterName)
|
||||
} else {
|
||||
fmt.Println("please enter the cluster name with -c flag & volume in desired units")
|
||||
}
|
||||
},
|
||||
Example: `
|
||||
#Extending the volume size of provided cluster
|
||||
kubectl pg ext-volume 2Gi -c cluster01
|
||||
`,
|
||||
}
|
||||
|
||||
// extend volume with provided size & cluster name
|
||||
func extVolume(increasedVolumeSize string, clusterName string) {
|
||||
config := getConfig()
|
||||
postgresConfig, err := PostgresqlLister.NewForConfig(config)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
namespace := getCurrentNamespace()
|
||||
postgresql, err := postgresConfig.Postgresqls(namespace).Get(context.TODO(), clusterName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
oldSize, err := resource.ParseQuantity(postgresql.Spec.Volume.Size)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
newSize, err := resource.ParseQuantity(increasedVolumeSize)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = strconv.Atoi(newSize.String())
|
||||
if err == nil {
|
||||
fmt.Println("provide the valid volume size with respective units i.e Ki, Mi, Gi")
|
||||
return
|
||||
}
|
||||
|
||||
if newSize.Value() > oldSize.Value() {
|
||||
patchInstances := volumePatch(newSize)
|
||||
response, err := postgresConfig.Postgresqls(namespace).Patch(context.TODO(), postgresql.Name, types.MergePatchType, patchInstances, metav1.PatchOptions{})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if postgresql.ResourceVersion != response.ResourceVersion {
|
||||
fmt.Printf("%s volume is extended to %s.\n", response.Name, increasedVolumeSize)
|
||||
} else {
|
||||
fmt.Printf("%s volume %s is unchanged.\n", response.Name, postgresql.Spec.Volume.Size)
|
||||
}
|
||||
} else if newSize.Value() == oldSize.Value() {
|
||||
fmt.Println("volume already has the desired size.")
|
||||
} else {
|
||||
fmt.Printf("volume %s size cannot be shrinked.\n", postgresql.Spec.Volume.Size)
|
||||
}
|
||||
}
|
||||
|
||||
func volumePatch(volume resource.Quantity) []byte {
|
||||
patchData := map[string]map[string]map[string]resource.Quantity{"spec": {"volume": {"size": volume}}}
|
||||
patch, err := json.Marshal(patchData)
|
||||
if err != nil {
|
||||
log.Fatal(err, "unable to parse patch to extend volume")
|
||||
}
|
||||
return patch
|
||||
}
|
||||
|
||||
func init() {
|
||||
extVolumeCmd.Flags().StringP("cluster", "c", "", "provide cluster name.")
|
||||
rootCmd.AddCommand(extVolumeCmd)
|
||||
}
|
||||
|
|
@ -1,125 +0,0 @@
|
|||
/*
|
||||
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
v1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1"
|
||||
PostgresqlLister "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
TrimCreateTimestamp = 6000000000
|
||||
)
|
||||
|
||||
// listCmd represents kubectl pg list.
|
||||
var listCmd = &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "Lists all the resources of kind postgresql",
|
||||
Long: `Lists all the info specific to postgresql objects.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
allNamespaces, _ := cmd.Flags().GetBool("all-namespaces")
|
||||
namespace, _ := cmd.Flags().GetString("namespace")
|
||||
if allNamespaces {
|
||||
list(allNamespaces, "")
|
||||
} else {
|
||||
list(allNamespaces, namespace)
|
||||
}
|
||||
|
||||
},
|
||||
Example: `
|
||||
#Lists postgres cluster in current namespace
|
||||
kubectl pg list
|
||||
|
||||
#Lists postgres clusters in all namespaces
|
||||
kubectl pg list -A
|
||||
`,
|
||||
}
|
||||
|
||||
// list command to list postgres.
|
||||
func list(allNamespaces bool, namespace string) {
|
||||
config := getConfig()
|
||||
postgresConfig, err := PostgresqlLister.NewForConfig(config)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
listPostgres, err := postgresConfig.Postgresqls(namespace).List(context.TODO(), metav1.ListOptions{})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if len(listPostgres.Items) == 0 {
|
||||
if namespace != "" {
|
||||
fmt.Printf("No Postgresql clusters found in namespace: %v\n", namespace)
|
||||
} else {
|
||||
fmt.Println("No Postgresql clusters found in all namespaces")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if allNamespaces {
|
||||
listAll(listPostgres)
|
||||
} else {
|
||||
listWithNamespace(listPostgres)
|
||||
}
|
||||
}
|
||||
|
||||
func listAll(listPostgres *v1.PostgresqlList) {
|
||||
template := "%-32s%-16s%-12s%-12s%-12s%-12s%-12s\n"
|
||||
fmt.Printf(template, "NAME", "STATUS", "INSTANCES", "VERSION", "AGE", "VOLUME", "NAMESPACE")
|
||||
for _, pgObjs := range listPostgres.Items {
|
||||
fmt.Printf(template, pgObjs.Name,
|
||||
pgObjs.Status.PostgresClusterStatus,
|
||||
strconv.Itoa(int(pgObjs.Spec.NumberOfInstances)),
|
||||
pgObjs.Spec.PostgresqlParam.PgVersion,
|
||||
time.Since(pgObjs.CreationTimestamp.Time).Truncate(TrimCreateTimestamp),
|
||||
pgObjs.Spec.Size, pgObjs.Namespace)
|
||||
}
|
||||
}
|
||||
|
||||
func listWithNamespace(listPostgres *v1.PostgresqlList) {
|
||||
template := "%-32s%-16s%-12s%-12s%-12s%-12s\n"
|
||||
fmt.Printf(template, "NAME", "STATUS", "INSTANCES", "VERSION", "AGE", "VOLUME")
|
||||
for _, pgObjs := range listPostgres.Items {
|
||||
fmt.Printf(template, pgObjs.Name,
|
||||
pgObjs.Status.PostgresClusterStatus,
|
||||
strconv.Itoa(int(pgObjs.Spec.NumberOfInstances)),
|
||||
pgObjs.Spec.PostgresqlParam.PgVersion,
|
||||
time.Since(pgObjs.CreationTimestamp.Time).Truncate(TrimCreateTimestamp),
|
||||
pgObjs.Spec.Size)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
listCmd.Flags().BoolP("all-namespaces", "A", false, "list pg resources across all namespaces.")
|
||||
listCmd.Flags().StringP("namespace", "n", getCurrentNamespace(), "provide the namespace")
|
||||
rootCmd.AddCommand(listCmd)
|
||||
}
|
||||
|
|
@ -1,143 +0,0 @@
|
|||
/*
|
||||
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
)
|
||||
|
||||
// logsCmd represents the logs command
|
||||
var logsCmd = &cobra.Command{
|
||||
Use: "logs",
|
||||
Short: "This will fetch the logs of the specified postgres cluster & postgres operator",
|
||||
Long: `Fetches the logs of the postgres cluster (i.e master( with -m flag) & replica with (-r 1 pod number) and without -m or -r connects to random replica`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
opLogs, _ := cmd.Flags().GetBool("operator")
|
||||
clusterName, _ := cmd.Flags().GetString("cluster")
|
||||
master, _ := cmd.Flags().GetBool("master")
|
||||
replica, _ := cmd.Flags().GetString("replica")
|
||||
|
||||
if opLogs {
|
||||
operatorLogs()
|
||||
} else {
|
||||
clusterLogs(clusterName, master, replica)
|
||||
}
|
||||
},
|
||||
Example: `
|
||||
#Fetch the logs of the postgres operator
|
||||
kubectl pg logs -o
|
||||
|
||||
#Fetch the logs of the master for provided cluster
|
||||
kubectl pg logs -c cluster01 -m
|
||||
|
||||
#Fetch the logs of the random replica for provided cluster
|
||||
kubectl pg logs -c cluster01
|
||||
|
||||
#Fetch the logs of the provided replica number of the cluster
|
||||
kubectl pg logs -c cluster01 -r 3
|
||||
`,
|
||||
}
|
||||
|
||||
func operatorLogs() {
|
||||
config := getConfig()
|
||||
client, err := kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
operator := getPostgresOperator(client)
|
||||
allPods, err := client.CoreV1().Pods(operator.Namespace).List(context.TODO(), metav1.ListOptions{})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var operatorPodName string
|
||||
for _, pod := range allPods.Items {
|
||||
for key, value := range pod.Labels {
|
||||
if (key == "name" && value == OperatorName) || (key == "app.kubernetes.io/name" && value == OperatorName) {
|
||||
operatorPodName = pod.Name
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
execRequest := client.CoreV1().RESTClient().Get().Namespace(operator.Namespace).
|
||||
Name(operatorPodName).
|
||||
Resource("pods").
|
||||
SubResource("log").
|
||||
Param("follow", "--follow").
|
||||
Param("container", OperatorName)
|
||||
|
||||
readCloser, err := execRequest.Stream(context.TODO())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
defer readCloser.Close()
|
||||
_, err = io.Copy(os.Stdout, readCloser)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func clusterLogs(clusterName string, master bool, replica string) {
|
||||
config := getConfig()
|
||||
client, err := kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
podName := getPodName(clusterName, master, replica)
|
||||
execRequest := client.CoreV1().RESTClient().Get().Namespace(getCurrentNamespace()).
|
||||
Name(podName).
|
||||
Resource("pods").
|
||||
SubResource("log").
|
||||
Param("follow", "--follow").
|
||||
Param("container", "postgres")
|
||||
|
||||
readCloser, err := execRequest.Stream(context.TODO())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
defer readCloser.Close()
|
||||
_, err = io.Copy(os.Stdout, readCloser)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(logsCmd)
|
||||
logsCmd.Flags().BoolP("operator", "o", false, "logs of operator")
|
||||
logsCmd.Flags().StringP("cluster", "c", "", "logs for the provided cluster")
|
||||
logsCmd.Flags().BoolP("master", "m", false, "Patroni logs of master")
|
||||
logsCmd.Flags().StringP("replica", "r", "", "Patroni logs of replica. Specify replica number.")
|
||||
}
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
/*
|
||||
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "kubectl-pg",
|
||||
Short: "kubectl plugin for the Zalando Postgres operator.",
|
||||
Long: `kubectl pg plugin for interaction with Zalando postgres operator.`,
|
||||
}
|
||||
|
||||
// Execute adds all child commands to the root command and sets flags appropriately.
|
||||
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
||||
func Execute() {
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
viper.SetDefault("author", "Vineeth Pothulapati <vineethpothulapati@outlook.com>")
|
||||
viper.SetDefault("license", "mit")
|
||||
}
|
||||
|
|
@ -1,194 +0,0 @@
|
|||
/*
|
||||
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
PostgresqlLister "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
// scaleCmd represents the scale command
|
||||
var scaleCmd = &cobra.Command{
|
||||
Use: "scale",
|
||||
Short: "Add/remove pods to a Postgres cluster",
|
||||
Long: `Scales the postgres objects using cluster-name.
|
||||
Scaling to 0 leads to down time.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
clusterName, err := cmd.Flags().GetString("cluster")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
namespace, err := cmd.Flags().GetString("namespace")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if len(args) > 0 {
|
||||
numberOfInstances, err := strconv.Atoi(args[0])
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
scale(int32(numberOfInstances), clusterName, namespace)
|
||||
} else {
|
||||
fmt.Println("Please enter number of instances to scale.")
|
||||
}
|
||||
|
||||
},
|
||||
Example: `
|
||||
#Usage
|
||||
kubectl pg scale [NUMBER-OF-INSTANCES] -c [CLUSTER-NAME] -n [NAMESPACE]
|
||||
|
||||
#Scales the number of instances of the provided cluster
|
||||
kubectl pg scale 5 -c cluster01
|
||||
`,
|
||||
}
|
||||
|
||||
func scale(numberOfInstances int32, clusterName string, namespace string) {
|
||||
config := getConfig()
|
||||
postgresConfig, err := PostgresqlLister.NewForConfig(config)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
postgresql, err := postgresConfig.Postgresqls(namespace).Get(context.TODO(), clusterName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
minInstances, maxInstances := allowedMinMaxInstances(config)
|
||||
|
||||
if minInstances == -1 && maxInstances == -1 {
|
||||
postgresql.Spec.NumberOfInstances = numberOfInstances
|
||||
} else if numberOfInstances <= maxInstances && numberOfInstances >= minInstances {
|
||||
postgresql.Spec.NumberOfInstances = numberOfInstances
|
||||
} else if minInstances == -1 && numberOfInstances < postgresql.Spec.NumberOfInstances ||
|
||||
maxInstances == -1 && numberOfInstances > postgresql.Spec.NumberOfInstances {
|
||||
postgresql.Spec.NumberOfInstances = numberOfInstances
|
||||
} else {
|
||||
log.Fatalf("cannot scale to the provided instances as they don't adhere to MIN_INSTANCES: %v and MAX_INSTANCES: %v provided in configmap or operatorconfiguration", maxInstances, minInstances)
|
||||
}
|
||||
|
||||
if numberOfInstances == 0 {
|
||||
fmt.Printf("Scaling to zero leads to down time. please type %s/%s and hit Enter this serves to confirm the action\n", namespace, clusterName)
|
||||
confirmAction(clusterName, namespace)
|
||||
}
|
||||
|
||||
patchInstances := scalePatch(numberOfInstances)
|
||||
UpdatedPostgres, err := postgresConfig.Postgresqls(namespace).Patch(context.TODO(), postgresql.Name, types.MergePatchType, patchInstances, metav1.PatchOptions{})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if UpdatedPostgres.ResourceVersion != postgresql.ResourceVersion {
|
||||
fmt.Printf("scaled postgresql %s/%s to %d instances\n", UpdatedPostgres.Namespace, UpdatedPostgres.Name, UpdatedPostgres.Spec.NumberOfInstances)
|
||||
return
|
||||
}
|
||||
fmt.Printf("postgresql %s is unchanged.\n", postgresql.Name)
|
||||
}
|
||||
|
||||
func scalePatch(value int32) []byte {
|
||||
instances := map[string]map[string]int32{"spec": {"numberOfInstances": value}}
|
||||
patchInstances, err := json.Marshal(instances)
|
||||
if err != nil {
|
||||
log.Fatal(err, "unable to parse patch for scale")
|
||||
}
|
||||
return patchInstances
|
||||
}
|
||||
|
||||
func allowedMinMaxInstances(config *rest.Config) (int32, int32) {
|
||||
k8sClient, err := kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
operator := getPostgresOperator(k8sClient)
|
||||
|
||||
operatorContainer := operator.Spec.Template.Spec.Containers
|
||||
var configMapName, operatorConfigName string
|
||||
// -1 indicates no limitations for min/max instances
|
||||
minInstances := -1
|
||||
maxInstances := -1
|
||||
for _, envData := range operatorContainer[0].Env {
|
||||
if envData.Name == "CONFIG_MAP_NAME" {
|
||||
configMapName = envData.Value
|
||||
}
|
||||
if envData.Name == "POSTGRES_OPERATOR_CONFIGURATION_OBJECT" {
|
||||
operatorConfigName = envData.Value
|
||||
}
|
||||
}
|
||||
|
||||
if operatorConfigName == "" {
|
||||
configMap, err := k8sClient.CoreV1().ConfigMaps(operator.Namespace).Get(context.TODO(), configMapName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
configMapData := configMap.Data
|
||||
for key, value := range configMapData {
|
||||
if key == "min_instances" {
|
||||
minInstances, err = strconv.Atoi(value)
|
||||
if err != nil {
|
||||
log.Fatalf("invalid min instances in configmap %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if key == "max_instances" {
|
||||
maxInstances, err = strconv.Atoi(value)
|
||||
if err != nil {
|
||||
log.Fatalf("invalid max instances in configmap %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if configMapName == "" {
|
||||
pgClient, err := PostgresqlLister.NewForConfig(config)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
operatorConfig, err := pgClient.OperatorConfigurations(operator.Namespace).Get(context.TODO(), operatorConfigName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
log.Fatalf("unable to read operator configuration %v", err)
|
||||
}
|
||||
|
||||
minInstances = int(operatorConfig.Configuration.MinInstances)
|
||||
maxInstances = int(operatorConfig.Configuration.MaxInstances)
|
||||
}
|
||||
return int32(minInstances), int32(maxInstances)
|
||||
}
|
||||
|
||||
func init() {
|
||||
namespace := getCurrentNamespace()
|
||||
scaleCmd.Flags().StringP("namespace", "n", namespace, "namespace of the cluster to be scaled")
|
||||
scaleCmd.Flags().StringP("cluster", "c", "", "provide the cluster name.")
|
||||
rootCmd.AddCommand(scaleCmd)
|
||||
}
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
/*
|
||||
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
v1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1"
|
||||
PostgresqlLister "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1"
|
||||
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// updateCmd represents kubectl pg update
|
||||
var updateCmd = &cobra.Command{
|
||||
Use: "update",
|
||||
Short: "Updates postgresql object using manifest file",
|
||||
Long: `Updates the state of cluster using manifest file to reflect the changes on the cluster.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fileName, _ := cmd.Flags().GetString("file")
|
||||
updatePgResources(fileName)
|
||||
},
|
||||
Example: `
|
||||
#usage
|
||||
kubectl pg update -f [File-NAME]
|
||||
|
||||
#update the postgres cluster with updated manifest file
|
||||
kubectl pg update -f cluster-manifest.yaml
|
||||
`,
|
||||
}
|
||||
|
||||
// Update postgresql resources.
|
||||
func updatePgResources(fileName string) {
|
||||
config := getConfig()
|
||||
postgresConfig, err := PostgresqlLister.NewForConfig(config)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
ymlFile, err := os.ReadFile(fileName)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
decode := scheme.Codecs.UniversalDeserializer().Decode
|
||||
obj, _, err := decode([]byte(ymlFile), nil, &v1.Postgresql{})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
newPostgresObj := obj.(*v1.Postgresql)
|
||||
oldPostgresObj, err := postgresConfig.Postgresqls(newPostgresObj.Namespace).Get(context.TODO(), newPostgresObj.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
newPostgresObj.ResourceVersion = oldPostgresObj.ResourceVersion
|
||||
response, err := postgresConfig.Postgresqls(newPostgresObj.Namespace).Update(context.TODO(), newPostgresObj, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if newPostgresObj.ResourceVersion != response.ResourceVersion {
|
||||
fmt.Printf("postgresql %s updated.\n", response.Name)
|
||||
} else {
|
||||
fmt.Printf("postgresql %s is unchanged.\n", response.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
updateCmd.Flags().StringP("file", "f", "", "manifest file with the cluster definition.")
|
||||
rootCmd.AddCommand(updateCmd)
|
||||
}
|
||||
|
|
@ -1,172 +0,0 @@
|
|||
/*
|
||||
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
PostgresqlLister "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1"
|
||||
v1 "k8s.io/api/apps/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/client-go/util/homedir"
|
||||
)
|
||||
|
||||
const (
|
||||
OperatorName = "postgres-operator"
|
||||
DefaultNamespace = "default"
|
||||
)
|
||||
|
||||
func getConfig() *restclient.Config {
|
||||
var kubeconfig *string
|
||||
var config *restclient.Config
|
||||
envKube := os.Getenv("KUBECONFIG")
|
||||
if envKube != "" {
|
||||
kubeconfig = &envKube
|
||||
} else {
|
||||
if home := homedir.HomeDir(); home != "" {
|
||||
kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
|
||||
} else {
|
||||
kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
|
||||
}
|
||||
}
|
||||
flag.Parse()
|
||||
var err error
|
||||
config, err = clientcmd.BuildConfigFromFlags("", *kubeconfig)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
func getCurrentNamespace() string {
|
||||
namespace, err := exec.Command("kubectl", "config", "view", "--minify", "--output", "jsonpath={..namespace}").CombinedOutput()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
currentNamespace := string(namespace)
|
||||
if currentNamespace == "" {
|
||||
currentNamespace = DefaultNamespace
|
||||
}
|
||||
return currentNamespace
|
||||
}
|
||||
|
||||
func confirmAction(clusterName string, namespace string) {
|
||||
for {
|
||||
confirmClusterDetails := ""
|
||||
_, err := fmt.Scan(&confirmClusterDetails)
|
||||
if err != nil {
|
||||
log.Fatalf("couldn't get confirmation from the user %v", err)
|
||||
}
|
||||
clusterDetails := strings.Split(confirmClusterDetails, "/")
|
||||
if clusterDetails[0] != namespace || clusterDetails[1] != clusterName {
|
||||
fmt.Printf("cluster name or namespace does not match. Please re-enter %s/%s\nHint: Press (ctrl+c) to exit\n", namespace, clusterName)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getPodName(clusterName string, master bool, replicaNumber string) string {
|
||||
config := getConfig()
|
||||
client, err := kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
postgresConfig, err := PostgresqlLister.NewForConfig(config)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
postgresCluster, err := postgresConfig.Postgresqls(getCurrentNamespace()).Get(context.TODO(), clusterName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
numOfInstances := postgresCluster.Spec.NumberOfInstances
|
||||
var podName string
|
||||
var podRole string
|
||||
replica := clusterName + "-" + replicaNumber
|
||||
|
||||
for ins := 0; ins < int(numOfInstances); ins++ {
|
||||
pod, err := client.CoreV1().Pods(getCurrentNamespace()).Get(context.TODO(), clusterName+"-"+strconv.Itoa(ins), metav1.GetOptions{})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
podRole = pod.Labels["spilo-role"]
|
||||
if podRole == "master" && master {
|
||||
podName = pod.Name
|
||||
fmt.Printf("connected to %s with pod name as %s\n", podRole, podName)
|
||||
break
|
||||
} else if podRole == "replica" && !master && (pod.Name == replica || replicaNumber == "") {
|
||||
podName = pod.Name
|
||||
fmt.Printf("connected to %s with pod name as %s\n", podRole, podName)
|
||||
break
|
||||
}
|
||||
}
|
||||
if podName == "" {
|
||||
log.Fatal("Provided replica doesn't exist")
|
||||
}
|
||||
return podName
|
||||
}
|
||||
|
||||
func getPostgresOperator(k8sClient *kubernetes.Clientset) *v1.Deployment {
|
||||
var operator *v1.Deployment
|
||||
operator, err := k8sClient.AppsV1().Deployments(getCurrentNamespace()).Get(context.TODO(), OperatorName, metav1.GetOptions{})
|
||||
if err == nil {
|
||||
return operator
|
||||
}
|
||||
|
||||
allDeployments := k8sClient.AppsV1().Deployments("")
|
||||
listDeployments, err := allDeployments.List(context.TODO(), metav1.ListOptions{})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for _, deployment := range listDeployments.Items {
|
||||
if deployment.Name == OperatorName {
|
||||
operator = deployment.DeepCopy()
|
||||
break
|
||||
} else {
|
||||
for key, value := range deployment.Labels {
|
||||
if key == "app.kubernetes.io/name" && value == OperatorName {
|
||||
operator = deployment.DeepCopy()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return operator
|
||||
}
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
)
|
||||
|
||||
var KubectlPgVersion string = "1.0"
|
||||
|
||||
// versionCmd represents the version command
|
||||
var versionCmd = &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "version of kubectl-pg & postgres-operator",
|
||||
Long: `version of kubectl-pg and current running postgres-operator`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
namespace, err := cmd.Flags().GetString("namespace")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
version(namespace)
|
||||
},
|
||||
Example: `
|
||||
#Lists the version of kubectl pg plugin and postgres operator in current namespace
|
||||
kubectl pg version
|
||||
|
||||
#Lists the version of kubectl pg plugin and postgres operator in provided namespace
|
||||
kubectl pg version -n namespace01
|
||||
`,
|
||||
}
|
||||
|
||||
func version(namespace string) {
|
||||
fmt.Printf("kubectl-pg: %s\n", KubectlPgVersion)
|
||||
|
||||
config := getConfig()
|
||||
client, err := kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
operatorDeployment := getPostgresOperator(client)
|
||||
if operatorDeployment.Name == "" {
|
||||
log.Fatalf("make sure zalando's postgres operator is running in namespace %s", namespace)
|
||||
}
|
||||
operatorImage := operatorDeployment.Spec.Template.Spec.Containers[0].Image
|
||||
imageDetails := strings.Split(operatorImage, ":")
|
||||
imageSplit := len(imageDetails)
|
||||
imageVersion := imageDetails[imageSplit-1]
|
||||
fmt.Printf("Postgres-Operator: %s\n", imageVersion)
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(versionCmd)
|
||||
versionCmd.Flags().StringP("namespace", "n", DefaultNamespace, "provide the namespace.")
|
||||
}
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
module github.com/zalando/postgres-operator/kubectl-pg
|
||||
|
||||
go 1.25.3
|
||||
|
||||
require (
|
||||
github.com/spf13/cobra v1.10.1
|
||||
github.com/spf13/viper v1.21.0
|
||||
github.com/zalando/postgres-operator v1.15.0
|
||||
k8s.io/api v0.32.9
|
||||
k8s.io/apiextensions-apiserver v0.25.9
|
||||
k8s.io/apimachinery v0.32.9
|
||||
k8s.io/client-go v0.32.9
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/gnostic-models v0.6.9 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/moby/spdystream v0.5.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/motomux/pretty v0.0.0-20161209205251-b2aad2c9a95d // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/sagikazarmark/locafero v0.11.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
|
||||
github.com/spf13/afero v1.15.0 // indirect
|
||||
github.com/spf13/cast v1.10.0 // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/crypto v0.45.0 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
golang.org/x/oauth2 v0.27.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/term v0.37.0 // indirect
|
||||
golang.org/x/text v0.31.0 // indirect
|
||||
golang.org/x/time v0.9.0 // indirect
|
||||
google.golang.org/protobuf v1.36.5 // indirect
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/klog/v2 v2.130.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
|
||||
sigs.k8s.io/randfill v1.0.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
)
|
||||
|
|
@ -1,206 +0,0 @@
|
|||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.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/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
||||
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
||||
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
||||
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
|
||||
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
|
||||
github.com/go-openapi/swag v0.22.3/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/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/protobuf v1.5.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/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/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=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU=
|
||||
github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
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/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/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
|
||||
github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
|
||||
github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
|
||||
github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
|
||||
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
|
||||
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
||||
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
||||
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
|
||||
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
|
||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
|
||||
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/zalando/postgres-operator v1.15.0 h1:is/7cOrpuV7OwMiN7TG7GgiYHKvaWx8Ptw3hJruFO1I=
|
||||
github.com/zalando/postgres-operator v1.15.0/go.mod h1:1cSOA5dG2dEqdG0uami1RHTGYX92bgAKYASfAhuMtHE=
|
||||
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/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.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
|
||||
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/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-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
|
||||
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
|
||||
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
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.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
|
||||
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
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=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/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.25.9 h1:Pycd6lm2auABp9wKQHCFSEPG+NPdFSTJXPST6NJFzB8=
|
||||
k8s.io/apiextensions-apiserver v0.25.9/go.mod h1:ijGxmSG1GLOEaWhTuaEr0M7KUeia3mWCZa6FFQqpt1M=
|
||||
k8s.io/apimachinery v0.32.9 h1:fXk8ktfsxrdThaEOAQFgkhCK7iyoyvS8nbYJ83o/SSs=
|
||||
k8s.io/apimachinery v0.32.9/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
|
||||
k8s.io/client-go v0.32.9 h1:ZMyIQ1TEpTDAQni3L2gH1NZzyOA/gHfNcAazzCxMJ0c=
|
||||
k8s.io/client-go v0.32.9/go.mod h1:2OT8aFSYvUjKGadaeT+AVbhkXQSpMAkiSb88Kz2WggI=
|
||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4=
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
|
||||
sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
|
||||
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps=
|
||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/zalando/postgres-operator/kubectl-pg/cmd"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cmd.Execute()
|
||||
}
|
||||
|
|
@ -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
|
|
@ -87,6 +87,16 @@ configuration:
|
|||
# inherited_labels:
|
||||
# - application
|
||||
# - environment
|
||||
# liveness_probe:
|
||||
# httpGet:
|
||||
# scheme: HTTP
|
||||
# path: /liveness
|
||||
# port: 8008
|
||||
# initialDelaySeconds: 10
|
||||
# periodSeconds: 10
|
||||
# timeoutSeconds: 5
|
||||
# successThreshold: 1
|
||||
# failureThreshold: 3
|
||||
master_pod_move_timeout: 20m
|
||||
# node_readiness_label:
|
||||
# status: ready
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ kind: CustomResourceDefinition
|
|||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.17.3
|
||||
labels:
|
||||
app.kubernetes.io/name: postgres-operator
|
||||
name: postgresqls.acid.zalan.do
|
||||
spec:
|
||||
group: acid.zalan.do
|
||||
|
|
@ -108,7 +110,7 @@ spec:
|
|||
description: load balancers' source ranges are the same for master
|
||||
and replica services
|
||||
items:
|
||||
pattern: ^(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\/(\d|[1-2]\d|3[0-2])$
|
||||
pattern: ^((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\/(\d|[1-2]\d|3[0-2])|(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))\/(12[0-8]|1[01][0-9]|[1-9]?[0-9]))$
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
|
|
@ -274,14 +276,26 @@ spec:
|
|||
vars that enable load balancers are pointers because it is important to know if any of them is omitted from the Postgres manifest
|
||||
in that case the var evaluates to nil and the value is taken from the operator config
|
||||
type: boolean
|
||||
enableMasterNodePort:
|
||||
description: |-
|
||||
vars to enable and configure nodeport services
|
||||
set ports to 0 or nil to let kubernetes decide which port to use
|
||||
overrides loadbalancer configuration
|
||||
type: boolean
|
||||
enableMasterPoolerLoadBalancer:
|
||||
type: boolean
|
||||
enableMasterPoolerNodePort:
|
||||
type: boolean
|
||||
enableReplicaConnectionPooler:
|
||||
type: boolean
|
||||
enableReplicaLoadBalancer:
|
||||
type: boolean
|
||||
enableReplicaNodePort:
|
||||
type: boolean
|
||||
enableReplicaPoolerLoadBalancer:
|
||||
type: boolean
|
||||
enableReplicaPoolerNodePort:
|
||||
type: boolean
|
||||
enableShmVolume:
|
||||
type: boolean
|
||||
env:
|
||||
|
|
@ -290,7 +304,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: |-
|
||||
|
|
@ -348,6 +364,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
|
||||
|
|
@ -444,8 +497,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: |-
|
||||
|
|
@ -503,6 +557,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
|
||||
|
|
@ -563,14 +654,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
|
||||
|
|
@ -591,8 +682,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
|
||||
|
|
@ -855,6 +947,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: |-
|
||||
|
|
@ -1225,7 +1323,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.
|
||||
|
|
@ -1257,7 +1357,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.
|
||||
|
|
@ -1311,10 +1411,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"
|
||||
|
|
@ -1326,6 +1426,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.
|
||||
|
|
@ -1401,7 +1554,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:
|
||||
|
|
@ -1866,8 +2018,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: |-
|
||||
|
|
@ -1925,6 +2078,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
|
||||
|
|
@ -1985,14 +2175,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
|
||||
|
|
@ -2013,8 +2203,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
|
||||
|
|
@ -2277,6 +2468,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: |-
|
||||
|
|
@ -2647,7 +2844,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.
|
||||
|
|
@ -2679,7 +2878,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.
|
||||
|
|
@ -2733,10 +2932,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"
|
||||
|
|
@ -2748,6 +2947,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.
|
||||
|
|
@ -2823,7 +3075,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:
|
||||
|
|
@ -3246,6 +3497,159 @@ spec:
|
|||
- name
|
||||
type: object
|
||||
type: array
|
||||
livenessProbe:
|
||||
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
|
||||
logicalBackupRetention:
|
||||
type: string
|
||||
logicalBackupSchedule:
|
||||
|
|
@ -3256,6 +3660,12 @@ spec:
|
|||
pattern: '^\ *((Mon|Tue|Wed|Thu|Fri|Sat|Sun):(2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))-((2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))\ *$'
|
||||
type: string
|
||||
type: array
|
||||
masterNodePort:
|
||||
format: int32
|
||||
type: integer
|
||||
masterPoolerNodePort:
|
||||
format: int32
|
||||
type: integer
|
||||
masterServiceAnnotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
|
|
@ -3560,6 +3970,12 @@ spec:
|
|||
replicaLoadBalancer:
|
||||
description: deprecated
|
||||
type: boolean
|
||||
replicaNodePort:
|
||||
format: int32
|
||||
type: integer
|
||||
replicaPoolerNodePort:
|
||||
format: int32
|
||||
type: integer
|
||||
replicaServiceAnnotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
|
|
@ -3672,8 +4088,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: |-
|
||||
|
|
@ -3731,6 +4148,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
|
||||
|
|
@ -4037,9 +4491,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: |-
|
||||
|
|
@ -4057,21 +4512,178 @@ spec:
|
|||
type: object
|
||||
type: array
|
||||
topologySpreadConstraints:
|
||||
type: array
|
||||
nullable: true
|
||||
items:
|
||||
type: object
|
||||
description: TopologySpreadConstraint specifies how to spread matching
|
||||
pods among the given topology.
|
||||
properties:
|
||||
labelSelector:
|
||||
description: |-
|
||||
LabelSelector is used to find matching pods.
|
||||
Pods that match this label selector are counted to determine the number of pods
|
||||
in their corresponding topology domain.
|
||||
properties:
|
||||
matchExpressions:
|
||||
description: matchExpressions is a list of label selector
|
||||
requirements. The requirements are ANDed.
|
||||
items:
|
||||
description: |-
|
||||
A label selector requirement is a selector that contains values, a key, and an operator that
|
||||
relates the key and values.
|
||||
properties:
|
||||
key:
|
||||
description: key is the label key that the selector
|
||||
applies to.
|
||||
type: string
|
||||
operator:
|
||||
description: |-
|
||||
operator represents a key's relationship to a set of values.
|
||||
Valid operators are In, NotIn, Exists and DoesNotExist.
|
||||
type: string
|
||||
values:
|
||||
description: |-
|
||||
values is an array of string values. If the operator is In or NotIn,
|
||||
the values array must be non-empty. If the operator is Exists or DoesNotExist,
|
||||
the values array must be empty. This array is replaced during a strategic
|
||||
merge patch.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
x-kubernetes-list-type: atomic
|
||||
required:
|
||||
- key
|
||||
- operator
|
||||
type: object
|
||||
type: array
|
||||
x-kubernetes-list-type: atomic
|
||||
matchLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: |-
|
||||
matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
|
||||
map is equivalent to an element of matchExpressions, whose key field is "key", the
|
||||
operator is "In", and the values array contains only "value". The requirements are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
matchLabelKeys:
|
||||
description: |-
|
||||
MatchLabelKeys is a set of pod label keys to select the pods over which
|
||||
spreading will be calculated. The keys are used to lookup values from the
|
||||
incoming pod labels, those key-value labels are ANDed with labelSelector
|
||||
to select the group of existing pods over which spreading will be calculated
|
||||
for the incoming pod. The same key is forbidden to exist in both MatchLabelKeys and LabelSelector.
|
||||
MatchLabelKeys cannot be set when LabelSelector isn't set.
|
||||
Keys that don't exist in the incoming pod labels will
|
||||
be ignored. A null or empty list means only match against labelSelector.
|
||||
|
||||
This is a beta field and requires the MatchLabelKeysInPodTopologySpread feature gate to be enabled (enabled by default).
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
x-kubernetes-list-type: atomic
|
||||
maxSkew:
|
||||
type: integer
|
||||
description: |-
|
||||
MaxSkew describes the degree to which pods may be unevenly distributed.
|
||||
When `whenUnsatisfiable=DoNotSchedule`, it is the maximum permitted difference
|
||||
between the number of matching pods in the target topology and the global minimum.
|
||||
The global minimum is the minimum number of matching pods in an eligible domain
|
||||
or zero if the number of eligible domains is less than MinDomains.
|
||||
For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same
|
||||
labelSelector spread as 2/2/1:
|
||||
In this case, the global minimum is 1.
|
||||
| zone1 | zone2 | zone3 |
|
||||
| P P | P P | P |
|
||||
- if MaxSkew is 1, incoming pod can only be scheduled to zone3 to become 2/2/2;
|
||||
scheduling it onto zone1(zone2) would make the ActualSkew(3-1) on zone1(zone2)
|
||||
violate MaxSkew(1).
|
||||
- if MaxSkew is 2, incoming pod can be scheduled onto any zone.
|
||||
When `whenUnsatisfiable=ScheduleAnyway`, it is used to give higher precedence
|
||||
to topologies that satisfy it.
|
||||
It's a required field. Default value is 1 and 0 is not allowed.
|
||||
format: int32
|
||||
type: integer
|
||||
minDomains:
|
||||
description: |-
|
||||
MinDomains indicates a minimum number of eligible domains.
|
||||
When the number of eligible domains with matching topology keys is less than minDomains,
|
||||
Pod Topology Spread treats "global minimum" as 0, and then the calculation of Skew is performed.
|
||||
And when the number of eligible domains with matching topology keys equals or greater than minDomains,
|
||||
this value has no effect on scheduling.
|
||||
As a result, when the number of eligible domains is less than minDomains,
|
||||
scheduler won't schedule more than maxSkew Pods to those domains.
|
||||
If value is nil, the constraint behaves as if MinDomains is equal to 1.
|
||||
Valid values are integers greater than 0.
|
||||
When value is not nil, WhenUnsatisfiable must be DoNotSchedule.
|
||||
|
||||
For example, in a 3-zone cluster, MaxSkew is set to 2, MinDomains is set to 5 and pods with the same
|
||||
labelSelector spread as 2/2/2:
|
||||
| zone1 | zone2 | zone3 |
|
||||
| P P | P P | P P |
|
||||
The number of domains is less than 5(MinDomains), so "global minimum" is treated as 0.
|
||||
In this situation, new pod with the same labelSelector cannot be scheduled,
|
||||
because computed skew will be 3(3 - 0) if new Pod is scheduled to any of the three zones,
|
||||
it will violate MaxSkew.
|
||||
format: int32
|
||||
type: integer
|
||||
nodeAffinityPolicy:
|
||||
description: |-
|
||||
NodeAffinityPolicy indicates how we will treat Pod's nodeAffinity/nodeSelector
|
||||
when calculating pod topology spread skew. Options are:
|
||||
- Honor: only nodes matching nodeAffinity/nodeSelector are included in the calculations.
|
||||
- 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.
|
||||
type: string
|
||||
nodeTaintsPolicy:
|
||||
description: |-
|
||||
NodeTaintsPolicy indicates how we will treat node taints when calculating
|
||||
pod topology spread skew. Options are:
|
||||
- Honor: nodes without taints, along with tainted nodes for which the incoming pod
|
||||
has a toleration, are included.
|
||||
- Ignore: node taints are ignored. All nodes are included.
|
||||
|
||||
If this value is nil, the behavior is equivalent to the Ignore policy.
|
||||
type: string
|
||||
topologyKey:
|
||||
description: |-
|
||||
TopologyKey is the key of node labels. Nodes that have a label with this key
|
||||
and identical values are considered to be in the same topology.
|
||||
We consider each <key, value> as a "bucket", and try to put balanced number
|
||||
of pods into each bucket.
|
||||
We define a domain as a particular instance of a topology.
|
||||
Also, we define an eligible domain as a domain whose nodes meet the requirements of
|
||||
nodeAffinityPolicy and nodeTaintsPolicy.
|
||||
e.g. If TopologyKey is "kubernetes.io/hostname", each Node is a domain of that topology.
|
||||
And, if TopologyKey is "topology.kubernetes.io/zone", each zone is a domain of that topology.
|
||||
It's a required field.
|
||||
type: string
|
||||
whenUnsatisfiable:
|
||||
description: |-
|
||||
WhenUnsatisfiable indicates how to deal with a pod if it doesn't satisfy
|
||||
the spread constraint.
|
||||
- DoNotSchedule (default) tells the scheduler not to schedule it.
|
||||
- ScheduleAnyway tells the scheduler to schedule the pod in any location,
|
||||
but giving higher precedence to topologies that would help reduce the
|
||||
skew.
|
||||
A constraint is considered "Unsatisfiable" for an incoming pod
|
||||
if and only if every possible node assignment for that pod would violate
|
||||
"MaxSkew" on some topology.
|
||||
For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same
|
||||
labelSelector spread as 3/1/1:
|
||||
| zone1 | zone2 | zone3 |
|
||||
| P P P | P | P |
|
||||
If WhenUnsatisfiable is set to DoNotSchedule, incoming pod can only be scheduled
|
||||
to zone2(zone3) to become 3/2/1(3/1/2) as ActualSkew(2-1) on zone2(zone3) satisfies
|
||||
MaxSkew(1). In other words, the cluster can still be imbalanced, but scheduler
|
||||
won't make it *more* imbalanced.
|
||||
It's a required field.
|
||||
type: string
|
||||
enum:
|
||||
- DoNotSchedule
|
||||
- ScheduleAnyway
|
||||
required:
|
||||
- maxSkew
|
||||
- topologyKey
|
||||
- whenUnsatisfiable
|
||||
type: object
|
||||
type: array
|
||||
useLoadBalancer:
|
||||
description: |-
|
||||
deprecated load balancer settings maintained for backward compatibility
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ kind: CustomResourceDefinition
|
|||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.17.3
|
||||
labels:
|
||||
app.kubernetes.io/name: postgres-operator
|
||||
name: postgresteams.acid.zalan.do
|
||||
spec:
|
||||
group: acid.zalan.do
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -4,7 +4,6 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type postgresqlCopy Postgresql
|
||||
|
|
@ -120,29 +119,3 @@ func (p *Postgresql) UnmarshalJSON(data []byte) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON convert to Duration from byte slice of json
|
||||
func (d *Duration) UnmarshalJSON(b []byte) error {
|
||||
var (
|
||||
v interface{}
|
||||
err error
|
||||
)
|
||||
if err = json.Unmarshal(b, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
switch val := v.(type) {
|
||||
case string:
|
||||
t, err := time.ParseDuration(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*d = Duration(t)
|
||||
return nil
|
||||
case float64:
|
||||
t := time.Duration(val)
|
||||
*d = Duration(t)
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("could not recognize type %T as a valid type to unmarshal to Duration", val)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,19 +5,24 @@ package v1
|
|||
import (
|
||||
"github.com/zalando/postgres-operator/pkg/util/config"
|
||||
|
||||
"time"
|
||||
|
||||
"github.com/zalando/postgres-operator/pkg/spec"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// +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
|
||||
// +kubebuilder:metadata:labels=app.kubernetes.io/name=postgres-operator
|
||||
type OperatorConfiguration struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata"`
|
||||
|
|
@ -37,122 +42,210 @@ 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"`
|
||||
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 *metav1.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 *metav1.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"`
|
||||
PatroniAPICheckInterval Duration `json:"patroni_api_check_interval,omitempty"`
|
||||
PatroniAPICheckTimeout Duration `json:"patroni_api_check_timeout,omitempty"`
|
||||
// +kubebuilder:default="3s"
|
||||
// interval to wait between consecutive attempts to check for some K8s resources
|
||||
ResourceCheckInterval *metav1.Duration `json:"resource_check_interval,omitempty"`
|
||||
// +kubebuilder:default="10m"
|
||||
// timeout when waiting for the presence of a certain K8s resource
|
||||
ResourceCheckTimeout *metav1.Duration `json:"resource_check_timeout,omitempty"`
|
||||
// +kubebuilder:default="10m"
|
||||
// timeout when waiting for pod role and cluster labels
|
||||
PodLabelWaitTimeout *metav1.Duration `json:"pod_label_wait_timeout,omitempty"`
|
||||
// +kubebuilder:default="10m"
|
||||
// timeout when waiting for the Postgres pods to be deleted
|
||||
PodDeletionWaitTimeout *metav1.Duration `json:"pod_deletion_wait_timeout,omitempty"`
|
||||
// +kubebuilder:default="4s"
|
||||
// interval between consecutive attempts waiting for postgresql CRD to be created
|
||||
ReadyWaitInterval *metav1.Duration `json:"ready_wait_interval,omitempty"`
|
||||
// +kubebuilder:default="30s"
|
||||
// timeout for the complete postgres CRD creation
|
||||
ReadyWaitTimeout *metav1.Duration `json:"ready_wait_timeout,omitempty"`
|
||||
// +kubebuilder:default="1s"
|
||||
// interval between consecutive attempts of operator calling the Patroni API
|
||||
PatroniAPICheckInterval *metav1.Duration `json:"patroni_api_check_interval,omitempty"`
|
||||
// +kubebuilder:default="5s"
|
||||
// timeout when waiting for successful response from Patroni API
|
||||
PatroniAPICheckTimeout *metav1.Duration `json:"patroni_api_check_timeout,omitempty"`
|
||||
}
|
||||
|
||||
// LoadBalancerConfiguration defines the LB configuration
|
||||
type LoadBalancerConfiguration struct {
|
||||
DbHostedZone string `json:"db_hosted_zone,omitempty"`
|
||||
EnableMasterLoadBalancer bool `json:"enable_master_load_balancer,omitempty"`
|
||||
EnableMasterPoolerLoadBalancer bool `json:"enable_master_pooler_load_balancer,omitempty"`
|
||||
EnableReplicaLoadBalancer bool `json:"enable_replica_load_balancer,omitempty"`
|
||||
EnableReplicaPoolerLoadBalancer bool `json:"enable_replica_pooler_load_balancer,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"`
|
||||
ReplicaLegacyDNSNameFormat config.StringTemplate `json:"replica_legacy_dns_name_format,omitempty"`
|
||||
ExternalTrafficPolicy string `json:"external_traffic_policy" default:"Cluster"`
|
||||
DbHostedZone string `json:"db_hosted_zone,omitempty"`
|
||||
EnableMasterLoadBalancer bool `json:"enable_master_load_balancer,omitempty"`
|
||||
EnableMasterPoolerLoadBalancer bool `json:"enable_master_pooler_load_balancer,omitempty"`
|
||||
EnableReplicaLoadBalancer bool `json:"enable_replica_load_balancer,omitempty"`
|
||||
EnableReplicaPoolerLoadBalancer bool `json:"enable_replica_pooler_load_balancer,omitempty"`
|
||||
|
||||
// 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"`
|
||||
// +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"`
|
||||
// +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"`
|
||||
|
|
@ -161,70 +254,109 @@ 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"`
|
||||
EnableDBAccess bool `json:"enable_database_access,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"`
|
||||
// +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"`
|
||||
}
|
||||
|
||||
// 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"`
|
||||
|
|
@ -238,12 +370,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
|
||||
|
|
@ -253,45 +399,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 *metav1.Duration `json:"resync_period,omitempty"`
|
||||
// +kubebuilder:default="5m"
|
||||
// period between consecutive repair requests
|
||||
RepairPeriod *metav1.Duration `json:"repair_period,omitempty"`
|
||||
// +kubebuilder:default=true
|
||||
EnableMaintenanceWindows *bool `json:"enable_maintenance_windows,omitempty"`
|
||||
// +kubebuilder:validation:Type=array
|
||||
// +kubebuilder:validation:items:Pattern=`^\ *((Mon|Tue|Wed|Thu|Fri|Sat|Sun):(2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))-((2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))\ *$`
|
||||
MaintenanceWindows []string `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"`
|
||||
}
|
||||
|
||||
// Duration shortens this frequently used name
|
||||
type Duration time.Duration
|
||||
|
|
|
|||
|
|
@ -0,0 +1,967 @@
|
|||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.17.3
|
||||
labels:
|
||||
app.kubernetes.io/name: postgres-operator
|
||||
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
|
||||
type: string
|
||||
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
|
||||
type: string
|
||||
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:
|
||||
items:
|
||||
pattern: ^\ *((Mon|Tue|Wed|Thu|Fri|Sat|Sun):(2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))-((2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))\
|
||||
*$
|
||||
type: string
|
||||
type: array
|
||||
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
|
||||
type: string
|
||||
resync_period:
|
||||
default: 30m
|
||||
description: period between consecutive sync requests
|
||||
type: string
|
||||
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
|
||||
type: string
|
||||
patroni_api_check_timeout:
|
||||
default: 5s
|
||||
description: timeout when waiting for successful response from
|
||||
Patroni API
|
||||
type: string
|
||||
pod_deletion_wait_timeout:
|
||||
default: 10m
|
||||
description: timeout when waiting for the Postgres pods to be
|
||||
deleted
|
||||
type: string
|
||||
pod_label_wait_timeout:
|
||||
default: 10m
|
||||
description: timeout when waiting for pod role and cluster labels
|
||||
type: string
|
||||
ready_wait_interval:
|
||||
default: 4s
|
||||
description: interval between consecutive attempts waiting for
|
||||
postgresql CRD to be created
|
||||
type: string
|
||||
ready_wait_timeout:
|
||||
default: 30s
|
||||
description: timeout for the complete postgres CRD creation
|
||||
type: string
|
||||
resource_check_interval:
|
||||
default: 3s
|
||||
description: interval to wait between consecutive attempts to
|
||||
check for some K8s resources
|
||||
type: string
|
||||
resource_check_timeout:
|
||||
default: 10m
|
||||
description: timeout when waiting for the presence of a certain
|
||||
K8s resource
|
||||
type: string
|
||||
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: {}
|
||||
|
|
@ -11,6 +11,7 @@ import (
|
|||
// +k8s:deepcopy-gen=true
|
||||
// +kubebuilder:resource:shortName=pgteam,categories=all
|
||||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:metadata:labels=app.kubernetes.io/name=postgres-operator
|
||||
type PostgresTeam struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata"`
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ kind: CustomResourceDefinition
|
|||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.17.3
|
||||
labels:
|
||||
app.kubernetes.io/name: postgres-operator
|
||||
name: postgresqls.acid.zalan.do
|
||||
spec:
|
||||
group: acid.zalan.do
|
||||
|
|
@ -108,7 +110,7 @@ spec:
|
|||
description: load balancers' source ranges are the same for master
|
||||
and replica services
|
||||
items:
|
||||
pattern: ^(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\/(\d|[1-2]\d|3[0-2])$
|
||||
pattern: ^((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\/(\d|[1-2]\d|3[0-2])|(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))\/(12[0-8]|1[01][0-9]|[1-9]?[0-9]))$
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
|
|
@ -274,14 +276,26 @@ spec:
|
|||
vars that enable load balancers are pointers because it is important to know if any of them is omitted from the Postgres manifest
|
||||
in that case the var evaluates to nil and the value is taken from the operator config
|
||||
type: boolean
|
||||
enableMasterNodePort:
|
||||
description: |-
|
||||
vars to enable and configure nodeport services
|
||||
set ports to 0 or nil to let kubernetes decide which port to use
|
||||
overrides loadbalancer configuration
|
||||
type: boolean
|
||||
enableMasterPoolerLoadBalancer:
|
||||
type: boolean
|
||||
enableMasterPoolerNodePort:
|
||||
type: boolean
|
||||
enableReplicaConnectionPooler:
|
||||
type: boolean
|
||||
enableReplicaLoadBalancer:
|
||||
type: boolean
|
||||
enableReplicaNodePort:
|
||||
type: boolean
|
||||
enableReplicaPoolerLoadBalancer:
|
||||
type: boolean
|
||||
enableReplicaPoolerNodePort:
|
||||
type: boolean
|
||||
enableShmVolume:
|
||||
type: boolean
|
||||
env:
|
||||
|
|
@ -290,7 +304,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: |-
|
||||
|
|
@ -348,6 +364,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
|
||||
|
|
@ -444,8 +497,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: |-
|
||||
|
|
@ -503,6 +557,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
|
||||
|
|
@ -563,14 +654,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
|
||||
|
|
@ -591,8 +682,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
|
||||
|
|
@ -855,6 +947,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: |-
|
||||
|
|
@ -1225,7 +1323,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.
|
||||
|
|
@ -1257,7 +1357,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.
|
||||
|
|
@ -1311,10 +1411,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"
|
||||
|
|
@ -1326,6 +1426,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.
|
||||
|
|
@ -1401,7 +1554,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:
|
||||
|
|
@ -1866,8 +2018,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: |-
|
||||
|
|
@ -1925,6 +2078,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
|
||||
|
|
@ -1985,14 +2175,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
|
||||
|
|
@ -2013,8 +2203,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
|
||||
|
|
@ -2277,6 +2468,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: |-
|
||||
|
|
@ -2647,7 +2844,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.
|
||||
|
|
@ -2679,7 +2878,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.
|
||||
|
|
@ -2733,10 +2932,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"
|
||||
|
|
@ -2748,6 +2947,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.
|
||||
|
|
@ -2823,7 +3075,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:
|
||||
|
|
@ -3246,6 +3497,159 @@ spec:
|
|||
- name
|
||||
type: object
|
||||
type: array
|
||||
livenessProbe:
|
||||
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
|
||||
logicalBackupRetention:
|
||||
type: string
|
||||
logicalBackupSchedule:
|
||||
|
|
@ -3256,6 +3660,12 @@ spec:
|
|||
pattern: '^\ *((Mon|Tue|Wed|Thu|Fri|Sat|Sun):(2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))-((2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))\ *$'
|
||||
type: string
|
||||
type: array
|
||||
masterNodePort:
|
||||
format: int32
|
||||
type: integer
|
||||
masterPoolerNodePort:
|
||||
format: int32
|
||||
type: integer
|
||||
masterServiceAnnotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
|
|
@ -3560,6 +3970,12 @@ spec:
|
|||
replicaLoadBalancer:
|
||||
description: deprecated
|
||||
type: boolean
|
||||
replicaNodePort:
|
||||
format: int32
|
||||
type: integer
|
||||
replicaPoolerNodePort:
|
||||
format: int32
|
||||
type: integer
|
||||
replicaServiceAnnotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
|
|
@ -3672,8 +4088,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: |-
|
||||
|
|
@ -3731,6 +4148,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
|
||||
|
|
@ -4037,9 +4491,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: |-
|
||||
|
|
@ -4056,6 +4511,179 @@ spec:
|
|||
type: string
|
||||
type: object
|
||||
type: array
|
||||
topologySpreadConstraints:
|
||||
items:
|
||||
description: TopologySpreadConstraint specifies how to spread matching
|
||||
pods among the given topology.
|
||||
properties:
|
||||
labelSelector:
|
||||
description: |-
|
||||
LabelSelector is used to find matching pods.
|
||||
Pods that match this label selector are counted to determine the number of pods
|
||||
in their corresponding topology domain.
|
||||
properties:
|
||||
matchExpressions:
|
||||
description: matchExpressions is a list of label selector
|
||||
requirements. The requirements are ANDed.
|
||||
items:
|
||||
description: |-
|
||||
A label selector requirement is a selector that contains values, a key, and an operator that
|
||||
relates the key and values.
|
||||
properties:
|
||||
key:
|
||||
description: key is the label key that the selector
|
||||
applies to.
|
||||
type: string
|
||||
operator:
|
||||
description: |-
|
||||
operator represents a key's relationship to a set of values.
|
||||
Valid operators are In, NotIn, Exists and DoesNotExist.
|
||||
type: string
|
||||
values:
|
||||
description: |-
|
||||
values is an array of string values. If the operator is In or NotIn,
|
||||
the values array must be non-empty. If the operator is Exists or DoesNotExist,
|
||||
the values array must be empty. This array is replaced during a strategic
|
||||
merge patch.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
x-kubernetes-list-type: atomic
|
||||
required:
|
||||
- key
|
||||
- operator
|
||||
type: object
|
||||
type: array
|
||||
x-kubernetes-list-type: atomic
|
||||
matchLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: |-
|
||||
matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
|
||||
map is equivalent to an element of matchExpressions, whose key field is "key", the
|
||||
operator is "In", and the values array contains only "value". The requirements are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
matchLabelKeys:
|
||||
description: |-
|
||||
MatchLabelKeys is a set of pod label keys to select the pods over which
|
||||
spreading will be calculated. The keys are used to lookup values from the
|
||||
incoming pod labels, those key-value labels are ANDed with labelSelector
|
||||
to select the group of existing pods over which spreading will be calculated
|
||||
for the incoming pod. The same key is forbidden to exist in both MatchLabelKeys and LabelSelector.
|
||||
MatchLabelKeys cannot be set when LabelSelector isn't set.
|
||||
Keys that don't exist in the incoming pod labels will
|
||||
be ignored. A null or empty list means only match against labelSelector.
|
||||
|
||||
This is a beta field and requires the MatchLabelKeysInPodTopologySpread feature gate to be enabled (enabled by default).
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
x-kubernetes-list-type: atomic
|
||||
maxSkew:
|
||||
description: |-
|
||||
MaxSkew describes the degree to which pods may be unevenly distributed.
|
||||
When `whenUnsatisfiable=DoNotSchedule`, it is the maximum permitted difference
|
||||
between the number of matching pods in the target topology and the global minimum.
|
||||
The global minimum is the minimum number of matching pods in an eligible domain
|
||||
or zero if the number of eligible domains is less than MinDomains.
|
||||
For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same
|
||||
labelSelector spread as 2/2/1:
|
||||
In this case, the global minimum is 1.
|
||||
| zone1 | zone2 | zone3 |
|
||||
| P P | P P | P |
|
||||
- if MaxSkew is 1, incoming pod can only be scheduled to zone3 to become 2/2/2;
|
||||
scheduling it onto zone1(zone2) would make the ActualSkew(3-1) on zone1(zone2)
|
||||
violate MaxSkew(1).
|
||||
- if MaxSkew is 2, incoming pod can be scheduled onto any zone.
|
||||
When `whenUnsatisfiable=ScheduleAnyway`, it is used to give higher precedence
|
||||
to topologies that satisfy it.
|
||||
It's a required field. Default value is 1 and 0 is not allowed.
|
||||
format: int32
|
||||
type: integer
|
||||
minDomains:
|
||||
description: |-
|
||||
MinDomains indicates a minimum number of eligible domains.
|
||||
When the number of eligible domains with matching topology keys is less than minDomains,
|
||||
Pod Topology Spread treats "global minimum" as 0, and then the calculation of Skew is performed.
|
||||
And when the number of eligible domains with matching topology keys equals or greater than minDomains,
|
||||
this value has no effect on scheduling.
|
||||
As a result, when the number of eligible domains is less than minDomains,
|
||||
scheduler won't schedule more than maxSkew Pods to those domains.
|
||||
If value is nil, the constraint behaves as if MinDomains is equal to 1.
|
||||
Valid values are integers greater than 0.
|
||||
When value is not nil, WhenUnsatisfiable must be DoNotSchedule.
|
||||
|
||||
For example, in a 3-zone cluster, MaxSkew is set to 2, MinDomains is set to 5 and pods with the same
|
||||
labelSelector spread as 2/2/2:
|
||||
| zone1 | zone2 | zone3 |
|
||||
| P P | P P | P P |
|
||||
The number of domains is less than 5(MinDomains), so "global minimum" is treated as 0.
|
||||
In this situation, new pod with the same labelSelector cannot be scheduled,
|
||||
because computed skew will be 3(3 - 0) if new Pod is scheduled to any of the three zones,
|
||||
it will violate MaxSkew.
|
||||
format: int32
|
||||
type: integer
|
||||
nodeAffinityPolicy:
|
||||
description: |-
|
||||
NodeAffinityPolicy indicates how we will treat Pod's nodeAffinity/nodeSelector
|
||||
when calculating pod topology spread skew. Options are:
|
||||
- Honor: only nodes matching nodeAffinity/nodeSelector are included in the calculations.
|
||||
- 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.
|
||||
type: string
|
||||
nodeTaintsPolicy:
|
||||
description: |-
|
||||
NodeTaintsPolicy indicates how we will treat node taints when calculating
|
||||
pod topology spread skew. Options are:
|
||||
- Honor: nodes without taints, along with tainted nodes for which the incoming pod
|
||||
has a toleration, are included.
|
||||
- Ignore: node taints are ignored. All nodes are included.
|
||||
|
||||
If this value is nil, the behavior is equivalent to the Ignore policy.
|
||||
type: string
|
||||
topologyKey:
|
||||
description: |-
|
||||
TopologyKey is the key of node labels. Nodes that have a label with this key
|
||||
and identical values are considered to be in the same topology.
|
||||
We consider each <key, value> as a "bucket", and try to put balanced number
|
||||
of pods into each bucket.
|
||||
We define a domain as a particular instance of a topology.
|
||||
Also, we define an eligible domain as a domain whose nodes meet the requirements of
|
||||
nodeAffinityPolicy and nodeTaintsPolicy.
|
||||
e.g. If TopologyKey is "kubernetes.io/hostname", each Node is a domain of that topology.
|
||||
And, if TopologyKey is "topology.kubernetes.io/zone", each zone is a domain of that topology.
|
||||
It's a required field.
|
||||
type: string
|
||||
whenUnsatisfiable:
|
||||
description: |-
|
||||
WhenUnsatisfiable indicates how to deal with a pod if it doesn't satisfy
|
||||
the spread constraint.
|
||||
- DoNotSchedule (default) tells the scheduler not to schedule it.
|
||||
- ScheduleAnyway tells the scheduler to schedule the pod in any location,
|
||||
but giving higher precedence to topologies that would help reduce the
|
||||
skew.
|
||||
A constraint is considered "Unsatisfiable" for an incoming pod
|
||||
if and only if every possible node assignment for that pod would violate
|
||||
"MaxSkew" on some topology.
|
||||
For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same
|
||||
labelSelector spread as 3/1/1:
|
||||
| zone1 | zone2 | zone3 |
|
||||
| P P P | P | P |
|
||||
If WhenUnsatisfiable is set to DoNotSchedule, incoming pod can only be scheduled
|
||||
to zone2(zone3) to become 3/2/1(3/1/2) as ActualSkew(2-1) on zone2(zone3) satisfies
|
||||
MaxSkew(1). In other words, the cluster can still be imbalanced, but scheduler
|
||||
won't make it *more* imbalanced.
|
||||
It's a required field.
|
||||
type: string
|
||||
required:
|
||||
- maxSkew
|
||||
- topologyKey
|
||||
- whenUnsatisfiable
|
||||
type: object
|
||||
type: array
|
||||
useLoadBalancer:
|
||||
description: |-
|
||||
deprecated load balancer settings maintained for backward compatibility
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import (
|
|||
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`,description="Age of the PostgreSQL cluster"
|
||||
// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.PostgresClusterStatus`,description="Current sync status of postgresql resource"
|
||||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:metadata:labels=app.kubernetes.io/name=postgres-operator
|
||||
type Postgresql struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata"`
|
||||
|
|
@ -63,6 +64,18 @@ type PostgresSpec struct {
|
|||
EnableReplicaLoadBalancer *bool `json:"enableReplicaLoadBalancer,omitempty"`
|
||||
EnableReplicaPoolerLoadBalancer *bool `json:"enableReplicaPoolerLoadBalancer,omitempty"`
|
||||
|
||||
// vars to enable and configure nodeport services
|
||||
// set ports to 0 or nil to let kubernetes decide which port to use
|
||||
// overrides loadbalancer configuration
|
||||
EnableMasterNodePort *bool `json:"enableMasterNodePort,omitempty"`
|
||||
MasterNodePort *int32 `json:"masterNodePort,omitempty"`
|
||||
EnableMasterPoolerNodePort *bool `json:"enableMasterPoolerNodePort,omitempty"`
|
||||
MasterPoolerNodePort *int32 `json:"masterPoolerNodePort,omitempty"`
|
||||
EnableReplicaNodePort *bool `json:"enableReplicaNodePort,omitempty"`
|
||||
ReplicaNodePort *int32 `json:"replicaNodePort,omitempty"`
|
||||
EnableReplicaPoolerNodePort *bool `json:"enableReplicaPoolerNodePort,omitempty"`
|
||||
ReplicaPoolerNodePort *int32 `json:"replicaPoolerNodePort,omitempty"`
|
||||
|
||||
// deprecated load balancer settings maintained for backward compatibility
|
||||
// see "Load balancers" operator docs
|
||||
UseLoadBalancer *bool `json:"useLoadBalancer,omitempty"`
|
||||
|
|
@ -71,7 +84,7 @@ type PostgresSpec struct {
|
|||
|
||||
// load balancers' source ranges are the same for master and replica services
|
||||
// +nullable
|
||||
// +kubebuilder:validation:items:Pattern=`^(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\/(\d|[1-2]\d|3[0-2])$`
|
||||
// +kubebuilder:validation:items:Pattern=`^((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\/(\d|[1-2]\d|3[0-2])|(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))\/(12[0-8]|1[01][0-9]|[1-9]?[0-9]))$`
|
||||
// +optional
|
||||
AllowedSourceRanges []string `json:"allowedSourceRanges"`
|
||||
|
||||
|
|
@ -87,7 +100,6 @@ type PostgresSpec struct {
|
|||
NumberOfInstances int32 `json:"numberOfInstances"`
|
||||
// +kubebuilder:validation:Schemaless
|
||||
// +kubebuilder:validation:Type=array
|
||||
// +kubebuilde:validation:items:Type=string
|
||||
MaintenanceWindows []MaintenanceWindow `json:"maintenanceWindows,omitempty"`
|
||||
Clone *CloneDescription `json:"clone,omitempty"`
|
||||
// Note: usernames specified here as database owners must be declared
|
||||
|
|
@ -98,6 +110,7 @@ type PostgresSpec struct {
|
|||
NodeAffinity *v1.NodeAffinity `json:"nodeAffinity,omitempty"`
|
||||
TopologySpreadConstraints []v1.TopologySpreadConstraint `json:"topologySpreadConstraints,omitempty"`
|
||||
Tolerations []v1.Toleration `json:"tolerations,omitempty"`
|
||||
LivenessProbe *v1.Probe `json:"livenessProbe,omitempty"`
|
||||
Sidecars []Sidecar `json:"sidecars,omitempty"`
|
||||
InitContainers []v1.Container `json:"initContainers,omitempty"`
|
||||
PodPriorityClassName string `json:"podPriorityClassName,omitempty"`
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"encoding/json"
|
||||
"errors"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
|
@ -810,3 +811,47 @@ func TestPostgresqlClone(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllowedSourceRangesPattern(t *testing.T) {
|
||||
// pattern used in CRD validation for allowedSourceRanges
|
||||
pattern := `^((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\/(\d|[1-2]\d|3[0-2])|(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))\/(12[0-8]|1[01][0-9]|[1-9]?[0-9]))$`
|
||||
re := regexp.MustCompile(pattern)
|
||||
|
||||
valid := []string{
|
||||
// IPv4
|
||||
"192.168.1.0/24",
|
||||
"0.0.0.0/0",
|
||||
"127.0.0.1/32",
|
||||
"10.0.0.0/8",
|
||||
"185.85.220.0/22",
|
||||
// IPv6
|
||||
"fd01::/48",
|
||||
"::1/128",
|
||||
"::/0",
|
||||
"2001:db8::/32",
|
||||
"fe80::1/64",
|
||||
"2001:0db8:85a3:0000:0000:8a2e:0370:7334/128",
|
||||
}
|
||||
|
||||
invalid := []string{
|
||||
"999.999.999.999/24",
|
||||
"192.168.1.0/33",
|
||||
"192.168.1.0",
|
||||
"not-an-ip",
|
||||
"fd01::/129",
|
||||
"::gggg/64",
|
||||
"",
|
||||
}
|
||||
|
||||
for _, cidr := range valid {
|
||||
if !re.MatchString(cidr) {
|
||||
t.Errorf("expected %q to match allowedSourceRanges pattern", cidr)
|
||||
}
|
||||
}
|
||||
|
||||
for _, cidr := range invalid {
|
||||
if re.MatchString(cidr) {
|
||||
t.Errorf("expected %q NOT to match allowedSourceRanges pattern", cidr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -163,6 +163,16 @@ func (in *KubernetesMetaConfiguration) DeepCopyInto(out *KubernetesMetaConfigura
|
|||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
if in.PodTerminateGracePeriod != nil {
|
||||
in, out := &in.PodTerminateGracePeriod, &out.PodTerminateGracePeriod
|
||||
*out = new(metav1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.LivenessProbe != nil {
|
||||
in, out := &in.LivenessProbe, &out.LivenessProbe
|
||||
*out = new(corev1.Probe)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.SpiloAllowPrivilegeEscalation != nil {
|
||||
in, out := &in.SpiloAllowPrivilegeEscalation, &out.SpiloAllowPrivilegeEscalation
|
||||
*out = new(bool)
|
||||
|
|
@ -275,6 +285,11 @@ func (in *KubernetesMetaConfiguration) DeepCopyInto(out *KubernetesMetaConfigura
|
|||
}
|
||||
}
|
||||
out.PodEnvironmentConfigMap = in.PodEnvironmentConfigMap
|
||||
if in.MasterPodMoveTimeout != nil {
|
||||
in, out := &in.MasterPodMoveTimeout, &out.MasterPodMoveTimeout
|
||||
*out = new(metav1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.PersistentVolumeClaimRetentionPolicy != nil {
|
||||
in, out := &in.PersistentVolumeClaimRetentionPolicy, &out.PersistentVolumeClaimRetentionPolicy
|
||||
*out = make(map[string]string, len(*in))
|
||||
|
|
@ -423,16 +438,21 @@ 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))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.ResyncPeriod != nil {
|
||||
in, out := &in.ResyncPeriod, &out.ResyncPeriod
|
||||
*out = new(metav1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.RepairPeriod != nil {
|
||||
in, out := &in.RepairPeriod, &out.RepairPeriod
|
||||
*out = new(metav1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.EnableMaintenanceWindows != nil {
|
||||
in, out := &in.EnableMaintenanceWindows, &out.EnableMaintenanceWindows
|
||||
*out = new(bool)
|
||||
|
|
@ -440,10 +460,8 @@ func (in *OperatorConfigurationData) DeepCopyInto(out *OperatorConfigurationData
|
|||
}
|
||||
if in.MaintenanceWindows != nil {
|
||||
in, out := &in.MaintenanceWindows, &out.MaintenanceWindows
|
||||
*out = make([]MaintenanceWindow, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.ShmVolume != nil {
|
||||
in, out := &in.ShmVolume, &out.ShmVolume
|
||||
|
|
@ -468,14 +486,14 @@ func (in *OperatorConfigurationData) DeepCopyInto(out *OperatorConfigurationData
|
|||
in.MajorVersionUpgrade.DeepCopyInto(&out.MajorVersionUpgrade)
|
||||
in.Kubernetes.DeepCopyInto(&out.Kubernetes)
|
||||
out.PostgresPodResources = in.PostgresPodResources
|
||||
out.Timeouts = in.Timeouts
|
||||
in.Timeouts.DeepCopyInto(&out.Timeouts)
|
||||
in.LoadBalancer.DeepCopyInto(&out.LoadBalancer)
|
||||
out.AWSGCP = in.AWSGCP
|
||||
out.OperatorDebug = in.OperatorDebug
|
||||
in.OperatorDebug.DeepCopyInto(&out.OperatorDebug)
|
||||
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
|
||||
|
|
@ -527,6 +545,16 @@ func (in *OperatorConfigurationList) DeepCopyObject() runtime.Object {
|
|||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *OperatorDebugConfiguration) DeepCopyInto(out *OperatorDebugConfiguration) {
|
||||
*out = *in
|
||||
if in.DebugLogging != nil {
|
||||
in, out := &in.DebugLogging, &out.DebugLogging
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
if in.EnableDBAccess != nil {
|
||||
in, out := &in.EnableDBAccess, &out.EnableDBAccess
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -543,6 +571,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
|
||||
}
|
||||
|
||||
|
|
@ -559,6 +602,46 @@ func (in *OperatorLogicalBackupConfiguration) DeepCopy() *OperatorLogicalBackupC
|
|||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *OperatorTimeouts) DeepCopyInto(out *OperatorTimeouts) {
|
||||
*out = *in
|
||||
if in.ResourceCheckInterval != nil {
|
||||
in, out := &in.ResourceCheckInterval, &out.ResourceCheckInterval
|
||||
*out = new(metav1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.ResourceCheckTimeout != nil {
|
||||
in, out := &in.ResourceCheckTimeout, &out.ResourceCheckTimeout
|
||||
*out = new(metav1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.PodLabelWaitTimeout != nil {
|
||||
in, out := &in.PodLabelWaitTimeout, &out.PodLabelWaitTimeout
|
||||
*out = new(metav1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.PodDeletionWaitTimeout != nil {
|
||||
in, out := &in.PodDeletionWaitTimeout, &out.PodDeletionWaitTimeout
|
||||
*out = new(metav1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.ReadyWaitInterval != nil {
|
||||
in, out := &in.ReadyWaitInterval, &out.ReadyWaitInterval
|
||||
*out = new(metav1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.ReadyWaitTimeout != nil {
|
||||
in, out := &in.ReadyWaitTimeout, &out.ReadyWaitTimeout
|
||||
*out = new(metav1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.PatroniAPICheckInterval != nil {
|
||||
in, out := &in.PatroniAPICheckInterval, &out.PatroniAPICheckInterval
|
||||
*out = new(metav1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.PatroniAPICheckTimeout != nil {
|
||||
in, out := &in.PatroniAPICheckTimeout, &out.PatroniAPICheckTimeout
|
||||
*out = new(metav1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -720,6 +803,46 @@ func (in *PostgresSpec) DeepCopyInto(out *PostgresSpec) {
|
|||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
if in.EnableMasterNodePort != nil {
|
||||
in, out := &in.EnableMasterNodePort, &out.EnableMasterNodePort
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
if in.MasterNodePort != nil {
|
||||
in, out := &in.MasterNodePort, &out.MasterNodePort
|
||||
*out = new(int32)
|
||||
**out = **in
|
||||
}
|
||||
if in.EnableMasterPoolerNodePort != nil {
|
||||
in, out := &in.EnableMasterPoolerNodePort, &out.EnableMasterPoolerNodePort
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
if in.MasterPoolerNodePort != nil {
|
||||
in, out := &in.MasterPoolerNodePort, &out.MasterPoolerNodePort
|
||||
*out = new(int32)
|
||||
**out = **in
|
||||
}
|
||||
if in.EnableReplicaNodePort != nil {
|
||||
in, out := &in.EnableReplicaNodePort, &out.EnableReplicaNodePort
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
if in.ReplicaNodePort != nil {
|
||||
in, out := &in.ReplicaNodePort, &out.ReplicaNodePort
|
||||
*out = new(int32)
|
||||
**out = **in
|
||||
}
|
||||
if in.EnableReplicaPoolerNodePort != nil {
|
||||
in, out := &in.EnableReplicaPoolerNodePort, &out.EnableReplicaPoolerNodePort
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
if in.ReplicaPoolerNodePort != nil {
|
||||
in, out := &in.ReplicaPoolerNodePort, &out.ReplicaPoolerNodePort
|
||||
*out = new(int32)
|
||||
**out = **in
|
||||
}
|
||||
if in.UseLoadBalancer != nil {
|
||||
in, out := &in.UseLoadBalancer, &out.UseLoadBalancer
|
||||
*out = new(bool)
|
||||
|
|
@ -815,6 +938,11 @@ func (in *PostgresSpec) DeepCopyInto(out *PostgresSpec) {
|
|||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.LivenessProbe != nil {
|
||||
in, out := &in.LivenessProbe, &out.LivenessProbe
|
||||
*out = new(corev1.Probe)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Sidecars != nil {
|
||||
in, out := &in.Sidecars, &out.Sidecars
|
||||
*out = make([]Sidecar, len(*in))
|
||||
|
|
|
|||
|
|
@ -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,17 +126,20 @@ 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 = "md5"
|
||||
passwordEncryption = "scram-sha-256"
|
||||
}
|
||||
|
||||
cluster := &Cluster{
|
||||
|
|
@ -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,
|
||||
|
|
@ -625,6 +630,8 @@ func (c *Cluster) compareContainers(description string, setA, setB []v1.Containe
|
|||
func(a, b v1.Container) bool { return a.Name != b.Name }),
|
||||
newCheck("new %s's %s (index %d) readiness probe does not match the current one",
|
||||
func(a, b v1.Container) bool { return !reflect.DeepEqual(a.ReadinessProbe, b.ReadinessProbe) }),
|
||||
newCheck("new %s's %s (index %d) liveness probe does not match the current one",
|
||||
func(a, b v1.Container) bool { return !reflect.DeepEqual(a.LivenessProbe, b.LivenessProbe) }),
|
||||
newCheck("new %s's %s (index %d) ports do not match the current one",
|
||||
func(a, b v1.Container) bool { return !comparePorts(a.Ports, b.Ports) }),
|
||||
newCheck("new %s's %s (index %d) resources do not match the current ones",
|
||||
|
|
@ -857,6 +864,14 @@ func (c *Cluster) compareServices(old, new *v1.Service) (bool, string) {
|
|||
return false, "new service's ExternalTrafficPolicy does not match the current one"
|
||||
}
|
||||
|
||||
if len(old.Spec.Ports) > 0 && len(new.Spec.Ports) > 0 {
|
||||
// we need to check whether the new port is not zero (=user-defined)
|
||||
// and only overwrite if it is
|
||||
if new.Spec.Ports[0].NodePort != 0 && old.Spec.Ports[0].NodePort != new.Spec.Ports[0].NodePort {
|
||||
return false, "new service's NodePort does not match the current one"
|
||||
}
|
||||
}
|
||||
|
||||
return true, ""
|
||||
}
|
||||
|
||||
|
|
@ -891,6 +906,16 @@ func (c *Cluster) compareLogicalBackupJob(cur, new *batchv1.CronJob) *compareLog
|
|||
reasons = append(reasons, fmt.Sprintf("new job's env PG_VERSION %q does not match the current one %q", newPgVersion, curPgVersion))
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(cur.Labels, new.Labels) {
|
||||
match = false
|
||||
reasons = append(reasons, "new job's labels do not match the current ones")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(cur.Spec.JobTemplate.Labels, new.Spec.JobTemplate.Labels) {
|
||||
match = false
|
||||
reasons = append(reasons, "new job's template labels do not match the current ones")
|
||||
}
|
||||
|
||||
needsReplace := false
|
||||
contReasons := make([]string, 0)
|
||||
needsReplace, contReasons = c.compareContainers("cronjob container", cur.Spec.JobTemplate.Spec.Template.Spec.Containers, new.Spec.JobTemplate.Spec.Template.Spec.Containers, needsReplace, contReasons)
|
||||
|
|
@ -899,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}
|
||||
}
|
||||
|
||||
|
|
@ -1321,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)
|
||||
}
|
||||
|
|
@ -1361,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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,8 +33,8 @@ const (
|
|||
replicationUserName = "standby"
|
||||
poolerUserName = "pooler"
|
||||
adminUserName = "admin"
|
||||
exampleSpiloConfig = `{"postgresql":{"bin_dir":"/usr/lib/postgresql/12/bin","parameters":{"autovacuum_analyze_scale_factor":"0.1"},"pg_hba":["hostssl all all 0.0.0.0/0 md5","host all all 0.0.0.0/0 md5"]},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"},"data-checksums",{"encoding":"UTF8"},{"locale":"en_US.UTF-8"}],"dcs":{"ttl":30,"loop_wait":10,"retry_timeout":10,"maximum_lag_on_failover":33554432,"postgresql":{"parameters":{"max_connections":"100","max_locks_per_transaction":"64","max_worker_processes":"4"}}}}}`
|
||||
spiloConfigDiff = `{"postgresql":{"bin_dir":"/usr/lib/postgresql/12/bin","parameters":{"autovacuum_analyze_scale_factor":"0.1"},"pg_hba":["hostssl all all 0.0.0.0/0 md5","host all all 0.0.0.0/0 md5"]},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"},"data-checksums",{"encoding":"UTF8"},{"locale":"en_US.UTF-8"}],"dcs":{"loop_wait":10,"retry_timeout":10,"maximum_lag_on_failover":33554432,"postgresql":{"parameters":{"max_locks_per_transaction":"64","max_worker_processes":"4"}}}}}`
|
||||
exampleSpiloConfig = `{"postgresql":{"bin_dir":"/usr/lib/postgresql/12/bin","parameters":{"autovacuum_analyze_scale_factor":"0.1"},"pg_hba":["hostssl all all 0.0.0.0/0 scram-sha-256","host all all 0.0.0.0/0 scram-sha-256"]},"bootstrap":{"initdb":[{"auth-host":"scram-sha-256"},{"auth-local":"trust"},"data-checksums",{"encoding":"UTF8"},{"locale":"en_US.UTF-8"}],"dcs":{"ttl":30,"loop_wait":10,"retry_timeout":10,"maximum_lag_on_failover":33554432,"postgresql":{"parameters":{"max_connections":"100","max_locks_per_transaction":"64","max_worker_processes":"4"}}}}}`
|
||||
spiloConfigDiff = `{"postgresql":{"bin_dir":"/usr/lib/postgresql/12/bin","parameters":{"autovacuum_analyze_scale_factor":"0.1"},"pg_hba":["hostssl all all 0.0.0.0/0 scram-sha-256","host all all 0.0.0.0/0 scram-sha-256"]},"bootstrap":{"initdb":[{"auth-host":"scram-sha-256"},{"auth-local":"trust"},"data-checksums",{"encoding":"UTF8"},{"locale":"en_US.UTF-8"}],"dcs":{"loop_wait":10,"retry_timeout":10,"maximum_lag_on_failover":33554432,"postgresql":{"parameters":{"max_locks_per_transaction":"64","max_worker_processes":"4"}}}}}`
|
||||
)
|
||||
|
||||
var logger = logrus.New().WithField("test", "cluster")
|
||||
|
|
@ -138,7 +138,8 @@ func TestCreate(t *testing.T) {
|
|||
var cluster = New(
|
||||
Config{
|
||||
OpConfig: config.Config{
|
||||
PodManagementPolicy: "ordered_ready",
|
||||
PodManagementPolicy: "ordered_ready",
|
||||
PodTerminateGracePeriod: &metav1.Duration{Duration: 600 * time.Second},
|
||||
Resources: config.Resources{
|
||||
ClusterLabels: map[string]string{"application": "spilo"},
|
||||
ClusterNameLabel: "cluster-name",
|
||||
|
|
@ -147,8 +148,8 @@ func TestCreate(t *testing.T) {
|
|||
DefaultMemoryRequest: "300Mi",
|
||||
DefaultMemoryLimit: "300Mi",
|
||||
PodRoleLabel: "spilo-role",
|
||||
ResourceCheckInterval: time.Duration(3),
|
||||
ResourceCheckTimeout: time.Duration(10),
|
||||
ResourceCheckInterval: &metav1.Duration{Duration: 3 * time.Second},
|
||||
ResourceCheckTimeout: &metav1.Duration{Duration: 10 * time.Minute},
|
||||
},
|
||||
EnableFinalizers: util.True(),
|
||||
},
|
||||
|
|
@ -1186,11 +1187,11 @@ func TestCompareSpiloConfiguration(t *testing.T) {
|
|||
ExpectedResult bool
|
||||
}{
|
||||
{
|
||||
`{"postgresql":{"bin_dir":"/usr/lib/postgresql/12/bin","parameters":{"autovacuum_analyze_scale_factor":"0.1"},"pg_hba":["hostssl all all 0.0.0.0/0 md5","host all all 0.0.0.0/0 md5"]},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"},"data-checksums",{"encoding":"UTF8"},{"locale":"en_US.UTF-8"}],"dcs":{"ttl":30,"loop_wait":10,"retry_timeout":10,"maximum_lag_on_failover":33554432,"postgresql":{"parameters":{"max_connections":"100","max_locks_per_transaction":"64","max_worker_processes":"4"}}}}}`,
|
||||
`{"postgresql":{"bin_dir":"/usr/lib/postgresql/12/bin","parameters":{"autovacuum_analyze_scale_factor":"0.1"},"pg_hba":["hostssl all all 0.0.0.0/0 scram-sha-256","host all all 0.0.0.0/0 scram-sha-256"]},"bootstrap":{"initdb":[{"auth-host":"scram-sha-256"},{"auth-local":"trust"},"data-checksums",{"encoding":"UTF8"},{"locale":"en_US.UTF-8"}],"dcs":{"ttl":30,"loop_wait":10,"retry_timeout":10,"maximum_lag_on_failover":33554432,"postgresql":{"parameters":{"max_connections":"100","max_locks_per_transaction":"64","max_worker_processes":"4"}}}}}`,
|
||||
true,
|
||||
},
|
||||
{
|
||||
`{"postgresql":{"bin_dir":"/usr/lib/postgresql/12/bin","parameters":{"autovacuum_analyze_scale_factor":"0.1"},"pg_hba":["hostssl all all 0.0.0.0/0 md5","host all all 0.0.0.0/0 md5"]},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"},"data-checksums",{"encoding":"UTF8"},{"locale":"en_US.UTF-8"}],"dcs":{"ttl":30,"loop_wait":10,"retry_timeout":10,"maximum_lag_on_failover":33554432,"postgresql":{"parameters":{"max_connections":"200","max_locks_per_transaction":"64","max_worker_processes":"4"}}}}}`,
|
||||
`{"postgresql":{"bin_dir":"/usr/lib/postgresql/12/bin","parameters":{"autovacuum_analyze_scale_factor":"0.1"},"pg_hba":["hostssl all all 0.0.0.0/0 scram-sha-256","host all all 0.0.0.0/0 scram-sha-256"]},"bootstrap":{"initdb":[{"auth-host":"scram-sha-256"},{"auth-local":"trust"},"data-checksums",{"encoding":"UTF8"},{"locale":"en_US.UTF-8"}],"dcs":{"ttl":30,"loop_wait":10,"retry_timeout":10,"maximum_lag_on_failover":33554432,"postgresql":{"parameters":{"max_connections":"200","max_locks_per_transaction":"64","max_worker_processes":"4"}}}}}`,
|
||||
true,
|
||||
},
|
||||
{
|
||||
|
|
@ -1334,7 +1335,8 @@ func newService(
|
|||
svcType v1.ServiceType,
|
||||
sourceRanges []string,
|
||||
selector map[string]string,
|
||||
policy v1.ServiceExternalTrafficPolicyType) *v1.Service {
|
||||
policy v1.ServiceExternalTrafficPolicyType,
|
||||
nodePort *int32) *v1.Service {
|
||||
svc := &v1.Service{
|
||||
Spec: v1.ServiceSpec{
|
||||
Selector: selector,
|
||||
|
|
@ -1344,6 +1346,16 @@ func newService(
|
|||
},
|
||||
}
|
||||
svc.Annotations = annotations
|
||||
|
||||
if nodePort != nil {
|
||||
svc.Spec.Ports = []v1.ServicePort{
|
||||
{
|
||||
Name: "port",
|
||||
NodePort: *nodePort,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return svc
|
||||
}
|
||||
|
||||
|
|
@ -1370,6 +1382,7 @@ func TestCompareServices(t *testing.T) {
|
|||
[]string{"128.141.0.0/16", "137.138.0.0/16"},
|
||||
nil,
|
||||
defaultPolicy,
|
||||
nil,
|
||||
)
|
||||
|
||||
ownerRef := metav1.OwnerReference{
|
||||
|
|
@ -1381,6 +1394,9 @@ func TestCompareServices(t *testing.T) {
|
|||
|
||||
serviceWithOwnerReference.ObjectMeta.OwnerReferences = append(serviceWithOwnerReference.ObjectMeta.OwnerReferences, ownerRef)
|
||||
|
||||
portZero := int32(0)
|
||||
portNotZero := int32(1337)
|
||||
|
||||
tests := []struct {
|
||||
about string
|
||||
current *v1.Service
|
||||
|
|
@ -1396,14 +1412,14 @@ func TestCompareServices(t *testing.T) {
|
|||
},
|
||||
v1.ServiceTypeClusterIP,
|
||||
[]string{"128.141.0.0/16", "137.138.0.0/16"},
|
||||
nil, defaultPolicy),
|
||||
nil, defaultPolicy, nil),
|
||||
new: newService(
|
||||
map[string]string{
|
||||
constants.ZalandoDNSNameAnnotation: "clstr.acid.zalan.do",
|
||||
},
|
||||
v1.ServiceTypeClusterIP,
|
||||
[]string{"128.141.0.0/16", "137.138.0.0/16"},
|
||||
nil, defaultPolicy),
|
||||
nil, defaultPolicy, nil),
|
||||
match: true,
|
||||
},
|
||||
{
|
||||
|
|
@ -1414,14 +1430,14 @@ func TestCompareServices(t *testing.T) {
|
|||
},
|
||||
v1.ServiceTypeClusterIP,
|
||||
[]string{"128.141.0.0/16", "137.138.0.0/16"},
|
||||
nil, defaultPolicy),
|
||||
nil, defaultPolicy, nil),
|
||||
new: newService(
|
||||
map[string]string{
|
||||
constants.ZalandoDNSNameAnnotation: "clstr.acid.zalan.do",
|
||||
},
|
||||
v1.ServiceTypeLoadBalancer,
|
||||
[]string{"128.141.0.0/16", "137.138.0.0/16"},
|
||||
nil, defaultPolicy),
|
||||
nil, defaultPolicy, nil),
|
||||
match: false,
|
||||
reason: `new service's type "LoadBalancer" does not match the current one "ClusterIP"`,
|
||||
},
|
||||
|
|
@ -1433,14 +1449,14 @@ func TestCompareServices(t *testing.T) {
|
|||
},
|
||||
v1.ServiceTypeLoadBalancer,
|
||||
[]string{"128.141.0.0/16", "137.138.0.0/16"},
|
||||
nil, defaultPolicy),
|
||||
nil, defaultPolicy, nil),
|
||||
new: newService(
|
||||
map[string]string{
|
||||
constants.ZalandoDNSNameAnnotation: "clstr.acid.zalan.do",
|
||||
},
|
||||
v1.ServiceTypeLoadBalancer,
|
||||
[]string{"185.249.56.0/22"},
|
||||
nil, defaultPolicy),
|
||||
nil, defaultPolicy, nil),
|
||||
match: false,
|
||||
reason: `new service's LoadBalancerSourceRange does not match the current one`,
|
||||
},
|
||||
|
|
@ -1452,14 +1468,14 @@ func TestCompareServices(t *testing.T) {
|
|||
},
|
||||
v1.ServiceTypeLoadBalancer,
|
||||
[]string{"128.141.0.0/16", "137.138.0.0/16"},
|
||||
nil, defaultPolicy),
|
||||
nil, defaultPolicy, nil),
|
||||
new: newService(
|
||||
map[string]string{
|
||||
constants.ZalandoDNSNameAnnotation: "clstr.acid.zalan.do",
|
||||
},
|
||||
v1.ServiceTypeLoadBalancer,
|
||||
[]string{},
|
||||
nil, defaultPolicy),
|
||||
nil, defaultPolicy, nil),
|
||||
match: false,
|
||||
reason: `new service's LoadBalancerSourceRange does not match the current one`,
|
||||
},
|
||||
|
|
@ -1471,7 +1487,7 @@ func TestCompareServices(t *testing.T) {
|
|||
},
|
||||
v1.ServiceTypeClusterIP,
|
||||
[]string{"128.141.0.0/16", "137.138.0.0/16"},
|
||||
nil, defaultPolicy),
|
||||
nil, defaultPolicy, nil),
|
||||
new: serviceWithOwnerReference,
|
||||
match: false,
|
||||
},
|
||||
|
|
@ -1481,12 +1497,12 @@ func TestCompareServices(t *testing.T) {
|
|||
map[string]string{},
|
||||
v1.ServiceTypeClusterIP,
|
||||
[]string{},
|
||||
nil, defaultPolicy),
|
||||
nil, defaultPolicy, nil),
|
||||
new: newService(
|
||||
map[string]string{},
|
||||
v1.ServiceTypeClusterIP,
|
||||
[]string{},
|
||||
map[string]string{"cluster-name": "clstr", "spilo-role": "master"}, defaultPolicy),
|
||||
map[string]string{"cluster-name": "clstr", "spilo-role": "master"}, defaultPolicy, nil),
|
||||
match: false,
|
||||
},
|
||||
{
|
||||
|
|
@ -1495,14 +1511,42 @@ func TestCompareServices(t *testing.T) {
|
|||
map[string]string{},
|
||||
v1.ServiceTypeClusterIP,
|
||||
[]string{},
|
||||
nil, defaultPolicy),
|
||||
nil, defaultPolicy, nil),
|
||||
new: newService(
|
||||
map[string]string{},
|
||||
v1.ServiceTypeClusterIP,
|
||||
[]string{},
|
||||
nil, v1.ServiceExternalTrafficPolicyTypeLocal),
|
||||
nil, v1.ServiceExternalTrafficPolicyTypeLocal, nil),
|
||||
match: false,
|
||||
},
|
||||
{
|
||||
about: "services differ on node port",
|
||||
current: newService(
|
||||
map[string]string{},
|
||||
v1.ServiceTypeNodePort,
|
||||
[]string{},
|
||||
nil, defaultPolicy, &portZero),
|
||||
new: newService(
|
||||
map[string]string{},
|
||||
v1.ServiceTypeNodePort,
|
||||
[]string{},
|
||||
nil, defaultPolicy, &portNotZero),
|
||||
match: false,
|
||||
},
|
||||
{
|
||||
about: "services do not differ on node port when requesting 0",
|
||||
current: newService(
|
||||
map[string]string{},
|
||||
v1.ServiceTypeNodePort,
|
||||
[]string{},
|
||||
nil, defaultPolicy, &portNotZero),
|
||||
new: newService(
|
||||
map[string]string{},
|
||||
v1.ServiceTypeNodePort,
|
||||
[]string{},
|
||||
nil, defaultPolicy, &portZero),
|
||||
match: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
|
@ -1524,12 +1568,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{
|
||||
|
|
|
|||
|
|
@ -292,7 +292,7 @@ func (c *Cluster) generateConnectionPoolerPodTemplate(role PostgresRole) (
|
|||
if connectionPoolerSpec == nil {
|
||||
connectionPoolerSpec = &acidv1.ConnectionPooler{}
|
||||
}
|
||||
gracePeriod := int64(c.OpConfig.PodTerminateGracePeriod.Seconds())
|
||||
gracePeriod := int64(util.CoalesceDuration(c.OpConfig.PodTerminateGracePeriod, "5m").Seconds())
|
||||
resources, err := c.generateResourceRequirements(
|
||||
connectionPoolerSpec.Resources,
|
||||
makeDefaultConnectionPoolerResources(&c.OpConfig),
|
||||
|
|
@ -566,7 +566,9 @@ func (c *Cluster) generateConnectionPoolerService(connectionPooler *ConnectionPo
|
|||
},
|
||||
}
|
||||
|
||||
if c.shouldCreateLoadBalancerForPoolerService(poolerRole, spec) {
|
||||
if ok, port := c.shouldCreateNodePortForPoolerService(poolerRole, spec); ok {
|
||||
c.configureNodePortService(&serviceSpec, port)
|
||||
} else if c.shouldCreateLoadBalancerForPoolerService(poolerRole, spec) {
|
||||
c.configureLoadBalanceService(&serviceSpec, spec.AllowedSourceRanges)
|
||||
}
|
||||
|
||||
|
|
@ -594,7 +596,9 @@ func (c *Cluster) generatePoolerServiceAnnotations(role PostgresRole, spec *acid
|
|||
var dnsString string
|
||||
annotations := c.getCustomServiceAnnotations(role, spec)
|
||||
|
||||
if c.shouldCreateLoadBalancerForPoolerService(role, spec) {
|
||||
nodePort, _ := c.shouldCreateNodePortForPoolerService(role, spec)
|
||||
|
||||
if !nodePort && c.shouldCreateLoadBalancerForPoolerService(role, spec) {
|
||||
// -repl suffix will be added by replicaDNSName
|
||||
clusterNameWithPoolerSuffix := c.connectionPoolerName(Master)
|
||||
if role == Master {
|
||||
|
|
@ -635,6 +639,37 @@ func (c *Cluster) shouldCreateLoadBalancerForPoolerService(role PostgresRole, sp
|
|||
}
|
||||
}
|
||||
|
||||
func (c *Cluster) shouldCreateNodePortForPoolerService(role PostgresRole, spec *acidv1.PostgresSpec) (bool, int32) {
|
||||
switch role {
|
||||
case Replica:
|
||||
// if the value is explicitly set in a Postgresql manifest, follow this setting
|
||||
if spec.EnableReplicaPoolerNodePort != nil {
|
||||
port := int32(0)
|
||||
if spec.ReplicaPoolerNodePort != nil {
|
||||
port = *spec.ReplicaPoolerNodePort
|
||||
}
|
||||
|
||||
return *spec.EnableReplicaPoolerNodePort, port
|
||||
}
|
||||
|
||||
// otherwise, follow the operator configuration
|
||||
return c.OpConfig.EnableReplicaPoolerNodePort, 0
|
||||
case Master:
|
||||
if spec.EnableMasterPoolerNodePort != nil {
|
||||
port := int32(0)
|
||||
if spec.MasterPoolerNodePort != nil {
|
||||
port = *spec.MasterPoolerNodePort
|
||||
}
|
||||
|
||||
return *spec.EnableMasterPoolerNodePort, port
|
||||
}
|
||||
|
||||
return c.OpConfig.EnableMasterPoolerNodePort, 0
|
||||
default:
|
||||
panic(fmt.Sprintf("Unknown role %v", role))
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cluster) listPoolerPods(listOptions metav1.ListOptions) ([]v1.Pod, error) {
|
||||
pods, err := c.KubeClient.Pods(c.Namespace).List(context.TODO(), listOptions)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -1154,3 +1154,181 @@ func TestConnectionPoolerServiceSpec(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestConnectionPoolerServiceType(t *testing.T) {
|
||||
testName := "Test connection pooler service type selection"
|
||||
|
||||
cluster := New(
|
||||
Config{
|
||||
OpConfig: config.Config{
|
||||
ProtectedRoles: []string{"admin"},
|
||||
Auth: config.Auth{
|
||||
SuperUsername: superUserName,
|
||||
ReplicationUsername: replicationUserName,
|
||||
},
|
||||
ConnectionPooler: config.ConnectionPooler{
|
||||
ConnectionPoolerDefaultCPURequest: "100m",
|
||||
ConnectionPoolerDefaultCPULimit: "100m",
|
||||
ConnectionPoolerDefaultMemoryRequest: "100Mi",
|
||||
ConnectionPoolerDefaultMemoryLimit: "100Mi",
|
||||
},
|
||||
Resources: config.Resources{
|
||||
EnableOwnerReferences: util.True(),
|
||||
},
|
||||
},
|
||||
},
|
||||
k8sutil.KubernetesClient{},
|
||||
acidv1.Postgresql{},
|
||||
logger,
|
||||
eventRecorder,
|
||||
)
|
||||
|
||||
cluster.Statefulset = &appsv1.StatefulSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-sts",
|
||||
},
|
||||
}
|
||||
|
||||
cluster.ConnectionPooler = map[PostgresRole]*ConnectionPoolerObjects{
|
||||
Master: {
|
||||
Deployment: nil,
|
||||
Service: nil,
|
||||
LookupFunction: false,
|
||||
Role: Master,
|
||||
},
|
||||
Replica: {
|
||||
Deployment: nil,
|
||||
Service: nil,
|
||||
LookupFunction: false,
|
||||
Role: Replica,
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
subTest string
|
||||
spec *acidv1.PostgresSpec
|
||||
cluster *Cluster
|
||||
expectedType map[PostgresRole]v1.ServiceType
|
||||
}{
|
||||
{
|
||||
subTest: "default configuration -> ClusterIP for both",
|
||||
spec: &acidv1.PostgresSpec{
|
||||
ConnectionPooler: &acidv1.ConnectionPooler{},
|
||||
},
|
||||
cluster: cluster,
|
||||
expectedType: map[PostgresRole]v1.ServiceType{
|
||||
Master: v1.ServiceTypeClusterIP,
|
||||
Replica: v1.ServiceTypeClusterIP,
|
||||
},
|
||||
},
|
||||
{
|
||||
subTest: "LoadBalancer for both roles",
|
||||
spec: &acidv1.PostgresSpec{
|
||||
ConnectionPooler: &acidv1.ConnectionPooler{},
|
||||
EnableMasterPoolerLoadBalancer: boolToPointer(true),
|
||||
EnableReplicaPoolerLoadBalancer: boolToPointer(true),
|
||||
},
|
||||
cluster: cluster,
|
||||
expectedType: map[PostgresRole]v1.ServiceType{
|
||||
Master: v1.ServiceTypeLoadBalancer,
|
||||
Replica: v1.ServiceTypeLoadBalancer,
|
||||
},
|
||||
},
|
||||
{
|
||||
subTest: "LoadBalancer for master",
|
||||
spec: &acidv1.PostgresSpec{
|
||||
ConnectionPooler: &acidv1.ConnectionPooler{},
|
||||
EnableMasterPoolerLoadBalancer: boolToPointer(true),
|
||||
},
|
||||
cluster: cluster,
|
||||
expectedType: map[PostgresRole]v1.ServiceType{
|
||||
Master: v1.ServiceTypeLoadBalancer,
|
||||
Replica: v1.ServiceTypeClusterIP,
|
||||
},
|
||||
},
|
||||
{
|
||||
subTest: "LoadBalancer for replica",
|
||||
spec: &acidv1.PostgresSpec{
|
||||
ConnectionPooler: &acidv1.ConnectionPooler{},
|
||||
EnableReplicaPoolerLoadBalancer: boolToPointer(true),
|
||||
},
|
||||
cluster: cluster,
|
||||
expectedType: map[PostgresRole]v1.ServiceType{
|
||||
Master: v1.ServiceTypeClusterIP,
|
||||
Replica: v1.ServiceTypeLoadBalancer,
|
||||
},
|
||||
},
|
||||
{
|
||||
subTest: "NodePort for both roles",
|
||||
spec: &acidv1.PostgresSpec{
|
||||
ConnectionPooler: &acidv1.ConnectionPooler{},
|
||||
EnableMasterPoolerNodePort: boolToPointer(true),
|
||||
EnableReplicaPoolerNodePort: boolToPointer(true),
|
||||
},
|
||||
cluster: cluster,
|
||||
expectedType: map[PostgresRole]v1.ServiceType{
|
||||
Master: v1.ServiceTypeNodePort,
|
||||
Replica: v1.ServiceTypeNodePort,
|
||||
},
|
||||
},
|
||||
{
|
||||
subTest: "NodePort for master",
|
||||
spec: &acidv1.PostgresSpec{
|
||||
ConnectionPooler: &acidv1.ConnectionPooler{},
|
||||
EnableMasterPoolerNodePort: boolToPointer(true),
|
||||
},
|
||||
cluster: cluster,
|
||||
expectedType: map[PostgresRole]v1.ServiceType{
|
||||
Master: v1.ServiceTypeNodePort,
|
||||
Replica: v1.ServiceTypeClusterIP,
|
||||
},
|
||||
},
|
||||
{
|
||||
subTest: "NodePort for replica",
|
||||
spec: &acidv1.PostgresSpec{
|
||||
ConnectionPooler: &acidv1.ConnectionPooler{},
|
||||
EnableReplicaPoolerNodePort: boolToPointer(true),
|
||||
},
|
||||
cluster: cluster,
|
||||
expectedType: map[PostgresRole]v1.ServiceType{
|
||||
Master: v1.ServiceTypeClusterIP,
|
||||
Replica: v1.ServiceTypeNodePort,
|
||||
},
|
||||
},
|
||||
{
|
||||
subTest: "NodePort overrides LoadBalancer for both roles",
|
||||
spec: &acidv1.PostgresSpec{
|
||||
ConnectionPooler: &acidv1.ConnectionPooler{},
|
||||
EnableMasterPoolerLoadBalancer: boolToPointer(true),
|
||||
EnableReplicaPoolerLoadBalancer: boolToPointer(true),
|
||||
EnableMasterPoolerNodePort: boolToPointer(true),
|
||||
EnableReplicaPoolerNodePort: boolToPointer(true),
|
||||
},
|
||||
cluster: cluster,
|
||||
expectedType: map[PostgresRole]v1.ServiceType{
|
||||
Master: v1.ServiceTypeNodePort,
|
||||
Replica: v1.ServiceTypeNodePort,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
roles := []PostgresRole{Master, Replica}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt.cluster.Spec = *tt.spec
|
||||
|
||||
for _, role := range roles {
|
||||
svc := tt.cluster.generateConnectionPoolerService(tt.cluster.ConnectionPooler[role])
|
||||
|
||||
expected, ok := tt.expectedType[role]
|
||||
if !ok {
|
||||
t.Fatalf("%s [%s]: missing expectedType for role %v", testName, tt.subTest, role)
|
||||
}
|
||||
|
||||
if svc.Spec.Type != expected {
|
||||
t.Errorf("%s [%s] role=%s: service Type is incorrect, got %s, expected %s",
|
||||
testName, tt.subTest, role, svc.Spec.Type, expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -366,7 +366,7 @@ func generateSpiloJSONConfiguration(pg *acidv1.PostgresqlParam, patroni *acidv1.
|
|||
|
||||
config.Bootstrap = pgBootstrap{}
|
||||
|
||||
config.Bootstrap.Initdb = []interface{}{map[string]string{"auth-host": "md5"},
|
||||
config.Bootstrap.Initdb = []interface{}{map[string]string{"auth-host": "scram-sha-256"},
|
||||
map[string]string{"auth-local": "trust"}}
|
||||
|
||||
initdbOptionNames := []string{}
|
||||
|
|
@ -1165,7 +1165,7 @@ func (c *Cluster) getPodEnvironmentSecretVariables() ([]v1.EnvVar, error) {
|
|||
|
||||
secret := &v1.Secret{}
|
||||
var notFoundErr error
|
||||
err := retryutil.Retry(c.OpConfig.ResourceCheckInterval, c.OpConfig.ResourceCheckTimeout,
|
||||
err := retryutil.Retry(c.OpConfig.ResourceCheckInterval.Duration, c.OpConfig.ResourceCheckTimeout.Duration,
|
||||
func() (bool, error) {
|
||||
var err error
|
||||
secret, err = c.KubeClient.Secrets(c.Namespace).Get(
|
||||
|
|
@ -1299,6 +1299,19 @@ func generateSpiloReadinessProbe() *v1.Probe {
|
|||
}
|
||||
}
|
||||
|
||||
func generateSpiloLivenessProbe(probe, defaultProbe *v1.Probe) *v1.Probe {
|
||||
|
||||
if probe != nil {
|
||||
return probe
|
||||
}
|
||||
|
||||
if defaultProbe != nil {
|
||||
return defaultProbe
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.StatefulSet, error) {
|
||||
|
||||
var (
|
||||
|
|
@ -1405,6 +1418,8 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef
|
|||
spiloContainer.ReadinessProbe = generateSpiloReadinessProbe()
|
||||
}
|
||||
|
||||
spiloContainer.LivenessProbe = generateSpiloLivenessProbe(spec.LivenessProbe, c.OpConfig.LivenessProbe)
|
||||
|
||||
// generate container specs for sidecars specified in the cluster manifest
|
||||
clusterSpecificSidecars := []v1.Container{}
|
||||
if len(spec.Sidecars) > 0 {
|
||||
|
|
@ -1454,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)
|
||||
}
|
||||
|
|
@ -1483,7 +1498,7 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef
|
|||
&tolerationSpec,
|
||||
c.nodeAffinity(c.OpConfig.NodeReadinessLabel, spec.NodeAffinity),
|
||||
spec.SchedulerName,
|
||||
int64(c.OpConfig.PodTerminateGracePeriod.Seconds()),
|
||||
int64(util.CoalesceDuration(c.OpConfig.PodTerminateGracePeriod, "5m").Seconds()),
|
||||
c.OpConfig.PodServiceAccountName,
|
||||
c.OpConfig.KubeIAMRole,
|
||||
effectivePodPriorityClassName,
|
||||
|
|
@ -1989,6 +2004,37 @@ func (c *Cluster) shouldCreateLoadBalancerForService(role PostgresRole, spec *ac
|
|||
|
||||
}
|
||||
|
||||
func (c *Cluster) shouldCreateNodePortForService(role PostgresRole, spec *acidv1.PostgresSpec) (bool, int32) {
|
||||
switch role {
|
||||
case Replica:
|
||||
// if the value is explicitly set in a Postgresql manifest, follow this setting
|
||||
if spec.EnableReplicaNodePort != nil {
|
||||
port := int32(0)
|
||||
if spec.ReplicaNodePort != nil {
|
||||
port = *spec.ReplicaNodePort
|
||||
}
|
||||
|
||||
return *spec.EnableReplicaNodePort, port
|
||||
}
|
||||
|
||||
// otherwise, follow the operator configuration
|
||||
return c.OpConfig.EnableReplicaNodePort, 0
|
||||
case Master:
|
||||
if spec.EnableMasterNodePort != nil {
|
||||
port := int32(0)
|
||||
if spec.MasterNodePort != nil {
|
||||
port = *spec.MasterNodePort
|
||||
}
|
||||
|
||||
return *spec.EnableMasterNodePort, port
|
||||
}
|
||||
|
||||
return c.OpConfig.EnableMasterNodePort, 0
|
||||
default:
|
||||
panic(fmt.Sprintf("Unknown role %v", role))
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cluster) generateService(role PostgresRole, spec *acidv1.PostgresSpec) *v1.Service {
|
||||
serviceSpec := v1.ServiceSpec{
|
||||
Ports: []v1.ServicePort{{Name: "postgresql", Port: pgPort, TargetPort: intstr.IntOrString{IntVal: pgPort}}},
|
||||
|
|
@ -2001,7 +2047,9 @@ func (c *Cluster) generateService(role PostgresRole, spec *acidv1.PostgresSpec)
|
|||
serviceSpec.Selector = c.roleLabelsSet(false, role)
|
||||
}
|
||||
|
||||
if c.shouldCreateLoadBalancerForService(role, spec) {
|
||||
if ok, port := c.shouldCreateNodePortForService(role, spec); ok {
|
||||
c.configureNodePortService(&serviceSpec, port)
|
||||
} else if c.shouldCreateLoadBalancerForService(role, spec) {
|
||||
c.configureLoadBalanceService(&serviceSpec, spec.AllowedSourceRanges)
|
||||
}
|
||||
|
||||
|
|
@ -2034,10 +2082,21 @@ func (c *Cluster) configureLoadBalanceService(serviceSpec *v1.ServiceSpec, sourc
|
|||
serviceSpec.Type = v1.ServiceTypeLoadBalancer
|
||||
}
|
||||
|
||||
func (c *Cluster) configureNodePortService(serviceSpec *v1.ServiceSpec, port int32) {
|
||||
serviceSpec.ExternalTrafficPolicy = v1.ServiceExternalTrafficPolicyType(c.OpConfig.ExternalTrafficPolicy)
|
||||
serviceSpec.Type = v1.ServiceTypeNodePort
|
||||
|
||||
if port != 0 && len(serviceSpec.Ports) > 0 {
|
||||
serviceSpec.Ports[0].NodePort = port
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cluster) generateServiceAnnotations(role PostgresRole, spec *acidv1.PostgresSpec) map[string]string {
|
||||
annotations := c.getCustomServiceAnnotations(role, spec)
|
||||
|
||||
if c.shouldCreateLoadBalancerForService(role, spec) {
|
||||
nodePort, _ := c.shouldCreateNodePortForService(role, spec)
|
||||
|
||||
if !nodePort && c.shouldCreateLoadBalancerForService(role, spec) {
|
||||
dnsName := c.dnsName(role)
|
||||
|
||||
// External DNS name annotation is not customizable
|
||||
|
|
@ -2372,7 +2431,7 @@ func (c *Cluster) generateLogicalBackupJob() (*batchv1.CronJob, error) {
|
|||
&tolerationsSpec,
|
||||
c.nodeAffinity(c.OpConfig.NodeReadinessLabel, nil),
|
||||
nil,
|
||||
int64(c.OpConfig.PodTerminateGracePeriod.Seconds()),
|
||||
int64(util.CoalesceDuration(c.OpConfig.PodTerminateGracePeriod, "5m").Seconds()),
|
||||
c.OpConfig.PodServiceAccountName,
|
||||
c.OpConfig.KubeIAMRole,
|
||||
"",
|
||||
|
|
@ -2393,12 +2452,22 @@ 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
|
||||
|
||||
jobTemplateSpec := batchv1.JobTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: labels,
|
||||
Annotations: c.annotationsSet(annotations),
|
||||
},
|
||||
Spec: jobSpec,
|
||||
}
|
||||
|
||||
|
|
@ -2407,18 +2476,32 @@ 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(),
|
||||
Namespace: c.Namespace,
|
||||
Labels: c.labelsSet(true),
|
||||
Annotations: c.annotationsSet(nil),
|
||||
Labels: labels,
|
||||
Annotations: c.annotationsSet(annotations),
|
||||
OwnerReferences: c.ownerReferences(),
|
||||
},
|
||||
Spec: batchv1.CronJobSpec{
|
||||
Schedule: schedule,
|
||||
JobTemplate: jobTemplateSpec,
|
||||
ConcurrencyPolicy: batchv1.ForbidConcurrent,
|
||||
Schedule: schedule,
|
||||
JobTemplate: jobTemplateSpec,
|
||||
ConcurrencyPolicy: batchv1.ForbidConcurrent,
|
||||
SuccessfulJobsHistoryLimit: successfulJobsHistoryLimit,
|
||||
FailedJobsHistoryLimit: failedJobsHistoryLimit,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import (
|
|||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
|
|
@ -79,7 +78,7 @@ func TestGenerateSpiloJSONConfiguration(t *testing.T) {
|
|||
PamRoleName: "zalandos",
|
||||
},
|
||||
},
|
||||
result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/18/bin"},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"}],"dcs":{}}}`,
|
||||
result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/18/bin"},"bootstrap":{"initdb":[{"auth-host":"scram-sha-256"},{"auth-local":"trust"}],"dcs":{}}}`,
|
||||
},
|
||||
{
|
||||
subtest: "Patroni configured",
|
||||
|
|
@ -90,7 +89,7 @@ func TestGenerateSpiloJSONConfiguration(t *testing.T) {
|
|||
"locale": "en_US.UTF-8",
|
||||
"data-checksums": "true",
|
||||
},
|
||||
PgHba: []string{"hostssl all all 0.0.0.0/0 md5", "host all all 0.0.0.0/0 md5"},
|
||||
PgHba: []string{"hostssl all all 0.0.0.0/0 scram-sha-256", "host all all 0.0.0.0/0 scram-sha-256"},
|
||||
TTL: 30,
|
||||
LoopWait: 10,
|
||||
RetryTimeout: 10,
|
||||
|
|
@ -102,7 +101,7 @@ func TestGenerateSpiloJSONConfiguration(t *testing.T) {
|
|||
FailsafeMode: util.True(),
|
||||
},
|
||||
opConfig: &config.Config{},
|
||||
result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/18/bin","pg_hba":["hostssl all all 0.0.0.0/0 md5","host all all 0.0.0.0/0 md5"]},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"},"data-checksums",{"encoding":"UTF8"},{"locale":"en_US.UTF-8"}],"dcs":{"ttl":30,"loop_wait":10,"retry_timeout":10,"maximum_lag_on_failover":33554432,"synchronous_mode":true,"synchronous_mode_strict":true,"synchronous_node_count":1,"slots":{"permanent_logical_1":{"database":"foo","plugin":"pgoutput","type":"logical"}},"failsafe_mode":true}}}`,
|
||||
result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/18/bin","pg_hba":["hostssl all all 0.0.0.0/0 scram-sha-256","host all all 0.0.0.0/0 scram-sha-256"]},"bootstrap":{"initdb":[{"auth-host":"scram-sha-256"},{"auth-local":"trust"},"data-checksums",{"encoding":"UTF8"},{"locale":"en_US.UTF-8"}],"dcs":{"ttl":30,"loop_wait":10,"retry_timeout":10,"maximum_lag_on_failover":33554432,"synchronous_mode":true,"synchronous_mode_strict":true,"synchronous_node_count":1,"slots":{"permanent_logical_1":{"database":"foo","plugin":"pgoutput","type":"logical"}},"failsafe_mode":true}}}`,
|
||||
},
|
||||
{
|
||||
subtest: "Patroni failsafe_mode configured globally",
|
||||
|
|
@ -111,7 +110,7 @@ func TestGenerateSpiloJSONConfiguration(t *testing.T) {
|
|||
opConfig: &config.Config{
|
||||
EnablePatroniFailsafeMode: util.True(),
|
||||
},
|
||||
result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/18/bin"},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"}],"dcs":{"failsafe_mode":true}}}`,
|
||||
result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/18/bin"},"bootstrap":{"initdb":[{"auth-host":"scram-sha-256"},{"auth-local":"trust"}],"dcs":{"failsafe_mode":true}}}`,
|
||||
},
|
||||
{
|
||||
subtest: "Patroni failsafe_mode configured globally, disabled for cluster",
|
||||
|
|
@ -122,7 +121,7 @@ func TestGenerateSpiloJSONConfiguration(t *testing.T) {
|
|||
opConfig: &config.Config{
|
||||
EnablePatroniFailsafeMode: util.True(),
|
||||
},
|
||||
result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/18/bin"},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"}],"dcs":{"failsafe_mode":false}}}`,
|
||||
result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/18/bin"},"bootstrap":{"initdb":[{"auth-host":"scram-sha-256"},{"auth-local":"trust"}],"dcs":{"failsafe_mode":false}}}`,
|
||||
},
|
||||
{
|
||||
subtest: "Patroni failsafe_mode disabled globally, configured for cluster",
|
||||
|
|
@ -133,7 +132,7 @@ func TestGenerateSpiloJSONConfiguration(t *testing.T) {
|
|||
opConfig: &config.Config{
|
||||
EnablePatroniFailsafeMode: util.False(),
|
||||
},
|
||||
result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/18/bin"},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"}],"dcs":{"failsafe_mode":true}}}`,
|
||||
result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/18/bin"},"bootstrap":{"initdb":[{"auth-host":"scram-sha-256"},{"auth-local":"trust"}],"dcs":{"failsafe_mode":true}}}`,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
|
|
@ -361,8 +360,8 @@ func TestPodEnvironmentSecretVariables(t *testing.T) {
|
|||
opConfig: config.Config{
|
||||
Resources: config.Resources{
|
||||
PodEnvironmentSecret: testPodEnvironmentObjectNotExists,
|
||||
ResourceCheckInterval: time.Duration(testResourceCheckInterval),
|
||||
ResourceCheckTimeout: time.Duration(testResourceCheckTimeout),
|
||||
ResourceCheckInterval: &metav1.Duration{Duration: testResourceCheckInterval},
|
||||
ResourceCheckTimeout: &metav1.Duration{Duration: testResourceCheckTimeout},
|
||||
},
|
||||
},
|
||||
err: fmt.Errorf("could not read Secret PodEnvironmentSecretName: still failing after %d retries: secret.core %q not found", maxRetries, testPodEnvironmentObjectNotExists),
|
||||
|
|
@ -372,8 +371,8 @@ func TestPodEnvironmentSecretVariables(t *testing.T) {
|
|||
opConfig: config.Config{
|
||||
Resources: config.Resources{
|
||||
PodEnvironmentSecret: testPodEnvironmentSecretNameAPIError,
|
||||
ResourceCheckInterval: time.Duration(testResourceCheckInterval),
|
||||
ResourceCheckTimeout: time.Duration(testResourceCheckTimeout),
|
||||
ResourceCheckInterval: &metav1.Duration{Duration: testResourceCheckInterval},
|
||||
ResourceCheckTimeout: &metav1.Duration{Duration: testResourceCheckTimeout},
|
||||
},
|
||||
},
|
||||
err: fmt.Errorf("could not read Secret PodEnvironmentSecretName: Secret PodEnvironmentSecret API error"),
|
||||
|
|
@ -383,8 +382,8 @@ func TestPodEnvironmentSecretVariables(t *testing.T) {
|
|||
opConfig: config.Config{
|
||||
Resources: config.Resources{
|
||||
PodEnvironmentSecret: testPodEnvironmentSecretName,
|
||||
ResourceCheckInterval: time.Duration(testResourceCheckInterval),
|
||||
ResourceCheckTimeout: time.Duration(testResourceCheckTimeout),
|
||||
ResourceCheckInterval: &metav1.Duration{Duration: testResourceCheckInterval},
|
||||
ResourceCheckTimeout: &metav1.Duration{Duration: testResourceCheckTimeout},
|
||||
},
|
||||
},
|
||||
envVars: []v1.EnvVar{
|
||||
|
|
@ -848,8 +847,8 @@ func TestGenerateSpiloPodEnvVars(t *testing.T) {
|
|||
Name: testPodEnvironmentConfigMapName,
|
||||
},
|
||||
PodEnvironmentSecret: testPodEnvironmentSecretName,
|
||||
ResourceCheckInterval: time.Duration(testResourceCheckInterval),
|
||||
ResourceCheckTimeout: time.Duration(testResourceCheckTimeout),
|
||||
ResourceCheckInterval: &metav1.Duration{Duration: testResourceCheckInterval},
|
||||
ResourceCheckTimeout: &metav1.Duration{Duration: testResourceCheckTimeout},
|
||||
},
|
||||
},
|
||||
cloneDescription: &acidv1.CloneDescription{},
|
||||
|
|
@ -877,8 +876,8 @@ func TestGenerateSpiloPodEnvVars(t *testing.T) {
|
|||
Name: testPodEnvironmentConfigMapName,
|
||||
},
|
||||
PodEnvironmentSecret: testPodEnvironmentSecretName,
|
||||
ResourceCheckInterval: time.Duration(testResourceCheckInterval),
|
||||
ResourceCheckTimeout: time.Duration(testResourceCheckTimeout),
|
||||
ResourceCheckInterval: &metav1.Duration{Duration: testResourceCheckInterval},
|
||||
ResourceCheckTimeout: &metav1.Duration{Duration: testResourceCheckTimeout},
|
||||
},
|
||||
},
|
||||
cloneDescription: &acidv1.CloneDescription{},
|
||||
|
|
@ -968,8 +967,8 @@ func TestGenerateSpiloPodEnvVars(t *testing.T) {
|
|||
opConfig: config.Config{
|
||||
Resources: config.Resources{
|
||||
PodEnvironmentSecret: testPodEnvironmentSecretName,
|
||||
ResourceCheckInterval: time.Duration(testResourceCheckInterval),
|
||||
ResourceCheckTimeout: time.Duration(testResourceCheckTimeout),
|
||||
ResourceCheckInterval: &metav1.Duration{Duration: testResourceCheckInterval},
|
||||
ResourceCheckTimeout: &metav1.Duration{Duration: testResourceCheckTimeout},
|
||||
},
|
||||
WALES3Bucket: "global-s3-bucket",
|
||||
},
|
||||
|
|
@ -986,8 +985,8 @@ func TestGenerateSpiloPodEnvVars(t *testing.T) {
|
|||
opConfig: config.Config{
|
||||
Resources: config.Resources{
|
||||
PodEnvironmentSecret: testPodEnvironmentSecretName,
|
||||
ResourceCheckInterval: time.Duration(testResourceCheckInterval),
|
||||
ResourceCheckTimeout: time.Duration(testResourceCheckTimeout),
|
||||
ResourceCheckInterval: &metav1.Duration{Duration: testResourceCheckInterval},
|
||||
ResourceCheckTimeout: &metav1.Duration{Duration: testResourceCheckTimeout},
|
||||
},
|
||||
WALES3Bucket: "global-s3-bucket",
|
||||
},
|
||||
|
|
@ -2971,32 +2970,32 @@ func newLBFakeClient() (k8sutil.KubernetesClient, *fake.Clientset) {
|
|||
}, clientSet
|
||||
}
|
||||
|
||||
func getServices(serviceType v1.ServiceType, sourceRanges []string, extTrafficPolicy, clusterName string) []v1.ServiceSpec {
|
||||
func getServices(serviceType v1.ServiceType, sourceRanges []string, extTrafficPolicy, clusterName string, nodePort int32) []v1.ServiceSpec {
|
||||
return []v1.ServiceSpec{
|
||||
{
|
||||
ExternalTrafficPolicy: v1.ServiceExternalTrafficPolicyType(extTrafficPolicy),
|
||||
LoadBalancerSourceRanges: sourceRanges,
|
||||
Ports: []v1.ServicePort{{Name: "postgresql", Port: 5432, TargetPort: intstr.IntOrString{IntVal: 5432}}},
|
||||
Ports: []v1.ServicePort{{Name: "postgresql", Port: 5432, TargetPort: intstr.IntOrString{IntVal: 5432}, NodePort: nodePort}},
|
||||
Type: serviceType,
|
||||
},
|
||||
{
|
||||
ExternalTrafficPolicy: v1.ServiceExternalTrafficPolicyType(extTrafficPolicy),
|
||||
LoadBalancerSourceRanges: sourceRanges,
|
||||
Ports: []v1.ServicePort{{Name: clusterName + "-pooler", Port: 5432, TargetPort: intstr.IntOrString{IntVal: 5432}}},
|
||||
Ports: []v1.ServicePort{{Name: clusterName + "-pooler", Port: 5432, TargetPort: intstr.IntOrString{IntVal: 5432}, NodePort: nodePort}},
|
||||
Selector: map[string]string{"connection-pooler": clusterName + "-pooler"},
|
||||
Type: serviceType,
|
||||
},
|
||||
{
|
||||
ExternalTrafficPolicy: v1.ServiceExternalTrafficPolicyType(extTrafficPolicy),
|
||||
LoadBalancerSourceRanges: sourceRanges,
|
||||
Ports: []v1.ServicePort{{Name: "postgresql", Port: 5432, TargetPort: intstr.IntOrString{IntVal: 5432}}},
|
||||
Ports: []v1.ServicePort{{Name: "postgresql", Port: 5432, TargetPort: intstr.IntOrString{IntVal: 5432}, NodePort: nodePort}},
|
||||
Selector: map[string]string{"spilo-role": "replica", "application": "spilo", "cluster-name": clusterName},
|
||||
Type: serviceType,
|
||||
},
|
||||
{
|
||||
ExternalTrafficPolicy: v1.ServiceExternalTrafficPolicyType(extTrafficPolicy),
|
||||
LoadBalancerSourceRanges: sourceRanges,
|
||||
Ports: []v1.ServicePort{{Name: clusterName + "-pooler-repl", Port: 5432, TargetPort: intstr.IntOrString{IntVal: 5432}}},
|
||||
Ports: []v1.ServicePort{{Name: clusterName + "-pooler-repl", Port: 5432, TargetPort: intstr.IntOrString{IntVal: 5432}, NodePort: nodePort}},
|
||||
Selector: map[string]string{"connection-pooler": clusterName + "-pooler-repl"},
|
||||
Type: serviceType,
|
||||
},
|
||||
|
|
@ -3064,7 +3063,7 @@ func TestEnableLoadBalancers(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
expectedServices: getServices(v1.ServiceTypeClusterIP, nil, "", clusterName),
|
||||
expectedServices: getServices(v1.ServiceTypeClusterIP, nil, "", clusterName, 0),
|
||||
},
|
||||
{
|
||||
subTest: "LBs enabled in manifest, disabled in config",
|
||||
|
|
@ -3111,7 +3110,7 @@ func TestEnableLoadBalancers(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
expectedServices: getServices(v1.ServiceTypeLoadBalancer, sourceRanges, extTrafficPolicy, clusterName),
|
||||
expectedServices: getServices(v1.ServiceTypeLoadBalancer, sourceRanges, extTrafficPolicy, clusterName, 0),
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -3143,6 +3142,195 @@ func TestEnableLoadBalancers(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestEnableNodePorts(t *testing.T) {
|
||||
clusterName := "acid-test-cluster"
|
||||
namespace := "default"
|
||||
clusterNameLabel := "cluster-name"
|
||||
roleLabel := "spilo-role"
|
||||
roles := []PostgresRole{Master, Replica}
|
||||
extTrafficPolicy := "Cluster"
|
||||
port := int32(1337)
|
||||
|
||||
tests := []struct {
|
||||
subTest string
|
||||
config config.Config
|
||||
pgSpec acidv1.Postgresql
|
||||
expectedServices []v1.ServiceSpec
|
||||
}{
|
||||
{
|
||||
subTest: "NodePorts enabled in config, disabled in manifest",
|
||||
config: config.Config{
|
||||
ConnectionPooler: config.ConnectionPooler{
|
||||
ConnectionPoolerDefaultCPURequest: "100m",
|
||||
ConnectionPoolerDefaultCPULimit: "100m",
|
||||
ConnectionPoolerDefaultMemoryRequest: "100Mi",
|
||||
ConnectionPoolerDefaultMemoryLimit: "100Mi",
|
||||
NumberOfInstances: k8sutil.Int32ToPointer(1),
|
||||
},
|
||||
EnableMasterNodePort: true,
|
||||
EnableMasterPoolerNodePort: true,
|
||||
EnableReplicaNodePort: true,
|
||||
EnableReplicaPoolerNodePort: true,
|
||||
ExternalTrafficPolicy: extTrafficPolicy,
|
||||
Resources: config.Resources{
|
||||
ClusterLabels: map[string]string{"application": "spilo"},
|
||||
ClusterNameLabel: clusterNameLabel,
|
||||
PodRoleLabel: roleLabel,
|
||||
},
|
||||
},
|
||||
pgSpec: acidv1.Postgresql{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: clusterName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: acidv1.PostgresSpec{
|
||||
EnableConnectionPooler: util.True(),
|
||||
EnableReplicaConnectionPooler: util.True(),
|
||||
EnableMasterNodePort: util.False(),
|
||||
EnableMasterPoolerNodePort: util.False(),
|
||||
EnableReplicaNodePort: util.False(),
|
||||
EnableReplicaPoolerNodePort: util.False(),
|
||||
NumberOfInstances: 1,
|
||||
Resources: &acidv1.Resources{
|
||||
ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")},
|
||||
ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")},
|
||||
},
|
||||
TeamID: "acid",
|
||||
Volume: acidv1.Volume{
|
||||
Size: "1G",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedServices: getServices(v1.ServiceTypeClusterIP, nil, "", clusterName, 0),
|
||||
},
|
||||
{
|
||||
subTest: "NodePorts configured in manifest, disabled in config",
|
||||
config: config.Config{
|
||||
ConnectionPooler: config.ConnectionPooler{
|
||||
ConnectionPoolerDefaultCPURequest: "100m",
|
||||
ConnectionPoolerDefaultCPULimit: "100m",
|
||||
ConnectionPoolerDefaultMemoryRequest: "100Mi",
|
||||
ConnectionPoolerDefaultMemoryLimit: "100Mi",
|
||||
NumberOfInstances: k8sutil.Int32ToPointer(1),
|
||||
},
|
||||
EnableMasterNodePort: false,
|
||||
EnableMasterPoolerNodePort: false,
|
||||
EnableReplicaNodePort: false,
|
||||
EnableReplicaPoolerNodePort: false,
|
||||
ExternalTrafficPolicy: extTrafficPolicy,
|
||||
Resources: config.Resources{
|
||||
ClusterLabels: map[string]string{"application": "spilo"},
|
||||
ClusterNameLabel: clusterNameLabel,
|
||||
PodRoleLabel: roleLabel,
|
||||
},
|
||||
},
|
||||
pgSpec: acidv1.Postgresql{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: clusterName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: acidv1.PostgresSpec{
|
||||
EnableConnectionPooler: util.True(),
|
||||
EnableReplicaConnectionPooler: util.True(),
|
||||
EnableMasterNodePort: util.True(),
|
||||
EnableMasterPoolerNodePort: util.True(),
|
||||
EnableReplicaNodePort: util.True(),
|
||||
EnableReplicaPoolerNodePort: util.True(),
|
||||
NumberOfInstances: 1,
|
||||
Resources: &acidv1.Resources{
|
||||
ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")},
|
||||
ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")},
|
||||
},
|
||||
TeamID: "acid",
|
||||
Volume: acidv1.Volume{
|
||||
Size: "1G",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedServices: getServices(v1.ServiceTypeNodePort, nil, extTrafficPolicy, clusterName, 0),
|
||||
},
|
||||
{
|
||||
subTest: "NodePorts configured in manifest, disabled in config, custom port specified",
|
||||
config: config.Config{
|
||||
ConnectionPooler: config.ConnectionPooler{
|
||||
ConnectionPoolerDefaultCPURequest: "100m",
|
||||
ConnectionPoolerDefaultCPULimit: "100m",
|
||||
ConnectionPoolerDefaultMemoryRequest: "100Mi",
|
||||
ConnectionPoolerDefaultMemoryLimit: "100Mi",
|
||||
NumberOfInstances: k8sutil.Int32ToPointer(1),
|
||||
},
|
||||
EnableMasterNodePort: false,
|
||||
EnableMasterPoolerNodePort: false,
|
||||
EnableReplicaNodePort: false,
|
||||
EnableReplicaPoolerNodePort: false,
|
||||
ExternalTrafficPolicy: extTrafficPolicy,
|
||||
Resources: config.Resources{
|
||||
ClusterLabels: map[string]string{"application": "spilo"},
|
||||
ClusterNameLabel: clusterNameLabel,
|
||||
PodRoleLabel: roleLabel,
|
||||
},
|
||||
},
|
||||
pgSpec: acidv1.Postgresql{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: clusterName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: acidv1.PostgresSpec{
|
||||
EnableConnectionPooler: util.True(),
|
||||
EnableReplicaConnectionPooler: util.True(),
|
||||
EnableMasterNodePort: util.True(),
|
||||
MasterNodePort: &port,
|
||||
EnableMasterPoolerNodePort: util.True(),
|
||||
MasterPoolerNodePort: &port,
|
||||
EnableReplicaNodePort: util.True(),
|
||||
ReplicaNodePort: &port,
|
||||
EnableReplicaPoolerNodePort: util.True(),
|
||||
ReplicaPoolerNodePort: &port,
|
||||
NumberOfInstances: 1,
|
||||
Resources: &acidv1.Resources{
|
||||
ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")},
|
||||
ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")},
|
||||
},
|
||||
TeamID: "acid",
|
||||
Volume: acidv1.Volume{
|
||||
Size: "1G",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedServices: getServices(v1.ServiceTypeNodePort, nil, extTrafficPolicy, clusterName, port),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
client, _ := newLBFakeClient()
|
||||
|
||||
var cluster = New(
|
||||
Config{
|
||||
OpConfig: tt.config,
|
||||
}, client, tt.pgSpec, logger, eventRecorder)
|
||||
|
||||
cluster.Name = clusterName
|
||||
cluster.Namespace = namespace
|
||||
cluster.ConnectionPooler = map[PostgresRole]*ConnectionPoolerObjects{}
|
||||
generatedServices := make([]v1.ServiceSpec, 0)
|
||||
for _, role := range roles {
|
||||
cluster.syncService(role)
|
||||
cluster.ConnectionPooler[role] = &ConnectionPoolerObjects{
|
||||
Name: cluster.connectionPoolerName(role),
|
||||
ClusterName: cluster.Name,
|
||||
Namespace: cluster.Namespace,
|
||||
Role: role,
|
||||
}
|
||||
cluster.syncConnectionPoolerWorker(&tt.pgSpec, &tt.pgSpec, role)
|
||||
generatedServices = append(generatedServices, cluster.Services[role].Spec)
|
||||
generatedServices = append(generatedServices, cluster.ConnectionPooler[role].Service.Spec)
|
||||
}
|
||||
if !reflect.DeepEqual(tt.expectedServices, generatedServices) {
|
||||
t.Errorf("%s %s: expected %#v but got %#v", t.Name(), tt.subTest, tt.expectedServices, generatedServices)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateResourceRequirements(t *testing.T) {
|
||||
client, _ := newFakeK8sTestClient()
|
||||
clusterName := "acid-test-cluster"
|
||||
|
|
@ -3875,7 +4063,7 @@ func TestGenerateLogicalBackupJob(t *testing.T) {
|
|||
ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("100m"), Memory: k8sutil.StringToPointer("100Mi")},
|
||||
ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("500Mi")},
|
||||
},
|
||||
expectedLabel: map[string]string{configResources.ClusterNameLabel: clusterName, "team": teamId},
|
||||
expectedLabel: map[string]string{"application": "spilo-logical-backup", configResources.ClusterNameLabel: clusterName, "team": teamId},
|
||||
expectedAnnotation: nil,
|
||||
},
|
||||
{
|
||||
|
|
@ -3900,7 +4088,7 @@ func TestGenerateLogicalBackupJob(t *testing.T) {
|
|||
ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("10m"), Memory: k8sutil.StringToPointer("50Mi")},
|
||||
ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("300m"), Memory: k8sutil.StringToPointer("300Mi")},
|
||||
},
|
||||
expectedLabel: map[string]string{configResources.ClusterNameLabel: clusterName, "team": teamId},
|
||||
expectedLabel: map[string]string{"application": "spilo-logical-backup", configResources.ClusterNameLabel: clusterName, "team": teamId},
|
||||
expectedAnnotation: nil,
|
||||
},
|
||||
{
|
||||
|
|
@ -3923,7 +4111,7 @@ func TestGenerateLogicalBackupJob(t *testing.T) {
|
|||
ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("50m"), Memory: k8sutil.StringToPointer("100Mi")},
|
||||
ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("250m"), Memory: k8sutil.StringToPointer("500Mi")},
|
||||
},
|
||||
expectedLabel: map[string]string{configResources.ClusterNameLabel: clusterName, "team": teamId},
|
||||
expectedLabel: map[string]string{"application": "spilo-logical-backup", configResources.ClusterNameLabel: clusterName, "team": teamId},
|
||||
expectedAnnotation: nil,
|
||||
},
|
||||
{
|
||||
|
|
@ -3946,7 +4134,7 @@ func TestGenerateLogicalBackupJob(t *testing.T) {
|
|||
ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("100m"), Memory: k8sutil.StringToPointer("200Mi")},
|
||||
ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("200Mi")},
|
||||
},
|
||||
expectedLabel: map[string]string{configResources.ClusterNameLabel: clusterName, "team": teamId},
|
||||
expectedLabel: map[string]string{"application": "spilo-logical-backup", configResources.ClusterNameLabel: clusterName, "team": teamId},
|
||||
expectedAnnotation: nil,
|
||||
},
|
||||
{
|
||||
|
|
@ -3968,7 +4156,7 @@ func TestGenerateLogicalBackupJob(t *testing.T) {
|
|||
ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("100m"), Memory: k8sutil.StringToPointer("100Mi")},
|
||||
ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("500Mi")},
|
||||
},
|
||||
expectedLabel: map[string]string{"labelKey": "labelValue", "cluster-name": clusterName, "team": teamId},
|
||||
expectedLabel: map[string]string{"application": "spilo-logical-backup", "labelKey": "labelValue", "cluster-name": clusterName, "team": teamId},
|
||||
expectedAnnotation: nil,
|
||||
},
|
||||
{
|
||||
|
|
@ -3990,7 +4178,7 @@ func TestGenerateLogicalBackupJob(t *testing.T) {
|
|||
ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("100m"), Memory: k8sutil.StringToPointer("100Mi")},
|
||||
ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("500Mi")},
|
||||
},
|
||||
expectedLabel: map[string]string{configResources.ClusterNameLabel: clusterName, "team": teamId},
|
||||
expectedLabel: map[string]string{"application": "spilo-logical-backup", configResources.ClusterNameLabel: clusterName, "team": teamId},
|
||||
expectedAnnotation: map[string]string{"annotationKey": "annotationValue"},
|
||||
},
|
||||
}
|
||||
|
|
@ -4040,6 +4228,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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -334,7 +334,7 @@ func (c *Cluster) getPatroniConfig(pod *v1.Pod) (acidv1.Patroni, map[string]stri
|
|||
pgParameters map[string]string
|
||||
)
|
||||
podName := util.NameFromMeta(pod.ObjectMeta)
|
||||
err := retryutil.Retry(c.OpConfig.PatroniAPICheckInterval, c.OpConfig.PatroniAPICheckTimeout,
|
||||
err := retryutil.Retry(c.OpConfig.PatroniAPICheckInterval.Duration, c.OpConfig.PatroniAPICheckTimeout.Duration,
|
||||
func() (bool, error) {
|
||||
var err error
|
||||
patroniConfig, pgParameters, err = c.patroni.GetConfig(pod)
|
||||
|
|
@ -355,7 +355,7 @@ func (c *Cluster) getPatroniConfig(pod *v1.Pod) (acidv1.Patroni, map[string]stri
|
|||
|
||||
func (c *Cluster) getPatroniMemberData(pod *v1.Pod) (patroni.MemberData, error) {
|
||||
var memberData patroni.MemberData
|
||||
err := retryutil.Retry(c.OpConfig.PatroniAPICheckInterval, c.OpConfig.PatroniAPICheckTimeout,
|
||||
err := retryutil.Retry(c.OpConfig.PatroniAPICheckInterval.Duration, c.OpConfig.PatroniAPICheckTimeout.Duration,
|
||||
func() (bool, error) {
|
||||
var err error
|
||||
memberData, err = c.patroni.GetMemberData(pod)
|
||||
|
|
@ -506,7 +506,7 @@ func (c *Cluster) getSwitchoverCandidate(master *v1.Pod) (spec.NamespacedName, e
|
|||
candidates := make([]patroni.ClusterMember, 0)
|
||||
syncCandidates := make([]patroni.ClusterMember, 0)
|
||||
|
||||
err := retryutil.Retry(c.OpConfig.PatroniAPICheckInterval, c.OpConfig.PatroniAPICheckTimeout,
|
||||
err := retryutil.Retry(c.OpConfig.PatroniAPICheckInterval.Duration, c.OpConfig.PatroniAPICheckTimeout.Duration,
|
||||
func() (bool, error) {
|
||||
var err error
|
||||
members, err = c.patroni.GetClusterMembers(master)
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import (
|
|||
"github.com/zalando/postgres-operator/pkg/util/k8sutil"
|
||||
"github.com/zalando/postgres-operator/pkg/util/patroni"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestGetSwitchoverCandidate(t *testing.T) {
|
||||
|
|
@ -28,8 +29,8 @@ func TestGetSwitchoverCandidate(t *testing.T) {
|
|||
var cluster = New(
|
||||
Config{
|
||||
OpConfig: config.Config{
|
||||
PatroniAPICheckInterval: time.Duration(1),
|
||||
PatroniAPICheckTimeout: time.Duration(5),
|
||||
PatroniAPICheckInterval: &metav1.Duration{Duration: 1 * time.Second},
|
||||
PatroniAPICheckTimeout: &metav1.Duration{Duration: 5 * time.Second},
|
||||
},
|
||||
}, k8sutil.KubernetesClient{}, acidv1.Postgresql{}, logger, eventRecorder)
|
||||
|
||||
|
|
|
|||
|
|
@ -234,7 +234,7 @@ func (c *Cluster) replaceStatefulSet(newStatefulSet *appsv1.StatefulSet) error {
|
|||
// wait until the statefulset is truly deleted
|
||||
c.logger.Debug("waiting for the statefulset to be deleted")
|
||||
|
||||
err = retryutil.Retry(c.OpConfig.ResourceCheckInterval, c.OpConfig.ResourceCheckTimeout,
|
||||
err = retryutil.Retry(c.OpConfig.ResourceCheckInterval.Duration, c.OpConfig.ResourceCheckTimeout.Duration,
|
||||
func() (bool, error) {
|
||||
_, err2 := c.KubeClient.StatefulSets(oldStatefulset.Namespace).Get(context.TODO(), oldStatefulset.Name, metav1.GetOptions{})
|
||||
if err2 == nil {
|
||||
|
|
@ -324,9 +324,14 @@ func (c *Cluster) updateService(role PostgresRole, oldService *v1.Service, newSe
|
|||
// patch does not work because of LoadBalancerSourceRanges field (even if set to nil)
|
||||
oldServiceType := oldService.Spec.Type
|
||||
newServiceType := newService.Spec.Type
|
||||
if newServiceType == "ClusterIP" && newServiceType != oldServiceType {
|
||||
if newServiceType != oldServiceType && oldServiceType == v1.ServiceTypeLoadBalancer {
|
||||
// Kubernetes rejects updates that change type away from LoadBalancer while
|
||||
// loadBalancerSourceRanges is still set; clear it before updating
|
||||
newService.Spec.LoadBalancerSourceRanges = nil
|
||||
newService.ResourceVersion = oldService.ResourceVersion
|
||||
newService.Spec.ClusterIP = oldService.Spec.ClusterIP
|
||||
if newServiceType == v1.ServiceTypeClusterIP {
|
||||
newService.Spec.ClusterIP = oldService.Spec.ClusterIP
|
||||
}
|
||||
}
|
||||
svc, err = c.KubeClient.Services(serviceName.Namespace).Update(context.TODO(), newService, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
|
|
@ -545,7 +550,7 @@ func (c *Cluster) deletePrimaryPodDisruptionBudget() error {
|
|||
c.logger.Infof("pod disruption budget %q has been deleted", util.NameFromMeta(c.PrimaryPodDisruptionBudget.ObjectMeta))
|
||||
c.PrimaryPodDisruptionBudget = nil
|
||||
|
||||
err = retryutil.Retry(c.OpConfig.ResourceCheckInterval, c.OpConfig.ResourceCheckTimeout,
|
||||
err = retryutil.Retry(c.OpConfig.ResourceCheckInterval.Duration, c.OpConfig.ResourceCheckTimeout.Duration,
|
||||
func() (bool, error) {
|
||||
_, err2 := c.KubeClient.PodDisruptionBudgets(pdbName.Namespace).Get(context.TODO(), pdbName.Name, metav1.GetOptions{})
|
||||
if err2 == nil {
|
||||
|
|
@ -583,7 +588,7 @@ func (c *Cluster) deleteCriticalOpPodDisruptionBudget() error {
|
|||
c.logger.Infof("pod disruption budget %q has been deleted", util.NameFromMeta(c.CriticalOpPodDisruptionBudget.ObjectMeta))
|
||||
c.CriticalOpPodDisruptionBudget = nil
|
||||
|
||||
err = retryutil.Retry(c.OpConfig.ResourceCheckInterval, c.OpConfig.ResourceCheckTimeout,
|
||||
err = retryutil.Retry(c.OpConfig.ResourceCheckInterval.Duration, c.OpConfig.ResourceCheckTimeout.Duration,
|
||||
func() (bool, error) {
|
||||
_, err2 := c.KubeClient.PodDisruptionBudgets(pdbName.Namespace).Get(context.TODO(), pdbName.Name, metav1.GetOptions{})
|
||||
if err2 == nil {
|
||||
|
|
|
|||
|
|
@ -1769,6 +1769,16 @@ func (c *Cluster) syncLogicalBackupJob() error {
|
|||
}
|
||||
c.logger.Info("the logical backup job is synced")
|
||||
}
|
||||
if !reflect.DeepEqual(job.Labels, desiredJob.Labels) {
|
||||
patchData, err := metaLabelsPatch(desiredJob.Labels)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not form patch for the logical backup job %q labels: %v", jobName, err)
|
||||
}
|
||||
_, err = c.KubeClient.CronJobs(c.Namespace).Patch(context.TODO(), jobName, types.MergePatchType, []byte(patchData), metav1.PatchOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not patch labels of the logical backup job %q: %v", jobName, err)
|
||||
}
|
||||
}
|
||||
if changed, _ := c.compareAnnotations(job.Annotations, desiredJob.Annotations, nil); changed {
|
||||
patchData, err := metaAnnotationsPatch(desiredJob.Annotations)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -101,8 +101,8 @@ func TestSyncStatefulSetsAnnotations(t *testing.T) {
|
|||
DefaultMemoryLimit: "300Mi",
|
||||
InheritedAnnotations: []string{inheritedAnnotation},
|
||||
PodRoleLabel: "spilo-role",
|
||||
ResourceCheckInterval: time.Duration(3),
|
||||
ResourceCheckTimeout: time.Duration(10),
|
||||
ResourceCheckInterval: &metav1.Duration{Duration: 3 * time.Second},
|
||||
ResourceCheckTimeout: &metav1.Duration{Duration: 10 * time.Minute},
|
||||
},
|
||||
},
|
||||
}, client, pg, logger, eventRecorder)
|
||||
|
|
@ -187,8 +187,8 @@ func TestPodAnnotationsSync(t *testing.T) {
|
|||
var cluster = New(
|
||||
Config{
|
||||
OpConfig: config.Config{
|
||||
PatroniAPICheckInterval: time.Duration(1),
|
||||
PatroniAPICheckTimeout: time.Duration(5),
|
||||
PatroniAPICheckInterval: &metav1.Duration{Duration: 1 * time.Second},
|
||||
PatroniAPICheckTimeout: &metav1.Duration{Duration: 5 * time.Second},
|
||||
PodManagementPolicy: "ordered_ready",
|
||||
CustomPodAnnotations: customPodAnnotations,
|
||||
ConnectionPooler: config.ConnectionPooler{
|
||||
|
|
@ -207,8 +207,8 @@ func TestPodAnnotationsSync(t *testing.T) {
|
|||
DefaultMemoryLimit: "300Mi",
|
||||
MaxInstances: -1,
|
||||
PodRoleLabel: "spilo-role",
|
||||
ResourceCheckInterval: time.Duration(3),
|
||||
ResourceCheckTimeout: time.Duration(10),
|
||||
ResourceCheckInterval: &metav1.Duration{Duration: 3 * time.Second},
|
||||
ResourceCheckTimeout: &metav1.Duration{Duration: 10 * time.Minute},
|
||||
},
|
||||
},
|
||||
}, client, pg, logger, eventRecorder)
|
||||
|
|
@ -381,8 +381,8 @@ func TestCheckAndSetGlobalPostgreSQLConfiguration(t *testing.T) {
|
|||
DefaultMemoryRequest: "300Mi",
|
||||
DefaultMemoryLimit: "300Mi",
|
||||
PodRoleLabel: "spilo-role",
|
||||
ResourceCheckInterval: time.Duration(3),
|
||||
ResourceCheckTimeout: time.Duration(10),
|
||||
ResourceCheckInterval: &metav1.Duration{Duration: 3 * time.Second},
|
||||
ResourceCheckTimeout: &metav1.Duration{Duration: 10 * time.Minute},
|
||||
},
|
||||
},
|
||||
}, client, pg, logger, eventRecorder)
|
||||
|
|
@ -694,8 +694,8 @@ func TestSyncStandbyClusterConfiguration(t *testing.T) {
|
|||
var cluster = New(
|
||||
Config{
|
||||
OpConfig: config.Config{
|
||||
PatroniAPICheckInterval: time.Duration(1),
|
||||
PatroniAPICheckTimeout: time.Duration(5),
|
||||
PatroniAPICheckInterval: &metav1.Duration{Duration: 1 * time.Second},
|
||||
PatroniAPICheckTimeout: &metav1.Duration{Duration: 5 * time.Second},
|
||||
PodManagementPolicy: "ordered_ready",
|
||||
Resources: config.Resources{
|
||||
ClusterLabels: map[string]string{"application": applicationLabel},
|
||||
|
|
@ -707,8 +707,8 @@ func TestSyncStandbyClusterConfiguration(t *testing.T) {
|
|||
MinInstances: int32(-1),
|
||||
MaxInstances: int32(-1),
|
||||
PodRoleLabel: "spilo-role",
|
||||
ResourceCheckInterval: time.Duration(3),
|
||||
ResourceCheckTimeout: time.Duration(10),
|
||||
ResourceCheckInterval: &metav1.Duration{Duration: 3 * time.Second},
|
||||
ResourceCheckTimeout: &metav1.Duration{Duration: 10 * time.Minute},
|
||||
},
|
||||
},
|
||||
}, client, pg, logger, eventRecorder)
|
||||
|
|
|
|||
|
|
@ -167,6 +167,14 @@ func metaAnnotationsPatch(annotations map[string]string) ([]byte, error) {
|
|||
}{&meta})
|
||||
}
|
||||
|
||||
func metaLabelsPatch(labels map[string]string) ([]byte, error) {
|
||||
var meta metav1.ObjectMeta
|
||||
meta.Labels = labels
|
||||
return json.Marshal(struct {
|
||||
ObjMeta interface{} `json:"metadata"`
|
||||
}{&meta})
|
||||
}
|
||||
|
||||
func (c *Cluster) logPDBChanges(old, new *policyv1.PodDisruptionBudget, isUpdate bool, reason string) {
|
||||
if isUpdate {
|
||||
c.logger.Infof("pod disruption budget %q has been changed", util.NameFromMeta(old.ObjectMeta))
|
||||
|
|
@ -334,7 +342,7 @@ func (c *Cluster) annotationsSet(annotations map[string]string) map[string]strin
|
|||
}
|
||||
|
||||
func (c *Cluster) waitForPodLabel(podEvents chan PodEvent, stopCh chan struct{}, role *PostgresRole) (*v1.Pod, error) {
|
||||
timeout := time.After(c.OpConfig.PodLabelWaitTimeout)
|
||||
timeout := time.After(c.OpConfig.PodLabelWaitTimeout.Duration)
|
||||
for {
|
||||
select {
|
||||
case podEvent := <-podEvents:
|
||||
|
|
@ -356,7 +364,7 @@ func (c *Cluster) waitForPodLabel(podEvents chan PodEvent, stopCh chan struct{},
|
|||
}
|
||||
|
||||
func (c *Cluster) waitForPodDeletion(podEvents chan PodEvent) error {
|
||||
timeout := time.After(c.OpConfig.PodDeletionWaitTimeout)
|
||||
timeout := time.After(c.OpConfig.PodDeletionWaitTimeout.Duration)
|
||||
for {
|
||||
select {
|
||||
case podEvent := <-podEvents:
|
||||
|
|
@ -370,7 +378,7 @@ func (c *Cluster) waitForPodDeletion(podEvents chan PodEvent) error {
|
|||
}
|
||||
|
||||
func (c *Cluster) waitStatefulsetReady() error {
|
||||
return retryutil.Retry(c.OpConfig.ResourceCheckInterval, c.OpConfig.ResourceCheckTimeout,
|
||||
return retryutil.Retry(c.OpConfig.ResourceCheckInterval.Duration, c.OpConfig.ResourceCheckTimeout.Duration,
|
||||
func() (bool, error) {
|
||||
listOptions := metav1.ListOptions{
|
||||
LabelSelector: c.labelsSet(false).String(),
|
||||
|
|
@ -420,7 +428,7 @@ func (c *Cluster) _waitPodLabelsReady(anyReplica bool) error {
|
|||
c.logger.Debug("Waiting for any replica pod to become ready")
|
||||
}
|
||||
|
||||
err := retryutil.Retry(c.OpConfig.ResourceCheckInterval, c.OpConfig.ResourceCheckTimeout,
|
||||
err := retryutil.Retry(c.OpConfig.ResourceCheckInterval.Duration, c.OpConfig.ResourceCheckTimeout.Duration,
|
||||
func() (bool, error) {
|
||||
masterCount := 0
|
||||
if !anyReplica {
|
||||
|
|
|
|||
|
|
@ -297,8 +297,8 @@ func newInheritedAnnotationsCluster(client k8sutil.KubernetesClient) (*Cluster,
|
|||
cluster := New(
|
||||
Config{
|
||||
OpConfig: config.Config{
|
||||
PatroniAPICheckInterval: time.Duration(1),
|
||||
PatroniAPICheckTimeout: time.Duration(5),
|
||||
PatroniAPICheckInterval: &metav1.Duration{Duration: 1 * time.Second},
|
||||
PatroniAPICheckTimeout: &metav1.Duration{Duration: 5 * time.Second},
|
||||
KubernetesUseConfigMaps: true,
|
||||
ConnectionPooler: config.ConnectionPooler{
|
||||
ConnectionPoolerDefaultCPURequest: "100m",
|
||||
|
|
@ -318,8 +318,8 @@ func newInheritedAnnotationsCluster(client k8sutil.KubernetesClient) (*Cluster,
|
|||
DefaultMemoryLimit: "300Mi",
|
||||
InheritedAnnotations: []string{"owned-by"},
|
||||
PodRoleLabel: "spilo-role",
|
||||
ResourceCheckInterval: time.Duration(testResourceCheckInterval),
|
||||
ResourceCheckTimeout: time.Duration(testResourceCheckTimeout),
|
||||
ResourceCheckInterval: &metav1.Duration{Duration: testResourceCheckInterval},
|
||||
ResourceCheckTimeout: &metav1.Duration{Duration: testResourceCheckTimeout},
|
||||
MinInstances: -1,
|
||||
MaxInstances: -1,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import (
|
|||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
informers_core_v1 "k8s.io/client-go/informers/core/v1"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
|
@ -64,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
|
||||
|
|
@ -355,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)
|
||||
|
|
@ -401,16 +405,8 @@ func (c *Controller) initSharedInformers() {
|
|||
}
|
||||
|
||||
// Pods
|
||||
podLw := &cache.ListWatch{
|
||||
ListFunc: c.podListFunc,
|
||||
WatchFunc: c.podWatchFunc,
|
||||
}
|
||||
|
||||
c.podInformer = cache.NewSharedIndexInformer(
|
||||
podLw,
|
||||
&v1.Pod{},
|
||||
constants.QueueResyncPeriodPod,
|
||||
cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
|
||||
c.podInformer = informers_core_v1.NewPodInformer(c.KubeClient.Clientset,
|
||||
c.opConfig.WatchedNamespace, constants.QueueResyncPeriodPod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
|
||||
|
||||
c.podInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: c.podAdd,
|
||||
|
|
@ -419,15 +415,7 @@ func (c *Controller) initSharedInformers() {
|
|||
})
|
||||
|
||||
// Kubernetes Nodes
|
||||
nodeLw := &cache.ListWatch{
|
||||
ListFunc: c.nodeListFunc,
|
||||
WatchFunc: c.nodeWatchFunc,
|
||||
}
|
||||
|
||||
c.nodesInformer = cache.NewSharedIndexInformer(
|
||||
nodeLw,
|
||||
&v1.Node{},
|
||||
constants.QueueResyncPeriodNode,
|
||||
c.nodesInformer = informers_core_v1.NewNodeInformer(c.KubeClient.Clientset, constants.QueueResyncPeriodNode,
|
||||
cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
|
||||
|
||||
c.nodesInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -9,33 +9,11 @@ import (
|
|||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
|
||||
"github.com/zalando/postgres-operator/pkg/cluster"
|
||||
"github.com/zalando/postgres-operator/pkg/util"
|
||||
)
|
||||
|
||||
func (c *Controller) nodeListFunc(options metav1.ListOptions) (runtime.Object, error) {
|
||||
opts := metav1.ListOptions{
|
||||
Watch: options.Watch,
|
||||
ResourceVersion: options.ResourceVersion,
|
||||
TimeoutSeconds: options.TimeoutSeconds,
|
||||
}
|
||||
|
||||
return c.KubeClient.Nodes().List(context.TODO(), opts)
|
||||
}
|
||||
|
||||
func (c *Controller) nodeWatchFunc(options metav1.ListOptions) (watch.Interface, error) {
|
||||
opts := metav1.ListOptions{
|
||||
Watch: options.Watch,
|
||||
ResourceVersion: options.ResourceVersion,
|
||||
TimeoutSeconds: options.TimeoutSeconds,
|
||||
}
|
||||
|
||||
return c.KubeClient.Nodes().Watch(context.TODO(), opts)
|
||||
}
|
||||
|
||||
func (c *Controller) nodeAdd(obj interface{}) {
|
||||
node, ok := obj.(*v1.Node)
|
||||
if !ok {
|
||||
|
|
@ -174,7 +152,7 @@ func (c *Controller) nodeDelete(obj interface{}) {
|
|||
|
||||
func (c *Controller) moveMasterPodsOffNode(node *v1.Node) {
|
||||
// retry to move master until configured timeout is reached
|
||||
err := retryutil.Retry(1*time.Minute, c.opConfig.MasterPodMoveTimeout,
|
||||
err := retryutil.Retry(1*time.Minute, c.opConfig.MasterPodMoveTimeout.Duration,
|
||||
func() (bool, error) {
|
||||
err := c.attemptToMoveMasterPodsOffNode(node)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -4,8 +4,6 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
|
||||
"time"
|
||||
|
||||
acidv1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1"
|
||||
"github.com/zalando/postgres-operator/pkg/util"
|
||||
"github.com/zalando/postgres-operator/pkg/util/config"
|
||||
|
|
@ -31,7 +29,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
|
||||
|
|
@ -45,23 +42,14 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
|
|||
result.MaxInstances = fromCRD.MaxInstances
|
||||
result.IgnoreInstanceLimitsAnnotationKey = fromCRD.IgnoreInstanceLimitsAnnotationKey
|
||||
result.IgnoreResourcesLimitsAnnotationKey = fromCRD.IgnoreResourcesLimitsAnnotationKey
|
||||
result.ResyncPeriod = util.CoalesceDuration(time.Duration(fromCRD.ResyncPeriod), "30m")
|
||||
result.RepairPeriod = util.CoalesceDuration(time.Duration(fromCRD.RepairPeriod), "5m")
|
||||
result.ResyncPeriod = util.CoalesceDuration(fromCRD.ResyncPeriod, "30m")
|
||||
result.RepairPeriod = util.CoalesceDuration(fromCRD.RepairPeriod, "5m")
|
||||
result.SetMemoryRequestToLimit = fromCRD.SetMemoryRequestToLimit
|
||||
result.ShmVolume = util.CoalesceBool(fromCRD.ShmVolume, util.True())
|
||||
result.SidecarImages = fromCRD.SidecarImages
|
||||
result.SidecarContainers = fromCRD.SidecarContainers
|
||||
result.EnableMaintenanceWindows = util.CoalesceBool(fromCRD.EnableMaintenanceWindows, util.True())
|
||||
if len(fromCRD.MaintenanceWindows) > 0 {
|
||||
result.MaintenanceWindows = make([]string, 0, len(fromCRD.MaintenanceWindows))
|
||||
for _, window := range fromCRD.MaintenanceWindows {
|
||||
w, err := window.MarshalJSON()
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("could not marshal configured maintenance window: %v", err))
|
||||
}
|
||||
result.MaintenanceWindows = append(result.MaintenanceWindows, string(w))
|
||||
}
|
||||
}
|
||||
result.MaintenanceWindows = fromCRD.MaintenanceWindows
|
||||
|
||||
// user config
|
||||
result.SuperUsername = util.Coalesce(fromCRD.PostgresUsersConfiguration.SuperUsername, "postgres")
|
||||
|
|
@ -85,7 +73,8 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
|
|||
result.PodServiceAccountRoleBindingDefinition = fromCRD.Kubernetes.PodServiceAccountRoleBindingDefinition
|
||||
result.PodEnvironmentConfigMap = fromCRD.Kubernetes.PodEnvironmentConfigMap
|
||||
result.PodEnvironmentSecret = fromCRD.Kubernetes.PodEnvironmentSecret
|
||||
result.PodTerminateGracePeriod = util.CoalesceDuration(time.Duration(fromCRD.Kubernetes.PodTerminateGracePeriod), "5m")
|
||||
result.PodTerminateGracePeriod = util.CoalesceDuration(fromCRD.Kubernetes.PodTerminateGracePeriod, "5m")
|
||||
result.LivenessProbe = fromCRD.Kubernetes.LivenessProbe
|
||||
result.SpiloPrivileged = fromCRD.Kubernetes.SpiloPrivileged
|
||||
result.SpiloAllowPrivilegeEscalation = util.CoalesceBool(fromCRD.Kubernetes.SpiloAllowPrivilegeEscalation, util.True())
|
||||
result.SpiloRunAsUser = fromCRD.Kubernetes.SpiloRunAsUser
|
||||
|
|
@ -138,7 +127,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
|
|||
result.EnableSecretsDeletion = util.CoalesceBool(fromCRD.Kubernetes.EnableSecretsDeletion, util.True())
|
||||
result.EnablePersistentVolumeClaimDeletion = util.CoalesceBool(fromCRD.Kubernetes.EnablePersistentVolumeClaimDeletion, util.True())
|
||||
result.EnableReadinessProbe = fromCRD.Kubernetes.EnableReadinessProbe
|
||||
result.MasterPodMoveTimeout = util.CoalesceDuration(time.Duration(fromCRD.Kubernetes.MasterPodMoveTimeout), "10m")
|
||||
result.MasterPodMoveTimeout = util.CoalesceDuration(fromCRD.Kubernetes.MasterPodMoveTimeout, "10m")
|
||||
result.EnablePodAntiAffinity = fromCRD.Kubernetes.EnablePodAntiAffinity
|
||||
result.PodAntiAffinityTopologyKey = util.Coalesce(fromCRD.Kubernetes.PodAntiAffinityTopologyKey, "kubernetes.io/hostname")
|
||||
result.PodAntiAffinityPreferredDuringScheduling = fromCRD.Kubernetes.PodAntiAffinityPreferredDuringScheduling
|
||||
|
|
@ -155,14 +144,14 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
|
|||
result.MaxMemoryRequest = fromCRD.PostgresPodResources.MaxMemoryRequest
|
||||
|
||||
// timeout config
|
||||
result.ResourceCheckInterval = util.CoalesceDuration(time.Duration(fromCRD.Timeouts.ResourceCheckInterval), "3s")
|
||||
result.ResourceCheckTimeout = util.CoalesceDuration(time.Duration(fromCRD.Timeouts.ResourceCheckTimeout), "10m")
|
||||
result.PodLabelWaitTimeout = util.CoalesceDuration(time.Duration(fromCRD.Timeouts.PodLabelWaitTimeout), "10m")
|
||||
result.PodDeletionWaitTimeout = util.CoalesceDuration(time.Duration(fromCRD.Timeouts.PodDeletionWaitTimeout), "10m")
|
||||
result.ReadyWaitInterval = util.CoalesceDuration(time.Duration(fromCRD.Timeouts.ReadyWaitInterval), "4s")
|
||||
result.ReadyWaitTimeout = util.CoalesceDuration(time.Duration(fromCRD.Timeouts.ReadyWaitTimeout), "30s")
|
||||
result.PatroniAPICheckInterval = util.CoalesceDuration(time.Duration(fromCRD.Timeouts.PatroniAPICheckInterval), "1s")
|
||||
result.PatroniAPICheckTimeout = util.CoalesceDuration(time.Duration(fromCRD.Timeouts.PatroniAPICheckTimeout), "5s")
|
||||
result.ResourceCheckInterval = util.CoalesceDuration(fromCRD.Timeouts.ResourceCheckInterval, "3s")
|
||||
result.ResourceCheckTimeout = util.CoalesceDuration(fromCRD.Timeouts.ResourceCheckTimeout, "10m")
|
||||
result.PodLabelWaitTimeout = util.CoalesceDuration(fromCRD.Timeouts.PodLabelWaitTimeout, "10m")
|
||||
result.PodDeletionWaitTimeout = util.CoalesceDuration(fromCRD.Timeouts.PodDeletionWaitTimeout, "10m")
|
||||
result.ReadyWaitInterval = util.CoalesceDuration(fromCRD.Timeouts.ReadyWaitInterval, "4s")
|
||||
result.ReadyWaitTimeout = util.CoalesceDuration(fromCRD.Timeouts.ReadyWaitTimeout, "30s")
|
||||
result.PatroniAPICheckInterval = util.CoalesceDuration(fromCRD.Timeouts.PatroniAPICheckInterval, "1s")
|
||||
result.PatroniAPICheckTimeout = util.CoalesceDuration(fromCRD.Timeouts.PatroniAPICheckTimeout, "5s")
|
||||
|
||||
// load balancer config
|
||||
result.DbHostedZone = util.Coalesce(fromCRD.LoadBalancer.DbHostedZone, "db.example.com")
|
||||
|
|
@ -170,6 +159,10 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
|
|||
result.EnableMasterPoolerLoadBalancer = fromCRD.LoadBalancer.EnableMasterPoolerLoadBalancer
|
||||
result.EnableReplicaLoadBalancer = fromCRD.LoadBalancer.EnableReplicaLoadBalancer
|
||||
result.EnableReplicaPoolerLoadBalancer = fromCRD.LoadBalancer.EnableReplicaPoolerLoadBalancer
|
||||
result.EnableMasterNodePort = fromCRD.LoadBalancer.EnableMasterNodePort
|
||||
result.EnableMasterPoolerNodePort = fromCRD.LoadBalancer.EnableMasterPoolerNodePort
|
||||
result.EnableReplicaNodePort = fromCRD.LoadBalancer.EnableReplicaNodePort
|
||||
result.EnableReplicaPoolerNodePort = fromCRD.LoadBalancer.EnableReplicaPoolerNodePort
|
||||
result.CustomServiceAnnotations = fromCRD.LoadBalancer.CustomServiceAnnotations
|
||||
result.MasterDNSNameFormat = fromCRD.LoadBalancer.MasterDNSNameFormat
|
||||
result.MasterLegacyDNSNameFormat = fromCRD.LoadBalancer.MasterLegacyDNSNameFormat
|
||||
|
|
@ -212,10 +205,13 @@ 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 = fromCRD.OperatorDebug.DebugLogging
|
||||
result.EnableDBAccess = fromCRD.OperatorDebug.EnableDBAccess
|
||||
result.DebugLogging = *util.CoalesceBool(fromCRD.OperatorDebug.DebugLogging, util.True())
|
||||
result.EnableDBAccess = *util.CoalesceBool(fromCRD.OperatorDebug.EnableDBAccess, util.True())
|
||||
|
||||
// Teams API config
|
||||
result.EnableTeamsAPI = fromCRD.TeamsAPI.EnableTeamsAPI
|
||||
|
|
|
|||
|
|
@ -1,12 +1,7 @@
|
|||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
|
||||
"github.com/zalando/postgres-operator/pkg/cluster"
|
||||
"github.com/zalando/postgres-operator/pkg/spec"
|
||||
|
|
@ -14,26 +9,6 @@ import (
|
|||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
func (c *Controller) podListFunc(options metav1.ListOptions) (runtime.Object, error) {
|
||||
opts := metav1.ListOptions{
|
||||
Watch: options.Watch,
|
||||
ResourceVersion: options.ResourceVersion,
|
||||
TimeoutSeconds: options.TimeoutSeconds,
|
||||
}
|
||||
|
||||
return c.KubeClient.Pods(c.opConfig.WatchedNamespace).List(context.TODO(), opts)
|
||||
}
|
||||
|
||||
func (c *Controller) podWatchFunc(options metav1.ListOptions) (watch.Interface, error) {
|
||||
opts := metav1.ListOptions{
|
||||
Watch: options.Watch,
|
||||
ResourceVersion: options.ResourceVersion,
|
||||
TimeoutSeconds: options.TimeoutSeconds,
|
||||
}
|
||||
|
||||
return c.KubeClient.Pods(c.opConfig.WatchedNamespace).Watch(context.TODO(), opts)
|
||||
}
|
||||
|
||||
func (c *Controller) dispatchPodEvent(clusterName spec.NamespacedName, event cluster.PodEvent) {
|
||||
c.clustersMu.RLock()
|
||||
cluster, ok := c.clusters[clusterName]
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import (
|
|||
|
||||
func (c *Controller) clusterResync(stopCh <-chan struct{}, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
ticker := time.NewTicker(c.opConfig.ResyncPeriod)
|
||||
ticker := time.NewTicker(c.opConfig.ResyncPeriod.Duration)
|
||||
|
||||
for {
|
||||
select {
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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{}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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{} },
|
||||
),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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"):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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{}
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
}
|
||||
|
|
@ -3,34 +3,34 @@ package config
|
|||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"fmt"
|
||||
|
||||
"github.com/zalando/postgres-operator/pkg/spec"
|
||||
"github.com/zalando/postgres-operator/pkg/util/constants"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// CRD describes CustomResourceDefinition specific configuration parameters
|
||||
type CRD struct {
|
||||
ReadyWaitInterval time.Duration `name:"ready_wait_interval" default:"4s"`
|
||||
ReadyWaitTimeout time.Duration `name:"ready_wait_timeout" default:"30s"`
|
||||
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"`
|
||||
ReadyWaitInterval *metav1.Duration `name:"ready_wait_interval" default:"4s"`
|
||||
ReadyWaitTimeout *metav1.Duration `name:"ready_wait_timeout" default:"30s"`
|
||||
ResyncPeriod *metav1.Duration `name:"resync_period" default:"30m"`
|
||||
RepairPeriod *metav1.Duration `name:"repair_period" default:"5m"`
|
||||
EnableCRDRegistration *bool `name:"enable_crd_registration" default:"true"`
|
||||
CRDCategories []string `name:"crd_categories" default:"all"`
|
||||
}
|
||||
|
||||
// Resources describes kubernetes resource specific configuration parameters
|
||||
type Resources struct {
|
||||
EnableOwnerReferences *bool `name:"enable_owner_references" default:"false"`
|
||||
ResourceCheckInterval time.Duration `name:"resource_check_interval" default:"3s"`
|
||||
ResourceCheckTimeout time.Duration `name:"resource_check_timeout" default:"10m"`
|
||||
PodLabelWaitTimeout time.Duration `name:"pod_label_wait_timeout" default:"10m"`
|
||||
PodDeletionWaitTimeout time.Duration `name:"pod_deletion_wait_timeout" default:"10m"`
|
||||
PodTerminateGracePeriod time.Duration `name:"pod_terminate_grace_period" default:"5m"`
|
||||
ResourceCheckInterval *metav1.Duration `name:"resource_check_interval" default:"3s"`
|
||||
ResourceCheckTimeout *metav1.Duration `name:"resource_check_timeout" default:"10m"`
|
||||
PodLabelWaitTimeout *metav1.Duration `name:"pod_label_wait_timeout" default:"10m"`
|
||||
PodDeletionWaitTimeout *metav1.Duration `name:"pod_deletion_wait_timeout" default:"10m"`
|
||||
PodTerminateGracePeriod *metav1.Duration `name:"pod_terminate_grace_period" default:"5m"`
|
||||
LivenessProbe *v1.Probe `name:"-"`
|
||||
SpiloRunAsUser *int64 `name:"spilo_runasuser"`
|
||||
SpiloRunAsGroup *int64 `name:"spilo_runasgroup"`
|
||||
SpiloFSGroup *int64 `name:"spilo_fsgroup"`
|
||||
|
|
@ -148,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
|
||||
|
|
@ -185,7 +188,7 @@ type Config struct {
|
|||
// value of this string must be valid JSON or YAML; see initPodServiceAccount
|
||||
PodServiceAccountDefinition string `name:"pod_service_account_definition" default:""`
|
||||
PodServiceAccountRoleBindingDefinition string `name:"pod_service_account_role_binding_definition" default:""`
|
||||
MasterPodMoveTimeout time.Duration `name:"master_pod_move_timeout" default:"20m"`
|
||||
MasterPodMoveTimeout *metav1.Duration `name:"master_pod_move_timeout" default:"20m"`
|
||||
DbHostedZone string `name:"db_hosted_zone" default:"db.example.com"`
|
||||
AWSRegion string `name:"aws_region" default:"eu-central-1"`
|
||||
WALES3Bucket string `name:"wal_s3_bucket"`
|
||||
|
|
@ -212,6 +215,10 @@ type Config struct {
|
|||
EnableMasterPoolerLoadBalancer bool `name:"enable_master_pooler_load_balancer" default:"false"`
|
||||
EnableReplicaLoadBalancer bool `name:"enable_replica_load_balancer" default:"false"`
|
||||
EnableReplicaPoolerLoadBalancer bool `name:"enable_replica_pooler_load_balancer" default:"false"`
|
||||
EnableMasterNodePort bool `name:"enable_master_node_port" default:"false"`
|
||||
EnableMasterPoolerNodePort bool `name:"enable_master_pooler_node_port" default:"false"`
|
||||
EnableReplicaNodePort bool `name:"enable_replica_node_port" default:"false"`
|
||||
EnableReplicaPoolerNodePort bool `name:"enable_replica_pooler_node_port" default:"false"`
|
||||
CustomServiceAnnotations map[string]string `name:"custom_service_annotations"`
|
||||
CustomPodAnnotations map[string]string `name:"custom_pod_annotations"`
|
||||
EnablePodAntiAffinity bool `name:"enable_pod_antiaffinity" default:"false"`
|
||||
|
|
@ -235,7 +242,7 @@ type Config struct {
|
|||
RingLogLines int `name:"ring_log_lines" default:"100"`
|
||||
ClusterHistoryEntries int `name:"cluster_history_entries" default:"1000"`
|
||||
TeamAPIRoleConfiguration map[string]string `name:"team_api_role_configuration" default:"log_statement:all"`
|
||||
PodTerminateGracePeriod time.Duration `name:"pod_terminate_grace_period" default:"5m"`
|
||||
PodTerminateGracePeriod *metav1.Duration `name:"pod_terminate_grace_period" default:"5m"`
|
||||
PodManagementPolicy string `name:"pod_management_policy" default:"ordered_ready"`
|
||||
EnableReadinessProbe bool `name:"enable_readiness_probe" default:"false"`
|
||||
ProtectedRoles []string `name:"protected_role_names" default:"admin,cron_admin"`
|
||||
|
|
@ -251,8 +258,8 @@ type Config struct {
|
|||
MajorVersionUpgradeTeamAllowList []string `name:"major_version_upgrade_team_allow_list" default:""`
|
||||
MinimalMajorVersion string `name:"minimal_major_version" default:"14"`
|
||||
TargetMajorVersion string `name:"target_major_version" default:"18"`
|
||||
PatroniAPICheckInterval time.Duration `name:"patroni_api_check_interval" default:"1s"`
|
||||
PatroniAPICheckTimeout time.Duration `name:"patroni_api_check_timeout" default:"5s"`
|
||||
PatroniAPICheckInterval *metav1.Duration `name:"patroni_api_check_interval" default:"1s"`
|
||||
PatroniAPICheckTimeout *metav1.Duration `name:"patroni_api_check_timeout" default:"5s"`
|
||||
EnablePatroniFailsafeMode *bool `name:"enable_patroni_failsafe_mode" default:"false"`
|
||||
EnableSecretsDeletion *bool `name:"enable_secrets_deletion" default:"true"`
|
||||
EnablePersistentVolumeClaimDeletion *bool `name:"enable_persistent_volume_claim_deletion" default:"true"`
|
||||
|
|
|
|||
|
|
@ -2,10 +2,19 @@ package config
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
// Set OPERATOR_NAMESPACE to avoid log.Fatal in GetOperatorNamespace
|
||||
// when running tests outside a Kubernetes pod
|
||||
os.Setenv("OPERATOR_NAMESPACE", "default")
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
var getMapPairsFromStringTest = []struct {
|
||||
in string
|
||||
expected []string
|
||||
|
|
@ -29,3 +38,300 @@ func TestGetMapPairsFromString(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func int32Ptr(i int32) *int32 {
|
||||
return &i
|
||||
}
|
||||
|
||||
func boolPtr(b bool) *bool {
|
||||
return &b
|
||||
}
|
||||
|
||||
var validateTests = []struct {
|
||||
description string
|
||||
cfg Config
|
||||
expectError bool
|
||||
errorMsg string
|
||||
}{
|
||||
{
|
||||
description: "valid config",
|
||||
cfg: Config{
|
||||
Resources: Resources{
|
||||
MinInstances: 1,
|
||||
MaxInstances: 5,
|
||||
},
|
||||
Auth: Auth{
|
||||
SuperUsername: "postgres",
|
||||
},
|
||||
ConnectionPooler: ConnectionPooler{
|
||||
NumberOfInstances: int32Ptr(2),
|
||||
User: "pooler",
|
||||
},
|
||||
Workers: 4,
|
||||
},
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
description: "min instances greater than max instances",
|
||||
cfg: Config{
|
||||
Resources: Resources{
|
||||
MinInstances: 10,
|
||||
MaxInstances: 5,
|
||||
},
|
||||
Auth: Auth{
|
||||
SuperUsername: "postgres",
|
||||
},
|
||||
ConnectionPooler: ConnectionPooler{
|
||||
NumberOfInstances: int32Ptr(2),
|
||||
User: "pooler",
|
||||
},
|
||||
Workers: 4,
|
||||
},
|
||||
expectError: true,
|
||||
errorMsg: "minimum number of instances",
|
||||
},
|
||||
{
|
||||
description: "workers set to zero",
|
||||
cfg: Config{
|
||||
Resources: Resources{
|
||||
MinInstances: 1,
|
||||
MaxInstances: 5,
|
||||
},
|
||||
Auth: Auth{
|
||||
SuperUsername: "postgres",
|
||||
},
|
||||
ConnectionPooler: ConnectionPooler{
|
||||
NumberOfInstances: int32Ptr(2),
|
||||
User: "pooler",
|
||||
},
|
||||
Workers: 0,
|
||||
},
|
||||
expectError: true,
|
||||
errorMsg: "number of workers should be higher than 0",
|
||||
},
|
||||
{
|
||||
description: "connection pooler instances below minimum",
|
||||
cfg: Config{
|
||||
Resources: Resources{
|
||||
MinInstances: 1,
|
||||
MaxInstances: 5,
|
||||
},
|
||||
Auth: Auth{
|
||||
SuperUsername: "postgres",
|
||||
},
|
||||
ConnectionPooler: ConnectionPooler{
|
||||
NumberOfInstances: int32Ptr(0),
|
||||
User: "pooler",
|
||||
},
|
||||
Workers: 4,
|
||||
},
|
||||
expectError: true,
|
||||
errorMsg: "number of connection pooler instances",
|
||||
},
|
||||
{
|
||||
description: "connection pooler user same as super user",
|
||||
cfg: Config{
|
||||
Resources: Resources{
|
||||
MinInstances: 1,
|
||||
MaxInstances: 5,
|
||||
},
|
||||
Auth: Auth{
|
||||
SuperUsername: "postgres",
|
||||
},
|
||||
ConnectionPooler: ConnectionPooler{
|
||||
NumberOfInstances: int32Ptr(2),
|
||||
User: "postgres",
|
||||
},
|
||||
Workers: 4,
|
||||
},
|
||||
expectError: true,
|
||||
errorMsg: "connection pool user is not allowed to be the same as super user",
|
||||
},
|
||||
{
|
||||
description: "min and max instances both negative (disabled)",
|
||||
cfg: Config{
|
||||
Resources: Resources{
|
||||
MinInstances: -1,
|
||||
MaxInstances: -1,
|
||||
},
|
||||
Auth: Auth{
|
||||
SuperUsername: "postgres",
|
||||
},
|
||||
ConnectionPooler: ConnectionPooler{
|
||||
NumberOfInstances: int32Ptr(2),
|
||||
User: "pooler",
|
||||
},
|
||||
Workers: 4,
|
||||
},
|
||||
expectError: false,
|
||||
},
|
||||
}
|
||||
|
||||
func TestValidate(t *testing.T) {
|
||||
for _, tt := range validateTests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
err := validate(&tt.cfg)
|
||||
if tt.expectError {
|
||||
if err == nil {
|
||||
t.Errorf("expected error containing %q, got nil", tt.errorMsg)
|
||||
return
|
||||
}
|
||||
if !strings.Contains(err.Error(), tt.errorMsg) {
|
||||
t.Errorf("expected error containing %q, got %q", tt.errorMsg, err.Error())
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("expected no error, got %v", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var newFromMapTests = []struct {
|
||||
description string
|
||||
input map[string]string
|
||||
expectPanic bool
|
||||
panicMsg string
|
||||
validateFunc func(t *testing.T, cfg *Config)
|
||||
}{
|
||||
{
|
||||
description: "empty map uses defaults",
|
||||
input: map[string]string{},
|
||||
expectPanic: false,
|
||||
validateFunc: func(t *testing.T, cfg *Config) {
|
||||
if cfg.Workers != 8 {
|
||||
t.Errorf("expected default Workers=8, got %d", cfg.Workers)
|
||||
}
|
||||
if cfg.SuperUsername != "postgres" {
|
||||
t.Errorf("expected default SuperUsername=postgres, got %s", cfg.SuperUsername)
|
||||
}
|
||||
if cfg.ReplicationUsername != "standby" {
|
||||
t.Errorf("expected default ReplicationUsername=standby, got %s", cfg.ReplicationUsername)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "custom values override defaults",
|
||||
input: map[string]string{
|
||||
"workers": "16",
|
||||
"super_username": "admin",
|
||||
},
|
||||
expectPanic: false,
|
||||
validateFunc: func(t *testing.T, cfg *Config) {
|
||||
if cfg.Workers != 16 {
|
||||
t.Errorf("expected Workers=16, got %d", cfg.Workers)
|
||||
}
|
||||
if cfg.SuperUsername != "admin" {
|
||||
t.Errorf("expected SuperUsername=admin, got %s", cfg.SuperUsername)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "duration parsing",
|
||||
input: map[string]string{
|
||||
"patroni_api_check_interval": "1s",
|
||||
"patroni_api_check_timeout": "5s",
|
||||
},
|
||||
expectPanic: false,
|
||||
validateFunc: func(t *testing.T, cfg *Config) {
|
||||
if cfg.PatroniAPICheckInterval.Seconds() != 1 {
|
||||
t.Errorf("expected check interval of 1s, got %.0fs", cfg.PatroniAPICheckInterval.Seconds())
|
||||
}
|
||||
if cfg.PatroniAPICheckTimeout.Seconds() != 5 {
|
||||
t.Errorf("expected check timeout of 5s, got %.0fs", cfg.PatroniAPICheckTimeout.Seconds())
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "boolean parsing",
|
||||
input: map[string]string{
|
||||
"enable_teams_api": "false",
|
||||
"debug_logging": "false",
|
||||
},
|
||||
expectPanic: false,
|
||||
validateFunc: func(t *testing.T, cfg *Config) {
|
||||
if cfg.EnableTeamsAPI != false {
|
||||
t.Errorf("expected EnableTeamsAPI=false, got %v", cfg.EnableTeamsAPI)
|
||||
}
|
||||
if cfg.DebugLogging != false {
|
||||
t.Errorf("expected DebugLogging=false, got %v", cfg.DebugLogging)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "map parsing",
|
||||
input: map[string]string{
|
||||
"cluster_labels": "app:myapp,env:prod",
|
||||
},
|
||||
expectPanic: false,
|
||||
validateFunc: func(t *testing.T, cfg *Config) {
|
||||
if cfg.ClusterLabels["app"] != "myapp" {
|
||||
t.Errorf("expected ClusterLabels[app]=myapp, got %s", cfg.ClusterLabels["app"])
|
||||
}
|
||||
if cfg.ClusterLabels["env"] != "prod" {
|
||||
t.Errorf("expected ClusterLabels[env]=prod, got %s", cfg.ClusterLabels["env"])
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "slice parsing",
|
||||
input: map[string]string{
|
||||
"inherited_labels": "label1,label2,label3",
|
||||
},
|
||||
expectPanic: false,
|
||||
validateFunc: func(t *testing.T, cfg *Config) {
|
||||
expected := []string{"label1", "label2", "label3"}
|
||||
if !reflect.DeepEqual(cfg.InheritedLabels, expected) {
|
||||
t.Errorf("expected InheritedLabels=%v, got %v", expected, cfg.InheritedLabels)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "invalid workers triggers validation panic",
|
||||
input: map[string]string{
|
||||
"workers": "0",
|
||||
},
|
||||
expectPanic: true,
|
||||
panicMsg: "number of workers should be higher than 0",
|
||||
},
|
||||
{
|
||||
description: "invalid integer causes panic",
|
||||
input: map[string]string{
|
||||
"workers": "invalid",
|
||||
},
|
||||
expectPanic: true,
|
||||
panicMsg: "invalid syntax",
|
||||
},
|
||||
}
|
||||
|
||||
func TestNewFromMap(t *testing.T) {
|
||||
for _, tt := range newFromMapTests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
if tt.expectPanic {
|
||||
defer func() {
|
||||
r := recover()
|
||||
if r == nil {
|
||||
t.Errorf("expected panic with message containing %q, but no panic occurred", tt.panicMsg)
|
||||
return
|
||||
}
|
||||
errMsg := fmt.Sprintf("%v", r)
|
||||
if !strings.Contains(errMsg, tt.panicMsg) {
|
||||
t.Errorf("expected panic message containing %q, got %q", tt.panicMsg, errMsg)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
cfg := NewFromMap(tt.input)
|
||||
|
||||
if tt.expectPanic {
|
||||
t.Errorf("expected panic but NewFromMap returned successfully")
|
||||
return
|
||||
}
|
||||
|
||||
if tt.validateFunc != nil {
|
||||
tt.validateFunc(t, cfg)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type decoder interface {
|
||||
|
|
@ -101,13 +103,7 @@ func processField(value string, field reflect.Value) error {
|
|||
val int64
|
||||
err error
|
||||
)
|
||||
if field.Kind() == reflect.Int64 && typ.PkgPath() == "time" && typ.Name() == "Duration" {
|
||||
var d time.Duration
|
||||
d, err = time.ParseDuration(value)
|
||||
val = int64(d)
|
||||
} else {
|
||||
val, err = strconv.ParseInt(value, 0, typ.Bits())
|
||||
}
|
||||
val, err = strconv.ParseInt(value, 0, typ.Bits())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -165,6 +161,15 @@ func processField(value string, field reflect.Value) error {
|
|||
mp.SetMapIndex(k, v)
|
||||
}
|
||||
field.Set(mp)
|
||||
case reflect.Struct:
|
||||
if typ.Name() == "Duration" {
|
||||
var d time.Duration
|
||||
d, err := time.ParseDuration(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
field.Set(reflect.ValueOf(metav1.Duration{Duration: d}))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ type KubernetesClient struct {
|
|||
zalandov1.FabricEventStreamsGetter
|
||||
|
||||
RESTClient rest.Interface
|
||||
Clientset *kubernetes.Clientset
|
||||
AcidV1ClientSet *zalandoclient.Clientset
|
||||
Zalandov1ClientSet *zalandoclient.Clientset
|
||||
}
|
||||
|
|
@ -148,6 +149,7 @@ func NewFromConfig(cfg *rest.Config) (KubernetesClient, error) {
|
|||
return kubeClient, fmt.Errorf("could not get clientset: %v", err)
|
||||
}
|
||||
|
||||
kubeClient.Clientset = client
|
||||
kubeClient.PodsGetter = client.CoreV1()
|
||||
kubeClient.ServicesGetter = client.CoreV1()
|
||||
kubeClient.EndpointsGetter = client.CoreV1()
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue