diff --git a/.github/workflows/publish_ghcr_image.yaml b/.github/workflows/publish_ghcr_image.yaml index 5a0c3b045..2fc22d0c9 100644 --- a/.github/workflows/publish_ghcr_image.yaml +++ b/.github/workflows/publish_ghcr_image.yaml @@ -19,11 +19,11 @@ jobs: packages: write steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v6 - - uses: actions/setup-go@v2 + - uses: actions/setup-go@v6 with: - go-version: "^1.25.3" + go-version: "^1.26.4" - name: Run unit tests run: make test @@ -53,20 +53,20 @@ jobs: echo "BACKUP_IMAGE=$BACKUP_IMAGE" >> $GITHUB_OUTPUT - name: Set up QEMU - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v4 - name: Login to GHCR - uses: docker/login-action@v2 + uses: docker/login-action@v4 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push multiarch operator image to ghcr - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v7 with: context: . file: docker/Dockerfile @@ -76,7 +76,7 @@ jobs: platforms: linux/amd64,linux/arm64 - name: Build and push multiarch pooler image to ghcr - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v7 with: context: pooler push: true @@ -85,7 +85,7 @@ jobs: platforms: linux/amd64,linux/arm64 - name: Build and push multiarch ui image to ghcr - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v7 with: context: ui push: true @@ -94,7 +94,7 @@ jobs: platforms: linux/amd64,linux/arm64 - name: Build and push multiarch logical-backup image to ghcr - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v7 with: context: logical-backup push: true diff --git a/.github/workflows/run_e2e.yaml b/.github/workflows/run_e2e.yaml index e7c04c0c5..393109b00 100644 --- a/.github/workflows/run_e2e.yaml +++ b/.github/workflows/run_e2e.yaml @@ -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 diff --git a/.github/workflows/run_tests.yaml b/.github/workflows/run_tests.yaml index 7940b61f2..c511e8fa1 100644 --- a/.github/workflows/run_tests.yaml +++ b/.github/workflows/run_tests.yaml @@ -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 diff --git a/LICENSE b/LICENSE index 2141e8bcb..2f94edc31 100644 --- a/LICENSE +++ b/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 diff --git a/Makefile b/Makefile index c1becbc99..3613c1044 100644 --- a/Makefile +++ b/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,15 @@ $(GENERATED): go.mod $(CRD_SOURCES) $(GENERATED_CRDS): $(GENERATED) go tool controller-gen crd:crdVersions=v1,allowDangerousTypes=true paths=./pkg/apis/acid.zalan.do/... output:crd:dir=manifests - # only generate postgresteam.crd.yaml and postgresql.crd.yaml for now - @rm manifests/acid.zalan.do_operatorconfigurations.yaml @mv manifests/acid.zalan.do_postgresqls.yaml manifests/postgresql.crd.yaml @# hack to use lowercase kind and listKind @sed -i -e 's/kind: Postgresql/kind: postgresql/' manifests/postgresql.crd.yaml @sed -i -e 's/listKind: PostgresqlList/listKind: postgresqlList/' manifests/postgresql.crd.yaml @hack/adjust_postgresql_crd.sh + @mv manifests/acid.zalan.do_operatorconfigurations.yaml manifests/operatorconfiguration.crd.yaml @mv manifests/acid.zalan.do_postgresteams.yaml manifests/postgresteam.crd.yaml @cp manifests/postgresql.crd.yaml pkg/apis/acid.zalan.do/v1/postgresql.crd.yaml + @cp manifests/operatorconfiguration.crd.yaml pkg/apis/acid.zalan.do/v1/operatorconfiguration.crd.yaml local: ${SOURCES} $(GENERATED_CRDS) CGO_ENABLED=${CGO_ENABLED} go build -o build/${BINARY} $(LOCAL_BUILD_FLAGS) -ldflags "$(LDFLAGS)" $(SOURCES) @@ -83,10 +84,10 @@ wasm: ${SOURCES} $(GENERATED_CRDS) GOOS=wasip1 GOARCH=wasm CGO_ENABLED=${CGO_ENABLED} go build -o build/${BINARY}.wasm ${BUILD_FLAGS} -ldflags "$(LDFLAGS)" $(SOURCES) linux: ${SOURCES} $(GENERATED_CRDS) - GOOS=linux GOARCH=amd64 CGO_ENABLED=${CGO_ENABLED} go build -o build/linux/${BINARY} ${BUILD_FLAGS} -ldflags "$(LDFLAGS)" $(SOURCES) + GOOS=linux GOARCH=${GOARCH} CGO_ENABLED=${CGO_ENABLED} go build -o build/linux/${BINARY} ${BUILD_FLAGS} -ldflags "$(LDFLAGS)" $(SOURCES) macos: ${SOURCES} $(GENERATED_CRDS) - GOOS=darwin GOARCH=amd64 CGO_ENABLED=${CGO_ENABLED} go build -o build/macos/${BINARY} ${BUILD_FLAGS} -ldflags "$(LDFLAGS)" $(SOURCES) + GOOS=darwin GOARCH=${GOARCH} CGO_ENABLED=${CGO_ENABLED} go build -o build/macos/${BINARY} ${BUILD_FLAGS} -ldflags "$(LDFLAGS)" $(SOURCES) docker: $(GENERATED_CRDS) ${DOCKERDIR}/${DOCKERFILE} echo `(env)` @@ -100,10 +101,10 @@ pooler: cd pooler; docker build --rm -t "$(POOLER_TAG)" --build-arg VERSION="${VERSION}" --build-arg BASE_IMAGE="${BASE_IMAGE}" . indocker-race: - docker run --rm -v "${GOPATH}":"${GOPATH}" -e GOPATH="${GOPATH}" -e RACE=1 -w ${PWD} golang:1.25.3 bash -c "make linux" + docker run --rm -v "${GOPATH}":"${GOPATH}" -e GOPATH="${GOPATH}" -e RACE=1 -w ${PWD} golang:1.26.4 bash -c "make linux" mocks: - GO111MODULE=on go generate ./... + go generate ./... fmt: @gofmt -l -w -s $(DIRS) @@ -113,7 +114,7 @@ vet: @staticcheck $(PKG) test: mocks $(GENERATED) $(GENERATED_CRDS) - GO111MODULE=on go test ./... + go test ./... codegen: $(GENERATED) diff --git a/README.md b/README.md index 9e5bc886b..31e9c1748 100644 --- a/README.md +++ b/README.md @@ -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 | diff --git a/charts/postgres-operator/crds/operatorconfigurations.yaml b/charts/postgres-operator/crds/operatorconfigurations.yaml index ed06ce2cc..790b9effc 100644 --- a/charts/postgres-operator/crds/operatorconfigurations.yaml +++ b/charts/postgres-operator/crds/operatorconfigurations.yaml @@ -7,281 +7,292 @@ metadata: spec: group: acid.zalan.do names: + categories: + - all kind: OperatorConfiguration listKind: OperatorConfigurationList plural: operatorconfigurations - singular: operatorconfiguration shortNames: - opconfig - categories: - - all + singular: operatorconfiguration scope: Namespaced versions: - - name: v1 - served: true - storage: true - subresources: - status: {} - additionalPrinterColumns: - - name: Image - type: string - description: Spilo image to be used for Pods + - additionalPrinterColumns: + - description: Spilo image to be used for Pods jsonPath: .configuration.docker_image - - name: Cluster-Label + name: Image type: string - description: Label for K8s resources created by operator + - description: Label for K8s resources created by operator jsonPath: .configuration.kubernetes.cluster_name_label - - name: Service-Account + name: Cluster-Label type: string - description: Name of service account to be used + - description: Name of service account to be used jsonPath: .configuration.kubernetes.pod_service_account_name - - name: Min-Instances - type: integer - description: Minimum number of instances per Postgres cluster + name: Service-Account + type: string + - description: Minimum number of instances per Postgres cluster jsonPath: .configuration.min_instances - - name: Age - type: date + name: Min-Instances + type: integer + - description: Age of the OperatorConfiguration resource jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 schema: openAPIV3Schema: - type: object - required: - - kind - - apiVersion - - configuration + description: OperatorConfiguration defines the specification for the OperatorConfiguration. properties: - kind: - type: string - enum: - - OperatorConfiguration apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string - enum: - - acid.zalan.do/v1 configuration: - type: object + 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_pod_security_context: + type: object + x-kubernetes-preserve-unknown-fields: true + connection_pooler_schema: + default: pooler + type: string + connection_pooler_security_context: + type: object + x-kubernetes-preserve-unknown-fields: true + connection_pooler_user: + default: pooler + type: string + type: object crd_categories: - type: array - nullable: true 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 - default: "ghcr.io/zalando/spilo-18:4.1-p1" enable_crd_registration: - type: boolean default: true - enable_crd_validation: type: boolean - description: deprecated - default: true enable_lazy_spilo_upgrade: type: boolean - default: false enable_maintenance_windows: - type: boolean default: true + type: boolean enable_pgversion_env_var: - type: boolean default: true + type: boolean enable_shm_volume: - type: boolean default: true + type: boolean enable_spilo_wal_path_compat: type: boolean - default: false enable_team_id_clustername_prefix: type: boolean - default: false etcd_host: - type: string default: "" + type: string ignore_instance_limits_annotation_key: type: string ignore_resources_limits_annotation_key: type: string - kubernetes_use_configmaps: - type: boolean - default: false - maintenance_windows: - items: - pattern: '^\ *((Mon|Tue|Wed|Thu|Fri|Sat|Sun):(2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))-((2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))\ *$' - type: string - type: array - max_instances: - type: integer - description: "-1 = disabled" - minimum: -1 - default: -1 - min_instances: - type: integer - description: "-1 = disabled" - minimum: -1 - default: -1 - resync_period: - type: string - default: "30m" - repair_period: - type: string - default: "5m" - set_memory_request_to_limit: - type: boolean - default: false - sidecar_docker_images: - type: object - additionalProperties: - type: string - sidecars: - type: array - nullable: true - items: - type: object - x-kubernetes-preserve-unknown-fields: true - workers: - type: integer - minimum: 1 - default: 8 - users: - type: object - properties: - additional_owner_roles: - type: array - nullable: true - items: - type: string - enable_password_rotation: - type: boolean - default: false - password_rotation_interval: - type: integer - default: 90 - password_rotation_user_retention: - type: integer - default: 180 - replication_username: - type: string - default: standby - super_username: - type: string - default: postgres - major_version_upgrade: - type: object - properties: - major_version_upgrade_mode: - type: string - default: "manual" - major_version_upgrade_team_allow_list: - type: array - items: - type: string - minimal_major_version: - type: string - default: "14" - target_major_version: - type: string - default: "18" kubernetes: - type: object + description: KubernetesMetaConfiguration defines k8s conf required + for all Postgres clusters and the operator itself properties: additional_pod_capabilities: - type: array items: type: string + type: array cluster_domain: + default: cluster.local type: string - default: "cluster.local" cluster_labels: - type: object additionalProperties: type: string default: application: spilo - cluster_name_label: - type: string - default: "cluster-name" - custom_pod_annotations: 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: - type: array items: type: string + type: array enable_cross_namespace_secret: type: boolean - default: false enable_finalizers: type: boolean - default: false enable_init_containers: - type: boolean default: true + type: boolean enable_owner_references: type: boolean - default: false enable_persistent_volume_claim_deletion: - type: boolean default: true + type: boolean enable_pod_antiaffinity: type: boolean - default: false enable_pod_disruption_budget: - type: boolean default: true + type: boolean enable_readiness_probe: type: boolean - default: false enable_secrets_deletion: - type: boolean default: true + type: boolean enable_sidecars: - type: boolean default: true + type: boolean ignored_annotations: - type: array items: type: string - infrastructure_roles_secret_name: - type: string - infrastructure_roles_secrets: type: array - nullable: true + infrastructure_roles_secret_name: + description: |- + NamespacedName comprises a resource name, with a mandatory namespace, + rendered as "/". Being a type captures intent and + helps make sure that UIDs, namespaced names and non-namespaced names + do not get conflated in code. For most use cases, namespace and name + will already have been format validated at the API entry point, so we + don't do that here. Where that's not the case (e.g. in testing), + consider using NamespacedNameOrDie() in testing.go in this package. + + from: https://github.com/kubernetes/apimachinery/blob/master/pkg/types/namespacedname.go + 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: - type: object - required: - - secretname - - userkey - - passwordkey properties: - secretname: + defaultrolevalue: type: string - userkey: + 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 - defaultuservalue: - type: string - defaultrolevalue: - type: string - details: - 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: - type: array items: type: string + type: array inherited_labels: - type: array items: type: string + type: array liveness_probe: description: |- Probe describes a health check to be performed against a container to determine whether it is @@ -336,11 +347,11 @@ spec: "Host" in httpHeaders instead. type: string httpHeaders: - description: Custom headers to set in the request. HTTP allows - repeated headers. + 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 + description: HTTPHeader describes a custom header to + be used in HTTP probes properties: name: description: |- @@ -436,274 +447,236 @@ spec: type: integer type: object master_pod_move_timeout: - type: string - default: "20m" + default: 20m + description: timeout for successful migration of master pods from + unschedulable node + format: int64 + type: integer node_readiness_label: - type: object additionalProperties: type: string - node_readiness_label_merge: - type: string - enum: - - "AND" - - "OR" - oauth_token_secret_name: - type: string - default: "postgresql-operator" - pdb_master_label_selector: - type: boolean - default: true - pdb_name_format: - type: string - default: "postgres-{cluster}-pdb" - persistent_volume_claim_retention_policy: 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: - when_deleted: + name: type: string - enum: - - "delete" - - "retain" - when_scaled: + namespace: type: string - enum: - - "delete" - - "retain" + 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 - default: false pod_antiaffinity_topology_key: + default: kubernetes.io/hostname type: string - default: "kubernetes.io/hostname" pod_environment_configmap: - type: string + 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: - type: string + default: ordered_ready enum: - - "ordered_ready" - - "parallel" - default: "ordered_ready" + - ordered_ready + - parallel + type: string pod_priority_class_name: type: string pod_role_label: + default: spilo-role type: string - default: "spilo-role" pod_service_account_definition: type: string - default: "" pod_service_account_name: + default: postgres-pod type: string - default: "postgres-pod" pod_service_account_role_binding_definition: type: string - default: "" pod_terminate_grace_period: - type: string - default: "5m" + default: 5m + description: Postgres pods are terminated forcefully after this + timeout + format: int64 + type: integer secret_name_template: + default: '{username}.{cluster}.credentials.{tprkind}.{tprgroup}' + description: |- + template for database user secrets generated by the operator, + here username contains the namespace in the format namespace.username + if the user is in different namespace than cluster and cross namespace secrets + are enabled via `enable_cross_namespace_secret` flag in the configuration. type: string - default: "{username}.{cluster}.credentials.{tprkind}.{tprgroup}" share_pgsocket_with_sidecars: type: boolean - default: false spilo_allow_privilege_escalation: - type: boolean default: true - spilo_runasuser: - type: integer - spilo_runasgroup: - type: integer + type: boolean spilo_fsgroup: + format: int64 type: integer spilo_privileged: type: boolean - default: false + spilo_runasgroup: + format: int64 + type: integer + spilo_runasuser: + format: int64 + type: integer storage_resize_mode: - type: string + default: pvc enum: - - "ebs" - - "mixed" - - "pvc" - - "off" - default: "pvc" + - ebs + - mixed + - pvc + - "off" + type: string toleration: - type: object additionalProperties: type: string + type: object watched_namespace: type: string - postgres_pod_resources: type: object - properties: - default_cpu_limit: - type: string - pattern: '^(\d+m|\d+(\.\d{1,3})?)$|^$' - default_cpu_request: - type: string - pattern: '^(\d+m|\d+(\.\d{1,3})?)$|^$' - default_memory_limit: - type: string - pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$|^$' - default_memory_request: - type: string - pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$|^$' - max_cpu_request: - type: string - pattern: '^(\d+m|\d+(\.\d{1,3})?)$|^$' - max_memory_request: - type: string - pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$|^$' - min_cpu_limit: - type: string - pattern: '^(\d+m|\d+(\.\d{1,3})?)$|^$' - min_memory_limit: - type: string - pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$|^$' - timeouts: - type: object - properties: - patroni_api_check_interval: - type: string - default: "1s" - patroni_api_check_timeout: - type: string - default: "5s" - pod_label_wait_timeout: - type: string - default: "10m" - pod_deletion_wait_timeout: - type: string - default: "10m" - ready_wait_interval: - type: string - default: "4s" - ready_wait_timeout: - type: string - default: "30s" - resource_check_interval: - type: string - default: "3s" - resource_check_timeout: - type: string - default: "10m" + kubernetes_use_configmaps: + default: true + type: boolean load_balancer: - type: object + description: LoadBalancerConfiguration defines the LB configuration properties: custom_service_annotations: - type: object additionalProperties: type: string + type: object db_hosted_zone: type: string - default: "db.example.com" enable_master_load_balancer: type: boolean - default: true - enable_master_pooler_load_balancer: - type: boolean - default: false - enable_replica_load_balancer: - type: boolean - default: false - enable_replica_pooler_load_balancer: - type: boolean - default: false enable_master_node_port: type: boolean - default: false + enable_master_pooler_load_balancer: + type: boolean enable_master_pooler_node_port: type: boolean - default: false + enable_replica_load_balancer: + type: boolean enable_replica_node_port: type: boolean - default: false + enable_replica_pooler_load_balancer: + type: boolean enable_replica_pooler_node_port: type: boolean - default: false external_traffic_policy: - type: string + default: Cluster enum: - - "Cluster" - - "Local" - default: "Cluster" + - 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 - default: "{cluster}.{namespace}.{hostedzone}" master_legacy_dns_name_format: + default: '{cluster}.{team}.{hostedzone}' + description: deprecated DNS template for master load balancer + using team name type: string - default: "{cluster}.{team}.{hostedzone}" replica_dns_name_format: + default: '{cluster}-repl.{namespace}.{hostedzone}' + description: defines the DNS name string template for the replica + load balancer cluster type: string - default: "{cluster}-repl.{namespace}.{hostedzone}" replica_legacy_dns_name_format: + default: '{cluster}-repl.{team}.{hostedzone}' + description: deprecated DNS template for replica load balancer + using team name type: string - default: "{cluster}-repl.{team}.{hostedzone}" - aws_or_gcp: type: object + logging_rest_api: + description: LoggingRESTAPIConfiguration defines Logging API conf properties: - additional_secret_mount: - type: string - additional_secret_mount_path: - type: string - aws_region: - type: string - default: "eu-central-1" - enable_ebs_gp3_migration: - type: boolean - default: false - enable_ebs_gp3_migration_max_size: + api_port: + default: 8080 type: integer + cluster_history_entries: default: 1000 - 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 - logical_backup: + 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_azure_storage_account_key: - type: string logical_backup_cpu_limit: + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ type: string - pattern: '^(\d+m|\d+(\.\d{1,3})?)$' logical_backup_cpu_request: + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ + type: string + logical_backup_cronjob_environment_secret: type: string - pattern: '^(\d+m|\d+(\.\d{1,3})?)$' logical_backup_docker_image: + default: ghcr.io/zalando/postgres-operator/logical-backup:v1.15.1 type: string - default: "ghcr.io/zalando/postgres-operator/logical-backup:v1.15.1" + 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 - default: "logical-backup-" logical_backup_memory_limit: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ type: string - pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' logical_backup_memory_request: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ type: string - pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' logical_backup_provider: - type: string + default: s3 enum: - - "az" - - "gcs" - - "s3" - default: "s3" + - az + - gcs + - s3 + type: string logical_backup_s3_access_key_id: type: string logical_backup_s3_bucket: @@ -714,168 +687,290 @@ spec: 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_s3_retention_time: - type: string logical_backup_schedule: + default: 30 00 * * * + pattern: ^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$ type: string - pattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$' - default: "30 00 * * *" - logical_backup_cronjob_environment_secret: - type: string - debug: + logical_backup_successful_jobs_history_limit: + default: 3 + format: int32 + minimum: 0 + type: integer + logical_backup_ttl_seconds_after_finished: + default: 86400 + format: int32 + minimum: 0 + type: integer type: object + maintenance_windows: + type: array + major_version_upgrade: + description: MajorVersionUpgradeConfiguration defines how to execute + major version upgrades of Postgres. properties: - debug_logging: - type: boolean - default: true - enable_database_access: - type: boolean - default: true - teams_api: - type: object - properties: - enable_admin_role_for_users: - type: boolean - default: true - enable_postgres_team_crd: - type: boolean - default: true - enable_postgres_team_crd_superusers: - type: boolean - default: false - enable_team_member_deprecation: - type: boolean - default: false - enable_team_superuser: - type: boolean - default: false - enable_teams_api: - type: boolean - default: true - pam_configuration: + major_version_upgrade_mode: + default: manual + enum: + - "off" + - manual + - full type: string - default: "https://info.example.com/oauth2/tokeninfo?access_token= uid realm=/employees" - pam_role_name: - type: string - default: "zalandos" - postgres_superuser_teams: - type: array + major_version_upgrade_team_allow_list: items: type: string - protected_role_names: type: array - items: - type: string - default: - - admin - - cron_admin - role_deletion_suffix: + minimal_major_version: + default: "14" type: string - default: "_deleted" - team_admin_role: + target_major_version: + default: "18" type: string - default: "admin" - team_api_role_configuration: - type: object - additionalProperties: - type: string - default: - log_statement: all - teams_api_url: - type: string - default: "https://teams.example.com/api/" - logging_rest_api: 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: - api_port: - type: integer - default: 8080 - cluster_history_entries: - type: integer - default: 1000 - ring_log_lines: - type: integer - default: 100 - scalyr: # deprecated + enable_patroni_failsafe_mode: + type: boolean type: object + postgres_pod_resources: + description: PostgresPodResourcesDefaults defines the spec of default + resources + properties: + default_cpu_limit: + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ + type: string + default_cpu_request: + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ + type: string + default_memory_limit: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + default_memory_request: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + max_cpu_request: + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ + type: string + max_memory_request: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + min_cpu_limit: + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ + type: string + min_memory_limit: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + type: object + repair_period: + default: 5m + description: period between consecutive repair requests + format: int64 + type: integer + resync_period: + default: 30m + description: period between consecutive sync requests + format: int64 + type: integer + scalyr: + description: ScalyrConfiguration defines the configuration for ScalyrAPI properties: scalyr_api_key: type: string scalyr_cpu_limit: - type: string - pattern: '^(\d+m|\d+(\.\d{1,3})?)$' default: "1" - scalyr_cpu_request: + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ + type: string + scalyr_cpu_request: + default: 100m + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ type: string - pattern: '^(\d+m|\d+(\.\d{1,3})?)$' - default: "100m" scalyr_image: type: string scalyr_memory_limit: + default: 500Mi + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ type: string - pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' - default: "500Mi" scalyr_memory_request: + default: 50Mi + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ type: string - pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' - default: "50Mi" scalyr_server_url: + default: https://upload.eu.scalyr.com type: string - default: "https://upload.eu.scalyr.com" - connection_pooler: type: object - properties: - connection_pooler_schema: - type: string - default: "pooler" - connection_pooler_user: - type: string - default: "pooler" - connection_pooler_image: - type: string - default: "ghcr.io/zalando/postgres-operator/pgbouncer:latest" - connection_pooler_max_db_connections: - type: integer - default: 60 - connection_pooler_mode: - type: string - enum: - - "session" - - "transaction" - default: "transaction" - connection_pooler_number_of_instances: - type: integer - minimum: 1 - default: 2 - connection_pooler_default_cpu_limit: - type: string - pattern: '^(\d+m|\d+(\.\d{1,3})?)$' - connection_pooler_default_cpu_request: - type: string - pattern: '^(\d+m|\d+(\.\d{1,3})?)$' - connection_pooler_default_memory_limit: - type: string - pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' - connection_pooler_default_memory_request: - type: string - pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' - connection_pooler_pod_security_context: - type: object - x-kubernetes-preserve-unknown-fields: true - connection_pooler_security_context: - type: object - x-kubernetes-preserve-unknown-fields: true - patroni: + 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_patroni_failsafe_mode: + enable_admin_role_for_users: + default: true type: boolean - default: false - status: + enable_postgres_team_crd: + default: true + type: boolean + enable_postgres_team_crd_superusers: + type: boolean + enable_team_member_deprecation: + type: boolean + enable_team_superuser: + type: boolean + enable_teams_api: + type: boolean + pam_configuration: + default: https://info.example.com/oauth2/tokeninfo?access_token= + uid realm=/employees + type: string + pam_role_name: + default: zalandos + type: string + postgres_superuser_teams: + items: + type: string + type: array + protected_role_names: + default: '["admin", "cron_admin"]' + items: + type: string + type: array + role_deletion_suffix: + default: _deleted + type: string + team_admin_role: + default: admin + type: string + team_api_role_configuration: + additionalProperties: + type: string + default: + log_statement: all + type: object + teams_api_url: + default: https://teams.example.com/api/ + type: string + type: object + timeouts: + description: OperatorTimeouts defines the timeout of ResourceCheck, + PodWait, ReadyWait + properties: + patroni_api_check_interval: + default: 1s + description: interval between consecutive attempts of operator + calling the Patroni API + format: int64 + type: integer + patroni_api_check_timeout: + default: 5s + description: timeout when waiting for successful response from + Patroni API + format: int64 + type: integer + pod_deletion_wait_timeout: + default: 10m + description: timeout when waiting for the Postgres pods to be + deleted + format: int64 + type: integer + pod_label_wait_timeout: + default: 10m + description: timeout when waiting for pod role and cluster labels + format: int64 + type: integer + ready_wait_interval: + default: 4s + description: interval between consecutive attempts waiting for + postgresql CRD to be created + format: int64 + type: integer + ready_wait_timeout: + default: 30s + description: timeout for the complete postgres CRD creation + format: int64 + type: integer + resource_check_interval: + default: 3s + description: interval to wait between consecutive attempts to + check for some K8s resources + format: int64 + type: integer + resource_check_timeout: + default: 10m + description: timeout when waiting for the presence of a certain + K8s resource + format: int64 + type: integer + type: object + users: + description: PostgresUsersConfiguration defines the system users of + Postgres. + properties: + additional_owner_roles: + items: + type: string + type: array + enable_password_rotation: + type: boolean + password_rotation_interval: + default: 90 + format: int32 + type: integer + password_rotation_user_retention: + default: 120 + format: int32 + type: integer + replication_username: + default: standby + type: string + super_username: + default: postgres + type: string + type: object + workers: + default: 8 + format: int32 + minimum: 1 + type: integer type: object - additionalProperties: - 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 + required: + - configuration + - metadata + type: object + served: true + storage: true + subresources: + status: {} diff --git a/charts/postgres-operator/values.yaml b/charts/postgres-operator/values.yaml index a326c5a49..8bfc2ddac 100644 --- a/charts/postgres-operator/values.yaml +++ b/charts/postgres-operator/values.yaml @@ -415,6 +415,12 @@ configLogicalBackup: logical_backup_schedule: "30 00 * * *" # secret to be used as reference for env variables in cronjob logical_backup_cronjob_environment_secret: "" + # number of successful backup jobs to keep in cronjob history + logical_backup_successful_jobs_history_limit: 3 + # number of failed backup jobs to keep in cronjob history + logical_backup_failed_jobs_history_limit: 3 + # TTL in seconds after which finished backup jobs are automatically deleted + logical_backup_ttl_seconds_after_finished: 86400 # automate creation of human users with teams API service configTeamsApi: diff --git a/docker/DebugDockerfile b/docker/DebugDockerfile index c44002984..367735d08 100644 --- a/docker/DebugDockerfile +++ b/docker/DebugDockerfile @@ -1,4 +1,4 @@ -FROM golang:1.25-alpine +FROM golang:1.26-alpine LABEL maintainer="Team ACID @ Zalando " # We need root certificates to deal with teams api over https diff --git a/docker/Dockerfile b/docker/Dockerfile index 9eef4e68c..4ba9b7630 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -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 " LABEL org.opencontainers.image.source="https://github.com/zalando/postgres-operator" diff --git a/docker/build_operator.sh b/docker/build_operator.sh index 5abe56666..0b1bf4d82 100644 --- a/docker/build_operator.sh +++ b/docker/build_operator.sh @@ -13,7 +13,7 @@ apt-get install -y wget ( cd /tmp - wget -q "https://storage.googleapis.com/golang/go1.25.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 diff --git a/docs/reference/operator_parameters.md b/docs/reference/operator_parameters.md index b594c1337..0d2f3a091 100644 --- a/docs/reference/operator_parameters.md +++ b/docs/reference/operator_parameters.md @@ -79,11 +79,6 @@ Those are top-level keys, containing both leaf keys and groups. Instruct the operator to create/update the CRDs. If disabled the operator will rely on the CRDs being managed separately. The default is `true`. -* **enable_crd_validation** - *deprecated*: toggles if the operator will create or update CRDs with - [OpenAPI v3 schema validation](https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/#validation) - The default is `true`. `false` will be ignored, since `apiextensions.io/v1` requires a structural schema definition. - * **crd_categories** The operator will register CRDs in the `all` category by default so that they will be returned by a `kubectl get all` call. You are free to change categories or leave them empty. @@ -904,6 +899,15 @@ grouped under the `logical_backup` key. * **logical_backup_cronjob_environment_secret** Reference to a Kubernetes secret, which keys will be added as environment variables to the cronjob. Default: "" +* **logical_backup_successful_jobs_history_limit** + number of successful backup jobs to keep in cronjob history. The default is `3`. + +* **logical_backup_failed_jobs_history_limit** + number of failed backup jobs to keep in cronjob history. The default is `3`. + +* **logical_backup_ttl_seconds_after_finished** + TTL in seconds after which finished backup jobs are automatically deleted. The default is `86400`. + The following environment variables can be passed to the logical backup cronjob via `logical_backup_cronjob_environment_secret` to control connectivity checks before the backup starts: diff --git a/e2e/Dockerfile b/e2e/Dockerfile index 98bbf755a..1f4083116 100644 --- a/e2e/Dockerfile +++ b/e2e/Dockerfile @@ -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 \ diff --git a/e2e/Makefile b/e2e/Makefile index 5fa0de471..09ac74986 100644 --- a/e2e/Makefile +++ b/e2e/Makefile @@ -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 diff --git a/go.mod b/go.mod index 9efa24150..37c2d2b65 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,12 @@ module github.com/zalando/postgres-operator -go 1.25.3 +go 1.26.4 require ( - github.com/Masterminds/semver v1.5.0 - github.com/aws/aws-sdk-go v1.55.8 + github.com/Masterminds/semver/v3 v3.5.0 + github.com/aws/aws-sdk-go-v2 v1.42.0 + github.com/aws/aws-sdk-go-v2/config v1.32.24 + github.com/aws/aws-sdk-go-v2/service/ec2 v1.305.3 github.com/golang/mock v1.6.0 github.com/lib/pq v1.12.3 github.com/motomux/pretty v0.0.0-20161209205251-b2aad2c9a95d @@ -12,72 +14,80 @@ require ( github.com/r3labs/diff v1.1.0 github.com/sirupsen/logrus v1.9.4 github.com/stretchr/testify v1.11.1 - golang.org/x/crypto v0.45.0 - gopkg.in/yaml.v2 v2.4.0 - k8s.io/api v0.32.9 - k8s.io/apiextensions-apiserver v0.32.9 - k8s.io/apimachinery v0.32.9 - k8s.io/client-go v0.32.9 - sigs.k8s.io/yaml v1.4.0 + golang.org/x/crypto v0.51.0 + gopkg.in/yaml.v3 v3.0.1 + k8s.io/api v0.36.1 + k8s.io/apiextensions-apiserver v0.36.1 + k8s.io/apimachinery v0.36.1 + k8s.io/client-go v0.36.1 + sigs.k8s.io/yaml v1.6.0 ) require ( + github.com/aws/aws-sdk-go-v2/credentials v1.19.23 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.29 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.29 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.29 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.30 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.12 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.29 // indirect + github.com/aws/aws-sdk-go-v2/service/signin v1.1.5 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.31.3 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.6 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.43.3 // indirect + github.com/aws/smithy-go v1.27.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/emicklei/go-restful/v3 v3.13.0 // indirect github.com/fatih/color v1.18.0 // indirect - github.com/fxamacker/cbor/v2 v2.7.0 // indirect - github.com/go-logr/logr v1.4.2 // indirect + github.com/fxamacker/cbor/v2 v2.9.0 // indirect + github.com/go-logr/logr v1.4.3 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.23.0 // indirect github.com/gobuffalo/flect v1.0.3 // indirect - github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.4 // indirect - github.com/google/gnostic-models v0.6.9 // indirect - github.com/google/go-cmp v0.7.0 // indirect - github.com/google/gofuzz v1.2.0 // indirect + github.com/google/gnostic-models v0.7.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kr/text v0.2.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/moby/spdystream v0.5.0 // indirect + github.com/moby/spdystream v0.5.1 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/spf13/cobra v1.9.1 // indirect - github.com/spf13/pflag v1.0.6 // indirect + github.com/spf13/cobra v1.10.2 // indirect + github.com/spf13/pflag v1.0.9 // indirect github.com/x448/float16 v0.8.4 // indirect - golang.org/x/mod v0.29.0 // indirect - golang.org/x/net v0.47.0 // indirect - golang.org/x/oauth2 v0.27.0 // indirect - golang.org/x/sync v0.18.0 // indirect - golang.org/x/sys v0.38.0 // indirect - golang.org/x/term v0.37.0 // indirect - golang.org/x/text v0.31.0 // indirect - golang.org/x/time v0.9.0 // indirect - golang.org/x/tools v0.38.0 // indirect - golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated // indirect - google.golang.org/protobuf v1.36.5 // indirect - gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect + go.yaml.in/yaml/v2 v2.4.3 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/mod v0.36.0 // indirect + golang.org/x/net v0.55.0 // indirect + golang.org/x/oauth2 v0.34.0 // indirect + golang.org/x/sync v0.21.0 // indirect + golang.org/x/sys v0.46.0 // indirect + golang.org/x/term v0.44.0 // indirect + golang.org/x/text v0.38.0 // indirect + golang.org/x/time v0.14.0 // indirect + golang.org/x/tools v0.45.0 // indirect + google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af // indirect + gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/code-generator v0.32.9 // indirect - k8s.io/gengo/v2 v2.0.0-20240911193312-2b36238f13e9 // indirect - k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect - k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + k8s.io/code-generator v0.36.1 // indirect + k8s.io/gengo/v2 v2.0.0-20250922181213-ec3ebc5fd46b // indirect + k8s.io/klog/v2 v2.140.0 // indirect + k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a // indirect + k8s.io/streaming v0.36.1 // indirect + k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 // indirect sigs.k8s.io/controller-tools v0.17.3 // indirect - sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect + sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/randfill v1.0.0 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect + sigs.k8s.io/structured-merge-diff/v6 v6.3.2 // indirect ) tool ( diff --git a/go.sum b/go.sum index 0d0ebb7d2..c4927d42e 100644 --- a/go.sum +++ b/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= @@ -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= diff --git a/hack/update-codegen.sh b/hack/update-codegen.sh index 1363c2786..fa3efb599 100755 --- a/hack/update-codegen.sh +++ b/hack/update-codegen.sh @@ -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}" diff --git a/manifests/configmap.yaml b/manifests/configmap.yaml index 1096e0265..1c663c757 100644 --- a/manifests/configmap.yaml +++ b/manifests/configmap.yaml @@ -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" diff --git a/manifests/operatorconfiguration.crd.yaml b/manifests/operatorconfiguration.crd.yaml index 81bcd4381..5f347f2ac 100644 --- a/manifests/operatorconfiguration.crd.yaml +++ b/manifests/operatorconfiguration.crd.yaml @@ -1,285 +1,293 @@ +--- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.17.3 name: operatorconfigurations.acid.zalan.do spec: group: acid.zalan.do names: + categories: + - all kind: OperatorConfiguration listKind: OperatorConfigurationList plural: operatorconfigurations - singular: operatorconfiguration shortNames: - opconfig - categories: - - all + singular: operatorconfiguration scope: Namespaced versions: - - name: v1 - served: true - storage: true - subresources: - status: {} - additionalPrinterColumns: - - name: Image - type: string - description: Spilo image to be used for Pods + - additionalPrinterColumns: + - description: Spilo image to be used for Pods jsonPath: .configuration.docker_image - - name: Cluster-Label + name: Image type: string - description: Label for K8s resources created by operator + - description: Label for K8s resources created by operator jsonPath: .configuration.kubernetes.cluster_name_label - - name: Service-Account + name: Cluster-Label type: string - description: Name of service account to be used + - description: Name of service account to be used jsonPath: .configuration.kubernetes.pod_service_account_name - - name: Min-Instances - type: integer - description: Minimum number of instances per Postgres cluster + name: Service-Account + type: string + - description: Minimum number of instances per Postgres cluster jsonPath: .configuration.min_instances - - name: Age - type: date + name: Min-Instances + type: integer + - description: Age of the OperatorConfiguration resource jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 schema: openAPIV3Schema: - type: object - required: - - kind - - apiVersion - - configuration + description: OperatorConfiguration defines the specification for the OperatorConfiguration. properties: - kind: - type: string - enum: - - OperatorConfiguration apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string - enum: - - acid.zalan.do/v1 configuration: - type: object + 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: - type: array - nullable: true 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 - default: "ghcr.io/zalando/spilo-18:4.1-p1" enable_crd_registration: - type: boolean default: true - enable_crd_validation: type: boolean - description: deprecated - default: true enable_lazy_spilo_upgrade: type: boolean - default: false enable_maintenance_windows: - type: boolean default: true + type: boolean enable_pgversion_env_var: - type: boolean default: true + type: boolean enable_shm_volume: - type: boolean default: true + type: boolean enable_spilo_wal_path_compat: type: boolean - default: false enable_team_id_clustername_prefix: type: boolean - default: false etcd_host: - type: string default: "" + type: string ignore_instance_limits_annotation_key: type: string ignore_resources_limits_annotation_key: type: string - kubernetes_use_configmaps: - type: boolean - default: false - maintenance_windows: - items: - pattern: '^\ *((Mon|Tue|Wed|Thu|Fri|Sat|Sun):(2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))-((2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))\ *$' - type: string - type: array - max_instances: - type: integer - description: "-1 = disabled" - minimum: -1 - default: -1 - min_instances: - type: integer - description: "-1 = disabled" - minimum: -1 - default: -1 - resync_period: - type: string - default: "30m" - repair_period: - type: string - default: "5m" - set_memory_request_to_limit: - type: boolean - default: false - sidecar_docker_images: - type: object - additionalProperties: - type: string - sidecars: - type: array - nullable: true - items: - type: object - x-kubernetes-preserve-unknown-fields: true - workers: - type: integer - minimum: 1 - default: 8 - users: - type: object - properties: - additional_owner_roles: - type: array - nullable: true - items: - type: string - enable_password_rotation: - type: boolean - default: false - password_rotation_interval: - type: integer - default: 90 - password_rotation_user_retention: - type: integer - default: 180 - replication_username: - type: string - default: standby - super_username: - type: string - default: postgres - major_version_upgrade: - type: object - properties: - major_version_upgrade_mode: - type: string - default: "manual" - major_version_upgrade_team_allow_list: - type: array - items: - type: string - minimal_major_version: - type: string - default: "14" - target_major_version: - type: string - default: "18" kubernetes: - type: object + description: KubernetesMetaConfiguration defines k8s conf required + for all Postgres clusters and the operator itself properties: additional_pod_capabilities: - type: array items: type: string + type: array cluster_domain: + default: cluster.local type: string - default: "cluster.local" cluster_labels: - type: object additionalProperties: type: string default: application: spilo - cluster_name_label: - type: string - default: "cluster-name" - custom_pod_annotations: 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: - type: array items: type: string + type: array enable_cross_namespace_secret: type: boolean - default: false enable_finalizers: type: boolean - default: false enable_init_containers: - type: boolean default: true + type: boolean enable_owner_references: type: boolean - default: false enable_persistent_volume_claim_deletion: - type: boolean default: true + type: boolean enable_pod_antiaffinity: type: boolean - default: false enable_pod_disruption_budget: - type: boolean default: true + type: boolean enable_readiness_probe: type: boolean - default: false enable_secrets_deletion: - type: boolean default: true + type: boolean enable_sidecars: - type: boolean default: true + type: boolean ignored_annotations: - type: array items: type: string - infrastructure_roles_secret_name: - type: string - infrastructure_roles_secrets: type: array - nullable: true + infrastructure_roles_secret_name: + description: |- + NamespacedName comprises a resource name, with a mandatory namespace, + rendered as "/". Being a type captures intent and + helps make sure that UIDs, namespaced names and non-namespaced names + do not get conflated in code. For most use cases, namespace and name + will already have been format validated at the API entry point, so we + don't do that here. Where that's not the case (e.g. in testing), + consider using NamespacedNameOrDie() in testing.go in this package. + + from: https://github.com/kubernetes/apimachinery/blob/master/pkg/types/namespacedname.go + 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: - type: object - required: - - secretname - - userkey - - passwordkey properties: - secretname: + defaultrolevalue: type: string - userkey: + 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 - defaultuservalue: - type: string - defaultrolevalue: - type: string - details: - 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: - type: array items: type: string + type: array inherited_labels: - type: array items: type: string + type: array liveness_probe: description: |- Probe describes a health check to be performed against a container to determine whether it is @@ -334,11 +342,11 @@ spec: "Host" in httpHeaders instead. type: string httpHeaders: - description: Custom headers to set in the request. HTTP allows - repeated headers. + 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 + description: HTTPHeader describes a custom header to + be used in HTTP probes properties: name: description: |- @@ -434,262 +442,236 @@ spec: type: integer type: object master_pod_move_timeout: - type: string - default: "20m" + default: 20m + description: timeout for successful migration of master pods from + unschedulable node + format: int64 + type: integer node_readiness_label: - type: object additionalProperties: type: string - node_readiness_label_merge: - type: string - enum: - - "AND" - - "OR" - oauth_token_secret_name: - type: string - default: "postgresql-operator" - pdb_master_label_selector: - type: boolean - default: true - pdb_name_format: - type: string - default: "postgres-{cluster}-pdb" - persistent_volume_claim_retention_policy: 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: - when_deleted: + name: type: string - enum: - - "delete" - - "retain" - when_scaled: + namespace: type: string - enum: - - "delete" - - "retain" + 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 - default: false pod_antiaffinity_topology_key: + default: kubernetes.io/hostname type: string - default: "kubernetes.io/hostname" pod_environment_configmap: - type: string + 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: - type: string + default: ordered_ready enum: - - "ordered_ready" - - "parallel" - default: "ordered_ready" + - ordered_ready + - parallel + type: string pod_priority_class_name: type: string pod_role_label: + default: spilo-role type: string - default: "spilo-role" pod_service_account_definition: type: string - default: "" pod_service_account_name: + default: postgres-pod type: string - default: "postgres-pod" pod_service_account_role_binding_definition: type: string - default: "" pod_terminate_grace_period: - type: string - default: "5m" + default: 5m + description: Postgres pods are terminated forcefully after this + timeout + format: int64 + type: integer secret_name_template: + default: '{username}.{cluster}.credentials.{tprkind}.{tprgroup}' + description: |- + template for database user secrets generated by the operator, + here username contains the namespace in the format namespace.username + if the user is in different namespace than cluster and cross namespace secrets + are enabled via `enable_cross_namespace_secret` flag in the configuration. type: string - default: "{username}.{cluster}.credentials.{tprkind}.{tprgroup}" share_pgsocket_with_sidecars: type: boolean - default: false spilo_allow_privilege_escalation: - type: boolean default: true - spilo_runasuser: - type: integer - spilo_runasgroup: - type: integer + type: boolean spilo_fsgroup: + format: int64 type: integer spilo_privileged: type: boolean - default: false + spilo_runasgroup: + format: int64 + type: integer + spilo_runasuser: + format: int64 + type: integer storage_resize_mode: - type: string + default: pvc enum: - - "ebs" - - "mixed" - - "pvc" - - "off" - default: "pvc" + - ebs + - mixed + - pvc + - "off" + type: string toleration: - type: object additionalProperties: type: string + type: object watched_namespace: type: string - postgres_pod_resources: type: object - properties: - default_cpu_limit: - type: string - pattern: '^(\d+m|\d+(\.\d{1,3})?)$|^$' - default_cpu_request: - type: string - pattern: '^(\d+m|\d+(\.\d{1,3})?)$|^$' - default_memory_limit: - type: string - pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$|^$' - default_memory_request: - type: string - pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$|^$' - max_cpu_request: - type: string - pattern: '^(\d+m|\d+(\.\d{1,3})?)$|^$' - max_memory_request: - type: string - pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$|^$' - min_cpu_limit: - type: string - pattern: '^(\d+m|\d+(\.\d{1,3})?)$|^$' - min_memory_limit: - type: string - pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$|^$' - timeouts: - type: object - properties: - patroni_api_check_interval: - type: string - default: "1s" - patroni_api_check_timeout: - type: string - default: "5s" - pod_label_wait_timeout: - type: string - default: "10m" - pod_deletion_wait_timeout: - type: string - default: "10m" - ready_wait_interval: - type: string - default: "4s" - ready_wait_timeout: - type: string - default: "30s" - resource_check_interval: - type: string - default: "3s" - resource_check_timeout: - type: string - default: "10m" + kubernetes_use_configmaps: + default: true + type: boolean load_balancer: - type: object + description: LoadBalancerConfiguration defines the LB configuration properties: custom_service_annotations: - type: object additionalProperties: type: string + type: object db_hosted_zone: type: string - default: "db.example.com" enable_master_load_balancer: type: boolean - default: true + enable_master_node_port: + type: boolean enable_master_pooler_load_balancer: type: boolean - default: false + enable_master_pooler_node_port: + type: boolean enable_replica_load_balancer: type: boolean - default: false + enable_replica_node_port: + type: boolean enable_replica_pooler_load_balancer: type: boolean - default: false - external_traffic_policy: - type: string - enum: - - "Cluster" - - "Local" - default: "Cluster" - master_dns_name_format: - type: string - default: "{cluster}.{namespace}.{hostedzone}" - master_legacy_dns_name_format: - type: string - default: "{cluster}.{team}.{hostedzone}" - replica_dns_name_format: - type: string - default: "{cluster}-repl.{namespace}.{hostedzone}" - replica_legacy_dns_name_format: - type: string - default: "{cluster}-repl.{team}.{hostedzone}" - aws_or_gcp: - type: object - properties: - additional_secret_mount: - type: string - additional_secret_mount_path: - type: string - aws_region: - type: string - default: "eu-central-1" - enable_ebs_gp3_migration: + enable_replica_pooler_node_port: type: boolean - default: false - enable_ebs_gp3_migration_max_size: - type: integer - default: 1000 - gcp_credentials: + external_traffic_policy: + default: Cluster + enum: + - Cluster + - Local type: string - kube_iam_role: + master_dns_name_format: + default: '{cluster}.{namespace}.{hostedzone}' + description: defines the DNS name string template for the master + load balancer cluster type: string - log_s3_bucket: + master_legacy_dns_name_format: + default: '{cluster}.{team}.{hostedzone}' + description: deprecated DNS template for master load balancer + using team name type: string - wal_az_storage_account: + replica_dns_name_format: + default: '{cluster}-repl.{namespace}.{hostedzone}' + description: defines the DNS name string template for the replica + load balancer cluster type: string - wal_gs_bucket: + replica_legacy_dns_name_format: + default: '{cluster}-repl.{team}.{hostedzone}' + description: deprecated DNS template for replica load balancer + using team name type: string - wal_s3_bucket: - type: string - logical_backup: 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_azure_storage_account_key: - type: string logical_backup_cpu_limit: + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ type: string - pattern: '^(\d+m|\d+(\.\d{1,3})?)$' logical_backup_cpu_request: + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ + type: string + logical_backup_cronjob_environment_secret: type: string - pattern: '^(\d+m|\d+(\.\d{1,3})?)$' logical_backup_docker_image: + default: ghcr.io/zalando/postgres-operator/logical-backup:v1.15.1 type: string - default: "ghcr.io/zalando/postgres-operator/logical-backup:v1.15.1" + 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 - default: "logical-backup-" logical_backup_memory_limit: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ type: string - pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' logical_backup_memory_request: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ type: string - pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' logical_backup_provider: - type: string + default: s3 enum: - - "az" - - "gcs" - - "s3" - default: "s3" + - az + - gcs + - s3 + type: string logical_backup_s3_access_key_id: type: string logical_backup_s3_bucket: @@ -700,162 +682,290 @@ spec: 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_s3_retention_time: - type: string logical_backup_schedule: + default: 30 00 * * * + pattern: ^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$ type: string - pattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$' - default: "30 00 * * *" - logical_backup_cronjob_environment_secret: - type: string - debug: + logical_backup_successful_jobs_history_limit: + default: 3 + format: int32 + minimum: 0 + type: integer + logical_backup_ttl_seconds_after_finished: + default: 86400 + format: int32 + minimum: 0 + type: integer type: object + maintenance_windows: + type: array + major_version_upgrade: + description: MajorVersionUpgradeConfiguration defines how to execute + major version upgrades of Postgres. properties: - debug_logging: - type: boolean - default: true - enable_database_access: - type: boolean - default: true - teams_api: - type: object - properties: - enable_admin_role_for_users: - type: boolean - default: true - enable_postgres_team_crd: - type: boolean - default: true - enable_postgres_team_crd_superusers: - type: boolean - default: false - enable_team_member_deprecation: - type: boolean - default: false - enable_team_superuser: - type: boolean - default: false - enable_teams_api: - type: boolean - default: true - pam_configuration: + major_version_upgrade_mode: + default: manual + enum: + - "off" + - manual + - full type: string - default: "https://info.example.com/oauth2/tokeninfo?access_token= uid realm=/employees" - pam_role_name: - type: string - default: "zalandos" - postgres_superuser_teams: - type: array + major_version_upgrade_team_allow_list: items: type: string - protected_role_names: type: array - items: - type: string - default: - - admin - - cron_admin - role_deletion_suffix: + minimal_major_version: + default: "14" type: string - default: "_deleted" - team_admin_role: + target_major_version: + default: "18" type: string - default: "admin" - team_api_role_configuration: - type: object - additionalProperties: - type: string - default: - log_statement: all - teams_api_url: - type: string - default: "https://teams.example.com/api/" - logging_rest_api: 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: - api_port: - type: integer - default: 8080 - cluster_history_entries: - type: integer - default: 1000 - ring_log_lines: - type: integer - default: 100 - scalyr: # deprecated + enable_patroni_failsafe_mode: + type: boolean type: object + postgres_pod_resources: + description: PostgresPodResourcesDefaults defines the spec of default + resources + properties: + default_cpu_limit: + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ + type: string + default_cpu_request: + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ + type: string + default_memory_limit: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + default_memory_request: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + max_cpu_request: + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ + type: string + max_memory_request: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + min_cpu_limit: + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ + type: string + min_memory_limit: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + type: object + repair_period: + default: 5m + description: period between consecutive repair requests + format: int64 + type: integer + resync_period: + default: 30m + description: period between consecutive sync requests + format: int64 + type: integer + scalyr: + description: ScalyrConfiguration defines the configuration for ScalyrAPI properties: scalyr_api_key: type: string scalyr_cpu_limit: - type: string - pattern: '^(\d+m|\d+(\.\d{1,3})?)$' default: "1" - scalyr_cpu_request: + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ + type: string + scalyr_cpu_request: + default: 100m + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ type: string - pattern: '^(\d+m|\d+(\.\d{1,3})?)$' - default: "100m" scalyr_image: type: string scalyr_memory_limit: + default: 500Mi + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ type: string - pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' - default: "500Mi" scalyr_memory_request: + default: 50Mi + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ type: string - pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' - default: "50Mi" scalyr_server_url: + default: https://upload.eu.scalyr.com type: string - default: "https://upload.eu.scalyr.com" - connection_pooler: type: object - properties: - connection_pooler_schema: - type: string - default: "pooler" - connection_pooler_user: - type: string - default: "pooler" - connection_pooler_image: - type: string - default: "ghcr.io/zalando/postgres-operator/pgbouncer:latest" - connection_pooler_max_db_connections: - type: integer - default: 60 - connection_pooler_mode: - type: string - enum: - - "session" - - "transaction" - default: "transaction" - connection_pooler_number_of_instances: - type: integer - minimum: 1 - default: 2 - connection_pooler_default_cpu_limit: - type: string - pattern: '^(\d+m|\d+(\.\d{1,3})?)$' - connection_pooler_default_cpu_request: - type: string - pattern: '^(\d+m|\d+(\.\d{1,3})?)$' - connection_pooler_default_memory_limit: - type: string - pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' - connection_pooler_default_memory_request: - type: string - pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' - patroni: + 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_patroni_failsafe_mode: + enable_admin_role_for_users: + default: true type: boolean - default: false - status: + enable_postgres_team_crd: + default: true + type: boolean + enable_postgres_team_crd_superusers: + type: boolean + enable_team_member_deprecation: + type: boolean + enable_team_superuser: + type: boolean + enable_teams_api: + type: boolean + pam_configuration: + default: https://info.example.com/oauth2/tokeninfo?access_token= + uid realm=/employees + type: string + pam_role_name: + default: zalandos + type: string + postgres_superuser_teams: + items: + type: string + type: array + protected_role_names: + default: '["admin", "cron_admin"]' + items: + type: string + type: array + role_deletion_suffix: + default: _deleted + type: string + team_admin_role: + default: admin + type: string + team_api_role_configuration: + additionalProperties: + type: string + default: + log_statement: all + type: object + teams_api_url: + default: https://teams.example.com/api/ + type: string + type: object + timeouts: + description: OperatorTimeouts defines the timeout of ResourceCheck, + PodWait, ReadyWait + properties: + patroni_api_check_interval: + default: 1s + description: interval between consecutive attempts of operator + calling the Patroni API + format: int64 + type: integer + patroni_api_check_timeout: + default: 5s + description: timeout when waiting for successful response from + Patroni API + format: int64 + type: integer + pod_deletion_wait_timeout: + default: 10m + description: timeout when waiting for the Postgres pods to be + deleted + format: int64 + type: integer + pod_label_wait_timeout: + default: 10m + description: timeout when waiting for pod role and cluster labels + format: int64 + type: integer + ready_wait_interval: + default: 4s + description: interval between consecutive attempts waiting for + postgresql CRD to be created + format: int64 + type: integer + ready_wait_timeout: + default: 30s + description: timeout for the complete postgres CRD creation + format: int64 + type: integer + resource_check_interval: + default: 3s + description: interval to wait between consecutive attempts to + check for some K8s resources + format: int64 + type: integer + resource_check_timeout: + default: 10m + description: timeout when waiting for the presence of a certain + K8s resource + format: int64 + type: integer + type: object + users: + description: PostgresUsersConfiguration defines the system users of + Postgres. + properties: + additional_owner_roles: + items: + type: string + type: array + enable_password_rotation: + type: boolean + password_rotation_interval: + default: 90 + format: int32 + type: integer + password_rotation_user_retention: + default: 120 + format: int32 + type: integer + replication_username: + default: standby + type: string + super_username: + default: postgres + type: string + type: object + workers: + default: 8 + format: int32 + minimum: 1 + type: integer type: object - additionalProperties: - 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 + required: + - configuration + - metadata + type: object + served: true + storage: true + subresources: + status: {} diff --git a/manifests/postgresql.crd.yaml b/manifests/postgresql.crd.yaml index 72d7153ba..9b855eb68 100644 --- a/manifests/postgresql.crd.yaml +++ b/manifests/postgresql.crd.yaml @@ -302,7 +302,9 @@ spec: a Container. properties: name: - description: Name of the environment variable. Must be a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -360,6 +362,43 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume mount containing + the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -456,8 +495,9 @@ spec: in a Container. properties: name: - description: Name of the environment variable. Must be - a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -515,6 +555,43 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume mount containing + the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -575,14 +652,14 @@ spec: envFrom: description: |- List of sources to populate environment variables in the container. - The keys defined within a source must be a C_IDENTIFIER. All invalid keys - will be reported as an event when the container is starting. When a key exists in multiple + The keys defined within a source may consist of any printable ASCII characters except '='. + When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated. items: description: EnvFromSource represents the source of a set - of ConfigMaps + of ConfigMaps or Secrets properties: configMapRef: description: The ConfigMap to select from @@ -603,8 +680,9 @@ spec: type: object x-kubernetes-map-type: atomic prefix: - description: An optional identifier to prepend to each - key in the ConfigMap. Must be a C_IDENTIFIER. + description: |- + Optional text to prepend to the name of each environment variable. + May consist of any printable ASCII characters except '='. type: string secretRef: description: The Secret to select from @@ -867,6 +945,12 @@ spec: - port type: object type: object + stopSignal: + description: |- + StopSignal defines which signal will be sent to a container when it is being stopped. + If not specified, the default is defined by the container runtime in use. + StopSignal can only be set for Pods with a non-empty .spec.os.name + type: string type: object livenessProbe: description: |- @@ -1237,7 +1321,9 @@ spec: type: integer type: object resizePolicy: - description: Resources resize policy for the container. + description: |- + Resources resize policy for the container. + This field cannot be set on ephemeral containers. items: description: ContainerResizePolicy represents resource resize policy for the container. @@ -1269,7 +1355,7 @@ spec: Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. - This is an alpha field and requires enabling the + This field depends on the DynamicResourceAllocation feature gate. This field is immutable. It can only be set for containers. @@ -1323,10 +1409,10 @@ spec: restartPolicy: description: |- RestartPolicy defines the restart behavior of individual containers in a pod. - This field may only be set for init containers, and the only allowed value is "Always". - For non-init containers or when this field is not specified, + This overrides the pod-level restart policy. When this field is not specified, the restart behavior is defined by the Pod's restart policy and the container type. - Setting the RestartPolicy as "Always" for the init container will have the following effect: + Additionally, setting the RestartPolicy as "Always" for the init container will + have the following effect: this init container will be continually restarted on exit until all regular containers have terminated. Once all regular containers have completed, all init containers with restartPolicy "Always" @@ -1338,6 +1424,59 @@ spec: init container is started, or after any startupProbe has successfully completed. type: string + restartPolicyRules: + description: |- + Represents a list of rules to be checked to determine if the + container should be restarted on exit. The rules are evaluated in + order. Once a rule matches a container exit condition, the remaining + rules are ignored. If no rule matches the container exit condition, + the Container-level restart policy determines the whether the container + is restarted or not. Constraints on the rules: + - At most 20 rules are allowed. + - Rules can have the same action. + - Identical rules are not forbidden in validations. + When rules are specified, container MUST set RestartPolicy explicitly + even it if matches the Pod's RestartPolicy. + items: + description: ContainerRestartRule describes how a container + exit is handled. + properties: + action: + description: |- + Specifies the action taken on a container exit if the requirements + are satisfied. The only possible value is "Restart" to restart the + container. + type: string + exitCodes: + description: Represents the exit codes to check on container + exits. + properties: + operator: + description: |- + Represents the relationship between the container exit code(s) and the + specified values. Possible values are: + - In: the requirement is satisfied if the container exit code is in the + set of specified values. + - NotIn: the requirement is satisfied if the container exit code is + not in the set of specified values. + type: string + values: + description: |- + Specifies the set of values to check for container exit codes. + At most 255 elements are allowed. + items: + format: int32 + type: integer + type: array + x-kubernetes-list-type: set + required: + - operator + type: object + required: + - action + type: object + type: array + x-kubernetes-list-type: atomic securityContext: description: |- SecurityContext defines the security options the container should be run with. @@ -1413,7 +1552,6 @@ spec: procMount denotes the type of proc mount to use for the containers. The default value is Default which uses the container runtime defaults for readonly paths and masked paths. - This requires the ProcMountType feature flag to be enabled. Note that this field cannot be set when spec.os.name is windows. type: string readOnlyRootFilesystem: @@ -1878,8 +2016,9 @@ spec: in a Container. properties: name: - description: Name of the environment variable. Must be - a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -1937,6 +2076,43 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume mount containing + the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -1997,14 +2173,14 @@ spec: envFrom: description: |- List of sources to populate environment variables in the container. - The keys defined within a source must be a C_IDENTIFIER. All invalid keys - will be reported as an event when the container is starting. When a key exists in multiple + The keys defined within a source may consist of any printable ASCII characters except '='. + When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated. items: description: EnvFromSource represents the source of a set - of ConfigMaps + of ConfigMaps or Secrets properties: configMapRef: description: The ConfigMap to select from @@ -2025,8 +2201,9 @@ spec: type: object x-kubernetes-map-type: atomic prefix: - description: An optional identifier to prepend to each - key in the ConfigMap. Must be a C_IDENTIFIER. + description: |- + Optional text to prepend to the name of each environment variable. + May consist of any printable ASCII characters except '='. type: string secretRef: description: The Secret to select from @@ -2289,6 +2466,12 @@ spec: - port type: object type: object + stopSignal: + description: |- + StopSignal defines which signal will be sent to a container when it is being stopped. + If not specified, the default is defined by the container runtime in use. + StopSignal can only be set for Pods with a non-empty .spec.os.name + type: string type: object livenessProbe: description: |- @@ -2659,7 +2842,9 @@ spec: type: integer type: object resizePolicy: - description: Resources resize policy for the container. + description: |- + Resources resize policy for the container. + This field cannot be set on ephemeral containers. items: description: ContainerResizePolicy represents resource resize policy for the container. @@ -2691,7 +2876,7 @@ spec: Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. - This is an alpha field and requires enabling the + This field depends on the DynamicResourceAllocation feature gate. This field is immutable. It can only be set for containers. @@ -2745,10 +2930,10 @@ spec: restartPolicy: description: |- RestartPolicy defines the restart behavior of individual containers in a pod. - This field may only be set for init containers, and the only allowed value is "Always". - For non-init containers or when this field is not specified, + This overrides the pod-level restart policy. When this field is not specified, the restart behavior is defined by the Pod's restart policy and the container type. - Setting the RestartPolicy as "Always" for the init container will have the following effect: + Additionally, setting the RestartPolicy as "Always" for the init container will + have the following effect: this init container will be continually restarted on exit until all regular containers have terminated. Once all regular containers have completed, all init containers with restartPolicy "Always" @@ -2760,6 +2945,59 @@ spec: init container is started, or after any startupProbe has successfully completed. type: string + restartPolicyRules: + description: |- + Represents a list of rules to be checked to determine if the + container should be restarted on exit. The rules are evaluated in + order. Once a rule matches a container exit condition, the remaining + rules are ignored. If no rule matches the container exit condition, + the Container-level restart policy determines the whether the container + is restarted or not. Constraints on the rules: + - At most 20 rules are allowed. + - Rules can have the same action. + - Identical rules are not forbidden in validations. + When rules are specified, container MUST set RestartPolicy explicitly + even it if matches the Pod's RestartPolicy. + items: + description: ContainerRestartRule describes how a container + exit is handled. + properties: + action: + description: |- + Specifies the action taken on a container exit if the requirements + are satisfied. The only possible value is "Restart" to restart the + container. + type: string + exitCodes: + description: Represents the exit codes to check on container + exits. + properties: + operator: + description: |- + Represents the relationship between the container exit code(s) and the + specified values. Possible values are: + - In: the requirement is satisfied if the container exit code is in the + set of specified values. + - NotIn: the requirement is satisfied if the container exit code is + not in the set of specified values. + type: string + values: + description: |- + Specifies the set of values to check for container exit codes. + At most 255 elements are allowed. + items: + format: int32 + type: integer + type: array + x-kubernetes-list-type: set + required: + - operator + type: object + required: + - action + type: object + type: array + x-kubernetes-list-type: atomic securityContext: description: |- SecurityContext defines the security options the container should be run with. @@ -2835,7 +3073,6 @@ spec: procMount denotes the type of proc mount to use for the containers. The default value is Default which uses the container runtime defaults for readonly paths and masked paths. - This requires the ProcMountType feature flag to be enabled. Note that this field cannot be set when spec.os.name is windows. type: string readOnlyRootFilesystem: @@ -3849,8 +4086,9 @@ spec: in a Container. properties: name: - description: Name of the environment variable. Must be - a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -3908,6 +4146,43 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume mount containing + the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -4214,9 +4489,10 @@ spec: operator: description: |- Operator represents a key's relationship to the value. - Valid operators are Exists and Equal. Defaults to Equal. + Valid operators are Exists, Equal, Lt, and Gt. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category. + Lt and Gt perform numeric comparisons (requires feature gate TaintTolerationComparisonOperators). type: string tolerationSeconds: description: |- @@ -4355,7 +4631,6 @@ spec: - Ignore: nodeAffinity/nodeSelector are ignored. All nodes are included in the calculations. If this value is nil, the behavior is equivalent to the Honor policy. - This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag. type: string nodeTaintsPolicy: description: |- @@ -4366,7 +4641,6 @@ spec: - Ignore: node taints are ignored. All nodes are included. If this value is nil, the behavior is equivalent to the Ignore policy. - This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag. type: string topologyKey: description: |- diff --git a/pkg/apis/acid.zalan.do/v1/crds.go b/pkg/apis/acid.zalan.do/v1/crds.go index 14bfccb58..54aea7c1b 100644 --- a/pkg/apis/acid.zalan.do/v1/crds.go +++ b/pkg/apis/acid.zalan.do/v1/crds.go @@ -2,1131 +2,17 @@ package v1 import ( _ "embed" - "fmt" - acidzalando "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do" - "github.com/zalando/postgres-operator/pkg/util" apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/yaml" ) // CRDResource* define names necesssary for the k8s CRD API const ( - PostgresCRDResourceKind = "postgresql" - - OperatorConfigCRDResouceKind = "OperatorConfiguration" - OperatorConfigCRDResourcePlural = "operatorconfigurations" - OperatorConfigCRDResourceList = OperatorConfigCRDResouceKind + "List" - OperatorConfigCRDResourceName = OperatorConfigCRDResourcePlural + "." + acidzalando.GroupName - OperatorConfigCRDResourceShort = "opconfig" + PostgresCRDResourceKind = "postgresql" + OperatorConfigCRDResourceKind = "OperatorConfiguration" ) -// OperatorConfigCRDResourceColumns definition of AdditionalPrinterColumns for OperatorConfiguration CRD -var OperatorConfigCRDResourceColumns = []apiextv1.CustomResourceColumnDefinition{ - { - Name: "Image", - Type: "string", - Description: "Spilo image to be used for Pods", - JSONPath: ".configuration.docker_image", - }, - { - Name: "Cluster-Label", - Type: "string", - Description: "Label for K8s resources created by operator", - JSONPath: ".configuration.kubernetes.cluster_name_label", - }, - { - Name: "Service-Account", - Type: "string", - Description: "Name of service account to be used", - JSONPath: ".configuration.kubernetes.pod_service_account_name", - }, - { - Name: "Min-Instances", - Type: "integer", - Description: "Minimum number of instances per Postgres cluster", - JSONPath: ".configuration.min_instances", - }, - { - Name: "Age", - Type: "date", - JSONPath: ".metadata.creationTimestamp", - }, -} - -var min1 = 1.0 -var minLength1 int64 = 1 -var minDisable = -1.0 - -// OperatorConfigCRDResourceValidation to check applied manifest parameters -var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{ - OpenAPIV3Schema: &apiextv1.JSONSchemaProps{ - Type: "object", - Required: []string{"kind", "apiVersion", "configuration"}, - Properties: map[string]apiextv1.JSONSchemaProps{ - "kind": { - Type: "string", - Enum: []apiextv1.JSON{ - { - Raw: []byte(`"OperatorConfiguration"`), - }, - }, - }, - "apiVersion": { - Type: "string", - Enum: []apiextv1.JSON{ - { - Raw: []byte(`"acid.zalan.do/v1"`), - }, - }, - }, - "configuration": { - Type: "object", - Properties: map[string]apiextv1.JSONSchemaProps{ - "crd_categories": { - Type: "array", - Nullable: true, - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - "docker_image": { - Type: "string", - }, - "enable_crd_registration": { - Type: "boolean", - }, - "enable_crd_validation": { - Type: "boolean", - Description: "deprecated", - }, - "enable_lazy_spilo_upgrade": { - Type: "boolean", - }, - "enable_maintenance_windows": { - Type: "boolean", - }, - "enable_shm_volume": { - Type: "boolean", - }, - "enable_spilo_wal_path_compat": { - Type: "boolean", - Description: "deprecated", - }, - "enable_team_id_clustername_prefix": { - Type: "boolean", - }, - "etcd_host": { - Type: "string", - }, - "ignore_instance_limits_annotation_key": { - Type: "string", - }, - "ignore_resources_limits_annotation_key": { - Type: "string", - }, - "kubernetes_use_configmaps": { - Type: "boolean", - }, - "maintenance_windows": { - Type: "array", - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - Pattern: "^\\ *((Mon|Tue|Wed|Thu|Fri|Sat|Sun):(2[0-3]|[01]?\\d):([0-5]?\\d)|(2[0-3]|[01]?\\d):([0-5]?\\d))-((Mon|Tue|Wed|Thu|Fri|Sat|Sun):(2[0-3]|[01]?\\d):([0-5]?\\d)|(2[0-3]|[01]?\\d):([0-5]?\\d))\\ *$", - }, - }, - }, - "max_instances": { - Type: "integer", - Description: "-1 = disabled", - Minimum: &minDisable, - }, - "min_instances": { - Type: "integer", - Description: "-1 = disabled", - Minimum: &minDisable, - }, - "resync_period": { - Type: "string", - }, - "repair_period": { - Type: "string", - }, - "set_memory_request_to_limit": { - Type: "boolean", - }, - "sidecar_docker_images": { - Type: "object", - AdditionalProperties: &apiextv1.JSONSchemaPropsOrBool{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - "sidecars": { - Type: "array", - Nullable: true, - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "object", - XPreserveUnknownFields: util.True(), - }, - }, - }, - "workers": { - Type: "integer", - Minimum: &min1, - }, - "users": { - Type: "object", - Properties: map[string]apiextv1.JSONSchemaProps{ - "additional_owner_roles": { - Type: "array", - Nullable: true, - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - "enable_password_rotation": { - Type: "boolean", - }, - "password_rotation_interval": { - Type: "integer", - }, - "password_rotation_user_retention": { - Type: "integer", - }, - "replication_username": { - Type: "string", - }, - "super_username": { - Type: "string", - }, - }, - }, - "major_version_upgrade": { - Type: "object", - Properties: map[string]apiextv1.JSONSchemaProps{ - "major_version_upgrade_mode": { - Type: "string", - }, - "major_version_upgrade_team_allow_list": { - Type: "array", - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - "minimal_major_version": { - Type: "string", - }, - "target_major_version": { - Type: "string", - }, - }, - }, - "kubernetes": { - Type: "object", - Properties: map[string]apiextv1.JSONSchemaProps{ - "additional_pod_capabilities": { - Type: "array", - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - "cluster_domain": { - Type: "string", - }, - "cluster_labels": { - Type: "object", - AdditionalProperties: &apiextv1.JSONSchemaPropsOrBool{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - "cluster_name_label": { - Type: "string", - }, - "custom_pod_annotations": { - Type: "object", - AdditionalProperties: &apiextv1.JSONSchemaPropsOrBool{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - "delete_annotation_date_key": { - Type: "string", - }, - "delete_annotation_name_key": { - Type: "string", - }, - "downscaler_annotations": { - Type: "array", - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - "enable_cross_namespace_secret": { - Type: "boolean", - }, - "enable_finalizers": { - Type: "boolean", - }, - "enable_init_containers": { - Type: "boolean", - }, - "enable_owner_references": { - Type: "boolean", - }, - "enable_persistent_volume_claim_deletion": { - Type: "boolean", - }, - "enable_pod_antiaffinity": { - Type: "boolean", - }, - "enable_pod_disruption_budget": { - Type: "boolean", - }, - "enable_readiness_probe": { - Type: "boolean", - }, - "enable_secrets_deletion": { - Type: "boolean", - }, - "enable_sidecars": { - Type: "boolean", - }, - "ignored_annotations": { - Type: "array", - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - "infrastructure_roles_secret_name": { - Type: "string", - }, - "infrastructure_roles_secrets": { - Type: "array", - Nullable: true, - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "object", - Required: []string{"secretname", "userkey", "passwordkey"}, - Properties: map[string]apiextv1.JSONSchemaProps{ - "secretname": { - Type: "string", - }, - "userkey": { - Type: "string", - }, - "passwordkey": { - Type: "string", - }, - "rolekey": { - Type: "string", - }, - "defaultuservalue": { - Type: "string", - }, - "defaultrolevalue": { - Type: "string", - }, - "details": { - Type: "string", - }, - "template": { - Type: "boolean", - }, - }, - }, - }, - }, - "inherited_annotations": { - Type: "array", - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - "inherited_labels": { - Type: "array", - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - "liveness_probe": { - Description: "Periodic probe of container liveness. Container will be restarted if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", - Type: "object", - Properties: map[string]apiextv1.JSONSchemaProps{ - "exec": { - Description: "One and only one of the following should be specified. Exec specifies the action to take.", - Type: "object", - Properties: map[string]apiextv1.JSONSchemaProps{ - "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.", - Type: "array", - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - }, - }, - "failureThreshold": { - Description: "Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.", - Type: "integer", - Format: "int32", - }, - "httpGet": { - Description: "HTTPGet specifies the http request to perform.", - Type: "object", - Required: []string{"port"}, - Properties: map[string]apiextv1.JSONSchemaProps{ - "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.", - Type: "array", - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Description: "HTTPHeader describes a custom header to be used in HTTP probes", - Type: "object", - Required: []string{"name", "value"}, - Properties: map[string]apiextv1.JSONSchemaProps{ - "name": { - Description: "The header field name", - Type: "string", - }, - "value": { - Description: "The header field value", - Type: "string", - }, - }, - }, - }, - }, - "path": { - Description: "Path to access on the HTTP server.", - Type: "string", - }, - "port": { - 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.", - AnyOf: []apiextv1.JSONSchemaProps{ - { - Type: "integer", - }, - { - Type: "string", - }, - }, - XIntOrString: true, - }, - "scheme": { - Description: "Scheme to use for connecting to the host. Defaults to HTTP.", - Type: "string", - }, - }, - }, - "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", - Type: "integer", - Format: "int32", - }, - "periodSeconds": { - Description: "How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.", - Type: "integer", - Format: "int32", - }, - "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.", - Type: "integer", - Format: "int32", - }, - "tcpSocket": { - Description: "TCPSocket specifies an action involving a TCP port. TCP hooks not yet supported TODO: implement a realistic TCP lifecycle hook", - Type: "object", - Required: []string{"port"}, - Properties: map[string]apiextv1.JSONSchemaProps{ - "host": { - Description: "Optional: Host name to connect to, defaults to the pod IP.", - Type: "string", - }, - "port": { - 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.", - XIntOrString: true, - AnyOf: []apiextv1.JSONSchemaProps{ - { - Type: "integer", - }, - { - Type: "string", - }, - }, - }, - }, - }, - "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.", - Type: "integer", - Format: "int64", - }, - "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", - Type: "integer", - Format: "int32", - }, - }, - }, - "master_pod_move_timeout": { - Type: "string", - }, - "node_readiness_label": { - Type: "object", - AdditionalProperties: &apiextv1.JSONSchemaPropsOrBool{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - "node_readiness_label_merge": { - Type: "string", - Enum: []apiextv1.JSON{ - { - Raw: []byte(`"AND"`), - }, - { - Raw: []byte(`"OR"`), - }, - }, - }, - "oauth_token_secret_name": { - Type: "string", - }, - "pdb_name_format": { - Type: "string", - }, - "pdb_master_label_selector": { - Type: "boolean", - }, - "persistent_volume_claim_retention_policy": { - Type: "object", - Properties: map[string]apiextv1.JSONSchemaProps{ - "when_deleted": { - Type: "string", - Enum: []apiextv1.JSON{ - { - Raw: []byte(`"delete"`), - }, - { - Raw: []byte(`"retain"`), - }, - }, - }, - "when_scaled": { - Type: "string", - Enum: []apiextv1.JSON{ - { - Raw: []byte(`"delete"`), - }, - { - Raw: []byte(`"retain"`), - }, - }, - }, - }, - }, - "pod_antiaffinity_preferred_during_scheduling": { - Type: "boolean", - }, - "pod_antiaffinity_topology_key": { - Type: "string", - }, - "pod_environment_configmap": { - Type: "string", - }, - "pod_environment_secret": { - Type: "string", - }, - "pod_management_policy": { - Type: "string", - Enum: []apiextv1.JSON{ - { - Raw: []byte(`"ordered_ready"`), - }, - { - Raw: []byte(`"parallel"`), - }, - }, - }, - "pod_priority_class_name": { - Type: "string", - }, - "pod_role_label": { - Type: "string", - }, - "pod_service_account_definition": { - Type: "string", - }, - "pod_service_account_name": { - Type: "string", - }, - "pod_service_account_role_binding_definition": { - Type: "string", - }, - "pod_terminate_grace_period": { - Type: "string", - }, - "secret_name_template": { - Type: "string", - }, - "share_pgsocket_with_sidecars": { - Type: "boolean", - }, - "spilo_runasuser": { - Type: "integer", - }, - "spilo_runasgroup": { - Type: "integer", - }, - "spilo_fsgroup": { - Type: "integer", - }, - "spilo_privileged": { - Type: "boolean", - }, - "spilo_allow_privilege_escalation": { - Type: "boolean", - }, - "storage_resize_mode": { - Type: "string", - Enum: []apiextv1.JSON{ - { - Raw: []byte(`"ebs"`), - }, - { - Raw: []byte(`"mixed"`), - }, - { - Raw: []byte(`"pvc"`), - }, - { - Raw: []byte(`"off"`), - }, - }, - }, - "toleration": { - Type: "object", - AdditionalProperties: &apiextv1.JSONSchemaPropsOrBool{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - "watched_namespace": { - Type: "string", - }, - }, - }, - "patroni": { - Type: "object", - Properties: map[string]apiextv1.JSONSchemaProps{ - "enable_patroni_failsafe_mode": { - Type: "boolean", - }, - }, - }, - "postgres_pod_resources": { - Type: "object", - Properties: map[string]apiextv1.JSONSchemaProps{ - "default_cpu_limit": { - Type: "string", - Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$|^$", - }, - "default_cpu_request": { - Type: "string", - Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$|^$", - }, - "default_memory_limit": { - Type: "string", - Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$|^$", - }, - "default_memory_request": { - Type: "string", - Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$|^$", - }, - "max_cpu_request": { - Type: "string", - Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$|^$", - }, - "max_memory_request": { - Type: "string", - Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$|^$", - }, - "min_cpu_limit": { - Type: "string", - Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$|^$", - }, - "min_memory_limit": { - Type: "string", - Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$|^$", - }, - }, - }, - "timeouts": { - Type: "object", - Properties: map[string]apiextv1.JSONSchemaProps{ - "patroni_api_check_interval": { - Type: "string", - }, - "patroni_api_check_timeout": { - Type: "string", - }, - "pod_label_wait_timeout": { - Type: "string", - }, - "pod_deletion_wait_timeout": { - Type: "string", - }, - "ready_wait_interval": { - Type: "string", - }, - "ready_wait_timeout": { - Type: "string", - }, - "resource_check_interval": { - Type: "string", - }, - "resource_check_timeout": { - Type: "string", - }, - }, - }, - "load_balancer": { - Type: "object", - Properties: map[string]apiextv1.JSONSchemaProps{ - "custom_service_annotations": { - Type: "object", - AdditionalProperties: &apiextv1.JSONSchemaPropsOrBool{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - "db_hosted_zone": { - Type: "string", - }, - "enable_master_load_balancer": { - Type: "boolean", - }, - "enable_master_pooler_load_balancer": { - Type: "boolean", - }, - "enable_replica_load_balancer": { - Type: "boolean", - }, - "enable_replica_pooler_load_balancer": { - Type: "boolean", - }, - "enable_master_node_port": { - Type: "boolean", - }, - "enable_master_pooler_node_port": { - Type: "boolean", - }, - "enable_replica_node_port": { - Type: "boolean", - }, - "enable_replica_pooler_node_port": { - Type: "boolean", - }, - "external_traffic_policy": { - Type: "string", - Enum: []apiextv1.JSON{ - { - Raw: []byte(`"Cluster"`), - }, - { - Raw: []byte(`"Local"`), - }, - }, - }, - "master_dns_name_format": { - Type: "string", - }, - "master_legacy_dns_name_format": { - Type: "string", - }, - "replica_dns_name_format": { - Type: "string", - }, - "replica_legacy_dns_name_format": { - Type: "string", - }, - }, - }, - "aws_or_gcp": { - Type: "object", - Properties: map[string]apiextv1.JSONSchemaProps{ - "additional_secret_mount": { - Type: "string", - }, - "additional_secret_mount_path": { - Type: "string", - }, - "aws_region": { - Type: "string", - }, - "enable_ebs_gp3_migration": { - Type: "boolean", - }, - "enable_ebs_gp3_migration_max_size": { - Type: "integer", - }, - "gcp_credentials": { - Type: "string", - }, - "kube_iam_role": { - Type: "string", - }, - "log_s3_bucket": { - Type: "string", - }, - "wal_s3_bucket": { - Type: "string", - }, - }, - }, - "logical_backup": { - Type: "object", - Properties: map[string]apiextv1.JSONSchemaProps{ - "logical_backup_azure_storage_account_name": { - Type: "string", - }, - "logical_backup_azure_storage_container": { - Type: "string", - }, - "logical_backup_azure_storage_account_key": { - Type: "string", - }, - "logical_backup_cpu_limit": { - Type: "string", - Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$", - }, - "logical_backup_cpu_request": { - Type: "string", - Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$", - }, - "logical_backup_docker_image": { - Type: "string", - }, - "logical_backup_google_application_credentials": { - Type: "string", - }, - "logical_backup_job_prefix": { - Type: "string", - }, - "logical_backup_memory_limit": { - Type: "string", - Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$", - }, - "logical_backup_memory_request": { - Type: "string", - Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$", - }, - "logical_backup_provider": { - Type: "string", - Enum: []apiextv1.JSON{ - { - Raw: []byte(`"az"`), - }, - { - Raw: []byte(`"gcs"`), - }, - { - Raw: []byte(`"s3"`), - }, - }, - }, - "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_secret_access_key": { - Type: "string", - }, - "logical_backup_s3_sse": { - Type: "string", - }, - "logical_backup_s3_retention_time": { - Type: "string", - }, - "logical_backup_schedule": { - Type: "string", - Pattern: "^(\\d+|\\*)(/\\d+)?(\\s+(\\d+|\\*)(/\\d+)?){4}$", - }, - "logical_backup_cronjob_environment_secret": { - Type: "string", - }, - }, - }, - "debug": { - Type: "object", - Properties: map[string]apiextv1.JSONSchemaProps{ - "debug_logging": { - Type: "boolean", - }, - "enable_database_access": { - Type: "boolean", - }, - }, - }, - "teams_api": { - Type: "object", - Properties: map[string]apiextv1.JSONSchemaProps{ - "enable_admin_role_for_users": { - Type: "boolean", - }, - "enable_postgres_team_crd": { - 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": { - Type: "string", - }, - "pam_role_name": { - Type: "string", - }, - "postgres_superuser_teams": { - Type: "array", - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - "protected_role_names": { - Type: "array", - Items: &apiextv1.JSONSchemaPropsOrArray{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - "role_deletion_suffix": { - Type: "string", - }, - "team_admin_role": { - Type: "string", - }, - "team_api_role_configuration": { - Type: "object", - AdditionalProperties: &apiextv1.JSONSchemaPropsOrBool{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - "teams_api_url": { - Type: "string", - }, - }, - }, - "logging_rest_api": { - Type: "object", - Properties: map[string]apiextv1.JSONSchemaProps{ - "api_port": { - Type: "integer", - }, - "cluster_history_entries": { - Type: "integer", - }, - "ring_log_lines": { - Type: "integer", - }, - }, - }, - "scalyr": { - Type: "object", - Properties: map[string]apiextv1.JSONSchemaProps{ - "scalyr_api_key": { - Type: "string", - }, - "scalyr_cpu_limit": { - Type: "string", - Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$", - }, - "scalyr_cpu_request": { - Type: "string", - Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$", - }, - "scalyr_image": { - Type: "string", - }, - "scalyr_memory_limit": { - Type: "string", - Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$", - }, - "scalyr_memory_request": { - Type: "string", - Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$", - }, - "scalyr_server_url": { - Type: "string", - }, - }, - }, - "connection_pooler": { - Type: "object", - Properties: map[string]apiextv1.JSONSchemaProps{ - "connection_pooler_default_cpu_limit": { - Type: "string", - Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$", - }, - "connection_pooler_default_cpu_request": { - Type: "string", - Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$", - }, - "connection_pooler_default_memory_limit": { - Type: "string", - Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$", - }, - "connection_pooler_default_memory_request": { - Type: "string", - Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$", - }, - "connection_pooler_image": { - Type: "string", - }, - "connection_pooler_max_db_connections": { - Type: "integer", - }, - "connection_pooler_mode": { - Type: "string", - Enum: []apiextv1.JSON{ - { - Raw: []byte(`"session"`), - }, - { - Raw: []byte(`"transaction"`), - }, - }, - }, - "connection_pooler_number_of_instances": { - Type: "integer", - Minimum: &min1, - }, - "connection_pooler_schema": { - Type: "string", - }, - "connection_pooler_user": { - Type: "string", - }, - "connection_pooler_pod_security_context": { - Type: "object", - XPreserveUnknownFields: util.True(), - }, - "connection_pooler_security_context": { - Type: "object", - XPreserveUnknownFields: util.True(), - }, - }, - }, - }, - }, - "status": { - Type: "object", - AdditionalProperties: &apiextv1.JSONSchemaPropsOrBool{ - Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - }, - }, -} - -func buildCRD(name, kind, plural, list, short string, - categories []string, - columns []apiextv1.CustomResourceColumnDefinition, - validation apiextv1.CustomResourceValidation) *apiextv1.CustomResourceDefinition { - return &apiextv1.CustomResourceDefinition{ - TypeMeta: metav1.TypeMeta{ - APIVersion: fmt.Sprintf("%s/%s", apiextv1.GroupName, apiextv1.SchemeGroupVersion.Version), - Kind: "CustomResourceDefinition", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - }, - Spec: apiextv1.CustomResourceDefinitionSpec{ - Group: SchemeGroupVersion.Group, - Names: apiextv1.CustomResourceDefinitionNames{ - Kind: kind, - ListKind: list, - Plural: plural, - Singular: kind, - ShortNames: []string{short}, - Categories: categories, - }, - Scope: apiextv1.NamespaceScoped, - Versions: []apiextv1.CustomResourceDefinitionVersion{ - { - Name: SchemeGroupVersion.Version, - Served: true, - Storage: true, - Subresources: &apiextv1.CustomResourceSubresources{ - Status: &apiextv1.CustomResourceSubresourceStatus{}, - }, - AdditionalPrinterColumns: columns, - Schema: &validation, - }, - }, - }, - } -} - //go:embed postgresql.crd.yaml var postgresqlCRDYAML []byte @@ -1143,14 +29,18 @@ func PostgresCRD(crdCategories []string) (*apiextv1.CustomResourceDefinition, er return &crd, nil } -// ConfigurationCRD returns CustomResourceDefinition built from OperatorConfigCRDResource -func ConfigurationCRD(crdCategories []string) *apiextv1.CustomResourceDefinition { - return buildCRD(OperatorConfigCRDResourceName, - OperatorConfigCRDResouceKind, - OperatorConfigCRDResourcePlural, - OperatorConfigCRDResourceList, - OperatorConfigCRDResourceShort, - crdCategories, - OperatorConfigCRDResourceColumns, - OperatorConfigCRDResourceValidation) +//go:embed operatorconfiguration.crd.yaml +var operatorConfigurationCRDYAML []byte + +// OperatorConfigurationCRD returns CustomResourceDefinition built from OperatorConfigurationCRDResource +func OperatorConfigurationCRD(crdCategories []string) (*apiextv1.CustomResourceDefinition, error) { + var crd apiextv1.CustomResourceDefinition + err := yaml.Unmarshal(operatorConfigurationCRDYAML, &crd) + if err != nil { + return nil, err + } + + crd.Spec.Names.Categories = crdCategories + + return &crd, nil } diff --git a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go index 60cab70ea..3454fe351 100644 --- a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go +++ b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go @@ -13,11 +13,17 @@ import ( ) // +genclient -// +genclient:onlyVerbs=get -// +genclient:noStatus // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // OperatorConfiguration defines the specification for the OperatorConfiguration. +// +k8s:deepcopy-gen=true +// +kubebuilder:resource:categories=all,shortName=opconfig,scope=Namespaced +// +kubebuilder:printcolumn:name="Image",type=string,JSONPath=`.configuration.docker_image`,description="Spilo image to be used for Pods" +// +kubebuilder:printcolumn:name="Cluster-Label",type=string,JSONPath=`.configuration.kubernetes.cluster_name_label`,description="Label for K8s resources created by operator" +// +kubebuilder:printcolumn:name="Service-Account",type=string,JSONPath=`.configuration.kubernetes.pod_service_account_name`,description="Name of service account to be used" +// +kubebuilder:printcolumn:name="Min-Instances",type=integer,JSONPath=`.configuration.min_instances`,description="Minimum number of instances per Postgres cluster" +// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`,description="Age of the OperatorConfiguration resource" +// +kubebuilder:subresource:status type OperatorConfiguration struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata"` @@ -37,102 +43,170 @@ type OperatorConfigurationList struct { // PostgresUsersConfiguration defines the system users of Postgres. type PostgresUsersConfiguration struct { - SuperUsername string `json:"super_username,omitempty"` - ReplicationUsername string `json:"replication_username,omitempty"` - AdditionalOwnerRoles []string `json:"additional_owner_roles,omitempty"` - EnablePasswordRotation bool `json:"enable_password_rotation,omitempty"` - PasswordRotationInterval uint32 `json:"password_rotation_interval,omitempty"` - PasswordRotationUserRetention uint32 `json:"password_rotation_user_retention,omitempty"` + // +kubebuilder:default=postgres + SuperUsername string `json:"super_username,omitempty"` + // +kubebuilder:default=standby + ReplicationUsername string `json:"replication_username,omitempty"` + AdditionalOwnerRoles []string `json:"additional_owner_roles,omitempty"` + EnablePasswordRotation bool `json:"enable_password_rotation,omitempty"` + // +kubebuilder:default=90 + PasswordRotationInterval uint32 `json:"password_rotation_interval,omitempty"` + // +kubebuilder:default=120 + PasswordRotationUserRetention uint32 `json:"password_rotation_user_retention,omitempty"` } // MajorVersionUpgradeConfiguration defines how to execute major version upgrades of Postgres. type MajorVersionUpgradeConfiguration struct { - MajorVersionUpgradeMode string `json:"major_version_upgrade_mode" default:"manual"` // off - no actions, manual - manifest triggers action, full - manifest and minimal version violation trigger upgrade + // +kubebuilder:validation:Enum=off;manual;full + // +kubebuilder:default=manual + MajorVersionUpgradeMode string `json:"major_version_upgrade_mode,omitempty"` // off - no actions, manual - manifest triggers action, full - manifest and minimal version violation trigger upgrade MajorVersionUpgradeTeamAllowList []string `json:"major_version_upgrade_team_allow_list,omitempty"` - MinimalMajorVersion string `json:"minimal_major_version" default:"14"` - TargetMajorVersion string `json:"target_major_version" default:"18"` + // +kubebuilder:default="14" + MinimalMajorVersion string `json:"minimal_major_version,omitempty"` + // +kubebuilder:default="18" + TargetMajorVersion string `json:"target_major_version,omitempty"` } // KubernetesMetaConfiguration defines k8s conf required for all Postgres clusters and the operator itself type KubernetesMetaConfiguration struct { - EnableOwnerReferences *bool `json:"enable_owner_references,omitempty"` + EnableOwnerReferences *bool `json:"enable_owner_references,omitempty"` + // +kubebuilder:default=postgres-pod PodServiceAccountName string `json:"pod_service_account_name,omitempty"` // TODO: change it to the proper json - PodServiceAccountDefinition string `json:"pod_service_account_definition,omitempty"` - PodServiceAccountRoleBindingDefinition string `json:"pod_service_account_role_binding_definition,omitempty"` - PodTerminateGracePeriod Duration `json:"pod_terminate_grace_period,omitempty"` - LivenessProbe *v1.Probe `json:"liveness_probe"` - SpiloPrivileged bool `json:"spilo_privileged,omitempty"` - SpiloAllowPrivilegeEscalation *bool `json:"spilo_allow_privilege_escalation,omitempty"` - SpiloRunAsUser *int64 `json:"spilo_runasuser,omitempty"` - SpiloRunAsGroup *int64 `json:"spilo_runasgroup,omitempty"` - SpiloFSGroup *int64 `json:"spilo_fsgroup,omitempty"` - AdditionalPodCapabilities []string `json:"additional_pod_capabilities,omitempty"` - WatchedNamespace string `json:"watched_namespace,omitempty"` - PDBNameFormat config.StringTemplate `json:"pdb_name_format,omitempty"` - PDBMasterLabelSelector *bool `json:"pdb_master_label_selector,omitempty"` - EnablePodDisruptionBudget *bool `json:"enable_pod_disruption_budget,omitempty"` - StorageResizeMode string `json:"storage_resize_mode,omitempty"` - EnableInitContainers *bool `json:"enable_init_containers,omitempty"` - EnableSidecars *bool `json:"enable_sidecars,omitempty"` - SharePgSocketWithSidecars *bool `json:"share_pgsocket_with_sidecars,omitempty"` - SecretNameTemplate config.StringTemplate `json:"secret_name_template,omitempty"` - ClusterDomain string `json:"cluster_domain,omitempty"` - OAuthTokenSecretName spec.NamespacedName `json:"oauth_token_secret_name,omitempty"` - InfrastructureRolesSecretName spec.NamespacedName `json:"infrastructure_roles_secret_name,omitempty"` - InfrastructureRolesDefs []*config.InfrastructureRole `json:"infrastructure_roles_secrets,omitempty"` - PodRoleLabel string `json:"pod_role_label,omitempty"` - ClusterLabels map[string]string `json:"cluster_labels,omitempty"` - InheritedLabels []string `json:"inherited_labels,omitempty"` - InheritedAnnotations []string `json:"inherited_annotations,omitempty"` - DownscalerAnnotations []string `json:"downscaler_annotations,omitempty"` - IgnoredAnnotations []string `json:"ignored_annotations,omitempty"` - ClusterNameLabel string `json:"cluster_name_label,omitempty"` - DeleteAnnotationDateKey string `json:"delete_annotation_date_key,omitempty"` - DeleteAnnotationNameKey string `json:"delete_annotation_name_key,omitempty"` - NodeReadinessLabel map[string]string `json:"node_readiness_label,omitempty"` - NodeReadinessLabelMerge string `json:"node_readiness_label_merge,omitempty"` - CustomPodAnnotations map[string]string `json:"custom_pod_annotations,omitempty"` + PodServiceAccountDefinition string `json:"pod_service_account_definition,omitempty"` + PodServiceAccountRoleBindingDefinition string `json:"pod_service_account_role_binding_definition,omitempty"` + // +kubebuilder:default="5m" + // Postgres pods are terminated forcefully after this timeout + PodTerminateGracePeriod Duration `json:"pod_terminate_grace_period,omitempty"` + // +optional + LivenessProbe *v1.Probe `json:"liveness_probe"` + SpiloPrivileged bool `json:"spilo_privileged,omitempty"` + // +kubebuilder:default=true + SpiloAllowPrivilegeEscalation *bool `json:"spilo_allow_privilege_escalation,omitempty"` + SpiloRunAsUser *int64 `json:"spilo_runasuser,omitempty"` + SpiloRunAsGroup *int64 `json:"spilo_runasgroup,omitempty"` + SpiloFSGroup *int64 `json:"spilo_fsgroup,omitempty"` + AdditionalPodCapabilities []string `json:"additional_pod_capabilities,omitempty"` + WatchedNamespace string `json:"watched_namespace,omitempty"` + // +kubebuilder:default="postgres-{cluster}-pdb" + // defines the template for PDB names + PDBNameFormat config.StringTemplate `json:"pdb_name_format,omitempty"` + // +kubebuilder:default=true + PDBMasterLabelSelector *bool `json:"pdb_master_label_selector,omitempty"` + // +kubebuilder:default=true + EnablePodDisruptionBudget *bool `json:"enable_pod_disruption_budget,omitempty"` + // +kubebuilder:validation:Enum=ebs;mixed;pvc;off + // +kubebuilder:default=pvc + StorageResizeMode string `json:"storage_resize_mode,omitempty"` + // +kubebuilder:default=true + EnableInitContainers *bool `json:"enable_init_containers,omitempty"` + // +kubebuilder:default=true + EnableSidecars *bool `json:"enable_sidecars,omitempty"` + SharePgSocketWithSidecars *bool `json:"share_pgsocket_with_sidecars,omitempty"` + // +kubebuilder:default="{username}.{cluster}.credentials.{tprkind}.{tprgroup}" + // template for database user secrets generated by the operator, + // here username contains the namespace in the format namespace.username + // if the user is in different namespace than cluster and cross namespace secrets + // are enabled via `enable_cross_namespace_secret` flag in the configuration. + SecretNameTemplate config.StringTemplate `json:"secret_name_template,omitempty"` + // +kubebuilder:default="cluster.local" + ClusterDomain string `json:"cluster_domain,omitempty"` + // +kubebuilder:default=postgres-operator + // namespaced name of the secret containing the OAuth2 token to pass to the teams API + OAuthTokenSecretName spec.NamespacedName `json:"oauth_token_secret_name,omitempty"` + InfrastructureRolesSecretName spec.NamespacedName `json:"infrastructure_roles_secret_name,omitempty"` + // +kubebuilder:validation:Type=array + // namespaced name of the secret containing infrastructure roles names and passwords + InfrastructureRolesDefs []*config.InfrastructureRole `json:"infrastructure_roles_secrets,omitempty"` + // +kubebuilder:default=spilo-role + PodRoleLabel string `json:"pod_role_label,omitempty"` + // +kubebuilder:default={application: spilo} + ClusterLabels map[string]string `json:"cluster_labels,omitempty"` + InheritedLabels []string `json:"inherited_labels,omitempty"` + InheritedAnnotations []string `json:"inherited_annotations,omitempty"` + DownscalerAnnotations []string `json:"downscaler_annotations,omitempty"` + IgnoredAnnotations []string `json:"ignored_annotations,omitempty"` + // +kubebuilder:default=cluster-name + ClusterNameLabel string `json:"cluster_name_label,omitempty"` + DeleteAnnotationDateKey string `json:"delete_annotation_date_key,omitempty"` + DeleteAnnotationNameKey string `json:"delete_annotation_name_key,omitempty"` + NodeReadinessLabel map[string]string `json:"node_readiness_label,omitempty"` + // +kubebuilder:validation:Enum=AND;OR + NodeReadinessLabelMerge string `json:"node_readiness_label_merge,omitempty"` + CustomPodAnnotations map[string]string `json:"custom_pod_annotations,omitempty"` // TODO: use a proper toleration structure? - PodToleration map[string]string `json:"toleration,omitempty"` - PodEnvironmentConfigMap spec.NamespacedName `json:"pod_environment_configmap,omitempty"` - PodEnvironmentSecret string `json:"pod_environment_secret,omitempty"` - PodPriorityClassName string `json:"pod_priority_class_name,omitempty"` - MasterPodMoveTimeout Duration `json:"master_pod_move_timeout,omitempty"` - EnablePodAntiAffinity bool `json:"enable_pod_antiaffinity,omitempty"` - PodAntiAffinityPreferredDuringScheduling bool `json:"pod_antiaffinity_preferred_during_scheduling,omitempty"` - PodAntiAffinityTopologyKey string `json:"pod_antiaffinity_topology_key,omitempty"` - PodManagementPolicy string `json:"pod_management_policy,omitempty"` - PersistentVolumeClaimRetentionPolicy map[string]string `json:"persistent_volume_claim_retention_policy,omitempty"` - EnableSecretsDeletion *bool `json:"enable_secrets_deletion,omitempty"` - EnablePersistentVolumeClaimDeletion *bool `json:"enable_persistent_volume_claim_deletion,omitempty"` - EnableReadinessProbe bool `json:"enable_readiness_probe,omitempty"` - EnableCrossNamespaceSecret bool `json:"enable_cross_namespace_secret,omitempty"` - EnableFinalizers *bool `json:"enable_finalizers,omitempty"` + PodToleration map[string]string `json:"toleration,omitempty"` + // namespaced name of the ConfigMap with environment variables to populate on every pod + PodEnvironmentConfigMap spec.NamespacedName `json:"pod_environment_configmap,omitempty"` + PodEnvironmentSecret string `json:"pod_environment_secret,omitempty"` + PodPriorityClassName string `json:"pod_priority_class_name,omitempty"` + // +kubebuilder:default="20m" + // timeout for successful migration of master pods from unschedulable node + MasterPodMoveTimeout Duration `json:"master_pod_move_timeout,omitempty"` + EnablePodAntiAffinity bool `json:"enable_pod_antiaffinity,omitempty"` + PodAntiAffinityPreferredDuringScheduling bool `json:"pod_antiaffinity_preferred_during_scheduling,omitempty"` + // +kubebuilder:default="kubernetes.io/hostname" + PodAntiAffinityTopologyKey string `json:"pod_antiaffinity_topology_key,omitempty"` + // +kubebuilder:validation:Enum=ordered_ready;parallel + // +kubebuilder:default=ordered_ready + PodManagementPolicy string `json:"pod_management_policy,omitempty"` + PersistentVolumeClaimRetentionPolicy map[string]string `json:"persistent_volume_claim_retention_policy,omitempty"` + + // +kubebuilder:default=true + EnableSecretsDeletion *bool `json:"enable_secrets_deletion,omitempty"` + // +kubebuilder:default=true + EnablePersistentVolumeClaimDeletion *bool `json:"enable_persistent_volume_claim_deletion,omitempty"` + EnableReadinessProbe bool `json:"enable_readiness_probe,omitempty"` + EnableCrossNamespaceSecret bool `json:"enable_cross_namespace_secret,omitempty"` + EnableFinalizers *bool `json:"enable_finalizers,omitempty"` } // PostgresPodResourcesDefaults defines the spec of default resources type PostgresPodResourcesDefaults struct { - DefaultCPURequest string `json:"default_cpu_request,omitempty"` + // +kubebuilder:validation:Pattern=`^(\d+m|\d+(\.\d{1,3})?)$` + DefaultCPURequest string `json:"default_cpu_request,omitempty"` + // +kubebuilder:validation:Pattern=`^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$` DefaultMemoryRequest string `json:"default_memory_request,omitempty"` - DefaultCPULimit string `json:"default_cpu_limit,omitempty"` - DefaultMemoryLimit string `json:"default_memory_limit,omitempty"` - MinCPULimit string `json:"min_cpu_limit,omitempty"` - MinMemoryLimit string `json:"min_memory_limit,omitempty"` - MaxCPURequest string `json:"max_cpu_request,omitempty"` - MaxMemoryRequest string `json:"max_memory_request,omitempty"` + // +kubebuilder:validation:Pattern=`^(\d+m|\d+(\.\d{1,3})?)$` + DefaultCPULimit string `json:"default_cpu_limit,omitempty"` + // +kubebuilder:validation:Pattern=`^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$` + DefaultMemoryLimit string `json:"default_memory_limit,omitempty"` + // +kubebuilder:validation:Pattern=`^(\d+m|\d+(\.\d{1,3})?)$` + MinCPULimit string `json:"min_cpu_limit,omitempty"` + // +kubebuilder:validation:Pattern=`^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$` + MinMemoryLimit string `json:"min_memory_limit,omitempty"` + // +kubebuilder:validation:Pattern=`^(\d+m|\d+(\.\d{1,3})?)$` + MaxCPURequest string `json:"max_cpu_request,omitempty"` + // +kubebuilder:validation:Pattern=`^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$` + MaxMemoryRequest string `json:"max_memory_request,omitempty"` } // OperatorTimeouts defines the timeout of ResourceCheck, PodWait, ReadyWait type OperatorTimeouts struct { - ResourceCheckInterval Duration `json:"resource_check_interval,omitempty"` - ResourceCheckTimeout Duration `json:"resource_check_timeout,omitempty"` - PodLabelWaitTimeout Duration `json:"pod_label_wait_timeout,omitempty"` - PodDeletionWaitTimeout Duration `json:"pod_deletion_wait_timeout,omitempty"` - ReadyWaitInterval Duration `json:"ready_wait_interval,omitempty"` - ReadyWaitTimeout Duration `json:"ready_wait_timeout,omitempty"` + // +kubebuilder:default="3s" + // interval to wait between consecutive attempts to check for some K8s resources + ResourceCheckInterval Duration `json:"resource_check_interval,omitempty"` + // +kubebuilder:default="10m" + // timeout when waiting for the presence of a certain K8s resource + ResourceCheckTimeout Duration `json:"resource_check_timeout,omitempty"` + // +kubebuilder:default="10m" + // timeout when waiting for pod role and cluster labels + PodLabelWaitTimeout Duration `json:"pod_label_wait_timeout,omitempty"` + // +kubebuilder:default="10m" + // timeout when waiting for the Postgres pods to be deleted + PodDeletionWaitTimeout Duration `json:"pod_deletion_wait_timeout,omitempty"` + // +kubebuilder:default="4s" + // interval between consecutive attempts waiting for postgresql CRD to be created + ReadyWaitInterval Duration `json:"ready_wait_interval,omitempty"` + // +kubebuilder:default="30s" + // timeout for the complete postgres CRD creation + ReadyWaitTimeout Duration `json:"ready_wait_timeout,omitempty"` + // +kubebuilder:default="1s" + // interval between consecutive attempts of operator calling the Patroni API PatroniAPICheckInterval Duration `json:"patroni_api_check_interval,omitempty"` - PatroniAPICheckTimeout Duration `json:"patroni_api_check_timeout,omitempty"` + // +kubebuilder:default="5s" + // timeout when waiting for successful response from Patroni API + PatroniAPICheckTimeout Duration `json:"patroni_api_check_timeout,omitempty"` } // LoadBalancerConfiguration defines the LB configuration @@ -143,24 +217,36 @@ type LoadBalancerConfiguration struct { EnableReplicaLoadBalancer bool `json:"enable_replica_load_balancer,omitempty"` EnableReplicaPoolerLoadBalancer bool `json:"enable_replica_pooler_load_balancer,omitempty"` - // kept in LoadBalancerConfiguration because all the other parameters apply here too + // NodePort flags kept in LoadBalancerConfiguration because all the other parameters apply here too + EnableMasterNodePort bool `json:"enable_master_node_port,omitempty"` EnableMasterPoolerNodePort bool `json:"enable_master_pooler_node_port,omitempty"` EnableReplicaNodePort bool `json:"enable_replica_node_port,omitempty"` EnableReplicaPoolerNodePort bool `json:"enable_replica_pooler_node_port,omitempty"` - CustomServiceAnnotations map[string]string `json:"custom_service_annotations,omitempty"` - MasterDNSNameFormat config.StringTemplate `json:"master_dns_name_format,omitempty"` - MasterLegacyDNSNameFormat config.StringTemplate `json:"master_legacy_dns_name_format,omitempty"` - ReplicaDNSNameFormat config.StringTemplate `json:"replica_dns_name_format,omitempty"` + CustomServiceAnnotations map[string]string `json:"custom_service_annotations,omitempty"` + // +kubebuilder:default="{cluster}.{namespace}.{hostedzone}" + // defines the DNS name string template for the master load balancer cluster + MasterDNSNameFormat config.StringTemplate `json:"master_dns_name_format,omitempty"` + // +kubebuilder:default="{cluster}.{team}.{hostedzone}" + // deprecated DNS template for master load balancer using team name + MasterLegacyDNSNameFormat config.StringTemplate `json:"master_legacy_dns_name_format,omitempty"` + // +kubebuilder:default="{cluster}-repl.{namespace}.{hostedzone}" + // defines the DNS name string template for the replica load balancer cluster + ReplicaDNSNameFormat config.StringTemplate `json:"replica_dns_name_format,omitempty"` + // +kubebuilder:default="{cluster}-repl.{team}.{hostedzone}" + // deprecated DNS template for replica load balancer using team name ReplicaLegacyDNSNameFormat config.StringTemplate `json:"replica_legacy_dns_name_format,omitempty"` - ExternalTrafficPolicy string `json:"external_traffic_policy" default:"Cluster"` + // +kubebuilder:validation:Enum=Cluster;Local + // +kubebuilder:default=Cluster + ExternalTrafficPolicy string `json:"external_traffic_policy,omitempty"` } // AWSGCPConfiguration defines the configuration for AWS // TODO complete Google Cloud Platform (GCP) configuration type AWSGCPConfiguration struct { - WALES3Bucket string `json:"wal_s3_bucket,omitempty"` + WALES3Bucket string `json:"wal_s3_bucket,omitempty"` + // +kubebuilder:default=eu-central-1 AWSRegion string `json:"aws_region,omitempty"` WALGSBucket string `json:"wal_gs_bucket,omitempty"` GCPCredentials string `json:"gcp_credentials,omitempty"` @@ -169,75 +255,117 @@ type AWSGCPConfiguration struct { KubeIAMRole string `json:"kube_iam_role,omitempty"` AdditionalSecretMount string `json:"additional_secret_mount,omitempty"` AdditionalSecretMountPath string `json:"additional_secret_mount_path,omitempty"` - EnableEBSGp3Migration bool `json:"enable_ebs_gp3_migration" default:"false"` - EnableEBSGp3MigrationMaxSize int64 `json:"enable_ebs_gp3_migration_max_size" default:"1000"` + EnableEBSGp3Migration bool `json:"enable_ebs_gp3_migration,omitempty"` + EnableEBSGp3MigrationMaxSize int64 `json:"enable_ebs_gp3_migration_max_size,omitempty"` } // OperatorDebugConfiguration defines options for the debug mode type OperatorDebugConfiguration struct { - DebugLogging *bool `json:"debug_logging,omitempty"` + // +kubebuilder:default=true + DebugLogging *bool `json:"debug_logging,omitempty"` + // +kubebuilder:default=true EnableDBAccess *bool `json:"enable_database_access,omitempty"` } // TeamsAPIConfiguration defines the configuration of TeamsAPI type TeamsAPIConfiguration struct { - EnableTeamsAPI bool `json:"enable_teams_api,omitempty"` - TeamsAPIUrl string `json:"teams_api_url,omitempty"` - TeamAPIRoleConfiguration map[string]string `json:"team_api_role_configuration,omitempty"` - EnableTeamSuperuser bool `json:"enable_team_superuser,omitempty"` - EnableAdminRoleForUsers bool `json:"enable_admin_role_for_users,omitempty"` - TeamAdminRole string `json:"team_admin_role,omitempty"` - PamRoleName string `json:"pam_role_name,omitempty"` - PamConfiguration string `json:"pam_configuration,omitempty"` - ProtectedRoles []string `json:"protected_role_names,omitempty"` - PostgresSuperuserTeams []string `json:"postgres_superuser_teams,omitempty"` - EnablePostgresTeamCRD bool `json:"enable_postgres_team_crd,omitempty"` - EnablePostgresTeamCRDSuperusers bool `json:"enable_postgres_team_crd_superusers,omitempty"` - EnableTeamMemberDeprecation bool `json:"enable_team_member_deprecation,omitempty"` - RoleDeletionSuffix string `json:"role_deletion_suffix,omitempty"` + EnableTeamsAPI bool `json:"enable_teams_api,omitempty"` + // +kubebuilder:default="https://teams.example.com/api/" + TeamsAPIUrl string `json:"teams_api_url,omitempty"` + // +kubebuilder:default={log_statement: all} + TeamAPIRoleConfiguration map[string]string `json:"team_api_role_configuration,omitempty"` + EnableTeamSuperuser bool `json:"enable_team_superuser,omitempty"` + // +kubebuilder:default=true + EnableAdminRoleForUsers bool `json:"enable_admin_role_for_users,omitempty"` + // +kubebuilder:default=admin + TeamAdminRole string `json:"team_admin_role,omitempty"` + // +kubebuilder:default=zalandos + PamRoleName string `json:"pam_role_name,omitempty"` + // +kubebuilder:default="https://info.example.com/oauth2/tokeninfo?access_token= uid realm=/employees" + PamConfiguration string `json:"pam_configuration,omitempty"` + // +kubebuilder:default="[\"admin\", \"cron_admin\"]" + ProtectedRoles []string `json:"protected_role_names,omitempty"` + PostgresSuperuserTeams []string `json:"postgres_superuser_teams,omitempty"` + // +kubebuilder:default=true + EnablePostgresTeamCRD bool `json:"enable_postgres_team_crd,omitempty"` + EnablePostgresTeamCRDSuperusers bool `json:"enable_postgres_team_crd_superusers,omitempty"` + EnableTeamMemberDeprecation bool `json:"enable_team_member_deprecation,omitempty"` + // +kubebuilder:default=_deleted + RoleDeletionSuffix string `json:"role_deletion_suffix,omitempty"` } // LoggingRESTAPIConfiguration defines Logging API conf type LoggingRESTAPIConfiguration struct { - APIPort int `json:"api_port,omitempty"` - RingLogLines int `json:"ring_log_lines,omitempty"` + // +kubebuilder:default=8080 + APIPort int `json:"api_port,omitempty"` + // +kubebuilder:default=100 + RingLogLines int `json:"ring_log_lines,omitempty"` + // +kubebuilder:default=1000 ClusterHistoryEntries int `json:"cluster_history_entries,omitempty"` } // ScalyrConfiguration defines the configuration for ScalyrAPI type ScalyrConfiguration struct { - ScalyrAPIKey string `json:"scalyr_api_key,omitempty"` - ScalyrImage string `json:"scalyr_image,omitempty"` - ScalyrServerURL string `json:"scalyr_server_url,omitempty"` - ScalyrCPURequest string `json:"scalyr_cpu_request,omitempty"` + ScalyrAPIKey string `json:"scalyr_api_key,omitempty"` + ScalyrImage string `json:"scalyr_image,omitempty"` + // +kubebuilder:default="https://upload.eu.scalyr.com" + ScalyrServerURL string `json:"scalyr_server_url,omitempty"` + // +kubebuilder:validation:Pattern=`^(\d+m|\d+(\.\d{1,3})?)$` + // +kubebuilder:default="100m" + ScalyrCPURequest string `json:"scalyr_cpu_request,omitempty"` + // +kubebuilder:validation:Pattern=`^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$` + // +kubebuilder:default="50Mi" ScalyrMemoryRequest string `json:"scalyr_memory_request,omitempty"` - ScalyrCPULimit string `json:"scalyr_cpu_limit,omitempty"` - ScalyrMemoryLimit string `json:"scalyr_memory_limit,omitempty"` + // +kubebuilder:validation:Pattern=`^(\d+m|\d+(\.\d{1,3})?)$` + // +kubebuilder:default="1" + ScalyrCPULimit string `json:"scalyr_cpu_limit,omitempty"` + // +kubebuilder:validation:Pattern=`^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$` + // +kubebuilder:default="500Mi" + ScalyrMemoryLimit string `json:"scalyr_memory_limit,omitempty"` } // ConnectionPoolerConfiguration defines default configuration for connection pooler type ConnectionPoolerConfiguration struct { - NumberOfInstances *int32 `json:"connection_pooler_number_of_instances,omitempty"` - Schema string `json:"connection_pooler_schema,omitempty"` - User string `json:"connection_pooler_user,omitempty"` - Image string `json:"connection_pooler_image,omitempty"` - Mode string `json:"connection_pooler_mode,omitempty"` - MaxDBConnections *int32 `json:"connection_pooler_max_db_connections,omitempty"` - DefaultCPURequest string `json:"connection_pooler_default_cpu_request,omitempty"` + // +kubebuilder:validation:Minimum=1 + // +kubebuilder:default=2 + NumberOfInstances *int32 `json:"connection_pooler_number_of_instances,omitempty"` + // +kubebuilder:default=pooler + Schema string `json:"connection_pooler_schema,omitempty"` + // +kubebuilder:default=pooler + User string `json:"connection_pooler_user,omitempty"` + // +kubebuilder:default="ghcr.io/zalando/postgres-operator/pgbouncer:latest" + Image string `json:"connection_pooler_image,omitempty"` + // +kubebuilder:validation:Enum=session;transaction + // +kubebuilder:default=transaction + Mode string `json:"connection_pooler_mode,omitempty"` + MaxDBConnections *int32 `json:"connection_pooler_max_db_connections,omitempty"` + // +kubebuilder:validation:Pattern=`^(\d+m|\d+(\.\d{1,3})?)$` + DefaultCPURequest string `json:"connection_pooler_default_cpu_request,omitempty"` + // +kubebuilder:validation:Pattern=`^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$` DefaultMemoryRequest string `json:"connection_pooler_default_memory_request,omitempty"` - DefaultCPULimit string `json:"connection_pooler_default_cpu_limit,omitempty"` - DefaultMemoryLimit string `json:"connection_pooler_default_memory_limit,omitempty"` - // PodSecurityContext and SecurityContext allow overriding the pooler pod- and - // container-level security contexts (e.g. to match a hardened image's UID/GID or to - // satisfy restricted Pod Security Standards). When unset the operator keeps its defaults. + // +kubebuilder:validation:Pattern=`^(\d+m|\d+(\.\d{1,3})?)$` + DefaultCPULimit string `json:"connection_pooler_default_cpu_limit,omitempty"` + // +kubebuilder:validation:Pattern=`^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$` + DefaultMemoryLimit string `json:"connection_pooler_default_memory_limit,omitempty"` + // +kubebuilder:validation:XPreserveUnknownFields + // +kubebuilder:validation:Type=object + // +kubebuilder:validation:Schemaless PodSecurityContext *v1.PodSecurityContext `json:"connection_pooler_pod_security_context,omitempty"` + // +kubebuilder:validation:XPreserveUnknownFields + // +kubebuilder:validation:Type=object + // +kubebuilder:validation:Schemaless SecurityContext *v1.SecurityContext `json:"connection_pooler_security_context,omitempty"` } // OperatorLogicalBackupConfiguration defines configuration for logical backup type OperatorLogicalBackupConfiguration struct { - Schedule string `json:"logical_backup_schedule,omitempty"` - DockerImage string `json:"logical_backup_docker_image,omitempty"` + // +kubebuilder:validation:Pattern=`^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$` + // +kubebuilder:default="30 00 * * *" + Schedule string `json:"logical_backup_schedule,omitempty"` + // +kubebuilder:default="ghcr.io/zalando/postgres-operator/logical-backup:v1.15.1" + DockerImage string `json:"logical_backup_docker_image,omitempty"` + // +kubebuilder:validation:Enum=az;gcs;s3 + // +kubebuilder:default=s3 BackupProvider string `json:"logical_backup_provider,omitempty"` AzureStorageAccountName string `json:"logical_backup_azure_storage_account_name,omitempty"` AzureStorageContainer string `json:"logical_backup_azure_storage_container,omitempty"` @@ -251,12 +379,26 @@ type OperatorLogicalBackupConfiguration struct { S3SSE string `json:"logical_backup_s3_sse,omitempty"` RetentionTime string `json:"logical_backup_s3_retention_time,omitempty"` GoogleApplicationCredentials string `json:"logical_backup_google_application_credentials,omitempty"` - JobPrefix string `json:"logical_backup_job_prefix,omitempty"` - CronjobEnvironmentSecret string `json:"logical_backup_cronjob_environment_secret,omitempty"` - CPURequest string `json:"logical_backup_cpu_request,omitempty"` - MemoryRequest string `json:"logical_backup_memory_request,omitempty"` - CPULimit string `json:"logical_backup_cpu_limit,omitempty"` - MemoryLimit string `json:"logical_backup_memory_limit,omitempty"` + // +kubebuilder:default=logical-backup- + JobPrefix string `json:"logical_backup_job_prefix,omitempty"` + CronjobEnvironmentSecret string `json:"logical_backup_cronjob_environment_secret,omitempty"` + // +kubebuilder:validation:Pattern=`^(\d+m|\d+(\.\d{1,3})?)$` + CPURequest string `json:"logical_backup_cpu_request,omitempty"` + // +kubebuilder:validation:Pattern=`^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$` + MemoryRequest string `json:"logical_backup_memory_request,omitempty"` + // +kubebuilder:validation:Pattern=`^(\d+m|\d+(\.\d{1,3})?)$` + CPULimit string `json:"logical_backup_cpu_limit,omitempty"` + // +kubebuilder:validation:Pattern=`^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$` + MemoryLimit string `json:"logical_backup_memory_limit,omitempty"` + // +kubebuilder:validation:Minimum=0 + // +kubebuilder:default=3 + SuccessfulJobsHistoryLimit *int32 `json:"logical_backup_successful_jobs_history_limit,omitempty"` + // +kubebuilder:validation:Minimum=0 + // +kubebuilder:default=3 + FailedJobsHistoryLimit *int32 `json:"logical_backup_failed_jobs_history_limit,omitempty"` + // +kubebuilder:validation:Minimum=0 + // +kubebuilder:default=86400 + TTLSecondsAfterFinished *int32 `json:"logical_backup_ttl_seconds_after_finished,omitempty"` } // PatroniConfiguration defines configuration for Patroni @@ -266,42 +408,80 @@ type PatroniConfiguration struct { // OperatorConfigurationData defines the operation config type OperatorConfigurationData struct { - EnableCRDRegistration *bool `json:"enable_crd_registration,omitempty"` - EnableCRDValidation *bool `json:"enable_crd_validation,omitempty"` - CRDCategories []string `json:"crd_categories,omitempty"` - EnableLazySpiloUpgrade bool `json:"enable_lazy_spilo_upgrade,omitempty"` - EnablePgVersionEnvVar bool `json:"enable_pgversion_env_var,omitempty"` - EnableSpiloWalPathCompat bool `json:"enable_spilo_wal_path_compat,omitempty"` - EnableTeamIdClusternamePrefix bool `json:"enable_team_id_clustername_prefix,omitempty"` - EtcdHost string `json:"etcd_host,omitempty"` - KubernetesUseConfigMaps bool `json:"kubernetes_use_configmaps,omitempty"` - DockerImage string `json:"docker_image,omitempty"` - Workers uint32 `json:"workers,omitempty"` - ResyncPeriod Duration `json:"resync_period,omitempty"` - RepairPeriod Duration `json:"repair_period,omitempty"` - EnableMaintenanceWindows *bool `json:"enable_maintenance_windows,omitempty"` - MaintenanceWindows []MaintenanceWindow `json:"maintenance_windows,omitempty"` - SetMemoryRequestToLimit bool `json:"set_memory_request_to_limit,omitempty"` - ShmVolume *bool `json:"enable_shm_volume,omitempty"` - SidecarImages map[string]string `json:"sidecar_docker_images,omitempty"` // deprecated in favour of SidecarContainers - SidecarContainers []v1.Container `json:"sidecars,omitempty"` - PostgresUsersConfiguration PostgresUsersConfiguration `json:"users"` - MajorVersionUpgrade MajorVersionUpgradeConfiguration `json:"major_version_upgrade"` - Kubernetes KubernetesMetaConfiguration `json:"kubernetes"` - PostgresPodResources PostgresPodResourcesDefaults `json:"postgres_pod_resources"` - Timeouts OperatorTimeouts `json:"timeouts"` - LoadBalancer LoadBalancerConfiguration `json:"load_balancer"` - AWSGCP AWSGCPConfiguration `json:"aws_or_gcp"` - OperatorDebug OperatorDebugConfiguration `json:"debug"` - TeamsAPI TeamsAPIConfiguration `json:"teams_api"` - LoggingRESTAPI LoggingRESTAPIConfiguration `json:"logging_rest_api"` - Scalyr ScalyrConfiguration `json:"scalyr"` - LogicalBackup OperatorLogicalBackupConfiguration `json:"logical_backup"` - ConnectionPooler ConnectionPoolerConfiguration `json:"connection_pooler"` - Patroni PatroniConfiguration `json:"patroni"` + // +kubebuilder:default=true + EnableCRDRegistration *bool `json:"enable_crd_registration,omitempty"` + CRDCategories []string `json:"crd_categories,omitempty"` + EnableLazySpiloUpgrade bool `json:"enable_lazy_spilo_upgrade,omitempty"` + // +kubebuilder:default=true + EnablePgVersionEnvVar bool `json:"enable_pgversion_env_var,omitempty"` + EnableSpiloWalPathCompat bool `json:"enable_spilo_wal_path_compat,omitempty"` + EnableTeamIdClusternamePrefix bool `json:"enable_team_id_clustername_prefix,omitempty"` + // +kubebuilder:default="" + EtcdHost string `json:"etcd_host,omitempty"` + // +kubebuilder:default=true + KubernetesUseConfigMaps bool `json:"kubernetes_use_configmaps,omitempty"` + // +kubebuilder:default="ghcr.io/zalando/spilo-18:4.1-p1" + DockerImage string `json:"docker_image,omitempty"` + // +kubebuilder:validation:Minimum=1 + // +kubebuilder:default=8 + Workers uint32 `json:"workers,omitempty"` + // +kubebuilder:default="30m" + // period between consecutive sync requests + ResyncPeriod Duration `json:"resync_period,omitempty"` + // +kubebuilder:default="5m" + // period between consecutive repair requests + RepairPeriod Duration `json:"repair_period,omitempty"` + // +kubebuilder:default=true + EnableMaintenanceWindows *bool `json:"enable_maintenance_windows,omitempty"` + // +kubebuilder:validation:Schemaless + // +kubebuilder:validation:Type=array + MaintenanceWindows []MaintenanceWindow `json:"maintenance_windows,omitempty"` + SetMemoryRequestToLimit bool `json:"set_memory_request_to_limit,omitempty"` + // +kubebuilder:default=true + ShmVolume *bool `json:"enable_shm_volume,omitempty"` + SidecarImages map[string]string `json:"sidecar_docker_images,omitempty"` // deprecated in favour of SidecarContainers + // +kubebuilder:validation:XPreserveUnknownFields + // +kubebuilder:validation:Type=object + // +kubebuilder:validation:Schemaless + SidecarContainers []v1.Container `json:"sidecars,omitempty"` + // +optional + PostgresUsersConfiguration PostgresUsersConfiguration `json:"users"` + // +optional + MajorVersionUpgrade MajorVersionUpgradeConfiguration `json:"major_version_upgrade"` + // +optional + Kubernetes KubernetesMetaConfiguration `json:"kubernetes"` + // +optional + PostgresPodResources PostgresPodResourcesDefaults `json:"postgres_pod_resources"` + // +optional + Timeouts OperatorTimeouts `json:"timeouts"` + // +optional + LoadBalancer LoadBalancerConfiguration `json:"load_balancer"` + // +optional + AWSGCP AWSGCPConfiguration `json:"aws_or_gcp"` + // +optional + OperatorDebug OperatorDebugConfiguration `json:"debug"` + // +optional + TeamsAPI TeamsAPIConfiguration `json:"teams_api"` + // +optional + LoggingRESTAPI LoggingRESTAPIConfiguration `json:"logging_rest_api"` + // +optional + Scalyr ScalyrConfiguration `json:"scalyr"` + // +optional + LogicalBackup OperatorLogicalBackupConfiguration `json:"logical_backup"` + // +optional + ConnectionPooler ConnectionPoolerConfiguration `json:"connection_pooler"` + // +optional + Patroni PatroniConfiguration `json:"patroni"` + + // +kubebuilder:validation:Minimum=-1 + // +kubebuilder:default=-1 + // -1 = disabled + MinInstances int32 `json:"min_instances,omitempty"` + // +kubebuilder:validation:Minimum=-1 + // +kubebuilder:default=-1 + // -1 = disabled + MaxInstances int32 `json:"max_instances,omitempty"` - MinInstances int32 `json:"min_instances,omitempty"` - MaxInstances int32 `json:"max_instances,omitempty"` IgnoreInstanceLimitsAnnotationKey string `json:"ignore_instance_limits_annotation_key,omitempty"` IgnoreResourcesLimitsAnnotationKey string `json:"ignore_resources_limits_annotation_key,omitempty"` } diff --git a/pkg/apis/acid.zalan.do/v1/operatorconfiguration.crd.yaml b/pkg/apis/acid.zalan.do/v1/operatorconfiguration.crd.yaml new file mode 100644 index 000000000..5f347f2ac --- /dev/null +++ b/pkg/apis/acid.zalan.do/v1/operatorconfiguration.crd.yaml @@ -0,0 +1,971 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.17.3 + name: operatorconfigurations.acid.zalan.do +spec: + group: acid.zalan.do + names: + categories: + - all + kind: OperatorConfiguration + listKind: OperatorConfigurationList + plural: operatorconfigurations + shortNames: + - opconfig + singular: operatorconfiguration + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Spilo image to be used for Pods + jsonPath: .configuration.docker_image + name: Image + type: string + - description: Label for K8s resources created by operator + jsonPath: .configuration.kubernetes.cluster_name_label + name: Cluster-Label + type: string + - description: Name of service account to be used + jsonPath: .configuration.kubernetes.pod_service_account_name + name: Service-Account + type: string + - description: Minimum number of instances per Postgres cluster + jsonPath: .configuration.min_instances + name: Min-Instances + type: integer + - description: Age of the OperatorConfiguration resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: OperatorConfiguration defines the specification for the OperatorConfiguration. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + configuration: + description: OperatorConfigurationData defines the operation config + properties: + aws_or_gcp: + description: AWSGCPConfiguration defines the configuration for AWS + properties: + additional_secret_mount: + type: string + additional_secret_mount_path: + type: string + aws_region: + default: eu-central-1 + type: string + enable_ebs_gp3_migration: + type: boolean + enable_ebs_gp3_migration_max_size: + format: int64 + type: integer + gcp_credentials: + type: string + kube_iam_role: + type: string + log_s3_bucket: + type: string + wal_az_storage_account: + type: string + wal_gs_bucket: + type: string + wal_s3_bucket: + type: string + type: object + connection_pooler: + description: ConnectionPoolerConfiguration defines default configuration + for connection pooler + properties: + connection_pooler_default_cpu_limit: + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ + type: string + connection_pooler_default_cpu_request: + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ + type: string + connection_pooler_default_memory_limit: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + connection_pooler_default_memory_request: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + connection_pooler_image: + default: ghcr.io/zalando/postgres-operator/pgbouncer:latest + type: string + connection_pooler_max_db_connections: + format: int32 + type: integer + connection_pooler_mode: + default: transaction + enum: + - session + - transaction + type: string + connection_pooler_number_of_instances: + default: 2 + format: int32 + minimum: 1 + type: integer + connection_pooler_schema: + default: pooler + type: string + connection_pooler_user: + default: pooler + type: string + type: object + crd_categories: + items: + type: string + type: array + debug: + description: OperatorDebugConfiguration defines options for the debug + mode + properties: + debug_logging: + default: true + type: boolean + enable_database_access: + default: true + type: boolean + type: object + docker_image: + default: ghcr.io/zalando/spilo-18:4.1-p1 + type: string + enable_crd_registration: + default: true + type: boolean + enable_lazy_spilo_upgrade: + type: boolean + enable_maintenance_windows: + default: true + type: boolean + enable_pgversion_env_var: + default: true + type: boolean + enable_shm_volume: + default: true + type: boolean + enable_spilo_wal_path_compat: + type: boolean + enable_team_id_clustername_prefix: + type: boolean + etcd_host: + default: "" + type: string + ignore_instance_limits_annotation_key: + type: string + ignore_resources_limits_annotation_key: + type: string + kubernetes: + description: KubernetesMetaConfiguration defines k8s conf required + for all Postgres clusters and the operator itself + properties: + additional_pod_capabilities: + items: + type: string + type: array + cluster_domain: + default: cluster.local + type: string + cluster_labels: + additionalProperties: + type: string + default: + application: spilo + type: object + cluster_name_label: + default: cluster-name + type: string + custom_pod_annotations: + additionalProperties: + type: string + type: object + delete_annotation_date_key: + type: string + delete_annotation_name_key: + type: string + downscaler_annotations: + items: + type: string + type: array + enable_cross_namespace_secret: + type: boolean + enable_finalizers: + type: boolean + enable_init_containers: + default: true + type: boolean + enable_owner_references: + type: boolean + enable_persistent_volume_claim_deletion: + default: true + type: boolean + enable_pod_antiaffinity: + type: boolean + enable_pod_disruption_budget: + default: true + type: boolean + enable_readiness_probe: + type: boolean + enable_secrets_deletion: + default: true + type: boolean + enable_sidecars: + default: true + type: boolean + ignored_annotations: + items: + type: string + type: array + infrastructure_roles_secret_name: + description: |- + NamespacedName comprises a resource name, with a mandatory namespace, + rendered as "/". Being a type captures intent and + helps make sure that UIDs, namespaced names and non-namespaced names + do not get conflated in code. For most use cases, namespace and name + will already have been format validated at the API entry point, so we + don't do that here. Where that's not the case (e.g. in testing), + consider using NamespacedNameOrDie() in testing.go in this package. + + from: https://github.com/kubernetes/apimachinery/blob/master/pkg/types/namespacedname.go + properties: + name: + type: string + namespace: + type: string + required: + - name + type: object + infrastructure_roles_secrets: + description: namespaced name of the secret containing infrastructure + roles names and passwords + items: + properties: + defaultrolevalue: + type: string + defaultuservalue: + type: string + details: + description: This field point out the detailed yaml definition + of the role, if exists + type: string + passwordkey: + type: string + rolekey: + type: string + secretname: + description: |- + Name of a secret which describes the role, and optionally name of a + configmap with an extra information + properties: + name: + type: string + namespace: + type: string + required: + - name + type: object + template: + type: boolean + userkey: + type: string + type: object + type: array + inherited_annotations: + items: + type: string + type: array + inherited_labels: + items: + type: string + type: array + liveness_probe: + description: |- + Probe describes a health check to be performed against a container to determine whether it is + alive or ready to receive traffic. + properties: + exec: + description: Exec specifies a command to execute in the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + properties: + port: + description: Port number of the gRPC service. Number must + be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header to + be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies a connection to a TCP port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + master_pod_move_timeout: + default: 20m + description: timeout for successful migration of master pods from + unschedulable node + format: int64 + type: integer + node_readiness_label: + additionalProperties: + type: string + type: object + node_readiness_label_merge: + enum: + - AND + - OR + type: string + oauth_token_secret_name: + default: postgres-operator + description: namespaced name of the secret containing the OAuth2 + token to pass to the teams API + properties: + name: + type: string + namespace: + type: string + required: + - name + type: object + pdb_master_label_selector: + default: true + type: boolean + pdb_name_format: + default: postgres-{cluster}-pdb + description: defines the template for PDB names + type: string + persistent_volume_claim_retention_policy: + additionalProperties: + type: string + type: object + pod_antiaffinity_preferred_during_scheduling: + type: boolean + pod_antiaffinity_topology_key: + default: kubernetes.io/hostname + type: string + pod_environment_configmap: + description: namespaced name of the ConfigMap with environment + variables to populate on every pod + properties: + name: + type: string + namespace: + type: string + required: + - name + type: object + pod_environment_secret: + type: string + pod_management_policy: + default: ordered_ready + enum: + - ordered_ready + - parallel + type: string + pod_priority_class_name: + type: string + pod_role_label: + default: spilo-role + type: string + pod_service_account_definition: + type: string + pod_service_account_name: + default: postgres-pod + type: string + pod_service_account_role_binding_definition: + type: string + pod_terminate_grace_period: + default: 5m + description: Postgres pods are terminated forcefully after this + timeout + format: int64 + type: integer + secret_name_template: + default: '{username}.{cluster}.credentials.{tprkind}.{tprgroup}' + description: |- + template for database user secrets generated by the operator, + here username contains the namespace in the format namespace.username + if the user is in different namespace than cluster and cross namespace secrets + are enabled via `enable_cross_namespace_secret` flag in the configuration. + type: string + share_pgsocket_with_sidecars: + type: boolean + spilo_allow_privilege_escalation: + default: true + type: boolean + spilo_fsgroup: + format: int64 + type: integer + spilo_privileged: + type: boolean + spilo_runasgroup: + format: int64 + type: integer + spilo_runasuser: + format: int64 + type: integer + storage_resize_mode: + default: pvc + enum: + - ebs + - mixed + - pvc + - "off" + type: string + toleration: + additionalProperties: + type: string + type: object + watched_namespace: + type: string + type: object + kubernetes_use_configmaps: + default: true + type: boolean + load_balancer: + description: LoadBalancerConfiguration defines the LB configuration + properties: + custom_service_annotations: + additionalProperties: + type: string + type: object + db_hosted_zone: + type: string + enable_master_load_balancer: + type: boolean + enable_master_node_port: + type: boolean + enable_master_pooler_load_balancer: + type: boolean + enable_master_pooler_node_port: + type: boolean + enable_replica_load_balancer: + type: boolean + enable_replica_node_port: + type: boolean + enable_replica_pooler_load_balancer: + type: boolean + enable_replica_pooler_node_port: + type: boolean + external_traffic_policy: + default: Cluster + enum: + - Cluster + - Local + type: string + master_dns_name_format: + default: '{cluster}.{namespace}.{hostedzone}' + description: defines the DNS name string template for the master + load balancer cluster + type: string + master_legacy_dns_name_format: + default: '{cluster}.{team}.{hostedzone}' + description: deprecated DNS template for master load balancer + using team name + type: string + replica_dns_name_format: + default: '{cluster}-repl.{namespace}.{hostedzone}' + description: defines the DNS name string template for the replica + load balancer cluster + type: string + replica_legacy_dns_name_format: + default: '{cluster}-repl.{team}.{hostedzone}' + description: deprecated DNS template for replica load balancer + using team name + type: string + type: object + logging_rest_api: + description: LoggingRESTAPIConfiguration defines Logging API conf + properties: + api_port: + default: 8080 + type: integer + cluster_history_entries: + default: 1000 + type: integer + ring_log_lines: + default: 100 + type: integer + type: object + logical_backup: + description: OperatorLogicalBackupConfiguration defines configuration + for logical backup + properties: + logical_backup_azure_storage_account_key: + type: string + logical_backup_azure_storage_account_name: + type: string + logical_backup_azure_storage_container: + type: string + logical_backup_cpu_limit: + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ + type: string + logical_backup_cpu_request: + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ + type: string + logical_backup_cronjob_environment_secret: + type: string + logical_backup_docker_image: + default: ghcr.io/zalando/postgres-operator/logical-backup:v1.15.1 + type: string + logical_backup_failed_jobs_history_limit: + default: 3 + format: int32 + minimum: 0 + type: integer + logical_backup_google_application_credentials: + type: string + logical_backup_job_prefix: + default: logical-backup- + type: string + logical_backup_memory_limit: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + logical_backup_memory_request: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + logical_backup_provider: + default: s3 + enum: + - az + - gcs + - s3 + type: string + logical_backup_s3_access_key_id: + type: string + logical_backup_s3_bucket: + type: string + logical_backup_s3_bucket_prefix: + type: string + logical_backup_s3_endpoint: + type: string + logical_backup_s3_region: + type: string + logical_backup_s3_retention_time: + type: string + logical_backup_s3_secret_access_key: + type: string + logical_backup_s3_sse: + type: string + logical_backup_schedule: + default: 30 00 * * * + pattern: ^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$ + type: string + logical_backup_successful_jobs_history_limit: + default: 3 + format: int32 + minimum: 0 + type: integer + logical_backup_ttl_seconds_after_finished: + default: 86400 + format: int32 + minimum: 0 + type: integer + type: object + maintenance_windows: + type: array + major_version_upgrade: + description: MajorVersionUpgradeConfiguration defines how to execute + major version upgrades of Postgres. + properties: + major_version_upgrade_mode: + default: manual + enum: + - "off" + - manual + - full + type: string + major_version_upgrade_team_allow_list: + items: + type: string + type: array + minimal_major_version: + default: "14" + type: string + target_major_version: + default: "18" + type: string + type: object + max_instances: + default: -1 + description: -1 = disabled + format: int32 + minimum: -1 + type: integer + min_instances: + default: -1 + description: -1 = disabled + format: int32 + minimum: -1 + type: integer + patroni: + description: PatroniConfiguration defines configuration for Patroni + properties: + enable_patroni_failsafe_mode: + type: boolean + type: object + postgres_pod_resources: + description: PostgresPodResourcesDefaults defines the spec of default + resources + properties: + default_cpu_limit: + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ + type: string + default_cpu_request: + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ + type: string + default_memory_limit: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + default_memory_request: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + max_cpu_request: + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ + type: string + max_memory_request: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + min_cpu_limit: + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ + type: string + min_memory_limit: + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + type: object + repair_period: + default: 5m + description: period between consecutive repair requests + format: int64 + type: integer + resync_period: + default: 30m + description: period between consecutive sync requests + format: int64 + type: integer + scalyr: + description: ScalyrConfiguration defines the configuration for ScalyrAPI + properties: + scalyr_api_key: + type: string + scalyr_cpu_limit: + default: "1" + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ + type: string + scalyr_cpu_request: + default: 100m + pattern: ^(\d+m|\d+(\.\d{1,3})?)$ + type: string + scalyr_image: + type: string + scalyr_memory_limit: + default: 500Mi + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + scalyr_memory_request: + default: 50Mi + pattern: ^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$ + type: string + scalyr_server_url: + default: https://upload.eu.scalyr.com + type: string + type: object + set_memory_request_to_limit: + type: boolean + sidecar_docker_images: + additionalProperties: + type: string + type: object + sidecars: + type: object + x-kubernetes-preserve-unknown-fields: true + teams_api: + description: TeamsAPIConfiguration defines the configuration of TeamsAPI + properties: + enable_admin_role_for_users: + default: true + type: boolean + enable_postgres_team_crd: + default: true + type: boolean + enable_postgres_team_crd_superusers: + type: boolean + enable_team_member_deprecation: + type: boolean + enable_team_superuser: + type: boolean + enable_teams_api: + type: boolean + pam_configuration: + default: https://info.example.com/oauth2/tokeninfo?access_token= + uid realm=/employees + type: string + pam_role_name: + default: zalandos + type: string + postgres_superuser_teams: + items: + type: string + type: array + protected_role_names: + default: '["admin", "cron_admin"]' + items: + type: string + type: array + role_deletion_suffix: + default: _deleted + type: string + team_admin_role: + default: admin + type: string + team_api_role_configuration: + additionalProperties: + type: string + default: + log_statement: all + type: object + teams_api_url: + default: https://teams.example.com/api/ + type: string + type: object + timeouts: + description: OperatorTimeouts defines the timeout of ResourceCheck, + PodWait, ReadyWait + properties: + patroni_api_check_interval: + default: 1s + description: interval between consecutive attempts of operator + calling the Patroni API + format: int64 + type: integer + patroni_api_check_timeout: + default: 5s + description: timeout when waiting for successful response from + Patroni API + format: int64 + type: integer + pod_deletion_wait_timeout: + default: 10m + description: timeout when waiting for the Postgres pods to be + deleted + format: int64 + type: integer + pod_label_wait_timeout: + default: 10m + description: timeout when waiting for pod role and cluster labels + format: int64 + type: integer + ready_wait_interval: + default: 4s + description: interval between consecutive attempts waiting for + postgresql CRD to be created + format: int64 + type: integer + ready_wait_timeout: + default: 30s + description: timeout for the complete postgres CRD creation + format: int64 + type: integer + resource_check_interval: + default: 3s + description: interval to wait between consecutive attempts to + check for some K8s resources + format: int64 + type: integer + resource_check_timeout: + default: 10m + description: timeout when waiting for the presence of a certain + K8s resource + format: int64 + type: integer + type: object + users: + description: PostgresUsersConfiguration defines the system users of + Postgres. + properties: + additional_owner_roles: + items: + type: string + type: array + enable_password_rotation: + type: boolean + password_rotation_interval: + default: 90 + format: int32 + type: integer + password_rotation_user_retention: + default: 120 + format: int32 + type: integer + replication_username: + default: standby + type: string + super_username: + default: postgres + type: string + type: object + workers: + default: 8 + format: int32 + minimum: 1 + type: integer + type: object + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + required: + - configuration + - metadata + type: object + served: true + storage: true + subresources: + status: {} diff --git a/pkg/apis/acid.zalan.do/v1/postgresql.crd.yaml b/pkg/apis/acid.zalan.do/v1/postgresql.crd.yaml index 72d7153ba..9b855eb68 100644 --- a/pkg/apis/acid.zalan.do/v1/postgresql.crd.yaml +++ b/pkg/apis/acid.zalan.do/v1/postgresql.crd.yaml @@ -302,7 +302,9 @@ spec: a Container. properties: name: - description: Name of the environment variable. Must be a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -360,6 +362,43 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume mount containing + the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -456,8 +495,9 @@ spec: in a Container. properties: name: - description: Name of the environment variable. Must be - a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -515,6 +555,43 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume mount containing + the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -575,14 +652,14 @@ spec: envFrom: description: |- List of sources to populate environment variables in the container. - The keys defined within a source must be a C_IDENTIFIER. All invalid keys - will be reported as an event when the container is starting. When a key exists in multiple + The keys defined within a source may consist of any printable ASCII characters except '='. + When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated. items: description: EnvFromSource represents the source of a set - of ConfigMaps + of ConfigMaps or Secrets properties: configMapRef: description: The ConfigMap to select from @@ -603,8 +680,9 @@ spec: type: object x-kubernetes-map-type: atomic prefix: - description: An optional identifier to prepend to each - key in the ConfigMap. Must be a C_IDENTIFIER. + description: |- + Optional text to prepend to the name of each environment variable. + May consist of any printable ASCII characters except '='. type: string secretRef: description: The Secret to select from @@ -867,6 +945,12 @@ spec: - port type: object type: object + stopSignal: + description: |- + StopSignal defines which signal will be sent to a container when it is being stopped. + If not specified, the default is defined by the container runtime in use. + StopSignal can only be set for Pods with a non-empty .spec.os.name + type: string type: object livenessProbe: description: |- @@ -1237,7 +1321,9 @@ spec: type: integer type: object resizePolicy: - description: Resources resize policy for the container. + description: |- + Resources resize policy for the container. + This field cannot be set on ephemeral containers. items: description: ContainerResizePolicy represents resource resize policy for the container. @@ -1269,7 +1355,7 @@ spec: Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. - This is an alpha field and requires enabling the + This field depends on the DynamicResourceAllocation feature gate. This field is immutable. It can only be set for containers. @@ -1323,10 +1409,10 @@ spec: restartPolicy: description: |- RestartPolicy defines the restart behavior of individual containers in a pod. - This field may only be set for init containers, and the only allowed value is "Always". - For non-init containers or when this field is not specified, + This overrides the pod-level restart policy. When this field is not specified, the restart behavior is defined by the Pod's restart policy and the container type. - Setting the RestartPolicy as "Always" for the init container will have the following effect: + Additionally, setting the RestartPolicy as "Always" for the init container will + have the following effect: this init container will be continually restarted on exit until all regular containers have terminated. Once all regular containers have completed, all init containers with restartPolicy "Always" @@ -1338,6 +1424,59 @@ spec: init container is started, or after any startupProbe has successfully completed. type: string + restartPolicyRules: + description: |- + Represents a list of rules to be checked to determine if the + container should be restarted on exit. The rules are evaluated in + order. Once a rule matches a container exit condition, the remaining + rules are ignored. If no rule matches the container exit condition, + the Container-level restart policy determines the whether the container + is restarted or not. Constraints on the rules: + - At most 20 rules are allowed. + - Rules can have the same action. + - Identical rules are not forbidden in validations. + When rules are specified, container MUST set RestartPolicy explicitly + even it if matches the Pod's RestartPolicy. + items: + description: ContainerRestartRule describes how a container + exit is handled. + properties: + action: + description: |- + Specifies the action taken on a container exit if the requirements + are satisfied. The only possible value is "Restart" to restart the + container. + type: string + exitCodes: + description: Represents the exit codes to check on container + exits. + properties: + operator: + description: |- + Represents the relationship between the container exit code(s) and the + specified values. Possible values are: + - In: the requirement is satisfied if the container exit code is in the + set of specified values. + - NotIn: the requirement is satisfied if the container exit code is + not in the set of specified values. + type: string + values: + description: |- + Specifies the set of values to check for container exit codes. + At most 255 elements are allowed. + items: + format: int32 + type: integer + type: array + x-kubernetes-list-type: set + required: + - operator + type: object + required: + - action + type: object + type: array + x-kubernetes-list-type: atomic securityContext: description: |- SecurityContext defines the security options the container should be run with. @@ -1413,7 +1552,6 @@ spec: procMount denotes the type of proc mount to use for the containers. The default value is Default which uses the container runtime defaults for readonly paths and masked paths. - This requires the ProcMountType feature flag to be enabled. Note that this field cannot be set when spec.os.name is windows. type: string readOnlyRootFilesystem: @@ -1878,8 +2016,9 @@ spec: in a Container. properties: name: - description: Name of the environment variable. Must be - a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -1937,6 +2076,43 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume mount containing + the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -1997,14 +2173,14 @@ spec: envFrom: description: |- List of sources to populate environment variables in the container. - The keys defined within a source must be a C_IDENTIFIER. All invalid keys - will be reported as an event when the container is starting. When a key exists in multiple + The keys defined within a source may consist of any printable ASCII characters except '='. + When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated. items: description: EnvFromSource represents the source of a set - of ConfigMaps + of ConfigMaps or Secrets properties: configMapRef: description: The ConfigMap to select from @@ -2025,8 +2201,9 @@ spec: type: object x-kubernetes-map-type: atomic prefix: - description: An optional identifier to prepend to each - key in the ConfigMap. Must be a C_IDENTIFIER. + description: |- + Optional text to prepend to the name of each environment variable. + May consist of any printable ASCII characters except '='. type: string secretRef: description: The Secret to select from @@ -2289,6 +2466,12 @@ spec: - port type: object type: object + stopSignal: + description: |- + StopSignal defines which signal will be sent to a container when it is being stopped. + If not specified, the default is defined by the container runtime in use. + StopSignal can only be set for Pods with a non-empty .spec.os.name + type: string type: object livenessProbe: description: |- @@ -2659,7 +2842,9 @@ spec: type: integer type: object resizePolicy: - description: Resources resize policy for the container. + description: |- + Resources resize policy for the container. + This field cannot be set on ephemeral containers. items: description: ContainerResizePolicy represents resource resize policy for the container. @@ -2691,7 +2876,7 @@ spec: Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. - This is an alpha field and requires enabling the + This field depends on the DynamicResourceAllocation feature gate. This field is immutable. It can only be set for containers. @@ -2745,10 +2930,10 @@ spec: restartPolicy: description: |- RestartPolicy defines the restart behavior of individual containers in a pod. - This field may only be set for init containers, and the only allowed value is "Always". - For non-init containers or when this field is not specified, + This overrides the pod-level restart policy. When this field is not specified, the restart behavior is defined by the Pod's restart policy and the container type. - Setting the RestartPolicy as "Always" for the init container will have the following effect: + Additionally, setting the RestartPolicy as "Always" for the init container will + have the following effect: this init container will be continually restarted on exit until all regular containers have terminated. Once all regular containers have completed, all init containers with restartPolicy "Always" @@ -2760,6 +2945,59 @@ spec: init container is started, or after any startupProbe has successfully completed. type: string + restartPolicyRules: + description: |- + Represents a list of rules to be checked to determine if the + container should be restarted on exit. The rules are evaluated in + order. Once a rule matches a container exit condition, the remaining + rules are ignored. If no rule matches the container exit condition, + the Container-level restart policy determines the whether the container + is restarted or not. Constraints on the rules: + - At most 20 rules are allowed. + - Rules can have the same action. + - Identical rules are not forbidden in validations. + When rules are specified, container MUST set RestartPolicy explicitly + even it if matches the Pod's RestartPolicy. + items: + description: ContainerRestartRule describes how a container + exit is handled. + properties: + action: + description: |- + Specifies the action taken on a container exit if the requirements + are satisfied. The only possible value is "Restart" to restart the + container. + type: string + exitCodes: + description: Represents the exit codes to check on container + exits. + properties: + operator: + description: |- + Represents the relationship between the container exit code(s) and the + specified values. Possible values are: + - In: the requirement is satisfied if the container exit code is in the + set of specified values. + - NotIn: the requirement is satisfied if the container exit code is + not in the set of specified values. + type: string + values: + description: |- + Specifies the set of values to check for container exit codes. + At most 255 elements are allowed. + items: + format: int32 + type: integer + type: array + x-kubernetes-list-type: set + required: + - operator + type: object + required: + - action + type: object + type: array + x-kubernetes-list-type: atomic securityContext: description: |- SecurityContext defines the security options the container should be run with. @@ -2835,7 +3073,6 @@ spec: procMount denotes the type of proc mount to use for the containers. The default value is Default which uses the container runtime defaults for readonly paths and masked paths. - This requires the ProcMountType feature flag to be enabled. Note that this field cannot be set when spec.os.name is windows. type: string readOnlyRootFilesystem: @@ -3849,8 +4086,9 @@ spec: in a Container. properties: name: - description: Name of the environment variable. Must be - a C_IDENTIFIER. + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. type: string value: description: |- @@ -3908,6 +4146,43 @@ spec: - fieldPath type: object x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume mount containing + the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic resourceFieldRef: description: |- Selects a resource of the container: only resources limits and requests @@ -4214,9 +4489,10 @@ spec: operator: description: |- Operator represents a key's relationship to the value. - Valid operators are Exists and Equal. Defaults to Equal. + Valid operators are Exists, Equal, Lt, and Gt. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category. + Lt and Gt perform numeric comparisons (requires feature gate TaintTolerationComparisonOperators). type: string tolerationSeconds: description: |- @@ -4355,7 +4631,6 @@ spec: - Ignore: nodeAffinity/nodeSelector are ignored. All nodes are included in the calculations. If this value is nil, the behavior is equivalent to the Honor policy. - This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag. type: string nodeTaintsPolicy: description: |- @@ -4366,7 +4641,6 @@ spec: - Ignore: node taints are ignored. All nodes are included. If this value is nil, the behavior is equivalent to the Ignore policy. - This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag. type: string topologyKey: description: |- diff --git a/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go b/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go index 9ca9a54f1..5c9924d58 100644 --- a/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go +++ b/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go @@ -438,11 +438,6 @@ func (in *OperatorConfigurationData) DeepCopyInto(out *OperatorConfigurationData *out = new(bool) **out = **in } - if in.EnableCRDValidation != nil { - in, out := &in.EnableCRDValidation, &out.EnableCRDValidation - *out = new(bool) - **out = **in - } if in.CRDCategories != nil { in, out := &in.CRDCategories, &out.CRDCategories *out = make([]string, len(*in)) @@ -490,7 +485,7 @@ func (in *OperatorConfigurationData) DeepCopyInto(out *OperatorConfigurationData in.TeamsAPI.DeepCopyInto(&out.TeamsAPI) out.LoggingRESTAPI = in.LoggingRESTAPI out.Scalyr = in.Scalyr - out.LogicalBackup = in.LogicalBackup + in.LogicalBackup.DeepCopyInto(&out.LogicalBackup) in.ConnectionPooler.DeepCopyInto(&out.ConnectionPooler) in.Patroni.DeepCopyInto(&out.Patroni) return @@ -568,6 +563,21 @@ func (in *OperatorDebugConfiguration) DeepCopy() *OperatorDebugConfiguration { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OperatorLogicalBackupConfiguration) DeepCopyInto(out *OperatorLogicalBackupConfiguration) { *out = *in + if in.SuccessfulJobsHistoryLimit != nil { + in, out := &in.SuccessfulJobsHistoryLimit, &out.SuccessfulJobsHistoryLimit + *out = new(int32) + **out = **in + } + if in.FailedJobsHistoryLimit != nil { + in, out := &in.FailedJobsHistoryLimit, &out.FailedJobsHistoryLimit + *out = new(int32) + **out = **in + } + if in.TTLSecondsAfterFinished != nil { + in, out := &in.TTLSecondsAfterFinished, &out.TTLSecondsAfterFinished + *out = new(int32) + **out = **in + } return } diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index 6d9e6150a..eed4ee933 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -92,6 +92,7 @@ type Cluster struct { mu sync.Mutex userSyncStrategy spec.UserSyncer deleteOptions metav1.DeleteOptions + podEventsStore cache.Store podEventsQueue *cache.FIFO replicationSlots map[string]interface{} @@ -125,14 +126,17 @@ type compareLogicalBackupJobResult struct { func New(cfg Config, kubeClient k8sutil.KubernetesClient, pgSpec acidv1.Postgresql, logger *logrus.Entry, eventRecorder record.EventRecorder) *Cluster { deletePropagationPolicy := metav1.DeletePropagationOrphan - podEventsQueue := cache.NewFIFO(func(obj interface{}) (string, error) { + keyFn := func(obj interface{}) (string, error) { e, ok := obj.(PodEvent) if !ok { - return "", fmt.Errorf("could not cast to PodEvent") + return "", fmt.Errorf("could not cast to pod event") } return fmt.Sprintf("%s-%s", e.PodName, e.ResourceVersion), nil - }) + } + podEventsStore := cache.NewStore(keyFn) + podEventsQueue := cache.NewFIFO(keyFn) + passwordEncryption, ok := pgSpec.Spec.PostgresqlParam.Parameters["password_encryption"] if !ok { passwordEncryption = "scram-sha-256" @@ -159,6 +163,7 @@ func New(cfg Config, kubeClient k8sutil.KubernetesClient, pgSpec acidv1.Postgres AdditionalOwnerRoles: cfg.OpConfig.AdditionalOwnerRoles, }, deleteOptions: metav1.DeleteOptions{PropagationPolicy: &deletePropagationPolicy}, + podEventsStore: podEventsStore, podEventsQueue: podEventsQueue, KubeClient: kubeClient, currentMajorVersion: 0, @@ -919,6 +924,21 @@ func (c *Cluster) compareLogicalBackupJob(cur, new *batchv1.CronJob) *compareLog reasons = append(reasons, fmt.Sprintf("logical backup container specs do not match: %v", strings.Join(contReasons, `', '`))) } + if !reflect.DeepEqual(cur.Spec.SuccessfulJobsHistoryLimit, new.Spec.SuccessfulJobsHistoryLimit) { + match = false + reasons = append(reasons, fmt.Sprintf("new job's successfulJobsHistoryLimit %v does not match the current one %v", new.Spec.SuccessfulJobsHistoryLimit, cur.Spec.SuccessfulJobsHistoryLimit)) + } + + if !reflect.DeepEqual(cur.Spec.FailedJobsHistoryLimit, new.Spec.FailedJobsHistoryLimit) { + match = false + reasons = append(reasons, fmt.Sprintf("new job's failedJobsHistoryLimit %v does not match the current one %v", new.Spec.FailedJobsHistoryLimit, cur.Spec.FailedJobsHistoryLimit)) + } + + if !reflect.DeepEqual(cur.Spec.JobTemplate.Spec.TTLSecondsAfterFinished, new.Spec.JobTemplate.Spec.TTLSecondsAfterFinished) { + match = false + reasons = append(reasons, fmt.Sprintf("new job's TTLSecondsAfterFinished %v does not match the current one %v", new.Spec.JobTemplate.Spec.TTLSecondsAfterFinished, cur.Spec.JobTemplate.Spec.TTLSecondsAfterFinished)) + } + return &compareLogicalBackupJobResult{match: match, reasons: reasons, deletedPodAnnotations: deletedPodAnnotations} } @@ -1341,6 +1361,9 @@ func (c *Cluster) NeedsRepair() (bool, acidv1.PostgresStatus) { // ReceivePodEvent is called back by the controller in order to add the cluster's pod event to the queue. func (c *Cluster) ReceivePodEvent(event PodEvent) { + if err := c.podEventsStore.Add(event); err != nil { + c.logger.Errorf("error when receiving pod event for lookup: %v", err) + } if err := c.podEventsQueue.Add(event); err != nil { c.logger.Errorf("error when receiving pod events: %v", err) } @@ -1381,7 +1404,19 @@ func (c *Cluster) processPodEventQueue(stopCh <-chan struct{}) { case <-stopCh: return default: - if _, err := c.podEventsQueue.Pop(cache.PopProcessFunc(c.processPodEvent)); err != nil { + _, err := c.podEventsQueue.Pop(cache.PopProcessFunc(func(obj interface{}, isInInitialList bool) error { + event, ok := obj.(PodEvent) + if !ok { + c.logger.Errorf("could not cast to pod event") + return nil // skip event to keep processing + } + c.processPodEvent(event, isInInitialList) + if err := c.podEventsStore.Delete(obj); err != nil { + c.logger.Errorf("failed to delete key from lookup store: %v", err) + } + return nil + })) + if err != nil { c.logger.Errorf("error when processing pod event queue %v", err) } } diff --git a/pkg/cluster/cluster_test.go b/pkg/cluster/cluster_test.go index 5fdf1a220..95a445ff3 100644 --- a/pkg/cluster/cluster_test.go +++ b/pkg/cluster/cluster_test.go @@ -1567,12 +1567,21 @@ func TestCompareServices(t *testing.T) { } } +var ( + defaultSuccessfulJobsHistoryLimit = int32(3) + defaultFailedJobsHistoryLimit = int32(3) + defaultTTLSecondsAfterFinished = int32(86400) +) + func newCronJob(image, schedule string, vars []v1.EnvVar, mounts []v1.VolumeMount) *batchv1.CronJob { cron := &batchv1.CronJob{ Spec: batchv1.CronJobSpec{ - Schedule: schedule, + Schedule: schedule, + SuccessfulJobsHistoryLimit: &defaultSuccessfulJobsHistoryLimit, + FailedJobsHistoryLimit: &defaultFailedJobsHistoryLimit, JobTemplate: batchv1.JobTemplateSpec{ Spec: batchv1.JobSpec{ + TTLSecondsAfterFinished: &defaultTTLSecondsAfterFinished, Template: v1.PodTemplateSpec{ Spec: v1.PodSpec{ Containers: []v1.Container{ diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index 302797dc4..306185e90 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -171,7 +171,7 @@ func (c *Cluster) enforceMinResourceLimits(resources *v1.ResourceRequirements) e msg = fmt.Sprintf("defined CPU limit %s for %q container is below required minimum %s and will be increased", cpuLimit.String(), constants.PostgresContainerName, minCPULimit) c.logger.Warningf("%s", msg) - c.eventRecorder.Eventf(c.GetReference(), v1.EventTypeWarning, "ResourceLimits", msg) + c.eventRecorder.Event(c.GetReference(), v1.EventTypeWarning, "ResourceLimits", msg) resources.Limits[v1.ResourceCPU], _ = resource.ParseQuantity(minCPULimit) } } @@ -188,7 +188,7 @@ func (c *Cluster) enforceMinResourceLimits(resources *v1.ResourceRequirements) e msg = fmt.Sprintf("defined memory limit %s for %q container is below required minimum %s and will be increased", memoryLimit.String(), constants.PostgresContainerName, minMemoryLimit) c.logger.Warningf("%s", msg) - c.eventRecorder.Eventf(c.GetReference(), v1.EventTypeWarning, "ResourceLimits", msg) + c.eventRecorder.Event(c.GetReference(), v1.EventTypeWarning, "ResourceLimits", msg) resources.Limits[v1.ResourceMemory], _ = resource.ParseQuantity(minMemoryLimit) } } @@ -1469,7 +1469,7 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef } sidecarContainers, conflicts := mergeContainers(clusterSpecificSidecars, c.Config.OpConfig.SidecarContainers, globalSidecarContainersByDockerImage, scalyrSidecars) - for containerName := range conflicts { + for _, containerName := range conflicts { c.logger.Warningf("a sidecar is specified twice. Ignoring sidecar %q in favor of %q with high a precedence", containerName, containerName) } @@ -2452,7 +2452,13 @@ func (c *Cluster) generateLogicalBackupJob() (*batchv1.CronJob, error) { // configure a batch job jobSpec := batchv1.JobSpec{ - Template: *podTemplate, + Template: *podTemplate, + TTLSecondsAfterFinished: c.OpConfig.LogicalBackup.LogicalBackupTTLSecondsAfterFinished, + } + + if jobSpec.TTLSecondsAfterFinished == nil { + defaultTTL := int32(86400) + jobSpec.TTLSecondsAfterFinished = &defaultTTL } // configure a cron job @@ -2470,6 +2476,18 @@ func (c *Cluster) generateLogicalBackupJob() (*batchv1.CronJob, error) { schedule = c.OpConfig.LogicalBackupSchedule } + successfulJobsHistoryLimit := c.OpConfig.LogicalBackup.LogicalBackupSuccessfulJobsHistoryLimit + if successfulJobsHistoryLimit == nil { + defaultLimit := int32(3) + successfulJobsHistoryLimit = &defaultLimit + } + + failedJobsHistoryLimit := c.OpConfig.LogicalBackup.LogicalBackupFailedJobsHistoryLimit + if failedJobsHistoryLimit == nil { + defaultLimit := int32(3) + failedJobsHistoryLimit = &defaultLimit + } + cronJob := &batchv1.CronJob{ ObjectMeta: metav1.ObjectMeta{ Name: c.getLogicalBackupJobName(), @@ -2479,9 +2497,11 @@ func (c *Cluster) generateLogicalBackupJob() (*batchv1.CronJob, error) { OwnerReferences: c.ownerReferences(), }, Spec: batchv1.CronJobSpec{ - Schedule: schedule, - JobTemplate: jobTemplateSpec, - ConcurrencyPolicy: batchv1.ForbidConcurrent, + Schedule: schedule, + JobTemplate: jobTemplateSpec, + ConcurrencyPolicy: batchv1.ForbidConcurrent, + SuccessfulJobsHistoryLimit: successfulJobsHistoryLimit, + FailedJobsHistoryLimit: failedJobsHistoryLimit, }, } diff --git a/pkg/cluster/k8sres_test.go b/pkg/cluster/k8sres_test.go index bf21b8645..8cbecb2fa 100644 --- a/pkg/cluster/k8sres_test.go +++ b/pkg/cluster/k8sres_test.go @@ -4229,6 +4229,32 @@ func TestGenerateLogicalBackupJob(t *testing.T) { if !reflect.DeepEqual(tt.expectedResources, clusterResources) { t.Errorf("%s - %s: expected resources %#v, got %#v", t.Name(), tt.subTest, tt.expectedResources, clusterResources) } + + expectedSuccessfulJobsHistoryLimit := int32(3) + if cluster.OpConfig.LogicalBackup.LogicalBackupSuccessfulJobsHistoryLimit != nil { + expectedSuccessfulJobsHistoryLimit = *cluster.OpConfig.LogicalBackup.LogicalBackupSuccessfulJobsHistoryLimit + } + if *cronJob.Spec.SuccessfulJobsHistoryLimit != expectedSuccessfulJobsHistoryLimit { + t.Errorf("%s - %s: expected successfulJobsHistoryLimit %d, got %d", t.Name(), tt.subTest, expectedSuccessfulJobsHistoryLimit, *cronJob.Spec.SuccessfulJobsHistoryLimit) + } + + expectedFailedJobsHistoryLimit := int32(3) + if cluster.OpConfig.LogicalBackup.LogicalBackupFailedJobsHistoryLimit != nil { + expectedFailedJobsHistoryLimit = *cluster.OpConfig.LogicalBackup.LogicalBackupFailedJobsHistoryLimit + } + if *cronJob.Spec.FailedJobsHistoryLimit != expectedFailedJobsHistoryLimit { + t.Errorf("%s - %s: expected failedJobsHistoryLimit %d, got %d", t.Name(), tt.subTest, expectedFailedJobsHistoryLimit, *cronJob.Spec.FailedJobsHistoryLimit) + } + + expectedTTL := int32(86400) + if cluster.OpConfig.LogicalBackup.LogicalBackupTTLSecondsAfterFinished != nil { + expectedTTL = *cluster.OpConfig.LogicalBackup.LogicalBackupTTLSecondsAfterFinished + } + if cronJob.Spec.JobTemplate.Spec.TTLSecondsAfterFinished == nil { + t.Errorf("%s - %s: expected TTLSecondsAfterFinished to be set", t.Name(), tt.subTest) + } else if *cronJob.Spec.JobTemplate.Spec.TTLSecondsAfterFinished != expectedTTL { + t.Errorf("%s - %s: expected TTLSecondsAfterFinished %d, got %d", t.Name(), tt.subTest, expectedTTL, *cronJob.Spec.JobTemplate.Spec.TTLSecondsAfterFinished) + } } } diff --git a/pkg/cluster/majorversionupgrade.go b/pkg/cluster/majorversionupgrade.go index 6c754b14f..6995c50b5 100644 --- a/pkg/cluster/majorversionupgrade.go +++ b/pkg/cluster/majorversionupgrade.go @@ -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" diff --git a/pkg/cluster/volumes.go b/pkg/cluster/volumes.go index 115474fef..e32e558e6 100644 --- a/pkg/cluster/volumes.go +++ b/pkg/cluster/volumes.go @@ -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 } diff --git a/pkg/cluster/volumes_test.go b/pkg/cluster/volumes_test.go index 95ecc7624..d6472539b 100644 --- a/pkg/cluster/volumes_test.go +++ b/pkg/cluster/volumes_test.go @@ -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" diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go index 93a9c7f3e..879e44ad4 100644 --- a/pkg/controller/controller.go +++ b/pkg/controller/controller.go @@ -65,6 +65,7 @@ type Controller struct { nodesInformer cache.SharedIndexInformer podCh chan cluster.PodEvent + clusterEventStores []cache.Store // [workerID]Store clusterEventQueues []*cache.FIFO // [workerID]Queue lastClusterSyncTime int64 lastClusterRepairTime int64 @@ -356,17 +357,19 @@ func (c *Controller) initController() { c.config.InfrastructureRoles = infraRoles } + c.clusterEventStores = make([]cache.Store, c.opConfig.Workers) c.clusterEventQueues = make([]*cache.FIFO, c.opConfig.Workers) c.workerLogs = make(map[uint32]ringlog.RingLogger, c.opConfig.Workers) for i := range c.clusterEventQueues { - c.clusterEventQueues[i] = cache.NewFIFO(func(obj interface{}) (string, error) { + keyFn := func(obj interface{}) (string, error) { e, ok := obj.(ClusterEvent) if !ok { - return "", fmt.Errorf("could not cast to ClusterEvent") + return "", fmt.Errorf("could not cast to cluster event") } - return queueClusterKey(e.EventType, e.UID), nil - }) + } + c.clusterEventStores[i] = cache.NewStore(keyFn) + c.clusterEventQueues[i] = cache.NewFIFO(keyFn) } c.apiserver = apiserver.New(c, c.opConfig.APIPort, c.logger.Logger) diff --git a/pkg/controller/logs_and_api.go b/pkg/controller/logs_and_api.go index 4af5e1f36..24881f9d7 100644 --- a/pkg/controller/logs_and_api.go +++ b/pkg/controller/logs_and_api.go @@ -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 { diff --git a/pkg/controller/operator_config.go b/pkg/controller/operator_config.go index a85b418e7..6566a8d06 100644 --- a/pkg/controller/operator_config.go +++ b/pkg/controller/operator_config.go @@ -31,7 +31,6 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur // general config result.EnableCRDRegistration = util.CoalesceBool(fromCRD.EnableCRDRegistration, util.True()) - result.EnableCRDValidation = util.CoalesceBool(fromCRD.EnableCRDValidation, util.True()) result.CRDCategories = util.CoalesceStrArr(fromCRD.CRDCategories, []string{"all"}) result.EnableLazySpiloUpgrade = fromCRD.EnableLazySpiloUpgrade result.EnablePgVersionEnvVar = fromCRD.EnablePgVersionEnvVar @@ -217,6 +216,9 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur result.LogicalBackupMemoryRequest = fromCRD.LogicalBackup.MemoryRequest result.LogicalBackupCPULimit = fromCRD.LogicalBackup.CPULimit result.LogicalBackupMemoryLimit = fromCRD.LogicalBackup.MemoryLimit + result.LogicalBackupSuccessfulJobsHistoryLimit = util.CoalesceInt32(fromCRD.LogicalBackup.SuccessfulJobsHistoryLimit, k8sutil.Int32ToPointer(3)) + result.LogicalBackupFailedJobsHistoryLimit = util.CoalesceInt32(fromCRD.LogicalBackup.FailedJobsHistoryLimit, k8sutil.Int32ToPointer(3)) + result.LogicalBackupTTLSecondsAfterFinished = fromCRD.LogicalBackup.TTLSecondsAfterFinished // debug config result.DebugLogging = *util.CoalesceBool(fromCRD.OperatorDebug.DebugLogging, util.True()) diff --git a/pkg/controller/postgresql.go b/pkg/controller/postgresql.go index ab5e0d772..0933f7823 100644 --- a/pkg/controller/postgresql.go +++ b/pkg/controller/postgresql.go @@ -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) + } } } diff --git a/pkg/controller/util.go b/pkg/controller/util.go index 87962f7b9..6296e5341 100644 --- a/pkg/controller/util.go +++ b/pkg/controller/util.go @@ -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) { diff --git a/pkg/generated/clientset/versioned/fake/clientset_generated.go b/pkg/generated/clientset/versioned/fake/clientset_generated.go index 381ce23bd..f7f61ddea 100644 --- a/pkg/generated/clientset/versioned/fake/clientset_generated.go +++ b/pkg/generated/clientset/versioned/fake/clientset_generated.go @@ -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{} diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/acid.zalan.do_client.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/acid.zalan.do_client.go index b53b029fb..93564ec3b 100644 --- a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/acid.zalan.do_client.go +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/acid.zalan.do_client.go @@ -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 diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_operatorconfiguration.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_operatorconfiguration.go index 8c9790d18..66db8ec40 100644 --- a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_operatorconfiguration.go +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_operatorconfiguration.go @@ -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, } diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/operatorconfiguration.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/operatorconfiguration.go index 91dc27037..292fa2fce 100644 --- a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/operatorconfiguration.go +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/operatorconfiguration.go @@ -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{} }, ), } } diff --git a/pkg/generated/clientset/versioned/typed/zalando.org/v1/zalando.org_client.go b/pkg/generated/clientset/versioned/typed/zalando.org/v1/zalando.org_client.go index 99d795d76..803aee179 100644 --- a/pkg/generated/clientset/versioned/typed/zalando.org/v1/zalando.org_client.go +++ b/pkg/generated/clientset/versioned/typed/zalando.org/v1/zalando.org_client.go @@ -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 diff --git a/pkg/generated/informers/externalversions/acid.zalan.do/v1/interface.go b/pkg/generated/informers/externalversions/acid.zalan.do/v1/interface.go index 1ea652a3f..d176a2b35 100644 --- a/pkg/generated/informers/externalversions/acid.zalan.do/v1/interface.go +++ b/pkg/generated/informers/externalversions/acid.zalan.do/v1/interface.go @@ -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} diff --git a/pkg/generated/informers/externalversions/acid.zalan.do/v1/operatorconfiguration.go b/pkg/generated/informers/externalversions/acid.zalan.do/v1/operatorconfiguration.go new file mode 100644 index 000000000..e2fee1a4e --- /dev/null +++ b/pkg/generated/informers/externalversions/acid.zalan.do/v1/operatorconfiguration.go @@ -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()) +} diff --git a/pkg/generated/informers/externalversions/acid.zalan.do/v1/postgresql.go b/pkg/generated/informers/externalversions/acid.zalan.do/v1/postgresql.go index 1601f2bd6..c1b58ff05 100644 --- a/pkg/generated/informers/externalversions/acid.zalan.do/v1/postgresql.go +++ b/pkg/generated/informers/externalversions/acid.zalan.do/v1/postgresql.go @@ -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 { diff --git a/pkg/generated/informers/externalversions/acid.zalan.do/v1/postgresteam.go b/pkg/generated/informers/externalversions/acid.zalan.do/v1/postgresteam.go index b53862c78..954dfd19f 100644 --- a/pkg/generated/informers/externalversions/acid.zalan.do/v1/postgresteam.go +++ b/pkg/generated/informers/externalversions/acid.zalan.do/v1/postgresteam.go @@ -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 { diff --git a/pkg/generated/informers/externalversions/factory.go b/pkg/generated/informers/externalversions/factory.go index d25563014..fb9ba76ac 100644 --- a/pkg/generated/informers/externalversions/factory.go +++ b/pkg/generated/informers/externalversions/factory.go @@ -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) diff --git a/pkg/generated/informers/externalversions/generic.go b/pkg/generated/informers/externalversions/generic.go index ed27d5743..f5953bde6 100644 --- a/pkg/generated/informers/externalversions/generic.go +++ b/pkg/generated/informers/externalversions/generic.go @@ -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"): diff --git a/pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go b/pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go index 2037af01e..b8c3e7e51 100644 --- a/pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go +++ b/pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go @@ -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 +} diff --git a/pkg/generated/informers/externalversions/zalando.org/v1/fabriceventstream.go b/pkg/generated/informers/externalversions/zalando.org/v1/fabriceventstream.go index 264ebb985..675058f80 100644 --- a/pkg/generated/informers/externalversions/zalando.org/v1/fabriceventstream.go +++ b/pkg/generated/informers/externalversions/zalando.org/v1/fabriceventstream.go @@ -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 { diff --git a/pkg/generated/listers/acid.zalan.do/v1/expansion_generated.go b/pkg/generated/listers/acid.zalan.do/v1/expansion_generated.go index b71f44767..f9b4c79e7 100644 --- a/pkg/generated/listers/acid.zalan.do/v1/expansion_generated.go +++ b/pkg/generated/listers/acid.zalan.do/v1/expansion_generated.go @@ -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{} diff --git a/pkg/generated/listers/acid.zalan.do/v1/operatorconfiguration.go b/pkg/generated/listers/acid.zalan.do/v1/operatorconfiguration.go new file mode 100644 index 000000000..c00599718 --- /dev/null +++ b/pkg/generated/listers/acid.zalan.do/v1/operatorconfiguration.go @@ -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] +} diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index 35537cf44..0febb525b 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -19,7 +19,6 @@ type CRD struct { ResyncPeriod time.Duration `name:"resync_period" default:"30m"` RepairPeriod time.Duration `name:"repair_period" default:"5m"` EnableCRDRegistration *bool `name:"enable_crd_registration" default:"true"` - EnableCRDValidation *bool `name:"enable_crd_validation" default:"true"` CRDCategories []string `name:"crd_categories" default:"all"` } @@ -149,6 +148,9 @@ type LogicalBackup struct { LogicalBackupMemoryRequest string `name:"logical_backup_memory_request"` LogicalBackupCPULimit string `name:"logical_backup_cpu_limit"` LogicalBackupMemoryLimit string `name:"logical_backup_memory_limit"` + LogicalBackupSuccessfulJobsHistoryLimit *int32 `name:"logical_backup_successful_jobs_history_limit" default:"3"` + LogicalBackupFailedJobsHistoryLimit *int32 `name:"logical_backup_failed_jobs_history_limit" default:"3"` + LogicalBackupTTLSecondsAfterFinished *int32 `name:"logical_backup_ttl_seconds_after_finished" default:"86400"` } // Operator options for connection pooler diff --git a/pkg/util/volumes/ebs.go b/pkg/util/volumes/ebs.go index 45850d55f..bb7506d93 100644 --- a/pkg/util/volumes/ebs.go +++ b/pkg/util/volumes/ebs.go @@ -1,12 +1,13 @@ package volumes import ( + "context" "fmt" "strings" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2/types" v1 "k8s.io/api/core/v1" "github.com/zalando/postgres-operator/pkg/util/constants" @@ -15,17 +16,17 @@ import ( // EBSVolumeResizer implements volume resizing interface for AWS EBS volumes. type EBSVolumeResizer struct { - connection *ec2.EC2 + connection *ec2.Client AWSRegion string } // ConnectToProvider connects to AWS. func (r *EBSVolumeResizer) ConnectToProvider() error { - sess, err := session.NewSession(&aws.Config{Region: aws.String(r.AWSRegion)}) + cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion(r.AWSRegion)) if err != nil { return fmt.Errorf("could not establish AWS session: %v", err) } - r.connection = ec2.New(sess) + r.connection = ec2.NewFromConfig(cfg) return nil } @@ -77,7 +78,7 @@ func (r *EBSVolumeResizer) DescribeVolumes(volumeIds []string) ([]VolumeProperti } } - volumeOutput, err := r.connection.DescribeVolumes(&ec2.DescribeVolumesInput{VolumeIds: aws.StringSlice((volumeIds))}) + volumeOutput, err := r.connection.DescribeVolumes(context.TODO(), &ec2.DescribeVolumesInput{VolumeIds: volumeIds}) if err != nil { return nil, err } @@ -88,13 +89,13 @@ func (r *EBSVolumeResizer) DescribeVolumes(volumeIds []string) ([]VolumeProperti } for _, v := range volumeOutput.Volumes { - switch *v.VolumeType { + switch v.VolumeType { case "gp3": - p = append(p, VolumeProperties{VolumeID: *v.VolumeId, Size: *v.Size, VolumeType: *v.VolumeType, Iops: *v.Iops, Throughput: *v.Throughput}) + p = append(p, VolumeProperties{VolumeID: *v.VolumeId, Size: int64(*v.Size), VolumeType: string(v.VolumeType), Iops: int64(*v.Iops), Throughput: int64(*v.Throughput)}) case "gp2": - p = append(p, VolumeProperties{VolumeID: *v.VolumeId, Size: *v.Size, VolumeType: *v.VolumeType}) + p = append(p, VolumeProperties{VolumeID: *v.VolumeId, Size: int64(*v.Size), VolumeType: string(v.VolumeType)}) default: - return nil, fmt.Errorf("discovered unexpected volume type %s %s", *v.VolumeId, *v.VolumeType) + return nil, fmt.Errorf("discovered unexpected volume type %s %s", *v.VolumeId, v.VolumeType) } } @@ -104,7 +105,7 @@ func (r *EBSVolumeResizer) DescribeVolumes(volumeIds []string) ([]VolumeProperti // ResizeVolume actually calls AWS API to resize the EBS volume if necessary. func (r *EBSVolumeResizer) ResizeVolume(volumeID string, newSize int64) error { /* first check if the volume is already of a requested size */ - volumeOutput, err := r.connection.DescribeVolumes(&ec2.DescribeVolumesInput{VolumeIds: []*string{&volumeID}}) + volumeOutput, err := r.connection.DescribeVolumes(context.TODO(), &ec2.DescribeVolumesInput{VolumeIds: []string{volumeID}}) if err != nil { return fmt.Errorf("could not get information about the volume: %v", err) } @@ -112,17 +113,18 @@ func (r *EBSVolumeResizer) ResizeVolume(volumeID string, newSize int64) error { if *vol.VolumeId != volumeID { return fmt.Errorf("describe volume %q returned information about a non-matching volume %q", volumeID, *vol.VolumeId) } - if *vol.Size == newSize { + sizeInt32 := int32(newSize) + if *vol.Size == sizeInt32 { // nothing to do return nil } - input := ec2.ModifyVolumeInput{Size: &newSize, VolumeId: &volumeID} - output, err := r.connection.ModifyVolume(&input) + input := ec2.ModifyVolumeInput{Size: &sizeInt32, VolumeId: &volumeID} + output, err := r.connection.ModifyVolume(context.TODO(), &input) if err != nil { return fmt.Errorf("could not modify persistent volume: %v", err) } - state := *output.VolumeModification.ModificationState + state := output.VolumeModification.ModificationState if state == constants.EBSVolumeStateFailed { return fmt.Errorf("could not modify persistent volume %q: modification state failed", volumeID) } @@ -133,10 +135,10 @@ func (r *EBSVolumeResizer) ResizeVolume(volumeID string, newSize int64) error { return nil } // wait until the volume reaches the "optimizing" or "completed" state - in := ec2.DescribeVolumesModificationsInput{VolumeIds: []*string{&volumeID}} + in := ec2.DescribeVolumesModificationsInput{VolumeIds: []string{volumeID}} return retryutil.Retry(constants.EBSVolumeResizeWaitInterval, constants.EBSVolumeResizeWaitTimeout, func() (bool, error) { - out, err := r.connection.DescribeVolumesModifications(&in) + out, err := r.connection.DescribeVolumesModifications(context.TODO(), &in) if err != nil { return false, fmt.Errorf("could not describe volume modification: %v", err) } @@ -147,20 +149,35 @@ func (r *EBSVolumeResizer) ResizeVolume(volumeID string, newSize int64) error { return false, fmt.Errorf("non-matching volume id when describing modifications: %q is different from %q", *out.VolumesModifications[0].VolumeId, volumeID) } - return *out.VolumesModifications[0].ModificationState != constants.EBSVolumeStateModifying, nil + return out.VolumesModifications[0].ModificationState != constants.EBSVolumeStateModifying, nil }) } // ModifyVolume Modify EBS volume func (r *EBSVolumeResizer) ModifyVolume(volumeID string, newType *string, newSize *int64, iops *int64, throughput *int64) error { /* first check if the volume is already of a requested size */ - input := ec2.ModifyVolumeInput{Size: newSize, VolumeId: &volumeID, VolumeType: newType, Iops: iops, Throughput: throughput} - output, err := r.connection.ModifyVolume(&input) + var sizeInt32 *int32 + var iopsInt32 *int32 + var throughputInt32 *int32 + if newSize != nil { + s := int32(*newSize) + sizeInt32 = &s + } + if iops != nil { + i := int32(*iops) + iopsInt32 = &i + } + if throughput != nil { + t := int32(*throughput) + throughputInt32 = &t + } + input := ec2.ModifyVolumeInput{Size: sizeInt32, VolumeId: &volumeID, VolumeType: types.VolumeType(*newType), Iops: iopsInt32, Throughput: throughputInt32} + output, err := r.connection.ModifyVolume(context.TODO(), &input) if err != nil { return fmt.Errorf("could not modify persistent volume: %v", err) } - state := *output.VolumeModification.ModificationState + state := output.VolumeModification.ModificationState if state == constants.EBSVolumeStateFailed { return fmt.Errorf("could not modify persistent volume %q: modification state failed", volumeID) } @@ -171,10 +188,10 @@ func (r *EBSVolumeResizer) ModifyVolume(volumeID string, newType *string, newSiz return nil } // wait until the volume reaches the "optimizing" or "completed" state - in := ec2.DescribeVolumesModificationsInput{VolumeIds: []*string{&volumeID}} + in := ec2.DescribeVolumesModificationsInput{VolumeIds: []string{volumeID}} return retryutil.Retry(constants.EBSVolumeResizeWaitInterval, constants.EBSVolumeResizeWaitTimeout, func() (bool, error) { - out, err := r.connection.DescribeVolumesModifications(&in) + out, err := r.connection.DescribeVolumesModifications(context.TODO(), &in) if err != nil { return false, fmt.Errorf("could not describe volume modification: %v", err) } @@ -185,7 +202,7 @@ func (r *EBSVolumeResizer) ModifyVolume(volumeID string, newType *string, newSiz return false, fmt.Errorf("non-matching volume id when describing modifications: %q is different from %q", *out.VolumesModifications[0].VolumeId, volumeID) } - return *out.VolumesModifications[0].ModificationState != constants.EBSVolumeStateModifying, nil + return out.VolumesModifications[0].ModificationState != constants.EBSVolumeStateModifying, nil }) } diff --git a/ui/app/package.json b/ui/app/package.json index 7fd410bd7..7fa1ef373 100644 --- a/ui/app/package.json +++ b/ui/app/package.json @@ -38,7 +38,7 @@ "brfs": "^2.0.2", "dedent-js": "1.0.1", "eslint": "^8.32.0", - "js-yaml": "4.1.1", + "js-yaml": "4.2.0", "pug": "^3.0.2", "rimraf": "^4.1.2", "riot": "^3.13.2",