Generate postgresql CRD from go structs

Signed-off-by: Mikkel Oscar Lyderik Larsen <mikkel.larsen@zalando.de>
This commit is contained in:
Mikkel Oscar Lyderik Larsen 2025-12-04 14:30:12 +01:00
parent 06ac340fe0
commit 4e0692205b
5 changed files with 589 additions and 163 deletions

View File

@ -21,7 +21,7 @@ GITSTATUS = $(shell git status --porcelain || echo "no changes")
SOURCES = cmd/main.go
VERSION ?= $(shell git describe --tags --always --dirty)
CRD_SOURCES = $(shell find pkg/apis/zalando.org pkg/apis/acid.zalan.do -name '*.go' -not -name '*.deepcopy.go')
GENERATED_CRDS = manifests/postgresteam.crd.yaml
GENERATED_CRDS = manifests/postgresteam.crd.yaml manifests/postgresql.crd.yaml
GENERATED = pkg/apis/zalando.org/v1/zz_generated.deepcopy.go pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go
DIRS := cmd pkg
PKG := `go list ./... | grep -v /vendor/`
@ -54,6 +54,7 @@ default: local
clean:
rm -rf build
rm $(GENERATED)
verify:
hack/verify-codegen.sh
@ -63,9 +64,13 @@ $(GENERATED): go.mod $(CRD_SOURCES)
$(GENERATED_CRDS): $(GENERATED)
go tool controller-gen crd:crdVersions=v1,allowDangerousTypes=true paths=./pkg/apis/acid.zalan.do/... output:crd:dir=manifests
# only generate postgresteam.crd.yaml for now
# only generate postgresteam.crd.yaml and postgresql.crd.yaml for now
@rm manifests/acid.zalan.do_operatorconfigurations.yaml
@rm manifests/acid.zalan.do_postgresqls.yaml
@mv manifests/acid.zalan.do_postgresqls.yaml manifests/postgresql.crd.yaml
@# hack to use lowercase kind and listKind
@sed -i -e 's/kind: Postgresql/kind: postgresql/' manifests/postgresql.crd.yaml
@sed -i -e 's/listKind: PostgresqlList/listKind: postgresqlList/' manifests/postgresql.crd.yaml
@hack/adjust_postgresql_crd.sh
@mv manifests/acid.zalan.do_postgresteams.yaml manifests/postgresteam.crd.yaml
local: ${SOURCES} $(GENERATED_CRDS)

22
hack/adjust_postgresql_crd.sh Executable file
View File

@ -0,0 +1,22 @@
#!/usr/bin/env bash
# Hack to adjust the generated postgresql CRD YAML file and add missing field
# settings which can not be expressed via kubebuilder markers.
#
# Injections:
#
# * oneOf: for the standby field to enforce that only one of s3_wal_path, gs_wal_path or standby_host is set.
# * This can later be done with // +kubebuilder:validation:ExactlyOneOf marker, but this requires latest Kubernetes version. (Currently the operator depends on v1.32.9)
# * type: string and pattern for the maintenanceWindows items.
file="${1:-"manifests/postgresql.crd.yaml"}"
sed -i '/^[[:space:]]*standby:$/{
# Capture the indentation
s/^\([[:space:]]*\)standby:$/\1standby:\n\1 oneOf:\n\1 - required:\n\1 - s3_wal_path\n\1 - required:\n\1 - gs_wal_path\n\1 - required:\n\1 - standby_host/
}' "$file"
sed -i '/^[[:space:]]*maintenanceWindows:$/{
# Capture the indentation
s/^\([[:space:]]*\)maintenanceWindows:$/\1maintenanceWindows:\n\1 items:\n\1 pattern: '\''^\\ *((Mon|Tue|Wed|Thu|Fri|Sat|Sun):(2[0-3]|[01]?\\d):([0-5]?\\d)|(2[0-3]|[01]?\\d):([0-5]?\\d))-((2[0-3]|[01]?\\d):([0-5]?\\d)|(2[0-3]|[01]?\\d):([0-5]?\\d))\\ *$'\''\n\1 type: string/
}' "$file"

View File

@ -1,6 +1,9 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.17.3
name: postgresqls.acid.zalan.do
spec:
group: acid.zalan.do
@ -40,7 +43,8 @@ spec:
jsonPath: .spec.resources.requests.memory
name: Memory-Request
type: string
- jsonPath: .metadata.creationTimestamp
- description: Age of the PostgreSQL cluster
jsonPath: .metadata.creationTimestamp
name: Age
type: date
- description: Current sync status of postgresql resource
@ -50,19 +54,33 @@ spec:
name: v1
schema:
openAPIV3Schema:
description: Postgresql defines PostgreSQL Custom Resource Definition Object.
properties:
apiVersion:
enum:
- acid.zalan.do/v1
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:
enum:
- postgresql
description: |-
Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
type: string
metadata:
type: object
spec:
description: PostgresSpec defines the specification for the PostgreSQL
TPR.
properties:
additionalVolumes:
items:
description: AdditionalVolume specs additional optional volumes
for statefulset
properties:
isSubPathExpr:
type: boolean
@ -87,12 +105,16 @@ spec:
type: object
type: array
allowedSourceRanges:
description: load balancers' source ranges are the same for master
and replica services
items:
pattern: ^(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\/(\d|[1-2]\d|3[0-2])$
type: string
nullable: true
type: array
clone:
description: CloneDescription describes which cluster the new should
clone and up to which point in time
properties:
cluster:
type: string
@ -107,11 +129,11 @@ spec:
s3_wal_path:
type: string
timestamp:
pattern: '^([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])[Tt]([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(([+-]([01][0-9]|2[0-3]):[0-5][0-9]))$'
# The regexp matches the date-time format (RFC 3339 Section 5.6) that specifies a timezone as an offset relative to UTC
# Example: 1996-12-19T16:39:57-08:00
# Note: this field requires a timezone
description: |-
The regexp matches the date-time format (RFC 3339 Section 5.6) that specifies a timezone as an offset relative to UTC
Example: 1996-12-19T16:39:57-08:00
Note: this field requires a timezone
pattern: ^([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])[Tt]([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(([+-]([01][0-9]|2[0-3]):[0-5][0-9]))$
type: string
uid:
format: uuid
@ -120,37 +142,112 @@ spec:
- cluster
type: object
connectionPooler:
description: |-
ConnectionPooler Options for connection pooler
pgbouncer-large (with higher resources) or odyssey-small (with smaller
resources)
Type string `json:"type,omitempty"`
makes sense to expose. E.g. pool size (min/max boundaries), max client
connections etc.
properties:
dockerImage:
type: string
maxDBConnections:
format: int32
type: integer
mode:
enum:
- "session"
- "transaction"
- session
- transaction
type: string
numberOfInstances:
format: int32
minimum: 1
type: integer
resources:
description: Resources describes requests and limits for the cluster
resouces.
properties:
limits:
description: ResourceDescription describes CPU and memory
resources defined for a cluster.
properties:
cpu:
pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
description: |-
Decimal natural followed by m, or decimal natural followed by
dot followed by up to three decimal digits.
This is because the Kubernetes CPU resource has millis as the
maximum precision. The actual values are checked in code
because the regular expression would be huge and horrible and
not very helpful in validation error messages; this one checks
only the format of the given number.
https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu
Note: the value specified here must not be zero or be lower
than the corresponding request.
pattern: ^(\d+m|\d+(\.\d{1,3})?)$
type: string
hugepages-1Gi:
pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$
type: string
hugepages-2Mi:
pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$
type: string
memory:
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
description: |-
You can express memory as a plain integer or as a fixed-point
integer using one of these suffixes: E, P, T, G, M, k. You can
also use the power-of-two equivalents: Ei, Pi, Ti, Gi, Mi, Ki
https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-memory
Note: the value specified here must not be zero or be higher
than the corresponding limit.
pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$
type: string
type: object
requests:
description: ResourceDescription describes CPU and memory
resources defined for a cluster.
properties:
cpu:
pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
description: |-
Decimal natural followed by m, or decimal natural followed by
dot followed by up to three decimal digits.
This is because the Kubernetes CPU resource has millis as the
maximum precision. The actual values are checked in code
because the regular expression would be huge and horrible and
not very helpful in validation error messages; this one checks
only the format of the given number.
https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu
Note: the value specified here must not be zero or be lower
than the corresponding request.
pattern: ^(\d+m|\d+(\.\d{1,3})?)$
type: string
hugepages-1Gi:
pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$
type: string
hugepages-2Mi:
pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$
type: string
memory:
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
description: |-
You can express memory as a plain integer or as a fixed-point
integer using one of these suffixes: E, P, T, G, M, k. You can
also use the power-of-two equivalents: Ei, Pi, Ti, Gi, Mi, Ki
https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-memory
Note: the value specified here must not be zero or be higher
than the corresponding limit.
pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$
type: string
type: object
type: object
@ -162,7 +259,9 @@ spec:
databases:
additionalProperties:
type: string
# Note: usernames specified here as database owners must be declared in the users key of the spec key.
description: |-
Note: usernames specified here as database owners must be declared
in the users key of the spec key.
type: object
dockerImage:
type: string
@ -171,6 +270,9 @@ spec:
enableLogicalBackup:
type: boolean
enableMasterLoadBalancer:
description: |-
vars that enable load balancers are pointers because it is important to know if any of them is omitted from the Postgres manifest
in that case the var evaluates to nil and the value is taken from the operator config
type: boolean
enableMasterPoolerLoadBalancer:
type: boolean
@ -183,28 +285,19 @@ spec:
enableShmVolume:
type: boolean
env:
items:
type: object
x-kubernetes-preserve-unknown-fields: true
nullable: true
type: array
initContainers:
items:
type: object
x-kubernetes-preserve-unknown-fields: true
nullable: true
type: array
init_containers:
description: deprecated
items:
type: object
x-kubernetes-preserve-unknown-fields: true
description: deprecated json tags
type: object
x-kubernetes-preserve-unknown-fields: true
initContainers:
nullable: true
type: array
logicalBackupRetention:
type: string
logicalBackupSchedule:
pattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$'
pattern: ^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$
type: string
maintenanceWindows:
items:
@ -214,101 +307,214 @@ spec:
masterServiceAnnotations:
additionalProperties:
type: string
description: MasterServiceAnnotations takes precedence over ServiceAnnotations
for master role if not empty
type: object
nodeAffinity:
description: Node affinity is a group of node affinity scheduling
rules.
properties:
preferredDuringSchedulingIgnoredDuringExecution:
description: |-
The scheduler will prefer to schedule pods to nodes that satisfy
the affinity expressions specified by this field, but it may choose
a node that violates one or more of the expressions. The node that is
most preferred is the one with the greatest sum of weights, i.e.
for each node that meets all of the scheduling requirements (resource
request, requiredDuringScheduling affinity expressions, etc.),
compute a sum by iterating through the elements of this field and adding
"weight" to the sum if the node matches the corresponding matchExpressions; the
node(s) with the highest sum are the most preferred.
items:
description: |-
An empty preferred scheduling term matches all objects with implicit weight 0
(i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op).
properties:
preference:
description: A node selector term, associated with the corresponding
weight.
properties:
matchExpressions:
description: A list of node selector requirements by
node's labels.
items:
description: |-
A node selector requirement is a selector that contains values, a key, and an operator
that relates the key and values.
properties:
key:
description: The label key that the selector applies
to.
type: string
operator:
description: |-
Represents a key's relationship to a set of values.
Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.
type: string
values:
description: |-
An array of string values. If the operator is In or NotIn,
the values array must be non-empty. If the operator is Exists or DoesNotExist,
the values array must be empty. If the operator is Gt or Lt, the values
array must have a single element, which will be interpreted as an integer.
This array is replaced during a strategic merge patch.
items:
type: string
type: array
x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
x-kubernetes-list-type: atomic
matchFields:
description: A list of node selector requirements by
node's fields.
items:
description: |-
A node selector requirement is a selector that contains values, a key, and an operator
that relates the key and values.
properties:
key:
description: The label key that the selector applies
to.
type: string
operator:
description: |-
Represents a key's relationship to a set of values.
Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.
type: string
values:
description: |-
An array of string values. If the operator is In or NotIn,
the values array must be non-empty. If the operator is Exists or DoesNotExist,
the values array must be empty. If the operator is Gt or Lt, the values
array must have a single element, which will be interpreted as an integer.
This array is replaced during a strategic merge patch.
items:
type: string
type: array
x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
x-kubernetes-list-type: atomic
type: object
x-kubernetes-map-type: atomic
weight:
description: Weight associated with matching the corresponding
nodeSelectorTerm, in the range 1-100.
format: int32
type: integer
required:
- preference
- weight
type: object
type: array
x-kubernetes-list-type: atomic
requiredDuringSchedulingIgnoredDuringExecution:
description: |-
If the affinity requirements specified by this field are not met at
scheduling time, the pod will not be scheduled onto the node.
If the affinity requirements specified by this field cease to be met
at some point during pod execution (e.g. due to an update), the system
may or may not try to eventually evict the pod from its node.
properties:
nodeSelectorTerms:
description: Required. A list of node selector terms. The
terms are ORed.
items:
description: |-
A null or empty node selector term matches no objects. The requirements of
them are ANDed.
The TopologySelectorTerm type implements a subset of the NodeSelectorTerm.
properties:
matchExpressions:
description: A list of node selector requirements by
node's labels.
items:
description: |-
A node selector requirement is a selector that contains values, a key, and an operator
that relates the key and values.
properties:
key:
description: The label key that the selector applies
to.
type: string
operator:
description: |-
Represents a key's relationship to a set of values.
Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.
type: string
values:
description: |-
An array of string values. If the operator is In or NotIn,
the values array must be non-empty. If the operator is Exists or DoesNotExist,
the values array must be empty. If the operator is Gt or Lt, the values
array must have a single element, which will be interpreted as an integer.
This array is replaced during a strategic merge patch.
items:
type: string
type: array
x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
x-kubernetes-list-type: atomic
matchFields:
description: A list of node selector requirements by
node's fields.
items:
description: |-
A node selector requirement is a selector that contains values, a key, and an operator
that relates the key and values.
properties:
key:
description: The label key that the selector applies
to.
type: string
operator:
description: |-
Represents a key's relationship to a set of values.
Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.
type: string
values:
description: |-
An array of string values. If the operator is In or NotIn,
the values array must be non-empty. If the operator is Exists or DoesNotExist,
the values array must be empty. If the operator is Gt or Lt, the values
array must have a single element, which will be interpreted as an integer.
This array is replaced during a strategic merge patch.
items:
type: string
type: array
x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
x-kubernetes-list-type: atomic
type: object
x-kubernetes-map-type: atomic
type: array
x-kubernetes-list-type: atomic
required:
- nodeSelectorTerms
type: object
x-kubernetes-map-type: atomic
type: object
numberOfInstances:
format: int32
minimum: 0
type: integer
patroni:
description: Patroni contains Patroni-specific configuration
properties:
failsafe_mode:
type: boolean
@ -317,14 +523,17 @@ spec:
type: string
type: object
loop_wait:
format: int32
type: integer
maximum_lag_on_failover:
format: int64
type: integer
pg_hba:
items:
type: string
type: array
retry_timeout:
format: int32
type: integer
slots:
additionalProperties:
@ -337,20 +546,24 @@ spec:
synchronous_mode_strict:
type: boolean
synchronous_node_count:
format: int32
type: integer
ttl:
format: int32
type: integer
type: object
pod_priority_class_name:
description: deprecated
type: string
podAnnotations:
additionalProperties:
type: string
type: object
podPriorityClassName:
type: string
pod_priority_class_name:
description: deprecated
type: string
postgresql:
description: PostgresqlParam describes PostgreSQL version and pairs
of configuration parameter name - values.
properties:
parameters:
additionalProperties:
@ -358,17 +571,18 @@ spec:
type: object
version:
enum:
- "13"
- "14"
- "15"
- "16"
- "17"
- 13
- 14
- 15
- 16
- 17
type: string
required:
- version
type: object
preparedDatabases:
additionalProperties:
description: PreparedDatabase describes elements to be bootstrapped
properties:
defaultUsers:
type: boolean
@ -378,6 +592,8 @@ spec:
type: object
schemas:
additionalProperties:
description: PreparedSchema describes elements to be bootstrapped
per schema
properties:
defaultRoles:
type: boolean
@ -395,58 +611,91 @@ spec:
replicaServiceAnnotations:
additionalProperties:
type: string
description: ReplicaServiceAnnotations takes precedence over ServiceAnnotations
for replica role if not empty
type: object
resources:
description: Resources describes requests and limits for the cluster
resouces.
properties:
limits:
description: ResourceDescription describes CPU and memory resources
defined for a cluster.
properties:
cpu:
# Decimal natural followed by m, or decimal natural followed by
# dot followed by up to three decimal digits.
#
# This is because the Kubernetes CPU resource has millis as the
# maximum precision. The actual values are checked in code
# because the regular expression would be huge and horrible and
# not very helpful in validation error messages; this one checks
# only the format of the given number.
#
# https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu
pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
# Note: the value specified here must not be zero or be lower
# than the corresponding request.
description: |-
Decimal natural followed by m, or decimal natural followed by
dot followed by up to three decimal digits.
This is because the Kubernetes CPU resource has millis as the
maximum precision. The actual values are checked in code
because the regular expression would be huge and horrible and
not very helpful in validation error messages; this one checks
only the format of the given number.
https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu
Note: the value specified here must not be zero or be lower
than the corresponding request.
pattern: ^(\d+m|\d+(\.\d{1,3})?)$
type: string
hugepages-1Gi:
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$
type: string
hugepages-2Mi:
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$
type: string
memory:
# You can express memory as a plain integer or as a fixed-point
# integer using one of these suffixes: E, P, T, G, M, k. You can
# also use the power-of-two equivalents: Ei, Pi, Ti, Gi, Mi, Ki
#
# https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-memory
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
# Note: the value specified here must not be zero or be higher
# than the corresponding limit.
description: |-
You can express memory as a plain integer or as a fixed-point
integer using one of these suffixes: E, P, T, G, M, k. You can
also use the power-of-two equivalents: Ei, Pi, Ti, Gi, Mi, Ki
https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-memory
Note: the value specified here must not be zero or be higher
than the corresponding limit.
pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$
type: string
type: object
requests:
description: ResourceDescription describes CPU and memory resources
defined for a cluster.
properties:
cpu:
pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
description: |-
Decimal natural followed by m, or decimal natural followed by
dot followed by up to three decimal digits.
This is because the Kubernetes CPU resource has millis as the
maximum precision. The actual values are checked in code
because the regular expression would be huge and horrible and
not very helpful in validation error messages; this one checks
only the format of the given number.
https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu
Note: the value specified here must not be zero or be lower
than the corresponding request.
pattern: ^(\d+m|\d+(\.\d{1,3})?)$
type: string
hugepages-1Gi:
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$
type: string
hugepages-2Mi:
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$
type: string
memory:
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
description: |-
You can express memory as a plain integer or as a fixed-point
integer using one of these suffixes: E, P, T, G, M, k. You can
also use the power-of-two equivalents: Ei, Pi, Ti, Gi, Mi, Ki
https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-memory
Note: the value specified here must not be zero or be higher
than the corresponding limit.
pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$
type: string
type: object
type: object
@ -457,16 +706,16 @@ spec:
type: string
type: object
sidecars:
items:
type: object
x-kubernetes-preserve-unknown-fields: true
nullable: true
type: array
spiloFSGroup:
format: int64
type: integer
spiloRunAsGroup:
format: int64
type: integer
spiloRunAsUser:
format: int64
type: integer
standby:
oneOf:
@ -476,6 +725,8 @@ spec:
- gs_wal_path
- required:
- standby_host
description: StandbyDescription contains remote primary config or
s3/gs wal path
properties:
gs_wal_path:
type: string
@ -488,13 +739,16 @@ spec:
type: object
streams:
items:
description: Stream defines properties for creating FabricEventStream
resources
properties:
applicationId:
type: string
batchSize:
format: int32
type: integer
cpu:
pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
pattern: ^(\d+m|\d+(\.\d{1,3})?)$
type: string
database:
type: string
@ -505,10 +759,12 @@ spec:
type: string
type: object
memory:
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$
type: string
tables:
additionalProperties:
description: StreamTable defines properties of outbox tables
for FabricEventStreams
properties:
eventType:
type: string
@ -533,6 +789,7 @@ spec:
teamId:
type: string
tls:
description: TLSDescription specs TLS properties
properties:
caFile:
type: string
@ -549,31 +806,51 @@ spec:
type: object
tolerations:
items:
description: |-
The pod this Toleration is attached to tolerates any taint that matches
the triple <key,value,effect> using the matching operator <operator>.
properties:
effect:
enum:
- NoExecute
- NoSchedule
- PreferNoSchedule
description: |-
Effect indicates the taint effect to match. Empty means match all taint effects.
When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute.
type: string
key:
description: |-
Key is the taint key that the toleration applies to. Empty means match all taint keys.
If the key is empty, operator must be Exists; this combination means to match all values and all keys.
type: string
operator:
enum:
- Equal
- Exists
description: |-
Operator represents a key's relationship to the value.
Valid operators are Exists and Equal. Defaults to Equal.
Exists is equivalent to wildcard for value, so that a pod can
tolerate all taints of a particular category.
type: string
tolerationSeconds:
description: |-
TolerationSeconds represents the period of time the toleration (which must be
of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default,
it is not set, which means tolerate the taint forever (do not evict). Zero and
negative values will be treated as 0 (evict immediately) by the system.
format: int64
type: integer
value:
description: |-
Value is the taint value the toleration matches to.
If the operator is Exists, the value should be empty, otherwise just a regular string.
type: string
type: object
type: array
useLoadBalancer:
description: deprecated
description: |-
deprecated load balancer settings maintained for backward compatibility
see "Load balancers" operator docs
type: boolean
users:
additionalProperties:
description: UserFlags defines flags (such as superuser, nologin)
that could be assigned to individual users
items:
enum:
- bypassrls
@ -605,7 +882,6 @@ spec:
- nosuperuser
- NOSUPERUSER
type: string
nullable: true
type: array
type: object
usersIgnoringSecretRotation:
@ -624,65 +900,94 @@ spec:
nullable: true
type: array
volume:
description: Volume describes a single volume in the manifest.
properties:
iops:
format: int64
type: integer
isSubPathExpr:
type: boolean
selector:
description: |-
A label selector is a label query over a set of resources. The result of matchLabels and
matchExpressions are ANDed. An empty label selector matches all objects. A null
label selector matches no objects.
properties:
matchExpressions:
description: matchExpressions is a list of label selector
requirements. The requirements are ANDed.
items:
description: |-
A label selector requirement is a selector that contains values, a key, and an operator that
relates the key and values.
properties:
key:
description: key is the label key that the selector
applies to.
type: string
operator:
enum:
- DoesNotExist
- Exists
- In
- NotIn
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
x-kubernetes-preserve-unknown-fields: true
type: object
x-kubernetes-map-type: atomic
size:
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
# Note: the value specified here must not be zero.
pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$
type: string
storageClass:
type: string
subPath:
type: string
throughput:
format: int64
type: integer
type:
type: string
required:
- size
type: object
required:
- numberOfInstances
- teamId
- postgresql
- teamId
- volume
type: object
status:
additionalProperties:
type: string
description: PostgresStatus contains status of the PostgreSQL cluster
(running, creation failed etc.)
properties:
PostgresClusterStatus:
type: string
required:
- PostgresClusterStatus
type: object
required:
- kind
- apiVersion
- metadata
- spec
type: object
served: true

View File

@ -11,13 +11,25 @@ import (
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +kubebuilder:object:root=true
// Postgresql defines PostgreSQL Custom Resource Definition Object.
// +kubebuilder:resource:categories=all,shortName=pg,scope=Namespaced
// +kubebuilder:printcolumn:name="Team",type=string,JSONPath=`.spec.teamId`,description="Team responsible for Postgres cluster"
// +kubebuilder:printcolumn:name="Version",type=string,JSONPath=`.spec.postgresql.version`,description="PostgreSQL version"
// +kubebuilder:printcolumn:name="Pods",type=integer,JSONPath=`.spec.numberOfInstances`,description="Number of Pods per Postgres cluster"
// +kubebuilder:printcolumn:name="Volume",type=string,JSONPath=`.spec.volume.size`,description="Size of the bound volume"
// +kubebuilder:printcolumn:name="CPU-Request",type=string,JSONPath=`.spec.resources.requests.cpu`,description="Requested CPU for Postgres containers"
// +kubebuilder:printcolumn:name="Memory-Request",type=string,JSONPath=`.spec.resources.requests.memory`,description="Requested memory for Postgres containers"
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`,description="Age of the PostgreSQL cluster"
// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.PostgresClusterStatus`,description="Current sync status of postgresql resource"
// +kubebuilder:subresource:status
type Postgresql struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
metav1.ObjectMeta `json:"metadata"`
Spec PostgresSpec `json:"spec"`
Spec PostgresSpec `json:"spec"`
// +optional
Status PostgresStatus `json:"status"`
Error string `json:"-"`
}
@ -25,9 +37,10 @@ type Postgresql struct {
// PostgresSpec defines the specification for the PostgreSQL TPR.
type PostgresSpec struct {
PostgresqlParam `json:"postgresql"`
Volume `json:"volume,omitempty"`
Patroni `json:"patroni,omitempty"`
*Resources `json:"resources,omitempty"`
Volume `json:"volume"`
// +optional
Patroni `json:"patroni"`
*Resources `json:"resources,omitempty"`
EnableConnectionPooler *bool `json:"enableConnectionPooler,omitempty"`
EnableReplicaConnectionPooler *bool `json:"enableReplicaConnectionPooler,omitempty"`
@ -52,35 +65,59 @@ type PostgresSpec struct {
// deprecated load balancer settings maintained for backward compatibility
// see "Load balancers" operator docs
UseLoadBalancer *bool `json:"useLoadBalancer,omitempty"`
UseLoadBalancer *bool `json:"useLoadBalancer,omitempty"`
// deprecated
ReplicaLoadBalancer *bool `json:"replicaLoadBalancer,omitempty"`
// load balancers' source ranges are the same for master and replica services
// +nullable
// +kubebuilder:validation:items:Pattern=`^(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\/(\d|[1-2]\d|3[0-2])$`
// +optional
AllowedSourceRanges []string `json:"allowedSourceRanges"`
Users map[string]UserFlags `json:"users,omitempty"`
UsersIgnoringSecretRotation []string `json:"usersIgnoringSecretRotation,omitempty"`
UsersWithSecretRotation []string `json:"usersWithSecretRotation,omitempty"`
UsersWithInPlaceSecretRotation []string `json:"usersWithInPlaceSecretRotation,omitempty"`
Users map[string]UserFlags `json:"users,omitempty"`
// +nullable
UsersIgnoringSecretRotation []string `json:"usersIgnoringSecretRotation,omitempty"`
// +nullable
UsersWithSecretRotation []string `json:"usersWithSecretRotation,omitempty"`
// +nullable
UsersWithInPlaceSecretRotation []string `json:"usersWithInPlaceSecretRotation,omitempty"`
NumberOfInstances int32 `json:"numberOfInstances"`
MaintenanceWindows []MaintenanceWindow `json:"maintenanceWindows,omitempty"`
Clone *CloneDescription `json:"clone,omitempty"`
Databases map[string]string `json:"databases,omitempty"`
PreparedDatabases map[string]PreparedDatabase `json:"preparedDatabases,omitempty"`
SchedulerName *string `json:"schedulerName,omitempty"`
NodeAffinity *v1.NodeAffinity `json:"nodeAffinity,omitempty"`
Tolerations []v1.Toleration `json:"tolerations,omitempty"`
Sidecars []Sidecar `json:"sidecars,omitempty"`
InitContainers []v1.Container `json:"initContainers,omitempty"`
PodPriorityClassName string `json:"podPriorityClassName,omitempty"`
ShmVolume *bool `json:"enableShmVolume,omitempty"`
EnableLogicalBackup bool `json:"enableLogicalBackup,omitempty"`
LogicalBackupRetention string `json:"logicalBackupRetention,omitempty"`
LogicalBackupSchedule string `json:"logicalBackupSchedule,omitempty"`
StandbyCluster *StandbyDescription `json:"standby,omitempty"`
PodAnnotations map[string]string `json:"podAnnotations,omitempty"`
ServiceAnnotations map[string]string `json:"serviceAnnotations,omitempty"`
// +kubebuilder:validation:Minimum=0
NumberOfInstances int32 `json:"numberOfInstances"`
// +kubebuilder:validation:Schemaless
// +kubebuilder:validation:Type=array
// +kubebuilde:validation:items:Type=string
MaintenanceWindows []MaintenanceWindow `json:"maintenanceWindows,omitempty"`
Clone *CloneDescription `json:"clone,omitempty"`
// Note: usernames specified here as database owners must be declared
// in the users key of the spec key.
Databases map[string]string `json:"databases,omitempty"`
PreparedDatabases map[string]PreparedDatabase `json:"preparedDatabases,omitempty"`
SchedulerName *string `json:"schedulerName,omitempty"`
NodeAffinity *v1.NodeAffinity `json:"nodeAffinity,omitempty"`
Tolerations []v1.Toleration `json:"tolerations,omitempty"`
// +kubebuilder:validation:Type=array
// +kubebuilder:validation:Items:Type=object
// +kubebuilder:validation:Items:XPreserveUnknownFields
// +kubebuilder:validation:Schemaless
// +nullable
Sidecars []Sidecar `json:"sidecars,omitempty"`
// +kubebuilder:validation:Type=array
// +kubebuilder:validation:Items:Type=object
// +kubebuilder:validation:Items:XPreserveUnknownFields
// +kubebuilder:validation:Schemaless
// +nullable
InitContainers []v1.Container `json:"initContainers,omitempty"`
PodPriorityClassName string `json:"podPriorityClassName,omitempty"`
ShmVolume *bool `json:"enableShmVolume,omitempty"`
EnableLogicalBackup bool `json:"enableLogicalBackup,omitempty"`
LogicalBackupRetention string `json:"logicalBackupRetention,omitempty"`
// +kubebuilder:validation:Pattern=`^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$`
LogicalBackupSchedule string `json:"logicalBackupSchedule,omitempty"`
StandbyCluster *StandbyDescription `json:"standby,omitempty"`
PodAnnotations map[string]string `json:"podAnnotations,omitempty"`
ServiceAnnotations map[string]string `json:"serviceAnnotations,omitempty"`
// MasterServiceAnnotations takes precedence over ServiceAnnotations for master role if not empty
MasterServiceAnnotations map[string]string `json:"masterServiceAnnotations,omitempty"`
// ReplicaServiceAnnotations takes precedence over ServiceAnnotations for replica role if not empty
@ -88,11 +125,20 @@ type PostgresSpec struct {
TLS *TLSDescription `json:"tls,omitempty"`
AdditionalVolumes []AdditionalVolume `json:"additionalVolumes,omitempty"`
Streams []Stream `json:"streams,omitempty"`
Env []v1.EnvVar `json:"env,omitempty"`
// +kubebuilder:validation:Type=array
// +kubebuilder:validation:Items:Type=object
// +kubebuilder:validation:Items:XPreserveUnknownFields
// +kubebuilder:validation:Schemaless
// +nullable
Env []v1.EnvVar `json:"env,omitempty"`
// deprecated json tags
InitContainersOld []v1.Container `json:"init_containers,omitempty"`
PodPriorityClassNameOld string `json:"pod_priority_class_name,omitempty"`
// +kubebuilder:validation:XPreserveUnknownFields
// +kubebuilder:validation:Type=object
// +kubebuilder:validation:Schemaless
InitContainersOld []v1.Container `json:"init_containers,omitempty"`
// deprecated
PodPriorityClassNameOld string `json:"pod_priority_class_name,omitempty"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
@ -123,50 +169,84 @@ type PreparedSchema struct {
type MaintenanceWindow struct {
Everyday bool `json:"everyday,omitempty"`
Weekday time.Weekday `json:"weekday,omitempty"`
StartTime metav1.Time `json:"startTime,omitempty"`
EndTime metav1.Time `json:"endTime,omitempty"`
StartTime metav1.Time `json:"startTime"`
EndTime metav1.Time `json:"endTime"`
}
// Volume describes a single volume in the manifest.
type Volume struct {
Selector *metav1.LabelSelector `json:"selector,omitempty"`
Size string `json:"size"`
StorageClass string `json:"storageClass,omitempty"`
SubPath string `json:"subPath,omitempty"`
IsSubPathExpr *bool `json:"isSubPathExpr,omitempty"`
Iops *int64 `json:"iops,omitempty"`
Throughput *int64 `json:"throughput,omitempty"`
VolumeType string `json:"type,omitempty"`
Selector *metav1.LabelSelector `json:"selector,omitempty"`
// +kubebuilder:validation:Pattern=`^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$`
Size string `json:"size"`
StorageClass string `json:"storageClass,omitempty"`
SubPath string `json:"subPath,omitempty"`
IsSubPathExpr *bool `json:"isSubPathExpr,omitempty"`
Iops *int64 `json:"iops,omitempty"`
Throughput *int64 `json:"throughput,omitempty"`
VolumeType string `json:"type,omitempty"`
}
// AdditionalVolume specs additional optional volumes for statefulset
type AdditionalVolume struct {
Name string `json:"name"`
MountPath string `json:"mountPath"`
SubPath string `json:"subPath,omitempty"`
IsSubPathExpr *bool `json:"isSubPathExpr,omitempty"`
TargetContainers []string `json:"targetContainers"`
VolumeSource v1.VolumeSource `json:"volumeSource"`
Name string `json:"name"`
MountPath string `json:"mountPath"`
SubPath string `json:"subPath,omitempty"`
IsSubPathExpr *bool `json:"isSubPathExpr,omitempty"`
// +nullable
// +optional
TargetContainers []string `json:"targetContainers"`
// +kubebuilder:validation:XPreserveUnknownFields
// +kubebuilder:validation:Type=object
// +kubebuilder:validation:Schemaless
VolumeSource v1.VolumeSource `json:"volumeSource"`
}
// PostgresqlParam describes PostgreSQL version and pairs of configuration parameter name - values.
type PostgresqlParam struct {
// +kubebuilder:validation:Enum=13;14;15;16;17
PgVersion string `json:"version"`
Parameters map[string]string `json:"parameters,omitempty"`
}
// ResourceDescription describes CPU and memory resources defined for a cluster.
type ResourceDescription struct {
CPU *string `json:"cpu,omitempty"`
Memory *string `json:"memory,omitempty"`
// Decimal natural followed by m, or decimal natural followed by
// dot followed by up to three decimal digits.
//
// This is because the Kubernetes CPU resource has millis as the
// maximum precision. The actual values are checked in code
// because the regular expression would be huge and horrible and
// not very helpful in validation error messages; this one checks
// only the format of the given number.
//
// https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu
//
// Note: the value specified here must not be zero or be lower
// than the corresponding request.
// +kubebuilder:validation:Pattern=`^(\d+m|\d+(\.\d{1,3})?)$`
CPU *string `json:"cpu,omitempty"`
// You can express memory as a plain integer or as a fixed-point
// integer using one of these suffixes: E, P, T, G, M, k. You can
// also use the power-of-two equivalents: Ei, Pi, Ti, Gi, Mi, Ki
//
// https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-memory
//
// Note: the value specified here must not be zero or be higher
// than the corresponding limit.
// +kubebuilder:validation:Pattern=`^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$`
Memory *string `json:"memory,omitempty"`
// +kubebuilder:validation:Pattern=`^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$`
HugePages2Mi *string `json:"hugepages-2Mi,omitempty"`
// +kubebuilder:validation:Pattern=`^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$`
HugePages1Gi *string `json:"hugepages-1Gi,omitempty"`
}
// Resources describes requests and limits for the cluster resouces.
type Resources struct {
ResourceRequests ResourceDescription `json:"requests,omitempty"`
ResourceLimits ResourceDescription `json:"limits,omitempty"`
// +optional
ResourceRequests ResourceDescription `json:"requests"`
// +optional
ResourceLimits ResourceDescription `json:"limits"`
}
// Patroni contains Patroni-specific configuration
@ -176,7 +256,7 @@ type Patroni struct {
TTL uint32 `json:"ttl,omitempty"`
LoopWait uint32 `json:"loop_wait,omitempty"`
RetryTimeout uint32 `json:"retry_timeout,omitempty"`
MaximumLagOnFailover float32 `json:"maximum_lag_on_failover,omitempty"` // float32 because https://github.com/kubernetes/kubernetes/issues/30213
MaximumLagOnFailover int64 `json:"maximum_lag_on_failover,omitempty"`
Slots map[string]map[string]string `json:"slots,omitempty"`
SynchronousMode bool `json:"synchronous_mode,omitempty"`
SynchronousModeStrict bool `json:"synchronous_mode_strict,omitempty"`
@ -185,6 +265,7 @@ type Patroni struct {
}
// StandbyDescription contains remote primary config or s3/gs wal path
// +kubebuilder:validation:ExactlyOneOf=s3_wal_path;gs_wal_path;standby_host
type StandbyDescription struct {
S3WalPath string `json:"s3_wal_path,omitempty"`
GSWalPath string `json:"gs_wal_path,omitempty"`
@ -194,6 +275,7 @@ type StandbyDescription struct {
// TLSDescription specs TLS properties
type TLSDescription struct {
// +required
SecretName string `json:"secretName,omitempty"`
CertificateFile string `json:"certificateFile,omitempty"`
PrivateKeyFile string `json:"privateKeyFile,omitempty"`
@ -203,8 +285,14 @@ type TLSDescription struct {
// CloneDescription describes which cluster the new should clone and up to which point in time
type CloneDescription struct {
ClusterName string `json:"cluster,omitempty"`
UID string `json:"uid,omitempty"`
// +required
ClusterName string `json:"cluster,omitempty"`
// +kubebuilder:validation:Format=uuid
UID string `json:"uid,omitempty"`
// The regexp matches the date-time format (RFC 3339 Section 5.6) that specifies a timezone as an offset relative to UTC
// Example: 1996-12-19T16:39:57-08:00
// Note: this field requires a timezone
// +kubebuilder:validation:Pattern=`^([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])[Tt]([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(([+-]([01][0-9]|2[0-3]):[0-5][0-9]))$`
EndTimestamp string `json:"timestamp,omitempty"`
S3WalPath string `json:"s3_wal_path,omitempty"`
S3Endpoint string `json:"s3_endpoint,omitempty"`
@ -224,6 +312,8 @@ type Sidecar struct {
}
// UserFlags defines flags (such as superuser, nologin) that could be assigned to individual users
// +kubebuilder:validation:items:Enum=bypassrls;BYPASSRLS;nobypassrls;NOBYPASSRLS;createdb;CREATEDB;nocreatedb;NOCREATEDB;createrole;CREATEROLE;nocreaterole;NOCREATEROLE;inherit;INHERIT;noinherit;NOINHERIT;login;LOGIN;nologin;NOLOGIN;replication;REPLICATION;noreplication;NOREPLICATION;superuser;SUPERUSER;nosuperuser;NOSUPERUSER
// +nullable
type UserFlags []string
// PostgresStatus contains status of the PostgreSQL cluster (running, creation failed etc.)
@ -242,26 +332,30 @@ type PostgresStatus struct {
// makes sense to expose. E.g. pool size (min/max boundaries), max client
// connections etc.
type ConnectionPooler struct {
// +kubebuilder:validation:Minimum=1
NumberOfInstances *int32 `json:"numberOfInstances,omitempty"`
Schema string `json:"schema,omitempty"`
User string `json:"user,omitempty"`
Mode string `json:"mode,omitempty"`
DockerImage string `json:"dockerImage,omitempty"`
MaxDBConnections *int32 `json:"maxDBConnections,omitempty"`
// +kubebuilder:validation:Enum=session;transaction
Mode string `json:"mode,omitempty"`
DockerImage string `json:"dockerImage,omitempty"`
MaxDBConnections *int32 `json:"maxDBConnections,omitempty"`
*Resources `json:"resources,omitempty"`
}
// Stream defines properties for creating FabricEventStream resources
type Stream struct {
ApplicationId string `json:"applicationId"`
Database string `json:"database"`
Tables map[string]StreamTable `json:"tables"`
Filter map[string]*string `json:"filter,omitempty"`
BatchSize *uint32 `json:"batchSize,omitempty"`
CPU *string `json:"cpu,omitempty"`
Memory *string `json:"memory,omitempty"`
EnableRecovery *bool `json:"enableRecovery,omitempty"`
ApplicationId string `json:"applicationId"`
Database string `json:"database"`
Tables map[string]StreamTable `json:"tables"`
Filter map[string]*string `json:"filter,omitempty"`
BatchSize *uint32 `json:"batchSize,omitempty"`
// +kubebuilder:validation:Pattern=`^(\d+m|\d+(\.\d{1,3})?)$`
CPU *string `json:"cpu,omitempty"`
// +kubebuilder:validation:Pattern=`^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$`
Memory *string `json:"memory,omitempty"`
EnableRecovery *bool `json:"enableRecovery,omitempty"`
}
// StreamTable defines properties of outbox tables for FabricEventStreams

View File

@ -412,7 +412,7 @@ PatroniInitDBParams:
}
if patroni.MaximumLagOnFailover >= 0 {
config.Bootstrap.DCS.MaximumLagOnFailover = patroni.MaximumLagOnFailover
config.Bootstrap.DCS.MaximumLagOnFailover = float32(patroni.MaximumLagOnFailover)
}
if patroni.LoopWait != 0 {
config.Bootstrap.DCS.LoopWait = patroni.LoopWait