From 7230a00a8767b24df62baeaf922333360cdc27c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9F=D1=8B=D1=82=D0=BA=D0=BE=D0=B2=20=D0=A0=D0=BE=D0=BC?= =?UTF-8?q?=D0=B0=D0=BD?= Date: Thu, 14 Dec 2023 14:04:16 +0300 Subject: [PATCH] Pg, install scripts --- client/setup_pgsql_minimal.sh | 6 + client/setup_pgsql_operator.sh | 9 + client/setup_pgsql_operator_ui.sh | 6 + misc/start_pg_ui.sh | 2 + postgres-operator/manifests/api-service.yaml | 12 + .../manifests/complete-postgres-manifest.yaml | 232 ++++++ postgres-operator/manifests/configmap.yaml | 167 +++++ .../manifests/custom-team-membership.yaml | 13 + .../manifests/e2e-storage-class.yaml | 7 + .../manifests/fake-teams-api.yaml | 45 ++ .../infrastructure-roles-configmap.yaml | 11 + .../manifests/infrastructure-roles-new.yaml | 11 + .../manifests/infrastructure-roles.yaml | 24 + .../manifests/kustomization.yaml | 7 + .../minimal-fake-pooler-deployment.yaml | 35 + .../minimal-master-replica-svcmonitor.yaml | 131 ++++ .../minimal-postgres-manifest-12.yaml | 20 + .../manifests/minimal-postgres-manifest.yaml | 20 + ...erator-service-account-rbac-openshift.yaml | 283 +++++++ .../operator-service-account-rbac.yaml | 290 ++++++++ .../manifests/operatorconfiguration.crd.yaml | 690 ++++++++++++++++++ .../manifests/platform-credentials.yaml | 13 + .../manifests/postgres-operator.yaml | 45 ++ .../postgres-pod-priority-class.yaml | 11 + ...gresql-operator-default-configuration.yaml | 216 ++++++ .../manifests/postgresql.crd.yaml | 658 +++++++++++++++++ .../manifests/postgresteam.crd.yaml | 68 ++ .../manifests/standby-manifest.yaml | 16 + .../manifests/user-facing-clusterroles.yaml | 57 ++ .../ui/manifests/deployment.yaml | 105 +++ postgres-operator/ui/manifests/ingress.yaml | 20 + postgres-operator/ui/manifests/service.yaml | 15 + .../ui/manifests/ui-service-account-rbac.yaml | 66 ++ server/install.sh | 2 + 34 files changed, 3313 insertions(+) create mode 100755 client/setup_pgsql_minimal.sh create mode 100755 client/setup_pgsql_operator.sh create mode 100755 client/setup_pgsql_operator_ui.sh create mode 100755 misc/start_pg_ui.sh create mode 100644 postgres-operator/manifests/api-service.yaml create mode 100644 postgres-operator/manifests/complete-postgres-manifest.yaml create mode 100644 postgres-operator/manifests/configmap.yaml create mode 100644 postgres-operator/manifests/custom-team-membership.yaml create mode 100644 postgres-operator/manifests/e2e-storage-class.yaml create mode 100644 postgres-operator/manifests/fake-teams-api.yaml create mode 100644 postgres-operator/manifests/infrastructure-roles-configmap.yaml create mode 100644 postgres-operator/manifests/infrastructure-roles-new.yaml create mode 100644 postgres-operator/manifests/infrastructure-roles.yaml create mode 100644 postgres-operator/manifests/kustomization.yaml create mode 100644 postgres-operator/manifests/minimal-fake-pooler-deployment.yaml create mode 100644 postgres-operator/manifests/minimal-master-replica-svcmonitor.yaml create mode 100644 postgres-operator/manifests/minimal-postgres-manifest-12.yaml create mode 100644 postgres-operator/manifests/minimal-postgres-manifest.yaml create mode 100644 postgres-operator/manifests/operator-service-account-rbac-openshift.yaml create mode 100644 postgres-operator/manifests/operator-service-account-rbac.yaml create mode 100644 postgres-operator/manifests/operatorconfiguration.crd.yaml create mode 100644 postgres-operator/manifests/platform-credentials.yaml create mode 100644 postgres-operator/manifests/postgres-operator.yaml create mode 100644 postgres-operator/manifests/postgres-pod-priority-class.yaml create mode 100644 postgres-operator/manifests/postgresql-operator-default-configuration.yaml create mode 100644 postgres-operator/manifests/postgresql.crd.yaml create mode 100644 postgres-operator/manifests/postgresteam.crd.yaml create mode 100644 postgres-operator/manifests/standby-manifest.yaml create mode 100644 postgres-operator/manifests/user-facing-clusterroles.yaml create mode 100644 postgres-operator/ui/manifests/deployment.yaml create mode 100644 postgres-operator/ui/manifests/ingress.yaml create mode 100644 postgres-operator/ui/manifests/service.yaml create mode 100644 postgres-operator/ui/manifests/ui-service-account-rbac.yaml create mode 100644 server/install.sh diff --git a/client/setup_pgsql_minimal.sh b/client/setup_pgsql_minimal.sh new file mode 100755 index 0000000..9c9408d --- /dev/null +++ b/client/setup_pgsql_minimal.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +cd $(git rev-parse --show-toplevel)/postgres-operator + +kubectl apply -f manifests/minimal-postgres-manifest.yaml + diff --git a/client/setup_pgsql_operator.sh b/client/setup_pgsql_operator.sh new file mode 100755 index 0000000..85d03bf --- /dev/null +++ b/client/setup_pgsql_operator.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +cd $(git rev-parse --show-toplevel)/postgres-operator + +# apply the manifests in the following order +kubectl create -f manifests/configmap.yaml # configuration +kubectl create -f manifests/operator-service-account-rbac.yaml # identity and permissions +kubectl create -f manifests/postgres-operator.yaml # deployment +kubectl create -f manifests/api-service.yaml # operator API to be used by UI diff --git a/client/setup_pgsql_operator_ui.sh b/client/setup_pgsql_operator_ui.sh new file mode 100755 index 0000000..b28a7f4 --- /dev/null +++ b/client/setup_pgsql_operator_ui.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +cd $(git rev-parse --show-toplevel)/postgres-operator + +kubectl apply -f ui/manifests/ + diff --git a/misc/start_pg_ui.sh b/misc/start_pg_ui.sh new file mode 100755 index 0000000..5a4ef3f --- /dev/null +++ b/misc/start_pg_ui.sh @@ -0,0 +1,2 @@ +#!/bin/bash +kubectl port-forward svc/postgres-operator-ui 8081:80 diff --git a/postgres-operator/manifests/api-service.yaml b/postgres-operator/manifests/api-service.yaml new file mode 100644 index 0000000..6164481 --- /dev/null +++ b/postgres-operator/manifests/api-service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: postgres-operator +spec: + type: ClusterIP + ports: + - port: 8080 + protocol: TCP + targetPort: 8080 + selector: + name: postgres-operator diff --git a/postgres-operator/manifests/complete-postgres-manifest.yaml b/postgres-operator/manifests/complete-postgres-manifest.yaml new file mode 100644 index 0000000..aa94dab --- /dev/null +++ b/postgres-operator/manifests/complete-postgres-manifest.yaml @@ -0,0 +1,232 @@ +apiVersion: "acid.zalan.do/v1" +kind: postgresql +metadata: + name: acid-test-cluster +# labels: +# application: test-app +# environment: demo +# annotations: +# "acid.zalan.do/controller": "second-operator" +# "delete-date": "2020-08-31" # can only be deleted on that day if "delete-date "key is configured +# "delete-clustername": "acid-test-cluster" # can only be deleted when name matches if "delete-clustername" key is configured +spec: + dockerImage: ghcr.io/zalando/spilo-15:3.0-p1 + teamId: "acid" + numberOfInstances: 2 + users: # Application/Robot users + zalando: + - superuser + - createdb + foo_user: [] +# flyway: [] +# usersWithSecretRotation: +# - foo_user +# usersWithInPlaceSecretRotation: +# - flyway +# - bar_owner_user + enableMasterLoadBalancer: false + enableReplicaLoadBalancer: false + enableConnectionPooler: false # enable/disable connection pooler deployment + enableReplicaConnectionPooler: false # set to enable connectionPooler for replica service + enableMasterPoolerLoadBalancer: false + enableReplicaPoolerLoadBalancer: false + allowedSourceRanges: # load balancers' source ranges for both master and replica services + - 127.0.0.1/32 + databases: + foo: zalando + preparedDatabases: + bar: + defaultUsers: true + extensions: + pg_partman: public + pgcrypto: public + schemas: + data: {} + history: + defaultRoles: true + defaultUsers: false + postgresql: + version: "15" + parameters: # Expert section + shared_buffers: "32MB" + max_connections: "10" + log_statement: "all" +# env: +# - name: wal_s3_bucket +# value: my-custom-bucket + + volume: + size: 1Gi +# storageClass: my-sc +# iops: 1000 # for EBS gp3 +# throughput: 250 # in MB/s for EBS gp3 +# selector: +# matchExpressions: +# - { key: flavour, operator: In, values: [ "banana", "chocolate" ] } +# matchLabels: +# environment: dev +# service: postgres + additionalVolumes: + - name: empty + mountPath: /opt/empty + targetContainers: + - all + volumeSource: + emptyDir: {} +# - name: data +# mountPath: /home/postgres/pgdata/partitions +# targetContainers: +# - postgres +# volumeSource: +# PersistentVolumeClaim: +# claimName: pvc-postgresql-data-partitions +# readyOnly: false +# - name: conf +# mountPath: /etc/telegraf +# subPath: telegraf.conf +# targetContainers: +# - telegraf-sidecar +# volumeSource: +# configMap: +# name: my-config-map + + enableShmVolume: true +# spiloRunAsUser: 101 +# spiloRunAsGroup: 103 +# spiloFSGroup: 103 +# podAnnotations: +# annotation.key: value +# serviceAnnotations: +# annotation.key: value +# podPriorityClassName: "spilo-pod-priority" +# tolerations: +# - key: postgres +# operator: Exists +# effect: NoSchedule + resources: + requests: + cpu: 10m + memory: 100Mi + limits: + cpu: 500m + memory: 500Mi + patroni: + failsafe_mode: false + initdb: + encoding: "UTF8" + locale: "en_US.UTF-8" + data-checksums: "true" +# pg_hba: +# - hostssl all all 0.0.0.0/0 md5 +# - host all all 0.0.0.0/0 md5 +# slots: +# permanent_physical_1: +# type: physical +# permanent_logical_1: +# type: logical +# database: foo +# plugin: pgoutput + ttl: 30 + loop_wait: 10 + retry_timeout: 10 + synchronous_mode: false + synchronous_mode_strict: false + synchronous_node_count: 1 + maximum_lag_on_failover: 33554432 + +# restore a Postgres DB with point-in-time-recovery +# with a non-empty timestamp, clone from an S3 bucket using the latest backup before the timestamp +# with an empty/absent timestamp, clone from an existing alive cluster using pg_basebackup +# clone: +# uid: "efd12e58-5786-11e8-b5a7-06148230260c" +# cluster: "acid-minimal-cluster" +# timestamp: "2017-12-19T12:40:33+01:00" # timezone required (offset relative to UTC, see RFC 3339 section 5.6) +# s3_wal_path: "s3://custom/path/to/bucket" + +# run periodic backups with k8s cron jobs +# enableLogicalBackup: true +# logicalBackupSchedule: "30 00 * * *" + +# maintenanceWindows: +# - 01:00-06:00 #UTC +# - Sat:00:00-04:00 + +# overwrite custom properties for connection pooler deployments +# connectionPooler: +# numberOfInstances: 2 +# mode: "transaction" +# schema: "pooler" +# user: "pooler" +# maxDBConnections: 60 +# resources: +# requests: +# cpu: 300m +# memory: 100Mi +# limits: +# cpu: "1" +# memory: 100Mi + + initContainers: + - name: date + image: busybox + command: [ "/bin/date" ] +# sidecars: +# - name: "telegraf-sidecar" +# image: "telegraf:latest" +# ports: +# - name: metrics +# containerPort: 8094 +# protocol: TCP +# resources: +# limits: +# cpu: 500m +# memory: 500Mi +# requests: +# cpu: 100m +# memory: 100Mi +# env: +# - name: "USEFUL_VAR" +# value: "perhaps-true" + +# Custom TLS certificate. Disabled unless tls.secretName has a value. + tls: + secretName: "" # should correspond to a Kubernetes Secret resource to load + certificateFile: "tls.crt" + privateKeyFile: "tls.key" + caFile: "" # optionally configure Postgres with a CA certificate + caSecretName: "" # optionally the ca.crt can come from this secret instead. +# file names can be also defined with absolute path, and will no longer be relative +# to the "/tls/" path where the secret is being mounted by default, and "/tlsca/" +# where the caSecret is mounted by default. +# When TLS is enabled, also set spiloFSGroup parameter above to the relevant value. +# if unknown, set it to 103 which is the usual value in the default spilo images. +# In Openshift, there is no need to set spiloFSGroup/spilo_fsgroup. + +# Add node affinity support by allowing postgres pods to schedule only on nodes that +# have label: "postgres-operator:enabled" set. +# nodeAffinity: +# requiredDuringSchedulingIgnoredDuringExecution: +# nodeSelectorTerms: +# - matchExpressions: +# - key: postgres-operator +# operator: In +# values: +# - enabled + +# Enables change data capture streams for defined database tables +# streams: +# - applicationId: test-app +# database: foo +# tables: +# data.state_pending_outbox: +# eventType: test-app.status-pending +# data.state_approved_outbox: +# eventType: test-app.status-approved +# data.orders_outbox: +# eventType: test-app.order-completed +# idColumn: o_id +# payloadColumn: o_payload +# # Optional. Filter ignores events before a certain txnId and lsn. Can be used to skip bad events +# filter: +# data.orders_outbox: "[?(@.source.txId > 500 && @.source.lsn > 123456)]" +# batchSize: 1000 diff --git a/postgres-operator/manifests/configmap.yaml b/postgres-operator/manifests/configmap.yaml new file mode 100644 index 0000000..4b7c671 --- /dev/null +++ b/postgres-operator/manifests/configmap.yaml @@ -0,0 +1,167 @@ +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-27" + # 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-15:3.0-p1 + # downscaler_annotations: "deployment-time,downscaler/*" + # enable_admin_role_for_users: "true" + # enable_crd_registration: "true" + # enable_cross_namespace_secret: "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_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_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_docker_image: "registry.opensource.zalan.do/acid/logical-backup:v1.10.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_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_instances: "-1" + # min_instances: "-1" + # max_cpu_request: "1" + # max_memory_request: 4Gi + # min_cpu_limit: 250m + # min_memory_limit: 250Mi + # minimal_major_version: "11" + # 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_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: "15" + # 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: "4" diff --git a/postgres-operator/manifests/custom-team-membership.yaml b/postgres-operator/manifests/custom-team-membership.yaml new file mode 100644 index 0000000..9af1539 --- /dev/null +++ b/postgres-operator/manifests/custom-team-membership.yaml @@ -0,0 +1,13 @@ +apiVersion: "acid.zalan.do/v1" +kind: PostgresTeam +metadata: + name: custom-team-membership +spec: + additionalSuperuserTeams: + acid: + - "postgres_superusers" + additionalTeams: + acid: [] + additionalMembers: + acid: + - "elephant" diff --git a/postgres-operator/manifests/e2e-storage-class.yaml b/postgres-operator/manifests/e2e-storage-class.yaml new file mode 100644 index 0000000..3c70c40 --- /dev/null +++ b/postgres-operator/manifests/e2e-storage-class.yaml @@ -0,0 +1,7 @@ +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + name: standard + annotations: + storageclass.kubernetes.io/is-default-class: "true" +provisioner: kubernetes.io/host-path diff --git a/postgres-operator/manifests/fake-teams-api.yaml b/postgres-operator/manifests/fake-teams-api.yaml new file mode 100644 index 0000000..33f7c60 --- /dev/null +++ b/postgres-operator/manifests/fake-teams-api.yaml @@ -0,0 +1,45 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: fake-teams-api +spec: + replicas: 1 + selector: + matchLabels: + name: fake-teams-api + template: + metadata: + labels: + name: fake-teams-api + spec: + containers: + - name: fake-teams-api + image: ikitiki/fake-teams-api:latest + +--- + +apiVersion: v1 +kind: Service +metadata: + name: fake-teams-api +spec: + selector: + name: fake-teams-api + ports: + - name: server + port: 80 + protocol: TCP + targetPort: 80 + type: NodePort + +--- + +apiVersion: v1 +kind: Secret +metadata: + name: postgresql-operator + namespace: default +type: Opaque +data: + read-only-token-secret: dGVzdHRva2Vu + read-only-token-type: QmVhcmVy diff --git a/postgres-operator/manifests/infrastructure-roles-configmap.yaml b/postgres-operator/manifests/infrastructure-roles-configmap.yaml new file mode 100644 index 0000000..ffe98f6 --- /dev/null +++ b/postgres-operator/manifests/infrastructure-roles-configmap.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: postgresql-infrastructure-roles +data: + batman: | + inrole: [admin] # following roles will be assigned to the new user + user_flags: + - createdb + db_parameters: # db parameters, applyed for this particular user + log_statement: all diff --git a/postgres-operator/manifests/infrastructure-roles-new.yaml b/postgres-operator/manifests/infrastructure-roles-new.yaml new file mode 100644 index 0000000..d6b5fa9 --- /dev/null +++ b/postgres-operator/manifests/infrastructure-roles-new.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +data: + # infrastructure role definition in the new format + # robot_zmon_acid_monitoring_new + user: cm9ib3Rfem1vbl9hY2lkX21vbml0b3JpbmdfbmV3 + # foobar_new + password: Zm9vYmFyX25ldw== +kind: Secret +metadata: + name: postgresql-infrastructure-roles-new +type: Opaque diff --git a/postgres-operator/manifests/infrastructure-roles.yaml b/postgres-operator/manifests/infrastructure-roles.yaml new file mode 100644 index 0000000..975a028 --- /dev/null +++ b/postgres-operator/manifests/infrastructure-roles.yaml @@ -0,0 +1,24 @@ +apiVersion: v1 +data: + # required format (w/o quotes): 'propertyNumber: value' + # allowed properties: 'user', 'password', 'inrole' + # numbers >= 1 are mandatory + # alternatively, supply the user: password pairs and + # provide other options in the configmap. + # robot_zmon_acid_monitoring + user1: cm9ib3Rfem1vbl9hY2lkX21vbml0b3Jpbmc= + # foobar + password1: Zm9vYmFy + # robot_zmon + inrole1: cm9ib3Rfem1vbg== + # testuser + user2: dGVzdHVzZXI= + # testpassword + password2: dGVzdHBhc3N3b3Jk + # user batman with the password justice + # look for other fields in the infrastructure roles configmap + batman: anVzdGljZQ== +kind: Secret +metadata: + name: postgresql-infrastructure-roles +type: Opaque diff --git a/postgres-operator/manifests/kustomization.yaml b/postgres-operator/manifests/kustomization.yaml new file mode 100644 index 0000000..32d81d9 --- /dev/null +++ b/postgres-operator/manifests/kustomization.yaml @@ -0,0 +1,7 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: +- configmap.yaml +- operator-service-account-rbac.yaml +- postgres-operator.yaml +- api-service.yaml diff --git a/postgres-operator/manifests/minimal-fake-pooler-deployment.yaml b/postgres-operator/manifests/minimal-fake-pooler-deployment.yaml new file mode 100644 index 0000000..53332ba --- /dev/null +++ b/postgres-operator/manifests/minimal-fake-pooler-deployment.yaml @@ -0,0 +1,35 @@ +# will not run but is good enough for tests to fail +apiVersion: apps/v1 +kind: Deployment +metadata: + name: acid-minimal-cluster-pooler + labels: + application: db-connection-pooler + connection-pooler: acid-minimal-cluster-pooler +spec: + replicas: 1 + selector: + matchLabels: + application: db-connection-pooler + connection-pooler: acid-minimal-cluster-pooler + cluster-name: acid-minimal-cluster + template: + metadata: + labels: + application: db-connection-pooler + connection-pooler: acid-minimal-cluster-pooler + cluster-name: acid-minimal-cluster + spec: + serviceAccountName: postgres-operator + containers: + - name: postgres-operator + image: registry.opensource.zalan.do/acid/pgbouncer:master-27 + imagePullPolicy: IfNotPresent + resources: + requests: + cpu: 100m + memory: 250Mi + limits: + cpu: 500m + memory: 500Mi + env: [] diff --git a/postgres-operator/manifests/minimal-master-replica-svcmonitor.yaml b/postgres-operator/manifests/minimal-master-replica-svcmonitor.yaml new file mode 100644 index 0000000..67ed28c --- /dev/null +++ b/postgres-operator/manifests/minimal-master-replica-svcmonitor.yaml @@ -0,0 +1,131 @@ +# Here we use https://github.com/prometheus-community/helm-charts/charts/kube-prometheus-stack +# Please keep the ServiceMonitor's label same as the Helm release name of kube-prometheus-stack + +apiVersion: v1 +kind: Namespace +metadata: + name: test-pg +--- +apiVersion: "acid.zalan.do/v1" +kind: postgresql +metadata: + name: acid-minimal-cluster + namespace: test-pg + labels: + app: test-pg +spec: + teamId: "acid" + volume: + size: 1Gi + numberOfInstances: 2 + users: + zalando: # database owner + - superuser + - createdb + foo_user: [] # role for application foo + databases: + foo: zalando # dbname: owner + preparedDatabases: + bar: {} + postgresql: + version: "13" + sidecars: + - name: "exporter" + image: "wrouesnel/postgres_exporter" + ports: + - name: exporter + containerPort: 9187 + protocol: TCP + resources: + limits: + cpu: 500m + memory: 256M + requests: + cpu: 100m + memory: 200M +--- +apiVersion: v1 +kind: Service +metadata: + name: acid-minimal-cluster-svc-metrics-master + namespace: test-pg + labels: + app: test-pg + spilo-role: master + annotations: + prometheus.io/scrape: "true" + prometheus.io/port: "9187" +spec: + type: ClusterIP + ports: + - name: exporter + port: 9187 + targetPort: exporter + selector: + application: spilo + cluster-name: acid-minimal-cluster + spilo-role: master +--- +apiVersion: v1 +kind: Service +metadata: + name: acid-minimal-cluster-svc-metrics-replica + namespace: test-pg + labels: + app: test-pg + spilo-role: replica + annotations: + prometheus.io/scrape: "true" + prometheus.io/port: "9187" +spec: + type: ClusterIP + ports: + - name: exporter + port: 9187 + targetPort: exporter + selector: + application: spilo + cluster-name: acid-minimal-cluster + spilo-role: replica +--- +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: acid-minimal-cluster-svcm-master + namespace: test-pg + labels: + app: test-pg + spilo-role: master +spec: + endpoints: + - port: exporter + interval: 15s + scrapeTimeout: 10s + namespaceSelector: + matchNames: + - test-pg + selector: + matchLabels: + app: test-pg + spilo-role: master +--- +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: acid-minimal-cluster-svcm-replica + namespace: test-pg + labels: + app: test-pg + spilo-role: replica +spec: + endpoints: + - port: exporter + interval: 15s + scrapeTimeout: 10s + namespaceSelector: + matchNames: + - test-pg + selector: + matchLabels: + app: test-pg + spilo-role: replica diff --git a/postgres-operator/manifests/minimal-postgres-manifest-12.yaml b/postgres-operator/manifests/minimal-postgres-manifest-12.yaml new file mode 100644 index 0000000..d578ac4 --- /dev/null +++ b/postgres-operator/manifests/minimal-postgres-manifest-12.yaml @@ -0,0 +1,20 @@ +apiVersion: "acid.zalan.do/v1" +kind: postgresql +metadata: + name: acid-upgrade-test +spec: + teamId: "acid" + volume: + size: 1Gi + numberOfInstances: 2 + users: + zalando: # database owner + - superuser + - createdb + foo_user: [] # role for application foo + databases: + foo: zalando # dbname: owner + preparedDatabases: + bar: {} + postgresql: + version: "12" diff --git a/postgres-operator/manifests/minimal-postgres-manifest.yaml b/postgres-operator/manifests/minimal-postgres-manifest.yaml new file mode 100644 index 0000000..00f11eb --- /dev/null +++ b/postgres-operator/manifests/minimal-postgres-manifest.yaml @@ -0,0 +1,20 @@ +apiVersion: "acid.zalan.do/v1" +kind: postgresql +metadata: + name: acid-minimal-cluster +spec: + teamId: "acid" + volume: + size: 1Gi + numberOfInstances: 2 + users: + zalando: # database owner + - superuser + - createdb + foo_user: [] # role for application foo + databases: + foo: zalando # dbname: owner + preparedDatabases: + bar: {} + postgresql: + version: "15" diff --git a/postgres-operator/manifests/operator-service-account-rbac-openshift.yaml b/postgres-operator/manifests/operator-service-account-rbac-openshift.yaml new file mode 100644 index 0000000..e0e45cc --- /dev/null +++ b/postgres-operator/manifests/operator-service-account-rbac-openshift.yaml @@ -0,0 +1,283 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: postgres-operator + namespace: default + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +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 and manage ConfigMaps used by Patroni +- 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 CRUD secrets for database access +- apiGroups: + - "" + resources: + - secrets + verbs: + - create + - delete + - get + - update +# 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 +# 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 + namespace: default + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: postgres-pod +rules: +# Patroni needs to watch and manage config maps +- apiGroups: + - "" + resources: + - configmaps + 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 diff --git a/postgres-operator/manifests/operator-service-account-rbac.yaml b/postgres-operator/manifests/operator-service-account-rbac.yaml new file mode 100644 index 0000000..c10dc5f --- /dev/null +++ b/postgres-operator/manifests/operator-service-account-rbac.yaml @@ -0,0 +1,290 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: postgres-operator + namespace: default + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +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 +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get +# 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 +- 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 +# 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 +# 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 + namespace: default + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: postgres-pod +rules: +# Patroni needs to watch and manage endpoints +- 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 diff --git a/postgres-operator/manifests/operatorconfiguration.crd.yaml b/postgres-operator/manifests/operatorconfiguration.crd.yaml new file mode 100644 index 0000000..e3eff4f --- /dev/null +++ b/postgres-operator/manifests/operatorconfiguration.crd.yaml @@ -0,0 +1,690 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: operatorconfigurations.acid.zalan.do +spec: + group: acid.zalan.do + names: + kind: OperatorConfiguration + listKind: OperatorConfigurationList + plural: operatorconfigurations + singular: operatorconfiguration + shortNames: + - opconfig + categories: + - all + scope: Namespaced + versions: + - name: v1 + served: true + storage: true + subresources: + status: {} + additionalPrinterColumns: + - 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 + schema: + openAPIV3Schema: + type: object + required: + - kind + - apiVersion + - configuration + properties: + kind: + type: string + enum: + - OperatorConfiguration + apiVersion: + type: string + enum: + - acid.zalan.do/v1 + configuration: + type: object + properties: + crd_categories: + type: array + nullable: true + items: + type: string + docker_image: + type: string + default: "ghcr.io/zalando/spilo-15:3.0-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_pgversion_env_var: + type: boolean + default: true + enable_shm_volume: + type: boolean + default: true + enable_spilo_wal_path_compat: + type: boolean + default: false + enable_team_id_clustername_prefix: + type: boolean + default: false + etcd_host: + type: string + default: "" + ignore_instance_limits_annotation_key: + type: string + kubernetes_use_configmaps: + type: boolean + default: false + 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: "off" + major_version_upgrade_team_allow_list: + type: array + items: + type: string + minimal_major_version: + type: string + default: "11" + target_major_version: + type: string + default: "15" + kubernetes: + type: object + properties: + additional_pod_capabilities: + type: array + items: + type: string + cluster_domain: + 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 + additionalProperties: + type: string + delete_annotation_date_key: + type: string + delete_annotation_name_key: + type: string + downscaler_annotations: + type: array + items: + type: string + enable_cross_namespace_secret: + type: boolean + default: false + enable_init_containers: + type: boolean + default: true + enable_pod_antiaffinity: + type: boolean + default: false + enable_pod_disruption_budget: + type: boolean + default: true + enable_readiness_probe: + type: boolean + default: false + enable_sidecars: + type: boolean + default: true + ignored_annotations: + type: array + items: + type: string + infrastructure_roles_secret_name: + type: string + infrastructure_roles_secrets: + type: array + nullable: true + items: + type: object + required: + - secretname + - userkey + - passwordkey + properties: + 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: + type: string + inherited_labels: + type: array + items: + type: string + master_pod_move_timeout: + type: string + default: "20m" + 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_name_format: + type: string + default: "postgres-{cluster}-pdb" + persistent_volume_claim_retention_policy: + type: object + properties: + when_deleted: + type: string + enum: + - "delete" + - "retain" + when_scaled: + type: string + enum: + - "delete" + - "retain" + pod_antiaffinity_preferred_during_scheduling: + type: boolean + default: false + pod_antiaffinity_topology_key: + type: string + default: "kubernetes.io/hostname" + pod_environment_configmap: + type: string + pod_environment_secret: + type: string + pod_management_policy: + type: string + enum: + - "ordered_ready" + - "parallel" + default: "ordered_ready" + pod_priority_class_name: + type: string + pod_role_label: + type: string + default: "spilo-role" + pod_service_account_definition: + type: string + default: "" + pod_service_account_name: + type: string + default: "postgres-pod" + pod_service_account_role_binding_definition: + type: string + default: "" + pod_terminate_grace_period: + type: string + default: "5m" + secret_name_template: + 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 + spilo_fsgroup: + type: integer + spilo_privileged: + type: boolean + default: false + storage_resize_mode: + type: string + enum: + - "ebs" + - "mixed" + - "pvc" + - "off" + default: "pvc" + toleration: + type: object + additionalProperties: + type: string + watched_namespace: + type: string + postgres_pod_resources: + type: object + properties: + default_cpu_limit: + type: string + pattern: '^(\d+m|\d+(\.\d{1,3})?)$' + default: "1" + default_cpu_request: + type: string + pattern: '^(\d+m|\d+(\.\d{1,3})?)$' + default: "100m" + default_memory_limit: + type: string + pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' + default: "500Mi" + default_memory_request: + type: string + pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' + default: "100Mi" + 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})?)$' + default: "250m" + min_memory_limit: + type: string + pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' + default: "250Mi" + 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" + load_balancer: + type: object + properties: + custom_service_annotations: + type: object + additionalProperties: + type: string + 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 + 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 + default: "/meta/credentials" + aws_region: + type: string + default: "eu-central-1" + enable_ebs_gp3_migration: + type: boolean + default: false + enable_ebs_gp3_migration_max_size: + type: integer + 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: object + properties: + 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 + default: "registry.opensource.zalan.do/acid/logical-backup:v1.10.1" + logical_backup_google_application_credentials: + type: string + logical_backup_job_prefix: + type: string + default: "logical-backup-" + 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: + - "az" + - "gcs" + - "s3" + default: "s3" + logical_backup_s3_access_key_id: + type: string + logical_backup_s3_bucket: + 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}$' + default: "30 00 * * *" + debug: + type: object + 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: + 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 + items: + type: string + protected_role_names: + type: array + items: + type: string + default: + - admin + - cron_admin + role_deletion_suffix: + type: string + default: "_deleted" + team_admin_role: + 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 + properties: + api_port: + type: integer + default: 8080 + cluster_history_entries: + type: integer + default: 1000 + ring_log_lines: + type: integer + default: 100 + scalyr: # deprecated + type: object + properties: + scalyr_api_key: + type: string + scalyr_cpu_limit: + type: string + pattern: '^(\d+m|\d+(\.\d{1,3})?)$' + default: "1" + scalyr_cpu_request: + type: string + pattern: '^(\d+m|\d+(\.\d{1,3})?)$' + default: "100m" + scalyr_image: + type: string + scalyr_memory_limit: + type: string + pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' + default: "500Mi" + scalyr_memory_request: + type: string + pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' + default: "50Mi" + scalyr_server_url: + 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: "registry.opensource.zalan.do/acid/pgbouncer:master-27" + 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})?)$' + default: "1" + connection_pooler_default_cpu_request: + type: string + pattern: '^(\d+m|\d+(\.\d{1,3})?)$' + default: "500m" + connection_pooler_default_memory_limit: + type: string + pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' + default: "100Mi" + connection_pooler_default_memory_request: + type: string + pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' + default: "100Mi" + patroni: + type: object + properties: + enable_patroni_failsafe_mode: + type: boolean + default: false + status: + type: object + additionalProperties: + type: string diff --git a/postgres-operator/manifests/platform-credentials.yaml b/postgres-operator/manifests/platform-credentials.yaml new file mode 100644 index 0000000..44ecf7f --- /dev/null +++ b/postgres-operator/manifests/platform-credentials.yaml @@ -0,0 +1,13 @@ +apiVersion: "zalando.org/v1" +kind: PlatformCredentialsSet +metadata: + name: postgresql-operator +spec: + application: postgresql-operator + tokens: + read-only: + privileges: + cluster-registry-rw: + privileges: + cluster-rw: + privileges: diff --git a/postgres-operator/manifests/postgres-operator.yaml b/postgres-operator/manifests/postgres-operator.yaml new file mode 100644 index 0000000..0ea7e32 --- /dev/null +++ b/postgres-operator/manifests/postgres-operator.yaml @@ -0,0 +1,45 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: postgres-operator + labels: + application: postgres-operator +spec: + replicas: 1 + strategy: + type: "Recreate" + selector: + matchLabels: + name: postgres-operator + template: + metadata: + labels: + name: postgres-operator + spec: + serviceAccountName: postgres-operator + containers: + - name: postgres-operator + image: registry.opensource.zalan.do/acid/postgres-operator:v1.10.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" diff --git a/postgres-operator/manifests/postgres-pod-priority-class.yaml b/postgres-operator/manifests/postgres-pod-priority-class.yaml new file mode 100644 index 0000000..f1b565f --- /dev/null +++ b/postgres-operator/manifests/postgres-pod-priority-class.yaml @@ -0,0 +1,11 @@ +apiVersion: scheduling.k8s.io/v1 +description: 'This priority class must be used only for databases controlled by the + Postgres operator' +kind: PriorityClass +metadata: + labels: + application: postgres-operator + name: postgres-pod-priority +preemptionPolicy: PreemptLowerPriority +globalDefault: false +value: 1000000 diff --git a/postgres-operator/manifests/postgresql-operator-default-configuration.yaml b/postgres-operator/manifests/postgresql-operator-default-configuration.yaml new file mode 100644 index 0000000..3a43a87 --- /dev/null +++ b/postgres-operator/manifests/postgresql-operator-default-configuration.yaml @@ -0,0 +1,216 @@ +apiVersion: "acid.zalan.do/v1" +kind: OperatorConfiguration +metadata: + name: postgresql-operator-default-configuration +configuration: + docker_image: ghcr.io/zalando/spilo-15:3.0-p1 + # enable_crd_registration: true + # crd_categories: + # - all + # enable_lazy_spilo_upgrade: false + enable_pgversion_env_var: true + # enable_shm_volume: true + enable_spilo_wal_path_compat: false + enable_team_id_clustername_prefix: false + etcd_host: "" + # ignore_instance_limits_annotation_key: "" + # kubernetes_use_configmaps: false + max_instances: -1 + min_instances: -1 + resync_period: 30m + repair_period: 5m + # set_memory_request_to_limit: false + # sidecars: + # - image: image:123 + # name: global-sidecar-1 + # ports: + # - containerPort: 80 + # protocol: TCP + workers: 8 + users: + # additional_owner_roles: + # - cron_admin + enable_password_rotation: false + password_rotation_interval: 90 + password_rotation_user_retention: 180 + replication_username: standby + super_username: postgres + major_version_upgrade: + major_version_upgrade_mode: "off" + # major_version_upgrade_team_allow_list: + # - acid + minimal_major_version: "11" + target_major_version: "15" + kubernetes: + # additional_pod_capabilities: + # - "SYS_NICE" + cluster_domain: cluster.local + cluster_labels: + application: spilo + cluster_name_label: cluster-name + # custom_pod_annotations: + # keya: valuea + # keyb: valueb + # delete_annotation_date_key: delete-date + # delete_annotation_name_key: delete-clustername + # downscaler_annotations: + # - deployment-time + # - downscaler/* + # enable_cross_namespace_secret: "false" + enable_init_containers: true + enable_pod_antiaffinity: false + enable_pod_disruption_budget: true + enable_readiness_probe: false + enable_sidecars: true + # ignored_annotations: + # - k8s.v1.cni.cncf.io/network-status + # infrastructure_roles_secret_name: "postgresql-infrastructure-roles" + # infrastructure_roles_secrets: + # - secretname: "monitoring-roles" + # userkey: "user" + # passwordkey: "password" + # rolekey: "inrole" + # - secretname: "other-infrastructure-role" + # userkey: "other-user-key" + # passwordkey: "other-password-key" + # inherited_annotations: + # - owned-by + # inherited_labels: + # - application + # - environment + master_pod_move_timeout: 20m + # node_readiness_label: + # status: ready + # node_readiness_label_merge: "OR" + oauth_token_secret_name: postgresql-operator + 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_environment_configmap: "default/my-custom-config" + # pod_environment_secret: "my-custom-secret" + 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 + secret_name_template: "{username}.{cluster}.credentials.{tprkind}.{tprgroup}" + share_pgsocket_with_sidecars: false + spilo_allow_privilege_escalation: true + # spilo_runasuser: 101 + # spilo_runasgroup: 103 + # spilo_fsgroup: 103 + spilo_privileged: false + storage_resize_mode: pvc + # toleration: + # key: db-only + # operator: Exists + # effect: NoSchedule + # watched_namespace: "" + postgres_pod_resources: + default_cpu_limit: "1" + default_cpu_request: 100m + default_memory_limit: 500Mi + default_memory_request: 100Mi + # max_cpu_request: "1" + # max_memory_request: 4Gi + # min_cpu_limit: 250m + # min_memory_limit: 250Mi + timeouts: + patroni_api_check_interval: 1s + patroni_api_check_timeout: 5s + pod_label_wait_timeout: 10m + pod_deletion_wait_timeout: 10m + ready_wait_interval: 4s + ready_wait_timeout: 30s + resource_check_interval: 3s + resource_check_timeout: 10m + load_balancer: + # custom_service_annotations: + # keyx: valuex + # keyy: valuey + # db_hosted_zone: "" + enable_master_load_balancer: false + enable_master_pooler_load_balancer: false + enable_replica_load_balancer: false + enable_replica_pooler_load_balancer: false + external_traffic_policy: "Cluster" + master_dns_name_format: "{cluster}.{namespace}.{hostedzone}" + # master_legacy_dns_name_format: "{cluster}.{team}.{hostedzone}" + replica_dns_name_format: "{cluster}-repl.{namespace}.{hostedzone}" + # replica_dns_old_name_format: "{cluster}-repl.{team}.{hostedzone}" + aws_or_gcp: + # additional_secret_mount: "some-secret-name" + # additional_secret_mount_path: "/some/dir" + aws_region: eu-central-1 + enable_ebs_gp3_migration: false + # enable_ebs_gp3_migration_max_size: 1000 + # gcp_credentials: "" + # kube_iam_role: "" + # log_s3_bucket: "" + # wal_az_storage_account: "" + # wal_gs_bucket: "" + # wal_s3_bucket: "" + logical_backup: + # 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_memory_limit: "" + # logical_backup_memory_request: "" + logical_backup_docker_image: "registry.opensource.zalan.do/acid/logical-backup:v1.10.1" + # logical_backup_google_application_credentials: "" + 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_endpoint: "" + # logical_backup_s3_region: "" + # logical_backup_s3_secret_access_key: "" + logical_backup_s3_sse: "AES256" + # logical_backup_s3_retention_time: "" + logical_backup_schedule: "30 00 * * *" + debug: + debug_logging: true + enable_database_access: true + teams_api: + # enable_admin_role_for_users: true + # enable_postgres_team_crd: false + # enable_postgres_team_crd_superusers: false + enable_team_member_deprecation: false + enable_team_superuser: false + enable_teams_api: false + # pam_configuration: "" + pam_role_name: zalandos + # postgres_superuser_teams: + # - postgres_superusers + protected_role_names: + - admin + - cron_admin + role_deletion_suffix: "_deleted" + team_admin_role: admin + team_api_role_configuration: + log_statement: all + # teams_api_url: "" + logging_rest_api: + api_port: 8080 + cluster_history_entries: 1000 + ring_log_lines: 100 + connection_pooler: + 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-27" + # connection_pooler_max_db_connections: 60 + connection_pooler_mode: "transaction" + connection_pooler_number_of_instances: 2 + # connection_pooler_schema: "pooler" + # connection_pooler_user: "pooler" + patroni: + enable_patroni_failsafe_mode: false diff --git a/postgres-operator/manifests/postgresql.crd.yaml b/postgres-operator/manifests/postgresql.crd.yaml new file mode 100644 index 0000000..5f5b6ff --- /dev/null +++ b/postgres-operator/manifests/postgresql.crd.yaml @@ -0,0 +1,658 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: postgresqls.acid.zalan.do +spec: + group: acid.zalan.do + names: + kind: postgresql + listKind: postgresqlList + plural: postgresqls + singular: postgresql + shortNames: + - pg + categories: + - all + scope: Namespaced + versions: + - name: v1 + served: true + storage: true + subresources: + status: {} + additionalPrinterColumns: + - name: Team + type: string + description: Team responsible for Postgres cluster + jsonPath: .spec.teamId + - name: Version + type: string + description: PostgreSQL version + jsonPath: .spec.postgresql.version + - name: Pods + type: integer + description: Number of Pods per Postgres cluster + jsonPath: .spec.numberOfInstances + - name: Volume + type: string + description: Size of the bound volume + jsonPath: .spec.volume.size + - name: CPU-Request + type: string + description: Requested CPU for Postgres containers + jsonPath: .spec.resources.requests.cpu + - name: Memory-Request + type: string + description: Requested memory for Postgres containers + jsonPath: .spec.resources.requests.memory + - name: Age + type: date + jsonPath: .metadata.creationTimestamp + - name: Status + type: string + description: Current sync status of postgresql resource + jsonPath: .status.PostgresClusterStatus + schema: + openAPIV3Schema: + type: object + required: + - kind + - apiVersion + - spec + properties: + kind: + type: string + enum: + - postgresql + apiVersion: + type: string + enum: + - acid.zalan.do/v1 + spec: + type: object + required: + - numberOfInstances + - teamId + - postgresql + - volume + properties: + additionalVolumes: + type: array + items: + type: object + required: + - name + - mountPath + - volumeSource + properties: + name: + type: string + mountPath: + type: string + targetContainers: + type: array + nullable: true + items: + type: string + volumeSource: + type: object + x-kubernetes-preserve-unknown-fields: true + subPath: + type: string + allowedSourceRanges: + type: array + nullable: true + items: + type: string + pattern: '^(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\/(\d|[1-2]\d|3[0-2])$' + clone: + type: object + required: + - cluster + properties: + cluster: + type: string + s3_endpoint: + type: string + s3_access_key_id: + type: string + s3_secret_access_key: + type: string + s3_force_path_style: + type: boolean + s3_wal_path: + type: string + timestamp: + type: string + pattern: '^([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])[Tt]([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(([+-]([01][0-9]|2[0-3]):[0-5][0-9]))$' + # The regexp matches the date-time format (RFC 3339 Section 5.6) that specifies a timezone as an offset relative to UTC + # Example: 1996-12-19T16:39:57-08:00 + # Note: this field requires a timezone + uid: + format: uuid + type: string + connectionPooler: + type: object + properties: + dockerImage: + type: string + maxDBConnections: + type: integer + mode: + type: string + enum: + - "session" + - "transaction" + numberOfInstances: + type: integer + minimum: 1 + resources: + type: object + properties: + limits: + type: object + properties: + cpu: + type: string + pattern: '^(\d+m|\d+(\.\d{1,3})?)$' + memory: + type: string + pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' + requests: + type: object + properties: + cpu: + type: string + pattern: '^(\d+m|\d+(\.\d{1,3})?)$' + memory: + type: string + pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' + schema: + type: string + user: + type: string + databases: + type: object + additionalProperties: + type: string + # Note: usernames specified here as database owners must be declared in the users key of the spec key. + dockerImage: + type: string + enableConnectionPooler: + type: boolean + enableReplicaConnectionPooler: + type: boolean + enableLogicalBackup: + type: boolean + enableMasterLoadBalancer: + type: boolean + enableMasterPoolerLoadBalancer: + type: boolean + enableReplicaLoadBalancer: + type: boolean + enableReplicaPoolerLoadBalancer: + type: boolean + enableShmVolume: + type: boolean + env: + type: array + nullable: true + items: + type: object + x-kubernetes-preserve-unknown-fields: true + init_containers: + type: array + description: deprecated + nullable: true + items: + type: object + x-kubernetes-preserve-unknown-fields: true + initContainers: + type: array + nullable: true + items: + type: object + x-kubernetes-preserve-unknown-fields: true + logicalBackupSchedule: + type: string + pattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$' + maintenanceWindows: + type: array + items: + 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))\ *$' + masterServiceAnnotations: + type: object + additionalProperties: + type: string + nodeAffinity: + type: object + properties: + preferredDuringSchedulingIgnoredDuringExecution: + type: array + items: + type: object + required: + - preference + - weight + properties: + preference: + type: object + properties: + matchExpressions: + type: array + items: + type: object + required: + - key + - operator + properties: + key: + type: string + operator: + type: string + values: + type: array + items: + type: string + matchFields: + type: array + items: + type: object + required: + - key + - operator + properties: + key: + type: string + operator: + type: string + values: + type: array + items: + type: string + weight: + format: int32 + type: integer + requiredDuringSchedulingIgnoredDuringExecution: + type: object + required: + - nodeSelectorTerms + properties: + nodeSelectorTerms: + type: array + items: + type: object + properties: + matchExpressions: + type: array + items: + type: object + required: + - key + - operator + properties: + key: + type: string + operator: + type: string + values: + type: array + items: + type: string + matchFields: + type: array + items: + type: object + required: + - key + - operator + properties: + key: + type: string + operator: + type: string + values: + type: array + items: + type: string + numberOfInstances: + type: integer + minimum: 0 + patroni: + type: object + properties: + failsafe_mode: + type: boolean + initdb: + type: object + additionalProperties: + type: string + loop_wait: + type: integer + maximum_lag_on_failover: + type: integer + pg_hba: + type: array + items: + type: string + retry_timeout: + type: integer + slots: + type: object + additionalProperties: + type: object + additionalProperties: + type: string + synchronous_mode: + type: boolean + synchronous_mode_strict: + type: boolean + synchronous_node_count: + type: integer + ttl: + type: integer + podAnnotations: + type: object + additionalProperties: + type: string + pod_priority_class_name: + type: string + description: deprecated + podPriorityClassName: + type: string + postgresql: + type: object + required: + - version + properties: + version: + type: string + enum: + - "10" + - "11" + - "12" + - "13" + - "14" + - "15" + parameters: + type: object + additionalProperties: + type: string + preparedDatabases: + type: object + additionalProperties: + type: object + properties: + defaultUsers: + type: boolean + extensions: + type: object + additionalProperties: + type: string + schemas: + type: object + additionalProperties: + type: object + properties: + defaultUsers: + type: boolean + defaultRoles: + type: boolean + secretNamespace: + type: string + replicaLoadBalancer: + type: boolean + description: deprecated + replicaServiceAnnotations: + type: object + additionalProperties: + type: string + resources: + type: object + properties: + limits: + type: object + properties: + cpu: + type: string + # Decimal natural followed by m, or decimal natural followed by + # dot followed by up to three decimal digits. + # + # This is because the Kubernetes CPU resource has millis as the + # maximum precision. The actual values are checked in code + # because the regular expression would be huge and horrible and + # not very helpful in validation error messages; this one checks + # only the format of the given number. + # + # https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu + pattern: '^(\d+m|\d+(\.\d{1,3})?)$' + # Note: the value specified here must not be zero or be lower + # than the corresponding request. + memory: + type: string + # You can express memory as a plain integer or as a fixed-point + # integer using one of these suffixes: E, P, T, G, M, k. You can + # also use the power-of-two equivalents: Ei, Pi, Ti, Gi, Mi, Ki + # + # https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-memory + pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' + # Note: the value specified here must not be zero or be higher + # than the corresponding limit. + requests: + type: object + properties: + cpu: + type: string + pattern: '^(\d+m|\d+(\.\d{1,3})?)$' + memory: + type: string + pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' + schedulerName: + type: string + serviceAnnotations: + type: object + additionalProperties: + type: string + sidecars: + type: array + nullable: true + items: + type: object + x-kubernetes-preserve-unknown-fields: true + spiloRunAsUser: + type: integer + spiloRunAsGroup: + type: integer + spiloFSGroup: + type: integer + standby: + type: object + properties: + s3_wal_path: + type: string + gs_wal_path: + type: string + standby_host: + type: string + standby_port: + type: string + oneOf: + - required: + - s3_wal_path + - required: + - gs_wal_path + - required: + - standby_host + streams: + type: array + items: + type: object + required: + - applicationId + - database + - tables + properties: + applicationId: + type: string + batchSize: + type: integer + database: + type: string + enableRecovery: + type: boolean + filter: + type: object + additionalProperties: + type: string + tables: + type: object + additionalProperties: + type: object + required: + - eventType + properties: + eventType: + type: string + idColumn: + type: string + payloadColumn: + type: string + recoveryEventType: + type: string + teamId: + type: string + tls: + type: object + required: + - secretName + properties: + secretName: + type: string + certificateFile: + type: string + privateKeyFile: + type: string + caFile: + type: string + caSecretName: + type: string + tolerations: + type: array + items: + type: object + properties: + key: + type: string + operator: + type: string + enum: + - Equal + - Exists + value: + type: string + effect: + type: string + enum: + - NoExecute + - NoSchedule + - PreferNoSchedule + tolerationSeconds: + type: integer + useLoadBalancer: + type: boolean + description: deprecated + users: + type: object + additionalProperties: + type: array + nullable: true + items: + type: string + enum: + - bypassrls + - BYPASSRLS + - nobypassrls + - NOBYPASSRLS + - createdb + - CREATEDB + - nocreatedb + - NOCREATEDB + - createrole + - CREATEROLE + - nocreaterole + - NOCREATEROLE + - inherit + - INHERIT + - noinherit + - NOINHERIT + - login + - LOGIN + - nologin + - NOLOGIN + - replication + - REPLICATION + - noreplication + - NOREPLICATION + - superuser + - SUPERUSER + - nosuperuser + - NOSUPERUSER + usersWithInPlaceSecretRotation: + type: array + nullable: true + items: + type: string + usersWithSecretRotation: + type: array + nullable: true + items: + type: string + volume: + type: object + required: + - size + properties: + iops: + type: integer + selector: + type: object + properties: + matchExpressions: + type: array + items: + type: object + required: + - key + - operator + properties: + key: + type: string + operator: + type: string + enum: + - DoesNotExist + - Exists + - In + - NotIn + values: + type: array + items: + type: string + matchLabels: + type: object + x-kubernetes-preserve-unknown-fields: true + size: + type: string + pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' + # Note: the value specified here must not be zero. + storageClass: + type: string + subPath: + type: string + throughput: + type: integer + status: + type: object + additionalProperties: + type: string diff --git a/postgres-operator/manifests/postgresteam.crd.yaml b/postgres-operator/manifests/postgresteam.crd.yaml new file mode 100644 index 0000000..2588e53 --- /dev/null +++ b/postgres-operator/manifests/postgresteam.crd.yaml @@ -0,0 +1,68 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: postgresteams.acid.zalan.do +spec: + group: acid.zalan.do + names: + kind: PostgresTeam + listKind: PostgresTeamList + plural: postgresteams + singular: postgresteam + shortNames: + - pgteam + categories: + - all + scope: Namespaced + versions: + - name: v1 + served: true + storage: true + subresources: + status: {} + schema: + openAPIV3Schema: + type: object + required: + - kind + - apiVersion + - spec + properties: + kind: + type: string + enum: + - PostgresTeam + apiVersion: + type: string + enum: + - acid.zalan.do/v1 + spec: + type: object + properties: + additionalSuperuserTeams: + type: object + description: "Map for teamId and associated additional superuser teams" + additionalProperties: + type: array + nullable: true + description: "List of teams to become Postgres superusers" + items: + type: string + additionalTeams: + type: object + description: "Map for teamId and associated additional teams" + additionalProperties: + type: array + nullable: true + description: "List of teams whose members will also be added to the Postgres cluster" + items: + type: string + additionalMembers: + type: object + description: "Map for teamId and associated additional users" + additionalProperties: + type: array + nullable: true + description: "List of users who will also be added to the Postgres cluster" + items: + type: string diff --git a/postgres-operator/manifests/standby-manifest.yaml b/postgres-operator/manifests/standby-manifest.yaml new file mode 100644 index 0000000..2db4d48 --- /dev/null +++ b/postgres-operator/manifests/standby-manifest.yaml @@ -0,0 +1,16 @@ +apiVersion: "acid.zalan.do/v1" +kind: postgresql +metadata: + name: acid-standby-cluster +spec: + teamId: "acid" + volume: + size: 1Gi + numberOfInstances: 1 + postgresql: + version: "15" + # Make this a standby cluster and provide either the s3 bucket path of source cluster or the remote primary host for continuous streaming. + standby: + # s3_wal_path: "s3://mybucket/spilo/acid-minimal-cluster/abcd1234-2a4b-4b2a-8c9c-c1234defg567/wal/14/" + standby_host: "acid-minimal-cluster.default" + # standby_port: "5432" diff --git a/postgres-operator/manifests/user-facing-clusterroles.yaml b/postgres-operator/manifests/user-facing-clusterroles.yaml new file mode 100644 index 0000000..64392a1 --- /dev/null +++ b/postgres-operator/manifests/user-facing-clusterroles.yaml @@ -0,0 +1,57 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + rbac.authorization.k8s.io/aggregate-to-admin: "true" + name: zalando-postgres-operator:users:admin +rules: +- apiGroups: + - acid.zalan.do + resources: + - postgresqls + - postgresqls/status + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + rbac.authorization.k8s.io/aggregate-to-edit: "true" + name: zalando-postgres-operator:users:edit +rules: +- apiGroups: + - acid.zalan.do + resources: + - postgresqls + verbs: + - create + - update + - patch + - delete + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + rbac.authorization.k8s.io/aggregate-to-view: "true" + name: zalando-postgres-operator:users:view +rules: +- apiGroups: + - acid.zalan.do + resources: + - postgresqls + - postgresqls/status + verbs: + - get + - list + - watch diff --git a/postgres-operator/ui/manifests/deployment.yaml b/postgres-operator/ui/manifests/deployment.yaml new file mode 100644 index 0000000..f6f4523 --- /dev/null +++ b/postgres-operator/ui/manifests/deployment.yaml @@ -0,0 +1,105 @@ +apiVersion: "apps/v1" +kind: "Deployment" +metadata: + name: "postgres-operator-ui" + namespace: "default" + labels: + name: "postgres-operator-ui" +spec: + replicas: 1 + selector: + matchLabels: + name: "postgres-operator-ui" + template: + metadata: + labels: + name: "postgres-operator-ui" + spec: + serviceAccountName: postgres-operator-ui + containers: + - name: "service" + image: registry.opensource.zalan.do/acid/postgres-operator-ui:v1.10.1 + ports: + - containerPort: 8081 + protocol: "TCP" + readinessProbe: + httpGet: + path: "/health" + port: 8081 + initialDelaySeconds: 5 + timeoutSeconds: 1 + resources: + limits: + cpu: "200m" + memory: "200Mi" + requests: + cpu: "100m" + memory: "100Mi" + env: + - name: "APP_URL" + value: "http://localhost:8081" + - name: "OPERATOR_API_URL" + value: "http://postgres-operator:8080" + - name: "OPERATOR_CLUSTER_NAME_LABEL" + value: "cluster-name" + - name: "RESOURCES_VISIBLE" + value: "False" + - name: "TARGET_NAMESPACE" + value: "default" + - name: "TEAMS" + value: |- + [ + "acid" + ] + - name: "OPERATOR_UI_CONFIG" + value: |- + { + "docs_link":"https://postgres-operator.readthedocs.io/en/latest/", + "dns_format_string": "{0}.{1}", + "databases_visible": true, + "master_load_balancer_visible": true, + "nat_gateways_visible": false, + "replica_load_balancer_visible": true, + "resources_visible": true, + "users_visible": true, + "cost_ebs": 0.0952, + "cost_iops": 0.006, + "cost_throughput": 0.0476, + "cost_core": 0.0575, + "cost_memory": 0.014375, + "free_iops": 3000, + "free_throughput": 125, + "limit_iops": 16000, + "limit_throughput": 1000, + "postgresql_versions": [ + "15", + "14", + "13", + "12", + "11" + ] + } + # Exemple of settings to make snapshot view working in the ui when using AWS + # - name: WALE_S3_ENDPOINT + # value: https+path://s3.us-east-1.amazonaws.com:443 + # - name: SPILO_S3_BACKUP_PREFIX + # value: spilo/ + # - name: AWS_ACCESS_KEY_ID + # valueFrom: + # secretKeyRef: + # name: + # key: AWS_ACCESS_KEY_ID + # - name: AWS_SECRET_ACCESS_KEY + # valueFrom: + # secretKeyRef: + # name: + # key: AWS_SECRET_ACCESS_KEY + # - name: AWS_DEFAULT_REGION + # valueFrom: + # secretKeyRef: + # name: + # key: AWS_DEFAULT_REGION + # - name: SPILO_S3_BACKUP_BUCKET + # value: + # - name: "USE_AWS_INSTANCE_PROFILE" + # value: "true" diff --git a/postgres-operator/ui/manifests/ingress.yaml b/postgres-operator/ui/manifests/ingress.yaml new file mode 100644 index 0000000..3d721b9 --- /dev/null +++ b/postgres-operator/ui/manifests/ingress.yaml @@ -0,0 +1,20 @@ +apiVersion: "networking.k8s.io/v1" +kind: "Ingress" +metadata: + name: "postgres-operator-ui" + namespace: "default" + labels: + application: "postgres-operator-ui" +spec: + # ingressClassName: "ingress-nginx" + rules: + - host: "ui.example.org" + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: "postgres-operator-ui" + port: + number: 80 diff --git a/postgres-operator/ui/manifests/service.yaml b/postgres-operator/ui/manifests/service.yaml new file mode 100644 index 0000000..7b820ca --- /dev/null +++ b/postgres-operator/ui/manifests/service.yaml @@ -0,0 +1,15 @@ +apiVersion: "v1" +kind: "Service" +metadata: + name: "postgres-operator-ui" + namespace: "default" + labels: + application: "postgres-operator-ui" +spec: + type: "ClusterIP" + selector: + name: "postgres-operator-ui" + ports: + - port: 80 + protocol: "TCP" + targetPort: 8081 diff --git a/postgres-operator/ui/manifests/ui-service-account-rbac.yaml b/postgres-operator/ui/manifests/ui-service-account-rbac.yaml new file mode 100644 index 0000000..d4937b5 --- /dev/null +++ b/postgres-operator/ui/manifests/ui-service-account-rbac.yaml @@ -0,0 +1,66 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: postgres-operator-ui + namespace: default + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: postgres-operator-ui +rules: +- apiGroups: + - acid.zalan.do + resources: + - postgresqls + verbs: + - create + - delete + - get + - list + - patch + - update +- apiGroups: + - "" + resources: + - pods + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - services + verbs: + - get + - list +- apiGroups: + - apps + resources: + - deployments + - statefulsets + verbs: + - get + - list +- apiGroups: + - "" + resources: + - namespaces + verbs: + - get + - list +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: postgres-operator-ui +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: postgres-operator-ui +subjects: +- kind: ServiceAccount + name: postgres-operator-ui + namespace: default diff --git a/server/install.sh b/server/install.sh new file mode 100644 index 0000000..74c6439 --- /dev/null +++ b/server/install.sh @@ -0,0 +1,2 @@ +#!/bin/bash +curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="server --disable=traefik" sh -s -