From e0415d1471abd98c23bf77e4648a7d070e1dc406 Mon Sep 17 00:00:00 2001 From: Thomas von Dein Date: Tue, 18 Aug 2026 10:36:21 +0200 Subject: [PATCH 1/2] generate operator manifests yaml without kubectl directly --- Makefile | 7 +- external/svc-postgres-operator.yaml | 997 +++++++++++++++------------- gen-operator-yaml.sh | 49 ++ 3 files changed, 585 insertions(+), 468 deletions(-) create mode 100755 gen-operator-yaml.sh diff --git a/Makefile b/Makefile index 8b43f025..4b094b8c 100644 --- a/Makefile +++ b/Makefile @@ -135,12 +135,7 @@ configmap: > config/manager/configmap.yaml svc-postgres-operator-yaml: - kubectl apply \ - -f $(POSTGRES_OPERATOR_URL)/configmap.yaml \ - -f $(POSTGRES_OPERATOR_URL)/operator-service-account-rbac.yaml \ - -f $(POSTGRES_OPERATOR_URL)/postgres-operator.yaml \ - -f $(POSTGRES_OPERATOR_URL)/api-service.yaml \ - --dry-run=client -o yaml > external/svc-postgres-operator.yaml + ./gen-operator-yaml.sh $(POSTGRES_OPERATOR_VERSION) external/svc-postgres-operator.yaml # crd-postgresql-yaml: # kubectl apply -f $(POSTGRES_CRD_URL) --dry-run=client -o yaml > external/crd-postgresql.yaml diff --git a/external/svc-postgres-operator.yaml b/external/svc-postgres-operator.yaml index e05228d8..a5f2af0a 100644 --- a/external/svc-postgres-operator.yaml +++ b/external/svc-postgres-operator.yaml @@ -1,468 +1,541 @@ apiVersion: v1 +kind: List items: -- apiVersion: v1 - data: - api_port: "8080" - aws_region: eu-central-1 - cluster_domain: cluster.local - cluster_history_entries: "1000" - cluster_labels: application:spilo - cluster_name_label: cluster-name - connection_pooler_default_cpu_limit: "1" - connection_pooler_default_cpu_request: 500m - connection_pooler_default_memory_limit: 100Mi - connection_pooler_default_memory_request: 100Mi - connection_pooler_image: registry.opensource.zalan.do/acid/pgbouncer:master-32 - connection_pooler_max_db_connections: "60" - connection_pooler_mode: transaction - connection_pooler_number_of_instances: "2" - connection_pooler_schema: pooler - connection_pooler_user: pooler - crd_categories: all - db_hosted_zone: db.example.com - debug_logging: "true" - default_cpu_limit: "1" - default_cpu_request: 100m - default_memory_limit: 500Mi - default_memory_request: 100Mi - docker_image: ghcr.io/zalando/spilo-17:4.0-p3 - enable_admin_role_for_users: "true" - enable_crd_registration: "true" - enable_crd_validation: "true" - enable_cross_namespace_secret: "false" - enable_database_access: "true" - enable_ebs_gp3_migration: "false" - enable_ebs_gp3_migration_max_size: "1000" - enable_finalizers: "false" - enable_init_containers: "true" - enable_lazy_spilo_upgrade: "false" - enable_master_load_balancer: "false" - enable_master_pooler_load_balancer: "false" - enable_owner_references: "false" - enable_password_rotation: "false" - enable_patroni_failsafe_mode: "false" - enable_persistent_volume_claim_deletion: "true" - enable_pgversion_env_var: "true" - enable_pod_antiaffinity: "false" - enable_pod_disruption_budget: "true" - enable_postgres_team_crd: "false" - enable_postgres_team_crd_superusers: "false" - enable_readiness_probe: "false" - enable_replica_load_balancer: "false" - enable_replica_pooler_load_balancer: "false" - enable_secrets_deletion: "true" - enable_shm_volume: "true" - enable_sidecars: "true" - enable_spilo_wal_path_compat: "true" - enable_team_id_clustername_prefix: "false" - enable_team_member_deprecation: "false" - enable_team_superuser: "false" - enable_teams_api: "false" - etcd_host: "" - external_traffic_policy: Cluster - kubernetes_use_configmaps: "false" - logical_backup_cronjob_environment_secret: "" - logical_backup_docker_image: ghcr.io/zalando/postgres-operator/logical-backup:v1.15.1 - logical_backup_job_prefix: logical-backup- - logical_backup_provider: s3 - logical_backup_s3_access_key_id: "" - logical_backup_s3_bucket: my-bucket-url - logical_backup_s3_bucket_prefix: spilo - logical_backup_s3_endpoint: "" - logical_backup_s3_region: "" - logical_backup_s3_retention_time: "" - logical_backup_s3_secret_access_key: "" - logical_backup_s3_sse: AES256 - logical_backup_schedule: 30 00 * * * - major_version_upgrade_mode: manual - master_dns_name_format: '{cluster}.{namespace}.{hostedzone}' - master_legacy_dns_name_format: '{cluster}.{team}.{hostedzone}' - master_pod_move_timeout: 20m - max_instances: "-1" - min_cpu_limit: 250m - min_instances: "-1" - min_memory_limit: 250Mi - minimal_major_version: "13" - oauth_token_secret_name: postgresql-operator - pam_configuration: https://info.example.com/oauth2/tokeninfo?access_token= uid - realm=/employees - pam_role_name: zalandos - password_rotation_interval: "90" - password_rotation_user_retention: "180" - patroni_api_check_interval: 1s - patroni_api_check_timeout: 5s - pdb_master_label_selector: "true" - pdb_name_format: postgres-{cluster}-pdb - persistent_volume_claim_retention_policy: when_deleted:retain,when_scaled:retain - pod_antiaffinity_preferred_during_scheduling: "false" - pod_antiaffinity_topology_key: kubernetes.io/hostname - pod_deletion_wait_timeout: 10m - pod_label_wait_timeout: 10m - pod_management_policy: ordered_ready - pod_role_label: spilo-role - pod_service_account_definition: "" - pod_service_account_name: postgres-pod - pod_service_account_role_binding_definition: "" - pod_terminate_grace_period: 5m - postgres_superuser_teams: postgres_superusers - protected_role_names: admin,cron_admin - ready_wait_interval: 3s - ready_wait_timeout: 30s - repair_period: 5m - replica_dns_name_format: '{cluster}-repl.{namespace}.{hostedzone}' - replica_legacy_dns_name_format: '{cluster}-repl.{team}.{hostedzone}' - replication_username: standby - resource_check_interval: 3s - resource_check_timeout: 10m - resync_period: 30m - ring_log_lines: "100" - role_deletion_suffix: _deleted - secret_name_template: '{username}.{cluster}.credentials.{tprkind}.{tprgroup}' - set_memory_request_to_limit: "false" - share_pgsocket_with_sidecars: "false" - spilo_allow_privilege_escalation: "true" - spilo_privileged: "false" - storage_resize_mode: pvc - super_username: postgres - target_major_version: "17" - team_admin_role: admin - team_api_role_configuration: log_statement:all - teams_api_url: http://fake-teams-api.default.svc.cluster.local - watched_namespace: '*' - workers: "8" - kind: ConfigMap - metadata: - annotations: - kubectl.kubernetes.io/last-applied-configuration: | - {"apiVersion":"v1","data":{"api_port":"8080","aws_region":"eu-central-1","cluster_domain":"cluster.local","cluster_history_entries":"1000","cluster_labels":"application:spilo","cluster_name_label":"cluster-name","connection_pooler_default_cpu_limit":"1","connection_pooler_default_cpu_request":"500m","connection_pooler_default_memory_limit":"100Mi","connection_pooler_default_memory_request":"100Mi","connection_pooler_image":"registry.opensource.zalan.do/acid/pgbouncer:master-32","connection_pooler_max_db_connections":"60","connection_pooler_mode":"transaction","connection_pooler_number_of_instances":"2","connection_pooler_schema":"pooler","connection_pooler_user":"pooler","crd_categories":"all","db_hosted_zone":"db.example.com","debug_logging":"true","default_cpu_limit":"1","default_cpu_request":"100m","default_memory_limit":"500Mi","default_memory_request":"100Mi","docker_image":"ghcr.io/zalando/spilo-17:4.0-p3","enable_admin_role_for_users":"true","enable_crd_registration":"true","enable_crd_validation":"true","enable_cross_namespace_secret":"false","enable_database_access":"true","enable_ebs_gp3_migration":"false","enable_ebs_gp3_migration_max_size":"1000","enable_finalizers":"false","enable_init_containers":"true","enable_lazy_spilo_upgrade":"false","enable_master_load_balancer":"false","enable_master_pooler_load_balancer":"false","enable_owner_references":"false","enable_password_rotation":"false","enable_patroni_failsafe_mode":"false","enable_persistent_volume_claim_deletion":"true","enable_pgversion_env_var":"true","enable_pod_antiaffinity":"false","enable_pod_disruption_budget":"true","enable_postgres_team_crd":"false","enable_postgres_team_crd_superusers":"false","enable_readiness_probe":"false","enable_replica_load_balancer":"false","enable_replica_pooler_load_balancer":"false","enable_secrets_deletion":"true","enable_shm_volume":"true","enable_sidecars":"true","enable_spilo_wal_path_compat":"true","enable_team_id_clustername_prefix":"false","enable_team_member_deprecation":"false","enable_team_superuser":"false","enable_teams_api":"false","etcd_host":"","external_traffic_policy":"Cluster","kubernetes_use_configmaps":"false","logical_backup_cronjob_environment_secret":"","logical_backup_docker_image":"ghcr.io/zalando/postgres-operator/logical-backup:v1.15.1","logical_backup_job_prefix":"logical-backup-","logical_backup_provider":"s3","logical_backup_s3_access_key_id":"","logical_backup_s3_bucket":"my-bucket-url","logical_backup_s3_bucket_prefix":"spilo","logical_backup_s3_endpoint":"","logical_backup_s3_region":"","logical_backup_s3_retention_time":"","logical_backup_s3_secret_access_key":"","logical_backup_s3_sse":"AES256","logical_backup_schedule":"30 00 * * *","major_version_upgrade_mode":"manual","master_dns_name_format":"{cluster}.{namespace}.{hostedzone}","master_legacy_dns_name_format":"{cluster}.{team}.{hostedzone}","master_pod_move_timeout":"20m","max_instances":"-1","min_cpu_limit":"250m","min_instances":"-1","min_memory_limit":"250Mi","minimal_major_version":"13","oauth_token_secret_name":"postgresql-operator","pam_configuration":"https://info.example.com/oauth2/tokeninfo?access_token= uid realm=/employees","pam_role_name":"zalandos","password_rotation_interval":"90","password_rotation_user_retention":"180","patroni_api_check_interval":"1s","patroni_api_check_timeout":"5s","pdb_master_label_selector":"true","pdb_name_format":"postgres-{cluster}-pdb","persistent_volume_claim_retention_policy":"when_deleted:retain,when_scaled:retain","pod_antiaffinity_preferred_during_scheduling":"false","pod_antiaffinity_topology_key":"kubernetes.io/hostname","pod_deletion_wait_timeout":"10m","pod_label_wait_timeout":"10m","pod_management_policy":"ordered_ready","pod_role_label":"spilo-role","pod_service_account_definition":"","pod_service_account_name":"postgres-pod","pod_service_account_role_binding_definition":"","pod_terminate_grace_period":"5m","postgres_superuser_teams":"postgres_superusers","protected_role_names":"admin,cron_admin","ready_wait_interval":"3s","ready_wait_timeout":"30s","repair_period":"5m","replica_dns_name_format":"{cluster}-repl.{namespace}.{hostedzone}","replica_legacy_dns_name_format":"{cluster}-repl.{team}.{hostedzone}","replication_username":"standby","resource_check_interval":"3s","resource_check_timeout":"10m","resync_period":"30m","ring_log_lines":"100","role_deletion_suffix":"_deleted","secret_name_template":"{username}.{cluster}.credentials.{tprkind}.{tprgroup}","set_memory_request_to_limit":"false","share_pgsocket_with_sidecars":"false","spilo_allow_privilege_escalation":"true","spilo_privileged":"false","storage_resize_mode":"pvc","super_username":"postgres","target_major_version":"17","team_admin_role":"admin","team_api_role_configuration":"log_statement:all","teams_api_url":"http://fake-teams-api.default.svc.cluster.local","watched_namespace":"*","workers":"8"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"postgres-operator","namespace":"default"}} - name: postgres-operator - namespace: default -- apiVersion: v1 - kind: ServiceAccount - metadata: - annotations: - kubectl.kubernetes.io/last-applied-configuration: | - {"apiVersion":"v1","kind":"ServiceAccount","metadata":{"annotations":{},"name":"postgres-operator","namespace":"default"}} - name: postgres-operator - namespace: default -- apiVersion: rbac.authorization.k8s.io/v1 - kind: ClusterRole - metadata: - annotations: - kubectl.kubernetes.io/last-applied-configuration: | - {"apiVersion":"rbac.authorization.k8s.io/v1","kind":"ClusterRole","metadata":{"annotations":{},"name":"postgres-operator"},"rules":[{"apiGroups":["acid.zalan.do"],"resources":["postgresqls","postgresqls/status","operatorconfigurations"],"verbs":["create","delete","deletecollection","get","list","patch","update","watch"]},{"apiGroups":["acid.zalan.do"],"resources":["postgresteams"],"verbs":["get","list","watch"]},{"apiGroups":["apiextensions.k8s.io"],"resources":["customresourcedefinitions"],"verbs":["create","get","patch","update"]},{"apiGroups":[""],"resources":["configmaps"],"verbs":["create","delete","deletecollection","get","list","patch","update","watch"]},{"apiGroups":[""],"resources":["events"],"verbs":["create","get","list","patch","update","watch"]},{"apiGroups":[""],"resources":["endpoints"],"verbs":["create","delete","deletecollection","get","list","patch","update","watch"]},{"apiGroups":[""],"resources":["secrets"],"verbs":["create","delete","get","update","patch"]},{"apiGroups":[""],"resources":["nodes"],"verbs":["get","list","watch"]},{"apiGroups":[""],"resources":["persistentvolumeclaims"],"verbs":["delete","get","list","patch","update"]},{"apiGroups":[""],"resources":["persistentvolumes"],"verbs":["get","list","update"]},{"apiGroups":[""],"resources":["pods"],"verbs":["delete","get","list","patch","update","watch"]},{"apiGroups":[""],"resources":["pods/exec"],"verbs":["create"]},{"apiGroups":[""],"resources":["services"],"verbs":["create","delete","get","patch","update"]},{"apiGroups":["apps"],"resources":["statefulsets","deployments"],"verbs":["create","delete","get","list","patch","update"]},{"apiGroups":["batch"],"resources":["cronjobs"],"verbs":["create","delete","get","list","patch","update"]},{"apiGroups":[""],"resources":["namespaces"],"verbs":["get"]},{"apiGroups":["policy"],"resources":["poddisruptionbudgets"],"verbs":["create","delete","get"]},{"apiGroups":[""],"resources":["serviceaccounts"],"verbs":["get","create"]},{"apiGroups":["rbac.authorization.k8s.io"],"resources":["rolebindings"],"verbs":["get","create"]}]} - name: postgres-operator - rules: - - apiGroups: - - acid.zalan.do - resources: - - postgresqls - - postgresqls/status - - operatorconfigurations - verbs: - - create - - delete - - deletecollection - - get - - list - - patch - - update - - watch - - apiGroups: - - acid.zalan.do - resources: - - postgresteams - verbs: - - get - - list - - watch - - apiGroups: - - apiextensions.k8s.io - resources: - - customresourcedefinitions - verbs: - - create - - get - - patch - - update - - apiGroups: - - "" - resources: - - configmaps - verbs: - - create - - delete - - deletecollection - - get - - list - - patch - - update - - watch - - apiGroups: - - "" - resources: - - events - verbs: - - create - - get - - list - - patch - - update - - watch - - apiGroups: - - "" - resources: - - endpoints - verbs: - - create - - delete - - deletecollection - - get - - list - - patch - - update - - watch - - apiGroups: - - "" - resources: - - secrets - verbs: - - create - - delete - - get - - update - - patch - - apiGroups: - - "" - resources: - - nodes - verbs: - - get - - list - - watch - - apiGroups: - - "" - resources: - - persistentvolumeclaims - verbs: - - delete - - get - - list - - patch - - update - - apiGroups: - - "" - resources: - - persistentvolumes - verbs: - - get - - list - - update - - apiGroups: - - "" - resources: - - pods - verbs: - - delete - - get - - list - - patch - - update - - watch - - apiGroups: - - "" - resources: - - pods/exec - verbs: - - create - - apiGroups: - - "" - resources: - - services - verbs: - - create - - delete - - get - - patch - - update - - apiGroups: - - apps - resources: - - statefulsets - - deployments - verbs: - - create - - delete - - get - - list - - patch - - update - - apiGroups: - - batch - resources: - - cronjobs - verbs: - - create - - delete - - get - - list - - patch - - update - - apiGroups: - - "" - resources: - - namespaces - verbs: - - get - - apiGroups: - - policy - resources: - - poddisruptionbudgets - verbs: - - create - - delete - - get - - apiGroups: - - "" - resources: - - serviceaccounts - verbs: - - get - - create - - apiGroups: - - rbac.authorization.k8s.io - resources: - - rolebindings - verbs: - - get - - create -- apiVersion: rbac.authorization.k8s.io/v1 - kind: ClusterRoleBinding - metadata: - annotations: - kubectl.kubernetes.io/last-applied-configuration: | - {"apiVersion":"rbac.authorization.k8s.io/v1","kind":"ClusterRoleBinding","metadata":{"annotations":{},"name":"postgres-operator"},"roleRef":{"apiGroup":"rbac.authorization.k8s.io","kind":"ClusterRole","name":"postgres-operator"},"subjects":[{"kind":"ServiceAccount","name":"postgres-operator","namespace":"default"}]} - name: postgres-operator - roleRef: - apiGroup: rbac.authorization.k8s.io + - apiVersion: v1 + kind: ConfigMap + metadata: + name: postgres-operator + data: + # additional_owner_roles: "cron_admin" + # additional_pod_capabilities: "SYS_NICE" + # additional_secret_mount: "some-secret-name" + # additional_secret_mount_path: "/some/dir" + api_port: "8080" + aws_region: eu-central-1 + cluster_domain: cluster.local + cluster_history_entries: "1000" + cluster_labels: application:spilo + cluster_name_label: cluster-name + connection_pooler_default_cpu_limit: "1" + connection_pooler_default_cpu_request: "500m" + connection_pooler_default_memory_limit: 100Mi + connection_pooler_default_memory_request: 100Mi + connection_pooler_image: "registry.opensource.zalan.do/acid/pgbouncer:master-32" + connection_pooler_max_db_connections: "60" + connection_pooler_mode: "transaction" + connection_pooler_number_of_instances: "2" + connection_pooler_schema: "pooler" + connection_pooler_user: "pooler" + crd_categories: "all" + # custom_service_annotations: "keyx:valuez,keya:valuea" + # custom_pod_annotations: "keya:valuea,keyb:valueb" + db_hosted_zone: db.example.com + debug_logging: "true" + default_cpu_limit: "1" + default_cpu_request: 100m + default_memory_limit: 500Mi + default_memory_request: 100Mi + # delete_annotation_date_key: delete-date + # delete_annotation_name_key: delete-clustername + docker_image: ghcr.io/zalando/spilo-17:4.0-p3 + # 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" + enable_ebs_gp3_migration: "false" + enable_ebs_gp3_migration_max_size: "1000" + enable_init_containers: "true" + enable_lazy_spilo_upgrade: "false" + enable_master_load_balancer: "false" + enable_master_pooler_load_balancer: "false" + enable_password_rotation: "false" + enable_patroni_failsafe_mode: "false" + enable_owner_references: "false" + enable_persistent_volume_claim_deletion: "true" + enable_pgversion_env_var: "true" + enable_pod_antiaffinity: "false" + enable_pod_disruption_budget: "true" + enable_postgres_team_crd: "false" + enable_postgres_team_crd_superusers: "false" + enable_readiness_probe: "false" + enable_replica_load_balancer: "false" + enable_replica_pooler_load_balancer: "false" + enable_secrets_deletion: "true" + enable_shm_volume: "true" + enable_sidecars: "true" + enable_spilo_wal_path_compat: "true" + enable_team_id_clustername_prefix: "false" + enable_team_member_deprecation: "false" + enable_team_superuser: "false" + enable_teams_api: "false" + etcd_host: "" + external_traffic_policy: "Cluster" + # gcp_credentials: "" + # ignored_annotations: "" + # infrastructure_roles_secret_name: "postgresql-infrastructure-roles" + # infrastructure_roles_secrets: "secretname:monitoring-roles,userkey:user,passwordkey:password,rolekey:inrole" + # ignore_instance_limits_annotation_key: "" + # inherited_annotations: owned-by + # inherited_labels: application,environment + # kube_iam_role: "" + kubernetes_use_configmaps: "false" + # log_s3_bucket: "" + # logical_backup_azure_storage_account_name: "" + # logical_backup_azure_storage_container: "" + # logical_backup_azure_storage_account_key: "" + # logical_backup_cpu_limit: "" + # logical_backup_cpu_request: "" + logical_backup_cronjob_environment_secret: "" + logical_backup_docker_image: "ghcr.io/zalando/postgres-operator/logical-backup:v1.15.1" + # logical_backup_google_application_credentials: "" + logical_backup_job_prefix: "logical-backup-" + # logical_backup_memory_limit: "" + # logical_backup_memory_request: "" + logical_backup_provider: "s3" + logical_backup_s3_access_key_id: "" + logical_backup_s3_bucket: "my-bucket-url" + logical_backup_s3_bucket_prefix: "spilo" + logical_backup_s3_region: "" + logical_backup_s3_endpoint: "" + logical_backup_s3_secret_access_key: "" + logical_backup_s3_sse: "AES256" + logical_backup_s3_retention_time: "" + logical_backup_schedule: "30 00 * * *" + major_version_upgrade_mode: "manual" + # major_version_upgrade_team_allow_list: "" + master_dns_name_format: "{cluster}.{namespace}.{hostedzone}" + master_legacy_dns_name_format: "{cluster}.{team}.{hostedzone}" + master_pod_move_timeout: 20m + # max_cpu_request: "1" + max_instances: "-1" + # max_memory_request: 4Gi + min_cpu_limit: 250m + min_instances: "-1" + min_memory_limit: 250Mi + minimal_major_version: "13" + # node_readiness_label: "status:ready" + # node_readiness_label_merge: "OR" + oauth_token_secret_name: postgresql-operator + pam_configuration: "https://info.example.com/oauth2/tokeninfo?access_token= uid realm=/employees" + pam_role_name: zalandos + patroni_api_check_interval: "1s" + patroni_api_check_timeout: "5s" + password_rotation_interval: "90" + password_rotation_user_retention: "180" + pdb_master_label_selector: "true" + pdb_name_format: "postgres-{cluster}-pdb" + persistent_volume_claim_retention_policy: "when_deleted:retain,when_scaled:retain" + pod_antiaffinity_preferred_during_scheduling: "false" + pod_antiaffinity_topology_key: "kubernetes.io/hostname" + pod_deletion_wait_timeout: 10m + # pod_environment_configmap: "default/my-custom-config" + # pod_environment_secret: "my-custom-secret" + pod_label_wait_timeout: 10m + pod_management_policy: "ordered_ready" + # pod_priority_class_name: "postgres-pod-priority" + pod_role_label: spilo-role + pod_service_account_definition: "" + pod_service_account_name: "postgres-pod" + pod_service_account_role_binding_definition: "" + pod_terminate_grace_period: 5m + postgres_superuser_teams: "postgres_superusers" + protected_role_names: "admin,cron_admin" + ready_wait_interval: 3s + ready_wait_timeout: 30s + repair_period: 5m + replica_dns_name_format: "{cluster}-repl.{namespace}.{hostedzone}" + replica_legacy_dns_name_format: "{cluster}-repl.{team}.{hostedzone}" + replication_username: standby + resource_check_interval: 3s + resource_check_timeout: 10m + resync_period: 30m + ring_log_lines: "100" + role_deletion_suffix: "_deleted" + secret_name_template: "{username}.{cluster}.credentials.{tprkind}.{tprgroup}" + share_pgsocket_with_sidecars: "false" + # sidecar_docker_images: "" + set_memory_request_to_limit: "false" + spilo_allow_privilege_escalation: "true" + # spilo_runasuser: 101 + # spilo_runasgroup: 103 + # spilo_fsgroup: 103 + spilo_privileged: "false" + storage_resize_mode: "pvc" + super_username: postgres + target_major_version: "17" + team_admin_role: "admin" + team_api_role_configuration: "log_statement:all" + teams_api_url: http://fake-teams-api.default.svc.cluster.local + # toleration: "key:db-only,operator:Exists,effect:NoSchedule" + # wal_az_storage_account: "" + # wal_gs_bucket: "" + # wal_s3_bucket: "" + watched_namespace: "*" # listen to all namespaces + workers: "8" + - apiVersion: v1 + kind: ServiceAccount + metadata: + name: postgres-operator + namespace: default + - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole - name: postgres-operator - subjects: - - kind: ServiceAccount - name: postgres-operator - namespace: default -- apiVersion: rbac.authorization.k8s.io/v1 - kind: ClusterRole - metadata: - annotations: - kubectl.kubernetes.io/last-applied-configuration: | - {"apiVersion":"rbac.authorization.k8s.io/v1","kind":"ClusterRole","metadata":{"annotations":{},"name":"postgres-pod"},"rules":[{"apiGroups":[""],"resources":["configmaps"],"verbs":["create","delete","deletecollection","get","list","patch","update","watch"]},{"apiGroups":[""],"resources":["endpoints"],"verbs":["create","delete","deletecollection","get","list","patch","update","watch"]},{"apiGroups":[""],"resources":["pods"],"verbs":["get","list","patch","update","watch"]},{"apiGroups":[""],"resources":["services"],"verbs":["create"]}]} - name: postgres-pod - rules: - - apiGroups: - - "" - resources: - - configmaps - verbs: - - create - - delete - - deletecollection - - get - - list - - patch - - update - - watch - - apiGroups: - - "" - resources: - - endpoints - verbs: - - create - - delete - - deletecollection - - get - - list - - patch - - update - - watch - - apiGroups: - - "" - resources: - - pods - verbs: - - get - - list - - patch - - update - - watch - - apiGroups: - - "" - resources: - - services - verbs: - - create -- apiVersion: apps/v1 - kind: Deployment - metadata: - annotations: - kubectl.kubernetes.io/last-applied-configuration: | - {"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"labels":{"application":"postgres-operator"},"name":"postgres-operator","namespace":"default"},"spec":{"replicas":1,"selector":{"matchLabels":{"name":"postgres-operator"}},"strategy":{"type":"Recreate"},"template":{"metadata":{"labels":{"name":"postgres-operator"}},"spec":{"containers":[{"env":[{"name":"CONFIG_MAP_NAME","value":"postgres-operator"}],"image":"ghcr.io/zalando/postgres-operator:v1.15.1","imagePullPolicy":"IfNotPresent","name":"postgres-operator","resources":{"limits":{"cpu":"500m","memory":"500Mi"},"requests":{"cpu":"100m","memory":"250Mi"}},"securityContext":{"allowPrivilegeEscalation":false,"readOnlyRootFilesystem":true,"runAsNonRoot":true,"runAsUser":1000}}],"serviceAccountName":"postgres-operator"}}}} - labels: - application: postgres-operator - name: postgres-operator - namespace: default - spec: - replicas: 1 - selector: - matchLabels: + metadata: + name: postgres-operator + rules: + # all verbs allowed for custom operator resources + - apiGroups: + - acid.zalan.do + resources: + - postgresqls + - postgresqls/status + - operatorconfigurations + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + # operator only reads PostgresTeams + - apiGroups: + - acid.zalan.do + resources: + - postgresteams + verbs: + - get + - list + - watch + # all verbs allowed for event streams (Zalando-internal feature) + # - apiGroups: + # - zalando.org + # resources: + # - fabriceventstreams + # verbs: + # - create + # - delete + # - deletecollection + # - get + # - list + # - patch + # - update + # - watch + # to create or get/update CRDs when starting up + - apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + verbs: + - create + - get + - patch + - update + # to read configuration from ConfigMaps and help Patroni manage the cluster if endpoints are not used + - apiGroups: + - "" + resources: + - configmaps + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + # to send events to the CRs + - apiGroups: + - "" + resources: + - events + verbs: + - create + - get + - list + - patch + - update + - watch + # to manage endpoints which are also used by Patroni (if it is using config maps) + - apiGroups: + - "" + resources: + - endpoints + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + # to CRUD secrets for database access + - apiGroups: + - "" + resources: + - secrets + verbs: + - create + - delete + - get + - update + - patch + # to check nodes for node readiness label + - apiGroups: + - "" + resources: + - nodes + verbs: + - get + - list + - watch + # to read or delete existing PVCs. Creation via StatefulSet + - apiGroups: + - "" + resources: + - persistentvolumeclaims + verbs: + - delete + - get + - list + - patch + - update + # to read existing PVs. Creation should be done via dynamic provisioning + - apiGroups: + - "" + resources: + - persistentvolumes + verbs: + - get + - list + - update # only for resizing AWS volumes + # to watch Spilo pods and do rolling updates. Creation via StatefulSet + - apiGroups: + - "" + resources: + - pods + verbs: + - delete + - get + - list + - patch + - update + - watch + # to resize the filesystem in Spilo pods when increasing volume size + - apiGroups: + - "" + resources: + - pods/exec + verbs: + - create + # to CRUD services to point to Postgres cluster instances + - apiGroups: + - "" + resources: + - services + verbs: + - create + - delete + - get + - patch + - update + # to CRUD the StatefulSet which controls the Postgres cluster instances + - apiGroups: + - apps + resources: + - statefulsets + - deployments + verbs: + - create + - delete + - get + - list + - patch + - update + # to CRUD cron jobs for logical backups + - apiGroups: + - batch + resources: + - cronjobs + verbs: + - create + - delete + - get + - list + - patch + - update + # to get namespaces operator resources can run in + - apiGroups: + - "" + resources: + - namespaces + verbs: + - get + # to define PDBs. Update happens via delete/create + - apiGroups: + - policy + resources: + - poddisruptionbudgets + verbs: + - create + - delete + - get + # to create ServiceAccounts in each namespace the operator watches + - apiGroups: + - "" + resources: + - serviceaccounts + verbs: + - get + - create + # to create role bindings to the postgres-pod service account + - apiGroups: + - rbac.authorization.k8s.io + resources: + - rolebindings + verbs: + - get + - create + # to grant privilege to run privileged pods (not needed by default) + #- apiGroups: + # - extensions + # resources: + # - podsecuritypolicies + # resourceNames: + # - privileged + # verbs: + # - use + - apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRoleBinding + metadata: + name: postgres-operator + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: postgres-operator + subjects: + - kind: ServiceAccount name: postgres-operator - strategy: - type: Recreate - template: - metadata: - labels: - name: postgres-operator - spec: - containers: - - env: - - name: CONFIG_MAP_NAME - value: postgres-operator - image: ghcr.io/zalando/postgres-operator:v1.15.1 - imagePullPolicy: IfNotPresent + namespace: default + - apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRole + metadata: + name: postgres-pod + rules: + # Patroni needs to watch and manage config maps (or endpoints) + - apiGroups: + - "" + resources: + - configmaps + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + # Patroni needs to watch and manage endpoints (or config maps) + - apiGroups: + - "" + resources: + - endpoints + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + # Patroni needs to watch pods + - apiGroups: + - "" + resources: + - pods + verbs: + - get + - list + - patch + - update + - watch + # to let Patroni create a headless service + - apiGroups: + - "" + resources: + - services + verbs: + - create + # to grant privilege to run privileged pods (not needed by default) + #- apiGroups: + # - extensions + # resources: + # - podsecuritypolicies + # resourceNames: + # - privileged + # verbs: + # - use + - apiVersion: apps/v1 + kind: Deployment + metadata: + name: postgres-operator + labels: + application: postgres-operator + spec: + replicas: 1 + strategy: + type: "Recreate" + selector: + matchLabels: name: postgres-operator - resources: - limits: - cpu: 500m - memory: 500Mi - requests: - cpu: 100m - memory: 250Mi - securityContext: - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - seccompProfile: - type: RuntimeDefault - capabilities: - drop: - - ALL - serviceAccountName: postgres-operator -- apiVersion: v1 - kind: Service - metadata: - annotations: - kubectl.kubernetes.io/last-applied-configuration: | - {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"postgres-operator","namespace":"default"},"spec":{"ports":[{"port":8080,"protocol":"TCP","targetPort":8080}],"selector":{"name":"postgres-operator"},"type":"ClusterIP"}} - name: postgres-operator - namespace: default - spec: - ports: - - port: 8080 - protocol: TCP - targetPort: 8080 - selector: + template: + metadata: + labels: + name: postgres-operator + spec: + serviceAccountName: postgres-operator + containers: + - name: postgres-operator + image: ghcr.io/zalando/postgres-operator:v1.15.1 + imagePullPolicy: IfNotPresent + resources: + requests: + cpu: 100m + memory: 250Mi + limits: + cpu: 500m + memory: 500Mi + securityContext: + runAsUser: 1000 + runAsNonRoot: true + readOnlyRootFilesystem: true + allowPrivilegeEscalation: false + env: + # provided additional ENV vars can overwrite individual config map entries + - name: CONFIG_MAP_NAME + value: "postgres-operator" + # In order to use the CRD OperatorConfiguration instead, uncomment these lines and comment out the two lines above + # - name: POSTGRES_OPERATOR_CONFIGURATION_OBJECT + # value: postgresql-operator-default-configuration + # Define an ID to isolate controllers from each other + # - name: CONTROLLER_ID + # value: "second-operator" + - apiVersion: v1 + kind: Service + metadata: name: postgres-operator - type: ClusterIP -kind: List -metadata: {} + spec: + type: ClusterIP + ports: + - port: 8080 + protocol: TCP + targetPort: 8080 + selector: + name: postgres-operator diff --git a/gen-operator-yaml.sh b/gen-operator-yaml.sh new file mode 100755 index 00000000..f3738d8d --- /dev/null +++ b/gen-operator-yaml.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +# generate merged yaml file with default operator manifests + +set -e -o pipefail + +tmp=$(mktemp -d) +operator_version="$1" +output="$2" + +cleanup() { + rm -rf "${tmp}" +} + +trap cleanup INT TERM EXIT + +if [ -z "$2" ]; then + >&2 echo "Usage: $0 " + exit 1 +fi + +POSTGRES_OPERATOR_URL="https://raw.githubusercontent.com/zalando/postgres-operator/${operator_version}/manifests" + +# output all required yaml files as concatenated manifests, and split +# each manifest into a separate file +( + for file in ${POSTGRES_OPERATOR_URL}/configmap.yaml \ + ${POSTGRES_OPERATOR_URL}/operator-service-account-rbac.yaml \ + ${POSTGRES_OPERATOR_URL}/postgres-operator.yaml \ + ${POSTGRES_OPERATOR_URL}/api-service.yaml; do + echo "---" + curl -s "$file" + done +) | yq --split-exp "\"${tmp}/\" + \$index + \".yaml\"" --no-doc + +# generate yq's load() statements from generated manifest files +load="" +for yaml in ${tmp}/*; do + if [ -n "$load" ]; then + load="$load, " + fi + + load="$load load(\"${yaml}\")" +done + +# generate a list with all generated manifests as items +printf "apiVersion: v1\nkind: List\nitems:\n" | \ + yq ".items += [ ${load} ]" > "$output" + From 3de2b9d5d0486799b91d1693109eee35bdad0e69 Mon Sep 17 00:00:00 2001 From: "T.v.Dein" Date: Mon, 24 Aug 2026 09:08:44 +0200 Subject: [PATCH 2/2] Update gen-operator-yaml.sh Co-authored-by: Benjamin Ritter <7373184+l0wl3vel@users.noreply.github.com> --- gen-operator-yaml.sh | 50 ++++++++++---------------------------------- 1 file changed, 11 insertions(+), 39 deletions(-) diff --git a/gen-operator-yaml.sh b/gen-operator-yaml.sh index f3738d8d..bbbd3211 100755 --- a/gen-operator-yaml.sh +++ b/gen-operator-yaml.sh @@ -1,49 +1,21 @@ #!/bin/bash -# generate merged yaml file with default operator manifests +# Generate a single corev1.List of the zalando operator's default manifests, +# as consumed by pkg/operatormanager. -set -e -o pipefail +set -euo pipefail -tmp=$(mktemp -d) -operator_version="$1" -output="$2" - -cleanup() { - rm -rf "${tmp}" -} - -trap cleanup INT TERM EXIT - -if [ -z "$2" ]; then +if [ -z "${2:-}" ]; then >&2 echo "Usage: $0 " exit 1 fi -POSTGRES_OPERATOR_URL="https://raw.githubusercontent.com/zalando/postgres-operator/${operator_version}/manifests" - -# output all required yaml files as concatenated manifests, and split -# each manifest into a separate file -( - for file in ${POSTGRES_OPERATOR_URL}/configmap.yaml \ - ${POSTGRES_OPERATOR_URL}/operator-service-account-rbac.yaml \ - ${POSTGRES_OPERATOR_URL}/postgres-operator.yaml \ - ${POSTGRES_OPERATOR_URL}/api-service.yaml; do - echo "---" - curl -s "$file" - done -) | yq --split-exp "\"${tmp}/\" + \$index + \".yaml\"" --no-doc - -# generate yq's load() statements from generated manifest files -load="" -for yaml in ${tmp}/*; do - if [ -n "$load" ]; then - load="$load, " - fi - - load="$load load(\"${yaml}\")" -done +url="https://raw.githubusercontent.com/zalando/postgres-operator/$1/manifests" -# generate a list with all generated manifests as items -printf "apiVersion: v1\nkind: List\nitems:\n" | \ - yq ".items += [ ${load} ]" > "$output" +yq eval-all '[.] | {"apiVersion": "v1", "kind": "List", "items": .}' \ + <(curl -sSf "${url}/configmap.yaml") \ + <(curl -sSf "${url}/operator-service-account-rbac.yaml") \ + <(curl -sSf "${url}/postgres-operator.yaml") \ + <(curl -sSf "${url}/api-service.yaml") \ + > "$2"