From ad320488b065a3c89dee2873df2f8bed3f6be88f Mon Sep 17 00:00:00 2001 From: Soltvedt Date: Tue, 28 Jun 2022 09:02:34 +0000 Subject: [PATCH 01/77] Fixes a typo in the CRD yaml files. (#1941) Replaces 'DoesNotExists' with 'DoesNotExist'. Fixes #1868. Co-authored-by: Stian Soltvedt --- charts/postgres-operator/crds/postgresqls.yaml | 2 +- manifests/postgresql.crd.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/postgres-operator/crds/postgresqls.yaml b/charts/postgres-operator/crds/postgresqls.yaml index b8d3dcfdf..d2ad89da6 100644 --- a/charts/postgres-operator/crds/postgresqls.yaml +++ b/charts/postgres-operator/crds/postgresqls.yaml @@ -620,7 +620,7 @@ spec: operator: type: string enum: - - DoesNotExists + - DoesNotExist - Exists - In - NotIn diff --git a/manifests/postgresql.crd.yaml b/manifests/postgresql.crd.yaml index 427b9533f..b113c849f 100644 --- a/manifests/postgresql.crd.yaml +++ b/manifests/postgresql.crd.yaml @@ -618,7 +618,7 @@ spec: operator: type: string enum: - - DoesNotExists + - DoesNotExist - Exists - In - NotIn From 5e4badd99ce0adf9e6d436979d36f8addd56cb5f Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Thu, 30 Jun 2022 10:40:03 +0200 Subject: [PATCH 02/77] annotation to bypass globally configured instance limits (#1943) --- .../crds/operatorconfigurations.yaml | 2 + charts/postgres-operator/values.yaml | 9 +- docs/reference/operator_parameters.md | 6 + manifests/configmap.yaml | 1 + manifests/operatorconfiguration.crd.yaml | 2 + ...gresql-operator-default-configuration.yaml | 1 + pkg/apis/acid.zalan.do/v1/crds.go | 3 + .../v1/operator_configuration_type.go | 6 +- pkg/cluster/k8sres.go | 8 ++ pkg/cluster/k8sres_test.go | 130 ++++++++++++++++++ pkg/controller/operator_config.go | 1 + pkg/util/config/config.go | 6 +- 12 files changed, 169 insertions(+), 6 deletions(-) diff --git a/charts/postgres-operator/crds/operatorconfigurations.yaml b/charts/postgres-operator/crds/operatorconfigurations.yaml index c5b9a4c17..6b7c46307 100644 --- a/charts/postgres-operator/crds/operatorconfigurations.yaml +++ b/charts/postgres-operator/crds/operatorconfigurations.yaml @@ -91,6 +91,8 @@ spec: etcd_host: type: string default: "" + ignore_instance_limits_annotation_key: + type: string kubernetes_use_configmaps: type: boolean default: false diff --git a/charts/postgres-operator/values.yaml b/charts/postgres-operator/values.yaml index 2650824b5..86cbb2986 100644 --- a/charts/postgres-operator/values.yaml +++ b/charts/postgres-operator/values.yaml @@ -35,10 +35,15 @@ configGeneral: enable_spilo_wal_path_compat: false # etcd connection string for Patroni. Empty uses K8s-native DCS. etcd_host: "" - # Select if setup uses endpoints (default), or configmaps to manage leader (DCS=k8s) - # kubernetes_use_configmaps: false # Spilo docker image docker_image: registry.opensource.zalan.do/acid/spilo-14:2.1-p6 + + # key name for annotation to ignore globally configured instance limits + # ignore_instance_limits_annotation_key: "" + + # Select if setup uses endpoints (default), or configmaps to manage leader (DCS=k8s) + # kubernetes_use_configmaps: false + # min number of instances in Postgres cluster. -1 = no limit min_instances: -1 # max number of instances in Postgres cluster. -1 = no limit diff --git a/docs/reference/operator_parameters.md b/docs/reference/operator_parameters.md index b31b753f7..8aa76aa7b 100644 --- a/docs/reference/operator_parameters.md +++ b/docs/reference/operator_parameters.md @@ -147,6 +147,12 @@ Those are top-level keys, containing both leaf keys and groups. When `-1` is specified for `min_instances`, no limits are applied. The default is `-1`. +* **ignore_instance_limits_annotation_key** + for some clusters it might be required to scale beyond the limits that can be + configured with `min_instances` and `max_instances` options. You can define + an annotation key that can be used as a toggle in cluster manifests to ignore + globally configured instance limits. The default is empty. + * **resync_period** period between consecutive sync requests. The default is `30m`. diff --git a/manifests/configmap.yaml b/manifests/configmap.yaml index e43f92050..93e278047 100644 --- a/manifests/configmap.yaml +++ b/manifests/configmap.yaml @@ -66,6 +66,7 @@ data: # 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: "" diff --git a/manifests/operatorconfiguration.crd.yaml b/manifests/operatorconfiguration.crd.yaml index 091aa4eb3..aae9e502b 100644 --- a/manifests/operatorconfiguration.crd.yaml +++ b/manifests/operatorconfiguration.crd.yaml @@ -89,6 +89,8 @@ spec: etcd_host: type: string default: "" + ignore_instance_limits_annotation_key: + type: string kubernetes_use_configmaps: type: boolean default: false diff --git a/manifests/postgresql-operator-default-configuration.yaml b/manifests/postgresql-operator-default-configuration.yaml index 9ea313cfa..b18855fcf 100644 --- a/manifests/postgresql-operator-default-configuration.yaml +++ b/manifests/postgresql-operator-default-configuration.yaml @@ -12,6 +12,7 @@ configuration: # enable_shm_volume: true enable_spilo_wal_path_compat: false etcd_host: "" + # ignore_instance_limits_annotation_key: "" # kubernetes_use_configmaps: false max_instances: -1 min_instances: -1 diff --git a/pkg/apis/acid.zalan.do/v1/crds.go b/pkg/apis/acid.zalan.do/v1/crds.go index 5a2525007..e672e473d 100644 --- a/pkg/apis/acid.zalan.do/v1/crds.go +++ b/pkg/apis/acid.zalan.do/v1/crds.go @@ -1115,6 +1115,9 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{ "etcd_host": { Type: "string", }, + "ignore_instance_limits_annotation_key": { + Type: "string", + }, "kubernetes_use_configmaps": { Type: "boolean", }, diff --git a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go index 0be275553..2211b9ed3 100644 --- a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go +++ b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go @@ -236,8 +236,6 @@ type OperatorConfigurationData struct { KubernetesUseConfigMaps bool `json:"kubernetes_use_configmaps,omitempty"` DockerImage string `json:"docker_image,omitempty"` Workers uint32 `json:"workers,omitempty"` - MinInstances int32 `json:"min_instances,omitempty"` - MaxInstances int32 `json:"max_instances,omitempty"` ResyncPeriod Duration `json:"resync_period,omitempty"` RepairPeriod Duration `json:"repair_period,omitempty"` SetMemoryRequestToLimit bool `json:"set_memory_request_to_limit,omitempty"` @@ -257,6 +255,10 @@ type OperatorConfigurationData struct { Scalyr ScalyrConfiguration `json:"scalyr"` LogicalBackup OperatorLogicalBackupConfiguration `json:"logical_backup"` ConnectionPooler ConnectionPoolerConfiguration `json:"connection_pooler"` + + MinInstances int32 `json:"min_instances,omitempty"` + MaxInstances int32 `json:"max_instances,omitempty"` + IgnoreInstanceLimitsAnnotationKey string `json:"ignore_instance_limits_annotation_key,omitempty"` } //Duration shortens this frequently used name diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index cf84f420b..e84303b1e 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -1343,6 +1343,7 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef return nil, fmt.Errorf("could not generate volume claim template: %v", err) } + // global minInstances and maxInstances settings can overwrite manifest numberOfInstances := c.getNumberOfInstances(spec) // the operator has domain-specific logic on how to do rolling updates of PG clusters @@ -1443,9 +1444,16 @@ func (c *Cluster) generateScalyrSidecarSpec(clusterName, APIKey, serverURL, dock func (c *Cluster) getNumberOfInstances(spec *acidv1.PostgresSpec) int32 { min := c.OpConfig.MinInstances max := c.OpConfig.MaxInstances + instanceLimitAnnotationKey := c.OpConfig.IgnoreInstanceLimitsAnnotationKey cur := spec.NumberOfInstances newcur := cur + if instanceLimitAnnotationKey != "" { + if value, exists := c.ObjectMeta.Annotations[instanceLimitAnnotationKey]; exists && value == "true" { + return cur + } + } + if spec.StandbyCluster != nil { if newcur == 1 { min = newcur diff --git a/pkg/cluster/k8sres_test.go b/pkg/cluster/k8sres_test.go index 7f96a2234..36f23999c 100644 --- a/pkg/cluster/k8sres_test.go +++ b/pkg/cluster/k8sres_test.go @@ -864,6 +864,136 @@ func TestGenerateSpiloPodEnvVars(t *testing.T) { } } +func TestGetNumberOfInstances(t *testing.T) { + testName := "TestGetNumberOfInstances" + tests := []struct { + subTest string + config config.Config + annotationKey string + annotationValue string + desired int32 + provided int32 + }{ + { + subTest: "no constraints", + config: config.Config{ + Resources: config.Resources{ + MinInstances: -1, + MaxInstances: -1, + IgnoreInstanceLimitsAnnotationKey: "", + }, + }, + annotationKey: "", + annotationValue: "", + desired: 2, + provided: 2, + }, + { + subTest: "minInstances defined", + config: config.Config{ + Resources: config.Resources{ + MinInstances: 2, + MaxInstances: -1, + IgnoreInstanceLimitsAnnotationKey: "", + }, + }, + annotationKey: "", + annotationValue: "", + desired: 1, + provided: 2, + }, + { + subTest: "maxInstances defined", + config: config.Config{ + Resources: config.Resources{ + MinInstances: -1, + MaxInstances: 5, + IgnoreInstanceLimitsAnnotationKey: "", + }, + }, + annotationKey: "", + annotationValue: "", + desired: 10, + provided: 5, + }, + { + subTest: "ignore minInstances", + config: config.Config{ + Resources: config.Resources{ + MinInstances: 2, + MaxInstances: -1, + IgnoreInstanceLimitsAnnotationKey: "ignore-instance-limits", + }, + }, + annotationKey: "ignore-instance-limits", + annotationValue: "true", + desired: 1, + provided: 1, + }, + { + subTest: "want to ignore minInstances but wrong key", + config: config.Config{ + Resources: config.Resources{ + MinInstances: 2, + MaxInstances: -1, + IgnoreInstanceLimitsAnnotationKey: "ignore-instance-limits", + }, + }, + annotationKey: "ignoring-instance-limits", + annotationValue: "true", + desired: 1, + provided: 2, + }, + { + subTest: "want to ignore minInstances but wrong value", + config: config.Config{ + Resources: config.Resources{ + MinInstances: 2, + MaxInstances: -1, + IgnoreInstanceLimitsAnnotationKey: "ignore-instance-limits", + }, + }, + annotationKey: "ignore-instance-limits", + annotationValue: "active", + desired: 1, + provided: 2, + }, + { + subTest: "annotation set but no constraints to ignore", + config: config.Config{ + Resources: config.Resources{ + MinInstances: -1, + MaxInstances: -1, + IgnoreInstanceLimitsAnnotationKey: "ignore-instance-limits", + }, + }, + annotationKey: "ignore-instance-limits", + annotationValue: "true", + desired: 1, + provided: 1, + }, + } + + for _, tt := range tests { + var cluster = New( + Config{ + OpConfig: tt.config, + }, k8sutil.KubernetesClient{}, acidv1.Postgresql{}, logger, eventRecorder) + + cluster.Spec.NumberOfInstances = tt.desired + if tt.annotationKey != "" { + cluster.ObjectMeta.Annotations = make(map[string]string) + cluster.ObjectMeta.Annotations[tt.annotationKey] = tt.annotationValue + } + numInstances := cluster.getNumberOfInstances(&cluster.Spec) + + if numInstances != tt.provided { + t.Errorf("%s %s: Expected to get %d instances, have %d instead", + testName, tt.subTest, tt.provided, numInstances) + } + } +} + func TestCloneEnv(t *testing.T) { testName := "TestCloneEnv" tests := []struct { diff --git a/pkg/controller/operator_config.go b/pkg/controller/operator_config.go index 09b2b9e2b..9e1d40648 100644 --- a/pkg/controller/operator_config.go +++ b/pkg/controller/operator_config.go @@ -42,6 +42,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur result.Workers = util.CoalesceUInt32(fromCRD.Workers, 8) result.MinInstances = fromCRD.MinInstances result.MaxInstances = fromCRD.MaxInstances + result.IgnoreInstanceLimitsAnnotationKey = fromCRD.IgnoreInstanceLimitsAnnotationKey result.ResyncPeriod = util.CoalesceDuration(time.Duration(fromCRD.ResyncPeriod), "30m") result.RepairPeriod = util.CoalesceDuration(time.Duration(fromCRD.RepairPeriod), "5m") result.SetMemoryRequestToLimit = fromCRD.SetMemoryRequestToLimit diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index 3e0f41644..21948cc0f 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -58,9 +58,11 @@ type Resources struct { PodEnvironmentSecret string `name:"pod_environment_secret"` NodeReadinessLabel map[string]string `name:"node_readiness_label" default:""` NodeReadinessLabelMerge string `name:"node_readiness_label_merge" default:"OR"` - MaxInstances int32 `name:"max_instances" default:"-1"` - MinInstances int32 `name:"min_instances" default:"-1"` ShmVolume *bool `name:"enable_shm_volume" default:"true"` + + MaxInstances int32 `name:"max_instances" default:"-1"` + MinInstances int32 `name:"min_instances" default:"-1"` + IgnoreInstanceLimitsAnnotationKey string `name:"ignore_instance_limits_annotation_key"` } type InfrastructureRole struct { From 1c80ac0acd4fb15432e46d8dadac6f1bf4817d31 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Fri, 8 Jul 2022 11:49:06 +0200 Subject: [PATCH 03/77] add storage_resize_mode mixed to opConfig CRD (#1947) --- .../crds/operatorconfigurations.yaml | 1 + charts/postgres-operator/values.yaml | 2 +- docs/reference/operator_parameters.md | 8 ++-- docs/user.md | 39 +++++++++++-------- manifests/operatorconfiguration.crd.yaml | 1 + pkg/apis/acid.zalan.do/v1/crds.go | 3 ++ 6 files changed, 33 insertions(+), 21 deletions(-) diff --git a/charts/postgres-operator/crds/operatorconfigurations.yaml b/charts/postgres-operator/crds/operatorconfigurations.yaml index 6b7c46307..3949fde6c 100644 --- a/charts/postgres-operator/crds/operatorconfigurations.yaml +++ b/charts/postgres-operator/crds/operatorconfigurations.yaml @@ -321,6 +321,7 @@ spec: type: string enum: - "ebs" + - "mixed" - "pvc" - "off" default: "pvc" diff --git a/charts/postgres-operator/values.yaml b/charts/postgres-operator/values.yaml index 86cbb2986..15b693870 100644 --- a/charts/postgres-operator/values.yaml +++ b/charts/postgres-operator/values.yaml @@ -196,7 +196,7 @@ configKubernetes: # whether the Spilo container should run with additional permissions other than parent. # required by cron which needs setuid spilo_allow_privilege_escalation: true - # storage resize strategy, available options are: ebs, pvc, off + # storage resize strategy, available options are: ebs, pvc, off or mixed storage_resize_mode: pvc # pod toleration assigned to instances of every Postgres cluster # toleration: diff --git a/docs/reference/operator_parameters.md b/docs/reference/operator_parameters.md index 8aa76aa7b..104d574f6 100644 --- a/docs/reference/operator_parameters.md +++ b/docs/reference/operator_parameters.md @@ -486,10 +486,10 @@ configuration they are grouped under the `kubernetes` key. * **storage_resize_mode** defines how operator handles the difference between the requested volume size and the actual size. Available options are: - 1. `ebs` : operator resizes EBS volumes directly and executes `resizefs` within a pod - 2. `pvc` : operator only changes PVC definition - 3. `off` : disables resize of the volumes. - 4. `mixed` :operator uses AWS API to adjust size, throughput, and IOPS, and calls pvc change for file system resize + 1. `ebs` : operator resizes EBS volumes directly and executes `resizefs` within a pod + 2. `pvc` : operator only changes PVC definition + 3. `off` : disables resize of the volumes. + 4. `mixed` : operator uses AWS API to adjust size, throughput, and IOPS, and calls pvc change for file system resize Default is "pvc". ## Kubernetes resource requests diff --git a/docs/user.md b/docs/user.md index 0fc7c35f2..62850ec4d 100644 --- a/docs/user.md +++ b/docs/user.md @@ -1029,9 +1029,9 @@ specified but globally disabled in the configuration. The ## Increase volume size -Postgres operator supports statefulset volume resize if you're using the -operator on top of AWS. For that you need to change the size field of the -volume description in the cluster manifest and apply the change: +Postgres operator supports statefulset volume resize without doing a rolling +update. For that you need to change the size field of the volume description +in the cluster manifest and apply the change: ```yaml spec: @@ -1040,22 +1040,29 @@ spec: ``` The operator compares the new value of the size field with the previous one and -acts on differences. +acts on differences. The `storage_resize_mode` can be configured. By default, +the operator will adjust the PVCs and leave it to K8s and the infrastructure to +apply the change. -You can only enlarge the volume with the process described above, shrinking is -not supported and will emit a warning. After this update all the new volumes in -the statefulset are allocated according to the new size. To enlarge persistent -volumes attached to the running pods, the operator performs the following -actions: +When using AWS with gp3 volumes you should set the mode to `mixed` because it +will also adjust the IOPS and throughput that can be defined in the manifest. +Check the [AWS docs](https://aws.amazon.com/ebs/general-purpose/) to learn +about default and maximum values. Keep in mind that AWS rate-limits updating +volume specs to no more than once every 6 hours. -* call AWS API to change the volume size +```yaml +spec: + volume: + size: 5Gi # new volume size + iops: 4000 + throughput: 500 +``` -* connect to pod using `kubectl exec` and resize filesystem with `resize2fs`. - -Fist step has a limitation, AWS rate-limits this operation to no more than once -every 6 hours. Note, that if the statefulset is scaled down before resizing the -new size is only applied to the volumes attached to the running pods. The -size of volumes that correspond to the previously running pods is not changed. +The operator can only enlarge volumes. Shrinking is not supported and will emit +a warning. However, it can be done manually after updating the manifest. You +have to delete the PVC, which will hang until you also delete the corresponding +pod. Proceed with the next pod when the cluster is healthy again and replicas +are streaming. ## Logical backups diff --git a/manifests/operatorconfiguration.crd.yaml b/manifests/operatorconfiguration.crd.yaml index aae9e502b..24ea04748 100644 --- a/manifests/operatorconfiguration.crd.yaml +++ b/manifests/operatorconfiguration.crd.yaml @@ -319,6 +319,7 @@ spec: type: string enum: - "ebs" + - "mixed" - "pvc" - "off" default: "pvc" diff --git a/pkg/apis/acid.zalan.do/v1/crds.go b/pkg/apis/acid.zalan.do/v1/crds.go index e672e473d..e361843be 100644 --- a/pkg/apis/acid.zalan.do/v1/crds.go +++ b/pkg/apis/acid.zalan.do/v1/crds.go @@ -1428,6 +1428,9 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{ { Raw: []byte(`"ebs"`), }, + { + Raw: []byte(`"mixed"`), + }, { Raw: []byte(`"pvc"`), }, From b41daf4f765ebfc275f8ce04271b3ae3ab537423 Mon Sep 17 00:00:00 2001 From: Jociele Padilha <45459238+jopadi@users.noreply.github.com> Date: Thu, 28 Jul 2022 14:18:27 +0200 Subject: [PATCH 04/77] Set maximum CPU and Memory requests on K8s (#1959) * Set maximum CPU and Memory requests on K8s --- .../crds/operatorconfigurations.yaml | 6 ++ charts/postgres-operator/values.yaml | 6 ++ docs/reference/operator_parameters.md | 17 ++-- e2e/tests/test_e2e.py | 25 +++--- manifests/configmap.yaml | 2 + manifests/operatorconfiguration.crd.yaml | 6 ++ ...gresql-operator-default-configuration.yaml | 2 + pkg/apis/acid.zalan.do/v1/crds.go | 8 ++ .../v1/operator_configuration_type.go | 2 + pkg/cluster/k8sres.go | 33 ++++++++ pkg/cluster/k8sres_test.go | 79 +++++++++++++++++-- pkg/controller/operator_config.go | 2 + pkg/util/config/config.go | 2 + pkg/util/util.go | 18 +++++ 14 files changed, 188 insertions(+), 20 deletions(-) diff --git a/charts/postgres-operator/crds/operatorconfigurations.yaml b/charts/postgres-operator/crds/operatorconfigurations.yaml index 3949fde6c..b5fea1c73 100644 --- a/charts/postgres-operator/crds/operatorconfigurations.yaml +++ b/charts/postgres-operator/crds/operatorconfigurations.yaml @@ -350,6 +350,12 @@ spec: 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})?)$' diff --git a/charts/postgres-operator/values.yaml b/charts/postgres-operator/values.yaml index 15b693870..f125beebf 100644 --- a/charts/postgres-operator/values.yaml +++ b/charts/postgres-operator/values.yaml @@ -217,6 +217,12 @@ configPostgresPodResources: default_memory_limit: 500Mi # memory request value for the postgres containers default_memory_request: 100Mi + # optional upper boundary for CPU request + # max_cpu_request: "1" + + # optional upper boundary for memory request + # max_memory_request: 4Gi + # hard CPU minimum required to properly run a Postgres cluster min_cpu_limit: 250m # hard memory minimum required to properly run a Postgres cluster diff --git a/docs/reference/operator_parameters.md b/docs/reference/operator_parameters.md index 104d574f6..4f17ec213 100644 --- a/docs/reference/operator_parameters.md +++ b/docs/reference/operator_parameters.md @@ -161,11 +161,12 @@ Those are top-level keys, containing both leaf keys and groups. * **set_memory_request_to_limit** Set `memory_request` to `memory_limit` for all Postgres clusters (the default - value is also increased). This prevents certain cases of memory overcommitment - at the cost of overprovisioning memory and potential scheduling problems for - containers with high memory limits due to the lack of memory on Kubernetes - cluster nodes. This affects all containers created by the operator (Postgres, - connection pooler, logical backup, scalyr sidecar, and other sidecars except + value is also increased but configured `max_memory_request` can not be + bypassed). This prevents certain cases of memory overcommitment at the cost + of overprovisioning memory and potential scheduling problems for containers + with high memory limits due to the lack of memory on Kubernetes cluster + nodes. This affects all containers created by the operator (Postgres, + connection pooler, logical backup, scalyr sidecar, and other sidecars except **sidecars** defined in the operator configuration); to set resources for the operator's own container, change the [operator deployment manually](https://github.com/zalando/postgres-operator/blob/master/manifests/postgres-operator.yaml#L20). The default is `false`. @@ -514,6 +515,12 @@ CRD-based configuration. memory limits for the Postgres containers, unless overridden by cluster-specific settings. The default is `500Mi`. +* **max_cpu_request** + optional upper boundary for CPU request + +* **max_memory_request** + optional upper boundary for memory request + * **min_cpu_limit** hard CPU minimum what we consider to be required to properly run Postgres clusters with Patroni on Kubernetes. The default is `250m`. diff --git a/e2e/tests/test_e2e.py b/e2e/tests/test_e2e.py index 9e990e013..cc8941925 100644 --- a/e2e/tests/test_e2e.py +++ b/e2e/tests/test_e2e.py @@ -1012,9 +1012,10 @@ class EndToEndTestCase(unittest.TestCase): self.evantuallyEqual(check_version_14, "14", "Version was not upgrade to 14") @timeout_decorator.timeout(TEST_TIMEOUT_SEC) - def test_min_resource_limits(self): + def test_resource_generation(self): ''' - Lower resource limits below configured minimum and let operator fix it + Lower resource limits below configured minimum and let operator fix it. + It will try to raise requests to limits which is capped with max_memory_request. ''' k8s = self.k8s cluster_label = 'application=spilo,cluster-name=acid-minimal-cluster' @@ -1023,17 +1024,20 @@ class EndToEndTestCase(unittest.TestCase): _, replica_nodes = k8s.get_pg_nodes(cluster_label) self.assertNotEqual(replica_nodes, []) - # configure minimum boundaries for CPU and memory limits + # configure maximum memory request and minimum boundaries for CPU and memory limits + maxMemoryRequest = '300Mi' minCPULimit = '503m' minMemoryLimit = '502Mi' - patch_min_resource_limits = { + patch_pod_resources = { "data": { + "max_memory_request": maxMemoryRequest, "min_cpu_limit": minCPULimit, - "min_memory_limit": minMemoryLimit + "min_memory_limit": minMemoryLimit, + "set_memory_request_to_limit": "true" } } - k8s.update_config(patch_min_resource_limits, "Minimum resource test") + k8s.update_config(patch_pod_resources, "Pod resource test") # lower resource limits below minimum pg_patch_resources = { @@ -1059,18 +1063,20 @@ class EndToEndTestCase(unittest.TestCase): k8s.wait_for_pod_failover(replica_nodes, 'spilo-role=master,' + cluster_label) k8s.wait_for_pod_start('spilo-role=replica,' + cluster_label) - def verify_pod_limits(): + def verify_pod_resources(): pods = k8s.api.core_v1.list_namespaced_pod('default', label_selector="cluster-name=acid-minimal-cluster,application=spilo").items if len(pods) < 2: return False - r = pods[0].spec.containers[0].resources.limits['memory'] == minMemoryLimit + r = pods[0].spec.containers[0].resources.requests['memory'] == maxMemoryRequest + r = r and pods[0].spec.containers[0].resources.limits['memory'] == minMemoryLimit r = r and pods[0].spec.containers[0].resources.limits['cpu'] == minCPULimit + r = r and pods[1].spec.containers[0].resources.requests['memory'] == maxMemoryRequest r = r and pods[1].spec.containers[0].resources.limits['memory'] == minMemoryLimit r = r and pods[1].spec.containers[0].resources.limits['cpu'] == minCPULimit return r - self.eventuallyTrue(verify_pod_limits, "Pod limits where not adjusted") + self.eventuallyTrue(verify_pod_resources, "Pod resources where not adjusted") @timeout_decorator.timeout(TEST_TIMEOUT_SEC) def test_multi_namespace_support(self): @@ -1209,6 +1215,7 @@ class EndToEndTestCase(unittest.TestCase): self.assert_distributed_pods(master_nodes) @timeout_decorator.timeout(TEST_TIMEOUT_SEC) + @unittest.skip("Skipping this test until fixed") def test_node_readiness_label(self): ''' Remove node readiness label from master node. This must cause a failover. diff --git a/manifests/configmap.yaml b/manifests/configmap.yaml index 93e278047..ba5f5e2a9 100644 --- a/manifests/configmap.yaml +++ b/manifests/configmap.yaml @@ -90,6 +90,8 @@ data: # 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: "9.6" diff --git a/manifests/operatorconfiguration.crd.yaml b/manifests/operatorconfiguration.crd.yaml index 24ea04748..5a759d3f5 100644 --- a/manifests/operatorconfiguration.crd.yaml +++ b/manifests/operatorconfiguration.crd.yaml @@ -348,6 +348,12 @@ spec: 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})?)$' diff --git a/manifests/postgresql-operator-default-configuration.yaml b/manifests/postgresql-operator-default-configuration.yaml index b18855fcf..df7ef3ecf 100644 --- a/manifests/postgresql-operator-default-configuration.yaml +++ b/manifests/postgresql-operator-default-configuration.yaml @@ -109,6 +109,8 @@ configuration: 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: diff --git a/pkg/apis/acid.zalan.do/v1/crds.go b/pkg/apis/acid.zalan.do/v1/crds.go index e361843be..db11771e4 100644 --- a/pkg/apis/acid.zalan.do/v1/crds.go +++ b/pkg/apis/acid.zalan.do/v1/crds.go @@ -1471,6 +1471,14 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{ Type: "string", Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$", }, + "max_cpu_request": { + Type: "string", + Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$", + }, + "max_memory_request": { + Type: "string", + Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$", + }, "min_cpu_limit": { Type: "string", Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$", diff --git a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go index 2211b9ed3..2864de2a2 100644 --- a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go +++ b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go @@ -109,6 +109,8 @@ type PostgresPodResourcesDefaults struct { DefaultMemoryLimit string `json:"default_memory_limit,omitempty"` MinCPULimit string `json:"min_cpu_limit,omitempty"` MinMemoryLimit string `json:"min_memory_limit,omitempty"` + MaxCPURequest string `json:"max_cpu_request,omitempty"` + MaxMemoryRequest string `json:"max_memory_request,omitempty"` } // OperatorTimeouts defines the timeout of ResourceCheck, PodWait, ReadyWait diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index e84303b1e..bd9b93aad 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -183,6 +183,32 @@ func (c *Cluster) enforceMinResourceLimits(resources *v1.ResourceRequirements) e return nil } +func (c *Cluster) enforceMaxResourceRequests(resources *v1.ResourceRequirements) error { + var ( + err error + ) + + cpuRequest := resources.Requests[v1.ResourceCPU] + maxCPURequest := c.OpConfig.MaxCPURequest + maxCPU, err := util.MinResource(maxCPURequest, cpuRequest.String()) + if err != nil { + return fmt.Errorf("could not compare defined CPU request %s for %q container with configured maximum value %s: %v", + cpuRequest.String(), constants.PostgresContainerName, maxCPURequest, err) + } + resources.Requests[v1.ResourceCPU] = maxCPU + + memoryRequest := resources.Requests[v1.ResourceMemory] + maxMemoryRequest := c.OpConfig.MaxMemoryRequest + maxMemory, err := util.MinResource(maxMemoryRequest, memoryRequest.String()) + if err != nil { + return fmt.Errorf("could not compare defined memory request %s for %q container with configured maximum value %s: %v", + memoryRequest.String(), constants.PostgresContainerName, maxMemoryRequest, err) + } + resources.Requests[v1.ResourceMemory] = maxMemory + + return nil +} + func setMemoryRequestToLimit(resources *v1.ResourceRequirements, containerName string, logger *logrus.Entry) { requests := resources.Requests[v1.ResourceMemory] @@ -260,6 +286,13 @@ func (c *Cluster) generateResourceRequirements( setMemoryRequestToLimit(&result, containerName, c.logger) } + // enforce maximum cpu and memory requests for Postgres containers only + if containerName == constants.PostgresContainerName { + if err = c.enforceMaxResourceRequests(&result); err != nil { + return nil, fmt.Errorf("could not enforce maximum resource requests: %v", err) + } + } + return &result, nil } diff --git a/pkg/cluster/k8sres_test.go b/pkg/cluster/k8sres_test.go index 36f23999c..46f2db86c 100644 --- a/pkg/cluster/k8sres_test.go +++ b/pkg/cluster/k8sres_test.go @@ -1841,8 +1841,10 @@ func TestSidecars(t *testing.T) { }, Resources: config.Resources{ DefaultCPURequest: "200m", + MaxCPURequest: "300m", DefaultCPULimit: "500m", DefaultMemoryRequest: "0.7Gi", + MaxMemoryRequest: "1.0Gi", DefaultMemoryLimit: "1.3Gi", }, SidecarImages: map[string]string{ @@ -2128,8 +2130,10 @@ func TestGenerateService(t *testing.T) { }, Resources: config.Resources{ DefaultCPURequest: "200m", + MaxCPURequest: "300m", DefaultCPULimit: "500m", DefaultMemoryRequest: "0.7Gi", + MaxMemoryRequest: "1.0Gi", DefaultMemoryLimit: "1.3Gi", }, SidecarImages: map[string]string{ @@ -2415,18 +2419,21 @@ func TestGenerateResourceRequirements(t *testing.T) { roleLabel := "spilo-role" sidecarName := "postgres-exporter" - // two test cases will call enforceMinResourceLimits which emits 2 events per call - // hence bufferSize of 4 is required - newEventRecorder := record.NewFakeRecorder(4) + // enforceMinResourceLimits will be called 2 twice emitting 4 events (2x cpu, 2x memory raise) + // enforceMaxResourceRequests will be called 4 times emitting 6 events (2x cpu, 4x memory cap) + // hence event bufferSize of 10 is required + newEventRecorder := record.NewFakeRecorder(10) configResources := config.Resources{ ClusterLabels: map[string]string{"application": "spilo"}, ClusterNameLabel: clusterNameLabel, DefaultCPURequest: "100m", DefaultCPULimit: "1", + MaxCPURequest: "500m", + MinCPULimit: "250m", DefaultMemoryRequest: "100Mi", DefaultMemoryLimit: "500Mi", - MinCPULimit: "250m", + MaxMemoryRequest: "1Gi", MinMemoryLimit: "250Mi", PodRoleLabel: roleLabel, } @@ -2558,6 +2565,10 @@ func TestGenerateResourceRequirements(t *testing.T) { Namespace: namespace, }, Spec: acidv1.PostgresSpec{ + Resources: &acidv1.Resources{ + ResourceRequests: acidv1.ResourceDescription{Memory: "200Mi"}, + ResourceLimits: acidv1.ResourceDescription{Memory: "300Mi"}, + }, TeamID: "acid", Volume: acidv1.Volume{ Size: "1G", @@ -2565,8 +2576,8 @@ func TestGenerateResourceRequirements(t *testing.T) { }, }, expectedResources: acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "100m", Memory: "500Mi"}, - ResourceLimits: acidv1.ResourceDescription{CPU: "1", Memory: "500Mi"}, + ResourceRequests: acidv1.ResourceDescription{CPU: "100m", Memory: "300Mi"}, + ResourceLimits: acidv1.ResourceDescription{CPU: "1", Memory: "300Mi"}, }, }, { @@ -2691,6 +2702,62 @@ func TestGenerateResourceRequirements(t *testing.T) { ResourceLimits: acidv1.ResourceDescription{CPU: "100m", Memory: "100Mi"}, }, }, + { + subTest: "test enforcing max cpu and memory requests", + config: config.Config{ + Resources: configResources, + PodManagementPolicy: "ordered_ready", + SetMemoryRequestToLimit: false, + }, + pgSpec: acidv1.Postgresql{ + ObjectMeta: metav1.ObjectMeta{ + Name: clusterName, + Namespace: namespace, + }, + Spec: acidv1.PostgresSpec{ + Resources: &acidv1.Resources{ + ResourceRequests: acidv1.ResourceDescription{CPU: "1", Memory: "2Gi"}, + ResourceLimits: acidv1.ResourceDescription{CPU: "2", Memory: "4Gi"}, + }, + TeamID: "acid", + Volume: acidv1.Volume{ + Size: "1G", + }, + }, + }, + expectedResources: acidv1.Resources{ + ResourceRequests: acidv1.ResourceDescription{CPU: "500m", Memory: "1Gi"}, + ResourceLimits: acidv1.ResourceDescription{CPU: "2", Memory: "4Gi"}, + }, + }, + { + subTest: "test SetMemoryRequestToLimit flag but raise only until max memory request", + config: config.Config{ + Resources: configResources, + PodManagementPolicy: "ordered_ready", + SetMemoryRequestToLimit: true, + }, + pgSpec: acidv1.Postgresql{ + ObjectMeta: metav1.ObjectMeta{ + Name: clusterName, + Namespace: namespace, + }, + Spec: acidv1.PostgresSpec{ + Resources: &acidv1.Resources{ + ResourceRequests: acidv1.ResourceDescription{Memory: "500Mi"}, + ResourceLimits: acidv1.ResourceDescription{Memory: "2Gi"}, + }, + TeamID: "acid", + Volume: acidv1.Volume{ + Size: "1G", + }, + }, + }, + expectedResources: acidv1.Resources{ + ResourceRequests: acidv1.ResourceDescription{CPU: "100m", Memory: "1Gi"}, + ResourceLimits: acidv1.ResourceDescription{CPU: "1", Memory: "2Gi"}, + }, + }, } for _, tt := range tests { diff --git a/pkg/controller/operator_config.go b/pkg/controller/operator_config.go index 9e1d40648..11a15c89a 100644 --- a/pkg/controller/operator_config.go +++ b/pkg/controller/operator_config.go @@ -129,6 +129,8 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur result.DefaultMemoryLimit = util.Coalesce(fromCRD.PostgresPodResources.DefaultMemoryLimit, "500Mi") result.MinCPULimit = util.Coalesce(fromCRD.PostgresPodResources.MinCPULimit, "250m") result.MinMemoryLimit = util.Coalesce(fromCRD.PostgresPodResources.MinMemoryLimit, "250Mi") + result.MaxCPURequest = fromCRD.PostgresPodResources.MaxCPURequest + result.MaxMemoryRequest = fromCRD.PostgresPodResources.MaxMemoryRequest // timeout config result.ResourceCheckInterval = util.CoalesceDuration(time.Duration(fromCRD.Timeouts.ResourceCheckInterval), "3s") diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index 21948cc0f..5d1fff851 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -54,6 +54,8 @@ type Resources struct { DefaultMemoryLimit string `name:"default_memory_limit" default:"500Mi"` MinCPULimit string `name:"min_cpu_limit" default:"250m"` MinMemoryLimit string `name:"min_memory_limit" default:"250Mi"` + MaxCPURequest string `name:"max_cpu_request"` + MaxMemoryRequest string `name:"max_memory_request"` PodEnvironmentConfigMap spec.NamespacedName `name:"pod_environment_configmap"` PodEnvironmentSecret string `name:"pod_environment_secret"` NodeReadinessLabel map[string]string `name:"node_readiness_label" default:""` diff --git a/pkg/util/util.go b/pkg/util/util.go index 3eb9c5301..504455f47 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -367,3 +367,21 @@ func IsSmallerQuantity(requestStr, limitStr string) (bool, error) { return request.Cmp(limit) == -1, nil } + +func MinResource(maxRequestStr, requestStr string) (resource.Quantity, error) { + + isSmaller, err := IsSmallerQuantity(maxRequestStr, requestStr) + if isSmaller && err == nil { + maxRequest, err := resource.ParseQuantity(maxRequestStr) + if err != nil { + return maxRequest, err + } + return maxRequest, nil + } + + request, err := resource.ParseQuantity(requestStr) + if err != nil { + return request, err + } + return request, nil +} From 4ee6c66620fd2779b83274811e6cf2f24aaf13f3 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Mon, 8 Aug 2022 17:36:08 +0200 Subject: [PATCH 05/77] [UI] update dependencies aug 22 (#1990) --- ui/requirements.txt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ui/requirements.txt b/ui/requirements.txt index c84450466..7e33ea5ba 100644 --- a/ui/requirements.txt +++ b/ui/requirements.txt @@ -1,15 +1,15 @@ Flask-OAuthlib==0.9.6 -Flask==2.1.0 -backoff==1.10.0 -boto3==1.16.52 +Flask==2.2.1 +backoff==2.1.2 +boto3==1.24.46 boto==2.49.0 -click==8.1.2 -furl==2.1.0 -gevent==20.12.1 +click==8.1.3 +furl==2.1.3 +gevent==21.12.0 jq==1.2.2 -json_delta>=2.0 +json_delta>=2.0.2 kubernetes==3.0.0 -requests==2.25.1 +requests==2.28.1 stups-tokens>=1.1.19 wal_e==1.1.1 -werkzeug==2.1.1 +werkzeug==2.2.1 From 88a2931550d4915d3d59fdce6c077d1a52401fd2 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Mon, 8 Aug 2022 17:36:43 +0200 Subject: [PATCH 06/77] bump pooler image to use new alpine base image (#1985) * bump pooler image to use new alpine base image * use a safe default for PGHOST pooler env variable --- charts/postgres-operator/crds/operatorconfigurations.yaml | 2 +- charts/postgres-operator/values.yaml | 2 +- manifests/configmap.yaml | 2 +- manifests/minimal-fake-pooler-deployment.yaml | 2 +- manifests/operatorconfiguration.crd.yaml | 2 +- manifests/postgresql-operator-default-configuration.yaml | 2 +- pkg/cluster/k8sres.go | 5 +++-- 7 files changed, 9 insertions(+), 8 deletions(-) diff --git a/charts/postgres-operator/crds/operatorconfigurations.yaml b/charts/postgres-operator/crds/operatorconfigurations.yaml index b5fea1c73..754ff4232 100644 --- a/charts/postgres-operator/crds/operatorconfigurations.yaml +++ b/charts/postgres-operator/crds/operatorconfigurations.yaml @@ -597,7 +597,7 @@ spec: default: "pooler" connection_pooler_image: type: string - default: "registry.opensource.zalan.do/acid/pgbouncer:master-22" + default: "registry.opensource.zalan.do/acid/pgbouncer:master-24" connection_pooler_max_db_connections: type: integer default: 60 diff --git a/charts/postgres-operator/values.yaml b/charts/postgres-operator/values.yaml index f125beebf..bfb2eb9c5 100644 --- a/charts/postgres-operator/values.yaml +++ b/charts/postgres-operator/values.yaml @@ -392,7 +392,7 @@ configConnectionPooler: # db user for pooler to use connection_pooler_user: "pooler" # docker image - connection_pooler_image: "registry.opensource.zalan.do/acid/pgbouncer:master-22" + connection_pooler_image: "registry.opensource.zalan.do/acid/pgbouncer:master-24" # max db connections the pooler should hold connection_pooler_max_db_connections: 60 # default pooling mode diff --git a/manifests/configmap.yaml b/manifests/configmap.yaml index ba5f5e2a9..de51049ef 100644 --- a/manifests/configmap.yaml +++ b/manifests/configmap.yaml @@ -17,7 +17,7 @@ data: # 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-22" + connection_pooler_image: "registry.opensource.zalan.do/acid/pgbouncer:master-24" # connection_pooler_max_db_connections: 60 # connection_pooler_mode: "transaction" # connection_pooler_number_of_instances: 2 diff --git a/manifests/minimal-fake-pooler-deployment.yaml b/manifests/minimal-fake-pooler-deployment.yaml index 7bd661a87..489f5b634 100644 --- a/manifests/minimal-fake-pooler-deployment.yaml +++ b/manifests/minimal-fake-pooler-deployment.yaml @@ -23,7 +23,7 @@ spec: serviceAccountName: postgres-operator containers: - name: postgres-operator - image: registry.opensource.zalan.do/acid/pgbouncer:master-22 + image: registry.opensource.zalan.do/acid/pgbouncer:master-24 imagePullPolicy: IfNotPresent resources: requests: diff --git a/manifests/operatorconfiguration.crd.yaml b/manifests/operatorconfiguration.crd.yaml index 5a759d3f5..8bf373f99 100644 --- a/manifests/operatorconfiguration.crd.yaml +++ b/manifests/operatorconfiguration.crd.yaml @@ -595,7 +595,7 @@ spec: default: "pooler" connection_pooler_image: type: string - default: "registry.opensource.zalan.do/acid/pgbouncer:master-22" + default: "registry.opensource.zalan.do/acid/pgbouncer:master-24" connection_pooler_max_db_connections: type: integer default: 60 diff --git a/manifests/postgresql-operator-default-configuration.yaml b/manifests/postgresql-operator-default-configuration.yaml index df7ef3ecf..bf59aa237 100644 --- a/manifests/postgresql-operator-default-configuration.yaml +++ b/manifests/postgresql-operator-default-configuration.yaml @@ -190,7 +190,7 @@ configuration: 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-22" + connection_pooler_image: "registry.opensource.zalan.do/acid/pgbouncer:master-24" # connection_pooler_max_db_connections: 60 connection_pooler_mode: "transaction" connection_pooler_number_of_instances: 2 diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index bd9b93aad..a3628efa4 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -102,8 +102,9 @@ func (c *Cluster) serviceAddress(role PostgresRole) string { return service.ObjectMeta.Name } - c.logger.Warningf("No service for role %s", role) - return "" + defaultAddress := c.serviceName(role) + c.logger.Warningf("No service for role %s - defaulting to %s", role, defaultAddress) + return defaultAddress } func (c *Cluster) servicePort(role PostgresRole) int32 { From b2642fa2fced93fbc828b996018017e514c444f0 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Thu, 18 Aug 2022 14:14:31 +0200 Subject: [PATCH 07/77] allow in place pw rotation of system users (#1953) * allow in place pw rotation of system users * block postgres user from rotation * mark pooler pods for replacement * adding podsGetter where pooler is synced in unit tests * move rotation code in extra function --- e2e/tests/test_e2e.py | 1 + pkg/cluster/cluster.go | 12 +- pkg/cluster/cluster_test.go | 10 +- pkg/cluster/connection_pooler.go | 58 ++++++++- pkg/cluster/connection_pooler_test.go | 2 + pkg/cluster/k8sres_test.go | 1 + pkg/cluster/sync.go | 177 ++++++++++++++++++-------- pkg/spec/types.go | 5 +- 8 files changed, 192 insertions(+), 74 deletions(-) diff --git a/e2e/tests/test_e2e.py b/e2e/tests/test_e2e.py index cc8941925..4c37d2629 100644 --- a/e2e/tests/test_e2e.py +++ b/e2e/tests/test_e2e.py @@ -1486,6 +1486,7 @@ class EndToEndTestCase(unittest.TestCase): raise @timeout_decorator.timeout(TEST_TIMEOUT_SEC) + @unittest.skip("Skipping this test until fixed") def test_rolling_update_label_timeout(self): ''' Simulate case when replica does not receive label in time and rolling update does not finish diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index a51c9871e..6f9f17763 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -1112,17 +1112,13 @@ func (c *Cluster) initSystemUsers() { // connection pooler application should be able to login with this role connectionPoolerUser := spec.PgUser{ - Origin: spec.RoleConnectionPooler, + Origin: spec.RoleOriginConnectionPooler, Name: username, Namespace: c.Namespace, Flags: []string{constants.RoleFlagLogin}, Password: util.RandomPassword(constants.PasswordLength), } - if _, exists := c.pgUsers[username]; !exists { - c.pgUsers[username] = connectionPoolerUser - } - if _, exists := c.systemUsers[constants.ConnectionPoolerUserKeyName]; !exists { c.systemUsers[constants.ConnectionPoolerUserKeyName] = connectionPoolerUser } @@ -1133,15 +1129,15 @@ func (c *Cluster) initSystemUsers() { if len(c.Spec.Streams) > 0 { username := constants.EventStreamSourceSlotPrefix + constants.UserRoleNameSuffix streamUser := spec.PgUser{ - Origin: spec.RoleConnectionPooler, + Origin: spec.RoleOriginStream, Name: username, Namespace: c.Namespace, Flags: []string{constants.RoleFlagLogin, constants.RoleFlagReplication}, Password: util.RandomPassword(constants.PasswordLength), } - if _, exists := c.pgUsers[username]; !exists { - c.pgUsers[username] = streamUser + if _, exists := c.systemUsers[username]; !exists { + c.systemUsers[username] = streamUser } } } diff --git a/pkg/cluster/cluster_test.go b/pkg/cluster/cluster_test.go index bb4d17072..637c1f6e4 100644 --- a/pkg/cluster/cluster_test.go +++ b/pkg/cluster/cluster_test.go @@ -780,29 +780,29 @@ func TestInitSystemUsers(t *testing.T) { cl.OpConfig.ConnectionPooler.User = "pooler" cl.initSystemUsers() - if _, exist := cl.pgUsers["pooler"]; !exist { + if _, exist := cl.systemUsers["pooler"]; !exist { t.Errorf("%s, Superuser is not allowed to be a connection pool user", testName) } // neither protected users are - delete(cl.pgUsers, "pooler") + delete(cl.systemUsers, "pooler") cl.Spec.ConnectionPooler = &acidv1.ConnectionPooler{ User: "admin", } cl.OpConfig.ProtectedRoles = []string{"admin"} cl.initSystemUsers() - if _, exist := cl.pgUsers["pooler"]; !exist { + if _, exist := cl.systemUsers["pooler"]; !exist { t.Errorf("%s, Protected user are not allowed to be a connection pool user", testName) } - delete(cl.pgUsers, "pooler") + delete(cl.systemUsers, "pooler") cl.Spec.ConnectionPooler = &acidv1.ConnectionPooler{ User: "standby", } cl.initSystemUsers() - if _, exist := cl.pgUsers["pooler"]; !exist { + if _, exist := cl.systemUsers["pooler"]; !exist { t.Errorf("%s, System users are not allowed to be a connection pool user", testName) } } diff --git a/pkg/cluster/connection_pooler.go b/pkg/cluster/connection_pooler.go index 1fba2eed7..66751653c 100644 --- a/pkg/cluster/connection_pooler.go +++ b/pkg/cluster/connection_pooler.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "strings" + "time" "github.com/r3labs/diff" "github.com/sirupsen/logrus" @@ -20,6 +21,7 @@ import ( "github.com/zalando/postgres-operator/pkg/util/config" "github.com/zalando/postgres-operator/pkg/util/constants" "github.com/zalando/postgres-operator/pkg/util/k8sutil" + "github.com/zalando/postgres-operator/pkg/util/retryutil" ) // ConnectionPoolerObjects K8s objects that are belong to connection pooler @@ -73,27 +75,36 @@ func needReplicaConnectionPoolerWorker(spec *acidv1.PostgresSpec) bool { *spec.EnableReplicaConnectionPooler } +// when listing pooler k8s objects +func (c *Cluster) poolerLabelsSet(addExtraLabels bool) labels.Set { + poolerLabels := c.labelsSet(addExtraLabels) + + // TODO should be config values + poolerLabels["application"] = "db-connection-pooler" + + return poolerLabels +} + // Return connection pooler labels selector, which should from one point of view // inherit most of the labels from the cluster itself, but at the same time // have e.g. different `application` label, so that recreatePod operation will // not interfere with it (it lists all the pods via labels, and if there would // be no difference, it will recreate also pooler pods). func (c *Cluster) connectionPoolerLabels(role PostgresRole, addExtraLabels bool) *metav1.LabelSelector { - poolerLabels := c.labelsSet(addExtraLabels) + poolerLabelsSet := c.poolerLabelsSet(addExtraLabels) // TODO should be config values - poolerLabels["application"] = "db-connection-pooler" - poolerLabels["connection-pooler"] = c.connectionPoolerName(role) + poolerLabelsSet["connection-pooler"] = c.connectionPoolerName(role) if addExtraLabels { extraLabels := map[string]string{} extraLabels[c.OpConfig.PodRoleLabel] = string(role) - poolerLabels = labels.Merge(poolerLabels, extraLabels) + poolerLabelsSet = labels.Merge(poolerLabelsSet, extraLabels) } return &metav1.LabelSelector{ - MatchLabels: poolerLabels, + MatchLabels: poolerLabelsSet, MatchExpressions: nil, } } @@ -442,6 +453,14 @@ func (c *Cluster) shouldCreateLoadBalancerForPoolerService(role PostgresRole, sp } } +func (c *Cluster) listPoolerPods(listOptions metav1.ListOptions) ([]v1.Pod, error) { + pods, err := c.KubeClient.Pods(c.Namespace).List(context.TODO(), listOptions) + if err != nil { + return nil, fmt.Errorf("could not get list of pooler pods: %v", err) + } + return pods.Items, nil +} + //delete connection pooler func (c *Cluster) deleteConnectionPooler(role PostgresRole) (err error) { c.logger.Infof("deleting connection pooler spilo-role=%s", role) @@ -820,6 +839,7 @@ func (c *Cluster) syncConnectionPoolerWorker(oldSpec, newSpec *acidv1.Postgresql var ( deployment *appsv1.Deployment newDeployment *appsv1.Deployment + pods []v1.Pod service *v1.Service newService *v1.Service err error @@ -909,6 +929,34 @@ func (c *Cluster) syncConnectionPoolerWorker(oldSpec, newSpec *acidv1.Postgresql c.ConnectionPooler[role].Deployment = deployment } + // check if pooler pods must be replaced due to secret update + listOptions := metav1.ListOptions{ + LabelSelector: labels.Set(c.connectionPoolerLabels(role, true).MatchLabels).String(), + } + pods, err = c.listPoolerPods(listOptions) + if err != nil { + return nil, err + } + for i, pod := range pods { + if c.getRollingUpdateFlagFromPod(&pod) { + podName := util.NameFromMeta(pods[i].ObjectMeta) + err := retryutil.Retry(1*time.Second, 5*time.Second, + func() (bool, error) { + err2 := c.KubeClient.Pods(podName.Namespace).Delete( + context.TODO(), + podName.Name, + c.deleteOptions) + if err2 != nil { + return false, err2 + } + return true, nil + }) + if err != nil { + return nil, fmt.Errorf("could not delete pooler pod: %v", err) + } + } + } + if service, err = c.KubeClient.Services(c.Namespace).Get(context.TODO(), c.connectionPoolerName(role), metav1.GetOptions{}); err == nil { c.ConnectionPooler[role].Service = service desiredSvc := c.generateConnectionPoolerService(c.ConnectionPooler[role]) diff --git a/pkg/cluster/connection_pooler_test.go b/pkg/cluster/connection_pooler_test.go index da45899b4..13718ca06 100644 --- a/pkg/cluster/connection_pooler_test.go +++ b/pkg/cluster/connection_pooler_test.go @@ -263,6 +263,7 @@ func TestConnectionPoolerCreateDeletion(t *testing.T) { client := k8sutil.KubernetesClient{ StatefulSetsGetter: clientSet.AppsV1(), ServicesGetter: clientSet.CoreV1(), + PodsGetter: clientSet.CoreV1(), DeploymentsGetter: clientSet.AppsV1(), PostgresqlsGetter: acidClientSet.AcidV1(), SecretsGetter: clientSet.CoreV1(), @@ -372,6 +373,7 @@ func TestConnectionPoolerSync(t *testing.T) { client := k8sutil.KubernetesClient{ StatefulSetsGetter: clientSet.AppsV1(), ServicesGetter: clientSet.CoreV1(), + PodsGetter: clientSet.CoreV1(), DeploymentsGetter: clientSet.AppsV1(), PostgresqlsGetter: acidClientSet.AcidV1(), SecretsGetter: clientSet.CoreV1(), diff --git a/pkg/cluster/k8sres_test.go b/pkg/cluster/k8sres_test.go index 46f2db86c..8e748f042 100644 --- a/pkg/cluster/k8sres_test.go +++ b/pkg/cluster/k8sres_test.go @@ -2233,6 +2233,7 @@ func newLBFakeClient() (k8sutil.KubernetesClient, *fake.Clientset) { return k8sutil.KubernetesClient{ DeploymentsGetter: clientSet.AppsV1(), + PodsGetter: clientSet.CoreV1(), ServicesGetter: clientSet.CoreV1(), }, clientSet } diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index a34279533..1d73a2e0d 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -694,12 +694,10 @@ func (c *Cluster) updateSecret( retentionUsers *[]string, currentTime time.Time) error { var ( - secret *v1.Secret - err error - updateSecret bool - updateSecretMsg string - nextRotationDate time.Time - nextRotationDateStr string + secret *v1.Secret + err error + updateSecret bool + updateSecretMsg string ) // get the secret first @@ -717,6 +715,12 @@ func (c *Cluster) updateSecret( } else if secretUsername == c.systemUsers[constants.ReplicationUserKeyName].Name { userKey = constants.ReplicationUserKeyName userMap = c.systemUsers + } else if secretUsername == constants.ConnectionPoolerUserName { + userKey = constants.ConnectionPoolerUserName + userMap = c.systemUsers + } else if secretUsername == constants.EventStreamSourceSlotPrefix+constants.UserRoleNameSuffix { + userKey = constants.EventStreamSourceSlotPrefix + constants.UserRoleNameSuffix + userMap = c.systemUsers } else { userKey = secretUsername userMap = c.pgUsers @@ -725,46 +729,22 @@ func (c *Cluster) updateSecret( secretName := util.NameFromMeta(secret.ObjectMeta) // if password rotation is enabled update password and username if rotation interval has been passed - if (c.OpConfig.EnablePasswordRotation && !pwdUser.IsDbOwner && - pwdUser.Origin != spec.RoleOriginInfrastructure && pwdUser.Origin != spec.RoleOriginSystem) || - util.SliceContains(c.Spec.UsersWithSecretRotation, secretUsername) || - util.SliceContains(c.Spec.UsersWithInPlaceSecretRotation, secretUsername) { + // rotation can be enabled globally or via the manifest (excluding the Postgres superuser) + rotationEnabledInManifest := secretUsername != constants.SuperuserKeyName && + (util.SliceContains(c.Spec.UsersWithSecretRotation, secretUsername) || + util.SliceContains(c.Spec.UsersWithInPlaceSecretRotation, secretUsername)) - // initialize password rotation setting first rotation date - nextRotationDateStr = string(secret.Data["nextRotation"]) - if nextRotationDate, err = time.ParseInLocation(time.RFC3339, nextRotationDateStr, currentTime.UTC().Location()); err != nil { - nextRotationDate, nextRotationDateStr = c.getNextRotationDate(currentTime) - secret.Data["nextRotation"] = []byte(nextRotationDateStr) - updateSecret = true - updateSecretMsg = fmt.Sprintf("rotation date not found in secret %q. Setting it to %s", secretName, nextRotationDateStr) + // globally enabled rotation is only allowed for manifest and bootstrapped roles + allowedRoleTypes := []spec.RoleOrigin{spec.RoleOriginManifest, spec.RoleOriginBootstrap} + rotationAllowed := !pwdUser.IsDbOwner && util.SliceContains(allowedRoleTypes, pwdUser.Origin) + + if (c.OpConfig.EnablePasswordRotation && rotationAllowed) || rotationEnabledInManifest { + updateSecretMsg, err = c.rotatePasswordInSecret(secret, pwdUser, secretUsername, currentTime, rotationUsers, retentionUsers) + if err != nil { + c.logger.Warnf("password rotation failed for user %s: %v", secretUsername, err) } - - // check if next rotation can happen sooner - // if rotation interval has been decreased - currentRotationDate, nextRotationDateStr := c.getNextRotationDate(currentTime) - if nextRotationDate.After(currentRotationDate) { - nextRotationDate = currentRotationDate - } - - // update password and next rotation date if configured interval has passed - if currentTime.After(nextRotationDate) { - // create rotation user if role is not listed for in-place password update - if !util.SliceContains(c.Spec.UsersWithInPlaceSecretRotation, secretUsername) { - rotationUser := pwdUser - newRotationUsername := secretUsername + currentTime.Format("060102") - rotationUser.Name = newRotationUsername - rotationUser.MemberOf = []string{secretUsername} - (*rotationUsers)[newRotationUsername] = rotationUser - secret.Data["username"] = []byte(newRotationUsername) - - // whenever there is a rotation, check if old rotation users can be deleted - *retentionUsers = append(*retentionUsers, secretUsername) - } - secret.Data["password"] = []byte(util.RandomPassword(constants.PasswordLength)) - secret.Data["nextRotation"] = []byte(nextRotationDateStr) - + if updateSecretMsg != "" { updateSecret = true - updateSecretMsg = fmt.Sprintf("updating secret %q due to password rotation - next rotation date: %s", secretName, nextRotationDateStr) } } else { // username might not match if password rotation has been disabled again @@ -792,7 +772,7 @@ func (c *Cluster) updateSecret( if updateSecret { c.logger.Debugln(updateSecretMsg) if _, err = c.KubeClient.Secrets(secret.Namespace).Update(context.TODO(), secret, metav1.UpdateOptions{}); err != nil { - return fmt.Errorf("could not update secret %q: %v", secretName, err) + return fmt.Errorf("could not update secret %s: %v", secretName, err) } c.Secrets[secret.UID] = secret } @@ -800,11 +780,101 @@ func (c *Cluster) updateSecret( return nil } +func (c *Cluster) rotatePasswordInSecret( + secret *v1.Secret, + secretPgUser spec.PgUser, + secretUsername string, + currentTime time.Time, + rotationUsers *spec.PgUserMap, + retentionUsers *[]string) (string, error) { + var ( + err error + nextRotationDate time.Time + nextRotationDateStr string + updateSecretMsg string + ) + + secretName := util.NameFromMeta(secret.ObjectMeta) + + // initialize password rotation setting first rotation date + nextRotationDateStr = string(secret.Data["nextRotation"]) + if nextRotationDate, err = time.ParseInLocation(time.RFC3339, nextRotationDateStr, currentTime.UTC().Location()); err != nil { + nextRotationDate, nextRotationDateStr = c.getNextRotationDate(currentTime) + secret.Data["nextRotation"] = []byte(nextRotationDateStr) + updateSecretMsg = fmt.Sprintf("rotation date not found in secret %s. Setting it to %s", secretName, nextRotationDateStr) + } + + // check if next rotation can happen sooner + // if rotation interval has been decreased + currentRotationDate, nextRotationDateStr := c.getNextRotationDate(currentTime) + if nextRotationDate.After(currentRotationDate) { + nextRotationDate = currentRotationDate + } + + // update password and next rotation date if configured interval has passed + if currentTime.After(nextRotationDate) { + // create rotation user if role is not listed for in-place password update + if !util.SliceContains(c.Spec.UsersWithInPlaceSecretRotation, secretUsername) { + rotationUser := secretPgUser + newRotationUsername := secretUsername + currentTime.Format("060102") + rotationUser.Name = newRotationUsername + rotationUser.MemberOf = []string{secretUsername} + (*rotationUsers)[newRotationUsername] = rotationUser + secret.Data["username"] = []byte(newRotationUsername) + + // whenever there is a rotation, check if old rotation users can be deleted + *retentionUsers = append(*retentionUsers, secretUsername) + } else { + // when passwords of system users are rotated in place, pods have to be replaced + if secretPgUser.Origin == spec.RoleOriginSystem { + pods, err := c.listPods() + if err != nil { + return "", fmt.Errorf("could not list pods of the statefulset: %v", err) + } + for _, pod := range pods { + if err = c.markRollingUpdateFlagForPod(&pod, + fmt.Sprintf("replace pod due to password rotation of system user %s", secretUsername)); err != nil { + c.logger.Warnf("marking pod for rolling update due to password rotation failed: %v", err) + } + } + } + + // when password of connection pooler is rotated in place, pooler pods have to be replaced + if secretPgUser.Origin == spec.RoleOriginConnectionPooler { + listOptions := metav1.ListOptions{ + LabelSelector: c.poolerLabelsSet(true).String(), + } + poolerPods, err := c.listPoolerPods(listOptions) + if err != nil { + return "", fmt.Errorf("could not list pods of the pooler deployment: %v", err) + } + for _, poolerPod := range poolerPods { + if err = c.markRollingUpdateFlagForPod(&poolerPod, + fmt.Sprintf("replace pooler pod due to password rotation of pooler user %s", secretUsername)); err != nil { + c.logger.Warnf("marking pooler pod for rolling update due to password rotation failed: %v", err) + } + } + } + + // when password of stream user is rotated in place, it should trigger rolling update in FES deployment + if secretPgUser.Origin == spec.RoleOriginStream { + c.logger.Warnf("secret of stream user %s changed", constants.EventStreamSourceSlotPrefix+constants.UserRoleNameSuffix) + } + } + secret.Data["password"] = []byte(util.RandomPassword(constants.PasswordLength)) + secret.Data["nextRotation"] = []byte(nextRotationDateStr) + updateSecretMsg = fmt.Sprintf("updating secret %s due to password rotation - next rotation date: %s", secretName, nextRotationDateStr) + } + + return updateSecretMsg, nil +} + func (c *Cluster) syncRoles() (err error) { c.setProcessName("syncing roles") var ( dbUsers spec.PgUserMap + newUsers spec.PgUserMap userNames []string ) @@ -825,6 +895,7 @@ func (c *Cluster) syncRoles() (err error) { // mapping between original role name and with deletion suffix deletedUsers := map[string]string{} + newUsers = make(map[string]spec.PgUser) // create list of database roles to query for _, u := range c.pgUsers { @@ -845,15 +916,13 @@ func (c *Cluster) syncRoles() (err error) { } } - // add pooler user to list of pgUsers, too - // to check if the pooler user exists or has to be created - if needMasterConnectionPooler(&c.Spec) || needReplicaConnectionPooler(&c.Spec) { - connectionPoolerUser := c.systemUsers[constants.ConnectionPoolerUserKeyName] - userNames = append(userNames, connectionPoolerUser.Name) - - if _, exists := c.pgUsers[connectionPoolerUser.Name]; !exists { - c.pgUsers[connectionPoolerUser.Name] = connectionPoolerUser - } + // copy map for ProduceSyncRequests to include also system users + for userName, pgUser := range c.pgUsers { + newUsers[userName] = pgUser + } + for _, systemUser := range c.systemUsers { + userNames = append(userNames, systemUser.Name) + newUsers[systemUser.Name] = systemUser } dbUsers, err = c.readPgUsersFromDatabase(userNames) @@ -871,7 +940,7 @@ func (c *Cluster) syncRoles() (err error) { } } - pgSyncRequests := c.userSyncStrategy.ProduceSyncRequests(dbUsers, c.pgUsers) + pgSyncRequests := c.userSyncStrategy.ProduceSyncRequests(dbUsers, newUsers) if err = c.userSyncStrategy.ExecuteSyncRequests(pgSyncRequests, c.pgDb); err != nil { return fmt.Errorf("error executing sync statements: %v", err) } diff --git a/pkg/spec/types.go b/pkg/spec/types.go index 02f67d253..66f26a312 100644 --- a/pkg/spec/types.go +++ b/pkg/spec/types.go @@ -32,7 +32,8 @@ const ( RoleOriginTeamsAPI RoleOriginSystem RoleOriginBootstrap - RoleConnectionPooler + RoleOriginConnectionPooler + RoleOriginStream ) type syncUserOperation int @@ -194,7 +195,7 @@ func (r RoleOrigin) String() string { return "system role" case RoleOriginBootstrap: return "bootstrapped role" - case RoleConnectionPooler: + case RoleOriginConnectionPooler: return "connection pooler role" default: panic(fmt.Sprintf("bogus role origin value %d", r)) From b91b69c736adc6aa2bdf82e0fd2a81e6e2bf2614 Mon Sep 17 00:00:00 2001 From: JBWatenbergScality <75977494+JBWatenbergScality@users.noreply.github.com> Date: Fri, 19 Aug 2022 15:14:53 +0200 Subject: [PATCH 08/77] BugFix: Switchover (during a Node drain) fails randomly in synchronous mode (#1984) * Use getSwitchoverCandidate instead of masterCandidate when trying to migrating master pod to a replica Ref: #1983 * Remove unused masterCandidate (replaced by getSwitchoverCandidate) Ref: #1983 --- pkg/cluster/pod.go | 46 ++++++++++------------------------------------ 1 file changed, 10 insertions(+), 36 deletions(-) diff --git a/pkg/cluster/pod.go b/pkg/cluster/pod.go index 74ee59987..ea58184d3 100644 --- a/pkg/cluster/pod.go +++ b/pkg/cluster/pod.go @@ -3,7 +3,6 @@ package cluster import ( "context" "fmt" - "math/rand" "sort" "strconv" "time" @@ -212,42 +211,12 @@ func (c *Cluster) movePodFromEndOfLifeNode(pod *v1.Pod) (*v1.Pod, error) { return newPod, nil } -func (c *Cluster) masterCandidate(oldNodeName string) (*v1.Pod, error) { - - // Wait until at least one replica pod will come up - if err := c.waitForAnyReplicaLabelReady(); err != nil { - c.logger.Warningf("could not find at least one ready replica: %v", err) - } - - replicas, err := c.getRolePods(Replica) - if err != nil { - return nil, fmt.Errorf("could not get replica pods: %v", err) - } - - if len(replicas) == 0 { - c.logger.Warningf("no available master candidates, migration will cause longer downtime of Postgres cluster") - return nil, nil - } - - for i, pod := range replicas { - // look for replicas running on live nodes. Ignore errors when querying the nodes. - if pod.Spec.NodeName != oldNodeName { - eol, err := c.podIsEndOfLife(&pod) - if err == nil && !eol { - return &replicas[i], nil - } - } - } - c.logger.Warningf("no available master candidates on live nodes") - return &replicas[rand.Intn(len(replicas))], nil -} - // MigrateMasterPod migrates master pod via failover to a replica func (c *Cluster) MigrateMasterPod(podName spec.NamespacedName) error { var ( - masterCandidatePod *v1.Pod - err error - eol bool + masterCandidateName spec.NamespacedName + err error + eol bool ) oldMaster, err := c.KubeClient.Pods(podName.Namespace).Get(context.TODO(), podName.Name, metav1.GetOptions{}) @@ -283,13 +252,19 @@ func (c *Cluster) MigrateMasterPod(podName spec.NamespacedName) error { } // We may not have a cached statefulset if the initial cluster sync has aborted, revert to the spec in that case. if *c.Statefulset.Spec.Replicas > 1 { - if masterCandidatePod, err = c.masterCandidate(oldMaster.Spec.NodeName); err != nil { + if masterCandidateName, err = c.getSwitchoverCandidate(oldMaster); err != nil { return fmt.Errorf("could not find suitable replica pod as candidate for failover: %v", err) } } else { c.logger.Warningf("migrating single pod cluster %q, this will cause downtime of the Postgres cluster until pod is back", c.clusterName()) } + masterCandidatePod, err := c.KubeClient.Pods(masterCandidateName.Namespace).Get(context.TODO(), masterCandidateName.Name, metav1.GetOptions{}) + + if err != nil { + return fmt.Errorf("could not get master candidate pod: %v", err) + } + // there are two cases for each postgres cluster that has its master pod on the node to migrate from: // - the cluster has some replicas - migrate one of those if necessary and failover to it // - there are no replicas - just terminate the master and wait until it respawns @@ -306,7 +281,6 @@ func (c *Cluster) MigrateMasterPod(podName spec.NamespacedName) error { return fmt.Errorf("could not move pod: %v", err) } - masterCandidateName := util.NameFromMeta(masterCandidatePod.ObjectMeta) err = retryutil.Retry(1*time.Minute, 5*time.Minute, func() (bool, error) { err := c.Switchover(oldMaster, masterCandidateName) From 3bfd63cbe624eb303d40f6e511e987f4343bb1d7 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Wed, 24 Aug 2022 10:12:50 +0200 Subject: [PATCH 09/77] Make teamId in cluster name optional (#2001) * making teamId in clustername optional * move teamId check to addCluster function --- .../crds/operatorconfigurations.yaml | 3 + charts/postgres-operator/values.yaml | 2 + docs/reference/cluster_manifest.md | 3 +- docs/reference/operator_parameters.md | 5 ++ docs/user.md | 11 ++-- manifests/configmap.yaml | 1 + manifests/operatorconfiguration.crd.yaml | 3 + ...gresql-operator-default-configuration.yaml | 1 + pkg/apis/acid.zalan.do/v1/crds.go | 3 + pkg/apis/acid.zalan.do/v1/marshal.go | 8 +-- .../v1/operator_configuration_type.go | 59 ++++++++++--------- pkg/apis/acid.zalan.do/v1/util.go | 2 +- pkg/apis/acid.zalan.do/v1/util_test.go | 24 +------- pkg/cluster/util.go | 7 ++- pkg/controller/operator_config.go | 1 + pkg/controller/postgresql.go | 38 +++++++++--- pkg/util/config/config.go | 1 + 17 files changed, 96 insertions(+), 76 deletions(-) diff --git a/charts/postgres-operator/crds/operatorconfigurations.yaml b/charts/postgres-operator/crds/operatorconfigurations.yaml index 754ff4232..1fca35e22 100644 --- a/charts/postgres-operator/crds/operatorconfigurations.yaml +++ b/charts/postgres-operator/crds/operatorconfigurations.yaml @@ -88,6 +88,9 @@ spec: enable_spilo_wal_path_compat: type: boolean default: false + enable_team_id_clustername_prefix: + type: boolean + default: false etcd_host: type: string default: "" diff --git a/charts/postgres-operator/values.yaml b/charts/postgres-operator/values.yaml index bfb2eb9c5..9e04f8d2e 100644 --- a/charts/postgres-operator/values.yaml +++ b/charts/postgres-operator/values.yaml @@ -33,6 +33,8 @@ configGeneral: enable_shm_volume: true # enables backwards compatible path between Spilo 12 and Spilo 13+ images enable_spilo_wal_path_compat: false + # operator will sync only clusters where name starts with teamId prefix + enable_team_id_clustername_prefix: false # etcd connection string for Patroni. Empty uses K8s-native DCS. etcd_host: "" # Spilo docker image diff --git a/docs/reference/cluster_manifest.md b/docs/reference/cluster_manifest.md index b41550e22..ba4006f64 100644 --- a/docs/reference/cluster_manifest.md +++ b/docs/reference/cluster_manifest.md @@ -53,8 +53,7 @@ Those parameters are grouped under the `metadata` top-level key. These parameters are grouped directly under the `spec` key in the manifest. * **teamId** - name of the team the cluster belongs to. Changing it after the cluster - creation is not supported. Required field. + name of the team the cluster belongs to. Required field. * **numberOfInstances** total number of instances for a given cluster. The operator parameters diff --git a/docs/reference/operator_parameters.md b/docs/reference/operator_parameters.md index 4f17ec213..ba91146e7 100644 --- a/docs/reference/operator_parameters.md +++ b/docs/reference/operator_parameters.md @@ -92,6 +92,11 @@ Those are top-level keys, containing both leaf keys and groups. * **enable_spilo_wal_path_compat** enables backwards compatible path between Spilo 12 and Spilo 13+ images. The default is `false`. +* **enable_team_id_clustername_prefix** + To lower the risk of name clashes between clusters of different teams you + can turn on this flag and the operator will sync only clusters where the + name starts with the `teamId` (from `spec`) plus `-`. Default is `false`. + * **etcd_host** Etcd connection string for Patroni defined as `host:port`. Not required when Patroni native Kubernetes support is used. The default is empty (use diff --git a/docs/user.md b/docs/user.md index 62850ec4d..76774e8e3 100644 --- a/docs/user.md +++ b/docs/user.md @@ -45,11 +45,12 @@ Make sure, the `spec` section of the manifest contains at least a `teamId`, the The minimum volume size to run the `postgresql` resource on Elastic Block Storage (EBS) is `1Gi`. -Note, that the name of the cluster must start with the `teamId` and `-`. At -Zalando we use team IDs (nicknames) to lower the chance of duplicate cluster -names and colliding entities. The team ID would also be used to query an API to -get all members of a team and create [database roles](#teams-api-roles) for -them. Besides, the maximum cluster name length is 53 characters. +Note, that when `enable_team_id_clustername_prefix` is set to `true` the name +of the cluster must start with the `teamId` and `-`. At Zalando we use team IDs +(nicknames) to lower chances of duplicate cluster names and colliding entities. +The team ID would also be used to query an API to get all members of a team +and create [database roles](#teams-api-roles) for them. Besides, the maximum +cluster name length is 53 characters. ## Watch pods being created diff --git a/manifests/configmap.yaml b/manifests/configmap.yaml index de51049ef..371cd5bd1 100644 --- a/manifests/configmap.yaml +++ b/manifests/configmap.yaml @@ -57,6 +57,7 @@ data: # 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" diff --git a/manifests/operatorconfiguration.crd.yaml b/manifests/operatorconfiguration.crd.yaml index 8bf373f99..3d4892bab 100644 --- a/manifests/operatorconfiguration.crd.yaml +++ b/manifests/operatorconfiguration.crd.yaml @@ -86,6 +86,9 @@ spec: enable_spilo_wal_path_compat: type: boolean default: false + enable_team_id_clustername_prefix: + type: boolean + default: false etcd_host: type: string default: "" diff --git a/manifests/postgresql-operator-default-configuration.yaml b/manifests/postgresql-operator-default-configuration.yaml index bf59aa237..645e5138b 100644 --- a/manifests/postgresql-operator-default-configuration.yaml +++ b/manifests/postgresql-operator-default-configuration.yaml @@ -11,6 +11,7 @@ configuration: 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 diff --git a/pkg/apis/acid.zalan.do/v1/crds.go b/pkg/apis/acid.zalan.do/v1/crds.go index db11771e4..7b06ee233 100644 --- a/pkg/apis/acid.zalan.do/v1/crds.go +++ b/pkg/apis/acid.zalan.do/v1/crds.go @@ -1112,6 +1112,9 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{ "enable_spilo_wal_path_compat": { Type: "boolean", }, + "enable_team_id_clustername_prefix": { + Type: "boolean", + }, "etcd_host": { Type: "string", }, diff --git a/pkg/apis/acid.zalan.do/v1/marshal.go b/pkg/apis/acid.zalan.do/v1/marshal.go index f4167ce92..a221d622b 100644 --- a/pkg/apis/acid.zalan.do/v1/marshal.go +++ b/pkg/apis/acid.zalan.do/v1/marshal.go @@ -110,15 +110,9 @@ func (p *Postgresql) UnmarshalJSON(data []byte) error { } tmp2 := Postgresql(tmp) - if clusterName, err := extractClusterName(tmp2.ObjectMeta.Name, tmp2.Spec.TeamID); err != nil { - tmp2.Error = err.Error() - tmp2.Status = PostgresStatus{PostgresClusterStatus: ClusterStatusInvalid} - } else if err := validateCloneClusterDescription(tmp2.Spec.Clone); err != nil { - + if err := validateCloneClusterDescription(tmp2.Spec.Clone); err != nil { tmp2.Error = err.Error() tmp2.Status.PostgresClusterStatus = ClusterStatusInvalid - } else { - tmp2.Spec.ClusterName = clusterName } *p = tmp2 diff --git a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go index 2864de2a2..de149033a 100644 --- a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go +++ b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go @@ -228,35 +228,36 @@ type OperatorLogicalBackupConfiguration struct { // OperatorConfigurationData defines the operation config type OperatorConfigurationData struct { - EnableCRDRegistration *bool `json:"enable_crd_registration,omitempty"` - EnableCRDValidation *bool `json:"enable_crd_validation,omitempty"` - CRDCategories []string `json:"crd_categories,omitempty"` - EnableLazySpiloUpgrade bool `json:"enable_lazy_spilo_upgrade,omitempty"` - EnablePgVersionEnvVar bool `json:"enable_pgversion_env_var,omitempty"` - EnableSpiloWalPathCompat bool `json:"enable_spilo_wal_path_compat,omitempty"` - EtcdHost string `json:"etcd_host,omitempty"` - KubernetesUseConfigMaps bool `json:"kubernetes_use_configmaps,omitempty"` - DockerImage string `json:"docker_image,omitempty"` - Workers uint32 `json:"workers,omitempty"` - ResyncPeriod Duration `json:"resync_period,omitempty"` - RepairPeriod Duration `json:"repair_period,omitempty"` - SetMemoryRequestToLimit bool `json:"set_memory_request_to_limit,omitempty"` - ShmVolume *bool `json:"enable_shm_volume,omitempty"` - SidecarImages map[string]string `json:"sidecar_docker_images,omitempty"` // deprecated in favour of SidecarContainers - SidecarContainers []v1.Container `json:"sidecars,omitempty"` - PostgresUsersConfiguration PostgresUsersConfiguration `json:"users"` - MajorVersionUpgrade MajorVersionUpgradeConfiguration `json:"major_version_upgrade"` - Kubernetes KubernetesMetaConfiguration `json:"kubernetes"` - PostgresPodResources PostgresPodResourcesDefaults `json:"postgres_pod_resources"` - Timeouts OperatorTimeouts `json:"timeouts"` - LoadBalancer LoadBalancerConfiguration `json:"load_balancer"` - AWSGCP AWSGCPConfiguration `json:"aws_or_gcp"` - OperatorDebug OperatorDebugConfiguration `json:"debug"` - TeamsAPI TeamsAPIConfiguration `json:"teams_api"` - LoggingRESTAPI LoggingRESTAPIConfiguration `json:"logging_rest_api"` - Scalyr ScalyrConfiguration `json:"scalyr"` - LogicalBackup OperatorLogicalBackupConfiguration `json:"logical_backup"` - ConnectionPooler ConnectionPoolerConfiguration `json:"connection_pooler"` + EnableCRDRegistration *bool `json:"enable_crd_registration,omitempty"` + EnableCRDValidation *bool `json:"enable_crd_validation,omitempty"` + CRDCategories []string `json:"crd_categories,omitempty"` + EnableLazySpiloUpgrade bool `json:"enable_lazy_spilo_upgrade,omitempty"` + EnablePgVersionEnvVar bool `json:"enable_pgversion_env_var,omitempty"` + EnableSpiloWalPathCompat bool `json:"enable_spilo_wal_path_compat,omitempty"` + EnableTeamIdClusternamePrefix bool `json:"enable_team_id_clustername_prefix,omitempty"` + EtcdHost string `json:"etcd_host,omitempty"` + KubernetesUseConfigMaps bool `json:"kubernetes_use_configmaps,omitempty"` + DockerImage string `json:"docker_image,omitempty"` + Workers uint32 `json:"workers,omitempty"` + ResyncPeriod Duration `json:"resync_period,omitempty"` + RepairPeriod Duration `json:"repair_period,omitempty"` + SetMemoryRequestToLimit bool `json:"set_memory_request_to_limit,omitempty"` + ShmVolume *bool `json:"enable_shm_volume,omitempty"` + SidecarImages map[string]string `json:"sidecar_docker_images,omitempty"` // deprecated in favour of SidecarContainers + SidecarContainers []v1.Container `json:"sidecars,omitempty"` + PostgresUsersConfiguration PostgresUsersConfiguration `json:"users"` + MajorVersionUpgrade MajorVersionUpgradeConfiguration `json:"major_version_upgrade"` + Kubernetes KubernetesMetaConfiguration `json:"kubernetes"` + PostgresPodResources PostgresPodResourcesDefaults `json:"postgres_pod_resources"` + Timeouts OperatorTimeouts `json:"timeouts"` + LoadBalancer LoadBalancerConfiguration `json:"load_balancer"` + AWSGCP AWSGCPConfiguration `json:"aws_or_gcp"` + OperatorDebug OperatorDebugConfiguration `json:"debug"` + TeamsAPI TeamsAPIConfiguration `json:"teams_api"` + LoggingRESTAPI LoggingRESTAPIConfiguration `json:"logging_rest_api"` + Scalyr ScalyrConfiguration `json:"scalyr"` + LogicalBackup OperatorLogicalBackupConfiguration `json:"logical_backup"` + ConnectionPooler ConnectionPoolerConfiguration `json:"connection_pooler"` MinInstances int32 `json:"min_instances,omitempty"` MaxInstances int32 `json:"max_instances,omitempty"` diff --git a/pkg/apis/acid.zalan.do/v1/util.go b/pkg/apis/acid.zalan.do/v1/util.go index a795ec685..719defe93 100644 --- a/pkg/apis/acid.zalan.do/v1/util.go +++ b/pkg/apis/acid.zalan.do/v1/util.go @@ -46,7 +46,7 @@ func parseWeekday(s string) (time.Weekday, error) { return time.Weekday(weekday), nil } -func extractClusterName(clusterName string, teamName string) (string, error) { +func ExtractClusterName(clusterName string, teamName string) (string, error) { teamNameLen := len(teamName) if len(clusterName) < teamNameLen+2 { return "", fmt.Errorf("cluster name must match {TEAM}-{NAME} format. Got cluster name '%v', team name '%v'", clusterName, teamName) diff --git a/pkg/apis/acid.zalan.do/v1/util_test.go b/pkg/apis/acid.zalan.do/v1/util_test.go index 2ff40d347..8c570650e 100644 --- a/pkg/apis/acid.zalan.do/v1/util_test.go +++ b/pkg/apis/acid.zalan.do/v1/util_test.go @@ -330,29 +330,11 @@ var unmarshalCluster = []struct { Clone: &CloneDescription{ ClusterName: "acid-batman", }, - ClusterName: "testcluster1", }, Error: "", }, marshal: []byte(`{"kind":"Postgresql","apiVersion":"acid.zalan.do/v1","metadata":{"name":"acid-testcluster1","creationTimestamp":null},"spec":{"postgresql":{"version":"9.6","parameters":{"log_statement":"all","max_connections":"10","shared_buffers":"32MB"}},"pod_priority_class_name":"spilo-pod-priority","volume":{"size":"5Gi","storageClass":"SSD", "subPath": "subdir"},"enableShmVolume":false,"patroni":{"initdb":{"data-checksums":"true","encoding":"UTF8","locale":"en_US.UTF-8"},"pg_hba":["hostssl all all 0.0.0.0/0 md5","host all all 0.0.0.0/0 md5"],"ttl":30,"loop_wait":10,"retry_timeout":10,"maximum_lag_on_failover":33554432,"slots":{"permanent_logical_1":{"database":"foo","plugin":"pgoutput","type":"logical"}}},"resources":{"requests":{"cpu":"10m","memory":"50Mi"},"limits":{"cpu":"300m","memory":"3000Mi"}},"teamId":"acid","allowedSourceRanges":["127.0.0.1/32"],"numberOfInstances":2,"users":{"zalando":["superuser","createdb"]},"maintenanceWindows":["Mon:01:00-06:00","Sat:00:00-04:00","05:00-05:15"],"clone":{"cluster":"acid-batman"}},"status":{"PostgresClusterStatus":""}}`), err: nil}, - { - about: "example with teamId set in input", - in: []byte(`{"kind": "Postgresql","apiVersion": "acid.zalan.do/v1","metadata": {"name": "teapot-testcluster1"}, "spec": {"teamId": "acid"}}`), - out: Postgresql{ - TypeMeta: metav1.TypeMeta{ - Kind: "Postgresql", - APIVersion: "acid.zalan.do/v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "teapot-testcluster1", - }, - Spec: PostgresSpec{TeamID: "acid"}, - Status: PostgresStatus{PostgresClusterStatus: ClusterStatusInvalid}, - Error: errors.New("name must match {TEAM}-{NAME} format").Error(), - }, - marshal: []byte(`{"kind":"Postgresql","apiVersion":"acid.zalan.do/v1","metadata":{"name":"teapot-testcluster1","creationTimestamp":null},"spec":{"postgresql":{"version":"","parameters":null},"volume":{"size":"","storageClass":""},"patroni":{"initdb":null,"pg_hba":null,"ttl":0,"loop_wait":0,"retry_timeout":0,"maximum_lag_on_failover":0,"slots":null},"teamId":"acid","allowedSourceRanges":null,"numberOfInstances":0,"users":null,"clone":null},"status":{"PostgresClusterStatus":"Invalid"}}`), - err: nil}, { about: "example with clone", in: []byte(`{"kind": "Postgresql","apiVersion": "acid.zalan.do/v1","metadata": {"name": "acid-testcluster1"}, "spec": {"teamId": "acid", "clone": {"cluster": "team-batman"}}}`), @@ -369,7 +351,6 @@ var unmarshalCluster = []struct { Clone: &CloneDescription{ ClusterName: "team-batman", }, - ClusterName: "testcluster1", }, Error: "", }, @@ -391,7 +372,6 @@ var unmarshalCluster = []struct { StandbyCluster: &StandbyDescription{ S3WalPath: "s3://custom/path/to/bucket/", }, - ClusterName: "testcluster1", }, Error: "", }, @@ -628,10 +608,10 @@ func TestServiceAnnotations(t *testing.T) { func TestClusterName(t *testing.T) { for _, tt := range clusterNames { t.Run(tt.about, func(t *testing.T) { - name, err := extractClusterName(tt.in, tt.inTeam) + name, err := ExtractClusterName(tt.in, tt.inTeam) if err != nil { if tt.err == nil || err.Error() != tt.err.Error() { - t.Errorf("extractClusterName expected error: %v, got: %v", tt.err, err) + t.Errorf("ExtractClusterName expected error: %v, got: %v", tt.err, err) } return } else if tt.err != nil { diff --git a/pkg/cluster/util.go b/pkg/cluster/util.go index 0bfda78bf..37dead012 100644 --- a/pkg/cluster/util.go +++ b/pkg/cluster/util.go @@ -244,7 +244,12 @@ func getPostgresContainer(podSpec *v1.PodSpec) (pgContainer v1.Container) { func (c *Cluster) getTeamMembers(teamID string) ([]string, error) { if teamID == "" { - return nil, fmt.Errorf("no teamId specified") + msg := "no teamId specified" + if c.OpConfig.EnableTeamIdClusternamePrefix { + return nil, fmt.Errorf(msg) + } + c.logger.Warnf(msg) + return nil, nil } members := []string{} diff --git a/pkg/controller/operator_config.go b/pkg/controller/operator_config.go index 11a15c89a..d66ff7f11 100644 --- a/pkg/controller/operator_config.go +++ b/pkg/controller/operator_config.go @@ -36,6 +36,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur result.EnableLazySpiloUpgrade = fromCRD.EnableLazySpiloUpgrade result.EnablePgVersionEnvVar = fromCRD.EnablePgVersionEnvVar result.EnableSpiloWalPathCompat = fromCRD.EnableSpiloWalPathCompat + result.EnableTeamIdClusternamePrefix = fromCRD.EnableTeamIdClusternamePrefix result.EtcdHost = fromCRD.EtcdHost result.KubernetesUseConfigMaps = fromCRD.KubernetesUseConfigMaps result.DockerImage = util.Coalesce(fromCRD.DockerImage, "registry.opensource.zalan.do/acid/spilo-14:2.1-p6") diff --git a/pkg/controller/postgresql.go b/pkg/controller/postgresql.go index 4e07b4e0d..861d95032 100644 --- a/pkg/controller/postgresql.go +++ b/pkg/controller/postgresql.go @@ -158,7 +158,15 @@ func (c *Controller) acquireInitialListOfClusters() error { return nil } -func (c *Controller) addCluster(lg *logrus.Entry, clusterName spec.NamespacedName, pgSpec *acidv1.Postgresql) *cluster.Cluster { +func (c *Controller) addCluster(lg *logrus.Entry, clusterName spec.NamespacedName, pgSpec *acidv1.Postgresql) (*cluster.Cluster, error) { + + if c.opConfig.EnableTeamIdClusternamePrefix { + if _, err := acidv1.ExtractClusterName(clusterName.Name, pgSpec.Spec.TeamID); err != nil { + c.KubeClient.SetPostgresCRDStatus(clusterName, acidv1.ClusterStatusInvalid) + return nil, err + } + } + cl := cluster.New(c.makeClusterConfig(), c.KubeClient, *pgSpec, lg, c.eventRecorder) cl.Run(c.stopCh) teamName := strings.ToLower(cl.Spec.TeamID) @@ -171,12 +179,13 @@ func (c *Controller) addCluster(lg *logrus.Entry, clusterName spec.NamespacedNam c.clusterLogs[clusterName] = ringlog.New(c.opConfig.RingLogLines) c.clusterHistory[clusterName] = ringlog.New(c.opConfig.ClusterHistoryEntries) - return cl + return cl, nil } func (c *Controller) processEvent(event ClusterEvent) { var clusterName spec.NamespacedName var clHistory ringlog.RingLogger + var err error lg := c.logger.WithField("worker", event.WorkerID) @@ -216,7 +225,7 @@ func (c *Controller) processEvent(event ClusterEvent) { c.mergeDeprecatedPostgreSQLSpecParameters(&event.NewSpec.Spec) } - if err := c.submitRBACCredentials(event); err != nil { + if err = c.submitRBACCredentials(event); err != nil { c.logger.Warnf("pods and/or Patroni may misfunction due to the lack of permissions: %v", err) } @@ -231,15 +240,20 @@ func (c *Controller) processEvent(event ClusterEvent) { lg.Infof("creating a new Postgres cluster") - cl = c.addCluster(lg, clusterName, event.NewSpec) + cl, err = c.addCluster(lg, clusterName, event.NewSpec) + if err != nil { + lg.Errorf("creation of cluster is blocked: %v", err) + return + } c.curWorkerCluster.Store(event.WorkerID, cl) - if err := cl.Create(); err != nil { + err = cl.Create() + if err != nil { + cl.Status = acidv1.PostgresStatus{PostgresClusterStatus: acidv1.ClusterStatusInvalid} cl.Error = fmt.Sprintf("could not create cluster: %v", err) lg.Error(cl.Error) c.eventRecorder.Eventf(cl.GetReference(), v1.EventTypeWarning, "Create", "%v", cl.Error) - return } @@ -252,7 +266,8 @@ func (c *Controller) processEvent(event ClusterEvent) { return } c.curWorkerCluster.Store(event.WorkerID, cl) - if err := cl.Update(event.OldSpec, event.NewSpec); err != nil { + err = cl.Update(event.OldSpec, event.NewSpec) + if err != nil { cl.Error = fmt.Sprintf("could not update cluster: %v", err) lg.Error(cl.Error) @@ -303,11 +318,16 @@ func (c *Controller) processEvent(event ClusterEvent) { // no race condition because a cluster is always processed by single worker if !clusterFound { - cl = c.addCluster(lg, clusterName, event.NewSpec) + cl, err = c.addCluster(lg, clusterName, event.NewSpec) + if err != nil { + lg.Errorf("syncing of cluster is blocked: %v", err) + return + } } c.curWorkerCluster.Store(event.WorkerID, cl) - if err := cl.Sync(event.NewSpec); err != nil { + err = cl.Sync(event.NewSpec) + if err != nil { cl.Error = fmt.Sprintf("could not sync cluster: %v", err) c.eventRecorder.Eventf(cl.GetReference(), v1.EventTypeWarning, "Sync", "%v", cl.Error) lg.Error(cl.Error) diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index 5d1fff851..231929bb8 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -226,6 +226,7 @@ type Config struct { EnableCrossNamespaceSecret bool `name:"enable_cross_namespace_secret" default:"false"` EnablePgVersionEnvVar bool `name:"enable_pgversion_env_var" default:"true"` EnableSpiloWalPathCompat bool `name:"enable_spilo_wal_path_compat" default:"false"` + EnableTeamIdClusternamePrefix bool `name:"enable_team_id_clustername_prefix" default:"false"` MajorVersionUpgradeMode string `name:"major_version_upgrade_mode" default:"off"` MajorVersionUpgradeTeamAllowList []string `name:"major_version_upgrade_team_allow_list" default:""` MinimalMajorVersion string `name:"minimal_major_version" default:"9.6"` From e11edcdcde897ec16e6a9903f97a6cad2e96eee2 Mon Sep 17 00:00:00 2001 From: Jacob Lorenzen Date: Wed, 24 Aug 2022 13:56:14 +0200 Subject: [PATCH 10/77] fix: api version for OperatorConfiguration (#2007) * fix: api version for OperatorConfiguration --- docs/user.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user.md b/docs/user.md index 76774e8e3..43e729909 100644 --- a/docs/user.md +++ b/docs/user.md @@ -223,7 +223,7 @@ the user name, password etc. The secret itself is referenced by the above list them separately. ```yaml -apiVersion: v1 +apiVersion: "acid.zalan.do/v1" kind: OperatorConfiguration metadata: name: postgresql-operator-configuration From ef324494a0fd7782b344a9421cd1cd4bbaf5adca Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Wed, 24 Aug 2022 16:28:49 +0200 Subject: [PATCH 11/77] fetch pooler and fes_user system user only when corresponding features are used (#2009) * fetch pooler and fes_user system user only when corresponding features are used * cover error case in unit test * use string formatting instead of + --- pkg/cluster/cluster.go | 16 ++++++++-------- pkg/cluster/cluster_test.go | 30 +++++++++++++++++++++++++++++- pkg/cluster/connection_pooler.go | 2 +- pkg/cluster/k8sres.go | 6 +++--- pkg/cluster/streams_test.go | 4 ++-- pkg/cluster/sync.go | 24 ++++++++++++++---------- pkg/util/constants/roles.go | 3 ++- 7 files changed, 59 insertions(+), 26 deletions(-) diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index 6f9f17763..611295f5f 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -1127,7 +1127,7 @@ func (c *Cluster) initSystemUsers() { // replication users for event streams are another exception // the operator will create one replication user for all streams if len(c.Spec.Streams) > 0 { - username := constants.EventStreamSourceSlotPrefix + constants.UserRoleNameSuffix + username := fmt.Sprintf("%s%s", constants.EventStreamSourceSlotPrefix, constants.UserRoleNameSuffix) streamUser := spec.PgUser{ Origin: spec.RoleOriginStream, Name: username, @@ -1136,8 +1136,8 @@ func (c *Cluster) initSystemUsers() { Password: util.RandomPassword(constants.PasswordLength), } - if _, exists := c.systemUsers[username]; !exists { - c.systemUsers[username] = streamUser + if _, exists := c.systemUsers[constants.EventStreamUserKeyName]; !exists { + c.systemUsers[constants.EventStreamUserKeyName] = streamUser } } } @@ -1155,9 +1155,9 @@ func (c *Cluster) initPreparedDatabaseRoles() error { constants.WriterRoleNameSuffix: constants.ReaderRoleNameSuffix, } defaultUsers := map[string]string{ - constants.OwnerRoleNameSuffix + constants.UserRoleNameSuffix: constants.OwnerRoleNameSuffix, - constants.ReaderRoleNameSuffix + constants.UserRoleNameSuffix: constants.ReaderRoleNameSuffix, - constants.WriterRoleNameSuffix + constants.UserRoleNameSuffix: constants.WriterRoleNameSuffix, + fmt.Sprintf("%s%s", constants.OwnerRoleNameSuffix, constants.UserRoleNameSuffix): constants.OwnerRoleNameSuffix, + fmt.Sprintf("%s%s", constants.ReaderRoleNameSuffix, constants.UserRoleNameSuffix): constants.ReaderRoleNameSuffix, + fmt.Sprintf("%s%s", constants.WriterRoleNameSuffix, constants.UserRoleNameSuffix): constants.WriterRoleNameSuffix, } for preparedDbName, preparedDB := range c.Spec.PreparedDatabases { @@ -1218,7 +1218,7 @@ func (c *Cluster) initDefaultRoles(defaultRoles map[string]string, admin, prefix c.logger.Warn("secretNamespace ignored because enable_cross_namespace_secret set to false. Creating secrets in cluster namespace.") } } - roleName := prefix + defaultRole + roleName := fmt.Sprintf("%s%s", prefix, defaultRole) flags := []string{constants.RoleFlagNoLogin} if defaultRole[len(defaultRole)-5:] == constants.UserRoleNameSuffix { @@ -1236,7 +1236,7 @@ func (c *Cluster) initDefaultRoles(defaultRoles map[string]string, admin, prefix adminRole = admin isOwner = true } else { - adminRole = prefix + constants.OwnerRoleNameSuffix + adminRole = fmt.Sprintf("%s%s", prefix, constants.OwnerRoleNameSuffix) } newRole := spec.PgUser{ diff --git a/pkg/cluster/cluster_test.go b/pkg/cluster/cluster_test.go index 637c1f6e4..f36db09b0 100644 --- a/pkg/cluster/cluster_test.go +++ b/pkg/cluster/cluster_test.go @@ -759,11 +759,14 @@ func TestServiceAnnotations(t *testing.T) { func TestInitSystemUsers(t *testing.T) { testName := "Test system users initialization" - // default cluster without connection pooler + // default cluster without connection pooler and event streams cl.initSystemUsers() if _, exist := cl.systemUsers[constants.ConnectionPoolerUserKeyName]; exist { t.Errorf("%s, connection pooler user is present", testName) } + if _, exist := cl.systemUsers[constants.EventStreamUserKeyName]; exist { + t.Errorf("%s, stream user is present", testName) + } // cluster with connection pooler cl.Spec.EnableConnectionPooler = boolToPointer(true) @@ -805,6 +808,31 @@ func TestInitSystemUsers(t *testing.T) { if _, exist := cl.systemUsers["pooler"]; !exist { t.Errorf("%s, System users are not allowed to be a connection pool user", testName) } + + // using stream user in manifest but no streams defined should be treated like normal robot user + streamUser := fmt.Sprintf("%s%s", constants.EventStreamSourceSlotPrefix, constants.UserRoleNameSuffix) + cl.Spec.Users = map[string]acidv1.UserFlags{streamUser: []string{}} + cl.initSystemUsers() + if _, exist := cl.systemUsers[constants.EventStreamUserKeyName]; exist { + t.Errorf("%s, stream user is present", testName) + } + + // cluster with streams + cl.Spec.Streams = []acidv1.Stream{ + { + ApplicationId: "test-app", + Database: "test_db", + Tables: map[string]acidv1.StreamTable{ + "data.test_table": acidv1.StreamTable{ + EventType: "test_event", + }, + }, + }, + } + cl.initSystemUsers() + if _, exist := cl.systemUsers[constants.EventStreamUserKeyName]; !exist { + t.Errorf("%s, stream user is not present", testName) + } } func TestPreparedDatabases(t *testing.T) { diff --git a/pkg/cluster/connection_pooler.go b/pkg/cluster/connection_pooler.go index 66751653c..340f6a3ae 100644 --- a/pkg/cluster/connection_pooler.go +++ b/pkg/cluster/connection_pooler.go @@ -46,7 +46,7 @@ type ConnectionPoolerObjects struct { func (c *Cluster) connectionPoolerName(role PostgresRole) string { name := c.Name + "-pooler" if role == Replica { - name = name + "-repl" + name = fmt.Sprintf("%s-%s", name, "repl") } return name } diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index a3628efa4..d14bc1572 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -80,7 +80,7 @@ func (c *Cluster) statefulSetName() string { func (c *Cluster) endpointName(role PostgresRole) string { name := c.Name if role == Replica { - name = name + "-repl" + name = fmt.Sprintf("%s-%s", name, "repl") } return name @@ -89,7 +89,7 @@ func (c *Cluster) endpointName(role PostgresRole) string { func (c *Cluster) serviceName(role PostgresRole) string { name := c.Name if role == Replica { - name = name + "-repl" + name = fmt.Sprintf("%s-%s", name, "repl") } return name @@ -2238,7 +2238,7 @@ func (c *Cluster) generateLogicalBackupPodEnvVars() []v1.EnvVar { // getLogicalBackupJobName returns the name; the job itself may not exists func (c *Cluster) getLogicalBackupJobName() (jobName string) { - return trimCronjobName(c.OpConfig.LogicalBackupJobPrefix + c.clusterName().Name) + return trimCronjobName(fmt.Sprintf("%s%s", c.OpConfig.LogicalBackupJobPrefix, c.clusterName().Name)) } // Return an array of ownerReferences to make an arbitraty object dependent on diff --git a/pkg/cluster/streams_test.go b/pkg/cluster/streams_test.go index 0094708a4..674d2738d 100644 --- a/pkg/cluster/streams_test.go +++ b/pkg/cluster/streams_test.go @@ -40,7 +40,7 @@ var ( namespace string = "default" appId string = "test-app" dbName string = "foo" - fesUser string = constants.EventStreamSourceSlotPrefix + constants.UserRoleNameSuffix + fesUser string = fmt.Sprintf("%s%s", constants.EventStreamSourceSlotPrefix, constants.UserRoleNameSuffix) fesName string = fmt.Sprintf("%s-%s", clusterName, appId) slotName string = fmt.Sprintf("%s_%s_%s", constants.EventStreamSourceSlotPrefix, dbName, strings.Replace(appId, "-", "_", -1)) @@ -55,7 +55,7 @@ var ( }, Spec: acidv1.PostgresSpec{ Databases: map[string]string{ - dbName: dbName + constants.UserRoleNameSuffix, + dbName: fmt.Sprintf("%s%s", dbName, constants.UserRoleNameSuffix), }, Streams: []acidv1.Stream{ { diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index 1d73a2e0d..28bd05a5a 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -715,12 +715,16 @@ func (c *Cluster) updateSecret( } else if secretUsername == c.systemUsers[constants.ReplicationUserKeyName].Name { userKey = constants.ReplicationUserKeyName userMap = c.systemUsers - } else if secretUsername == constants.ConnectionPoolerUserName { - userKey = constants.ConnectionPoolerUserName - userMap = c.systemUsers - } else if secretUsername == constants.EventStreamSourceSlotPrefix+constants.UserRoleNameSuffix { - userKey = constants.EventStreamSourceSlotPrefix + constants.UserRoleNameSuffix - userMap = c.systemUsers + } else if _, exists := c.systemUsers[constants.ConnectionPoolerUserKeyName]; exists { + if secretUsername == c.systemUsers[constants.ConnectionPoolerUserKeyName].Name { + userKey = constants.ConnectionPoolerUserName + userMap = c.systemUsers + } + } else if _, exists := c.systemUsers[constants.EventStreamUserKeyName]; exists { + if secretUsername == c.systemUsers[constants.EventStreamUserKeyName].Name { + userKey = fmt.Sprintf("%s%s", constants.EventStreamSourceSlotPrefix, constants.UserRoleNameSuffix) + userMap = c.systemUsers + } } else { userKey = secretUsername userMap = c.pgUsers @@ -816,7 +820,7 @@ func (c *Cluster) rotatePasswordInSecret( // create rotation user if role is not listed for in-place password update if !util.SliceContains(c.Spec.UsersWithInPlaceSecretRotation, secretUsername) { rotationUser := secretPgUser - newRotationUsername := secretUsername + currentTime.Format("060102") + newRotationUsername := fmt.Sprintf("%s%s", secretUsername, currentTime.Format("060102")) rotationUser.Name = newRotationUsername rotationUser.MemberOf = []string{secretUsername} (*rotationUsers)[newRotationUsername] = rotationUser @@ -976,7 +980,7 @@ func (c *Cluster) syncDatabases() error { for preparedDatabaseName := range c.Spec.PreparedDatabases { _, exists := currentDatabases[preparedDatabaseName] if !exists { - createDatabases[preparedDatabaseName] = preparedDatabaseName + constants.OwnerRoleNameSuffix + createDatabases[preparedDatabaseName] = fmt.Sprintf("%s%s", preparedDatabaseName, constants.OwnerRoleNameSuffix) preparedDatabases = append(preparedDatabases, preparedDatabaseName) } } @@ -1077,9 +1081,9 @@ func (c *Cluster) syncPreparedSchemas(databaseName string, preparedSchemas map[s if createPreparedSchemas, equal := util.SubstractStringSlices(schemas, currentSchemas); !equal { for _, schemaName := range createPreparedSchemas { owner := constants.OwnerRoleNameSuffix - dbOwner := databaseName + owner + dbOwner := fmt.Sprintf("%s%s", databaseName, owner) if preparedSchemas[schemaName].DefaultRoles == nil || *preparedSchemas[schemaName].DefaultRoles { - owner = databaseName + "_" + schemaName + owner + owner = fmt.Sprintf("%s_%s%s", databaseName, schemaName, owner) } else { owner = dbOwner } diff --git a/pkg/util/constants/roles.go b/pkg/util/constants/roles.go index dd906fe80..8c81e2f04 100644 --- a/pkg/util/constants/roles.go +++ b/pkg/util/constants/roles.go @@ -4,8 +4,9 @@ package constants const ( PasswordLength = 64 SuperuserKeyName = "superuser" - ConnectionPoolerUserKeyName = "pooler" ReplicationUserKeyName = "replication" + ConnectionPoolerUserKeyName = "pooler" + EventStreamUserKeyName = "streamer" RoleFlagSuperuser = "SUPERUSER" RoleFlagInherit = "INHERIT" RoleFlagLogin = "LOGIN" From 21d00e2ed736318deaeaf9d29ffd741d38a9dbac Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Wed, 24 Aug 2022 17:33:39 +0200 Subject: [PATCH 12/77] rework map selection in updateSecret (#2010) --- pkg/cluster/pod.go | 2 +- pkg/cluster/sync.go | 26 ++++++++++++++++---------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/pkg/cluster/pod.go b/pkg/cluster/pod.go index ea58184d3..73f077058 100644 --- a/pkg/cluster/pod.go +++ b/pkg/cluster/pod.go @@ -277,7 +277,7 @@ func (c *Cluster) MigrateMasterPod(podName spec.NamespacedName) error { return nil } - if masterCandidatePod, err = c.movePodFromEndOfLifeNode(masterCandidatePod); err != nil { + if _, err = c.movePodFromEndOfLifeNode(masterCandidatePod); err != nil { return fmt.Errorf("could not move pod: %v", err) } diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index 28bd05a5a..bb68eec1c 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -715,20 +715,26 @@ func (c *Cluster) updateSecret( } else if secretUsername == c.systemUsers[constants.ReplicationUserKeyName].Name { userKey = constants.ReplicationUserKeyName userMap = c.systemUsers - } else if _, exists := c.systemUsers[constants.ConnectionPoolerUserKeyName]; exists { - if secretUsername == c.systemUsers[constants.ConnectionPoolerUserKeyName].Name { - userKey = constants.ConnectionPoolerUserName - userMap = c.systemUsers - } - } else if _, exists := c.systemUsers[constants.EventStreamUserKeyName]; exists { - if secretUsername == c.systemUsers[constants.EventStreamUserKeyName].Name { - userKey = fmt.Sprintf("%s%s", constants.EventStreamSourceSlotPrefix, constants.UserRoleNameSuffix) - userMap = c.systemUsers - } } else { userKey = secretUsername userMap = c.pgUsers } + + // use system user when pooler is enabled and pooler user is specfied in manifest + if _, exists := c.systemUsers[constants.ConnectionPoolerUserKeyName]; exists { + if secretUsername == c.systemUsers[constants.ConnectionPoolerUserKeyName].Name { + userKey = constants.ConnectionPoolerUserName + userMap = c.systemUsers + } + } + // use system user when streams are defined and fes_user is specfied in manifest + if _, exists := c.systemUsers[constants.EventStreamUserKeyName]; exists { + if secretUsername == c.systemUsers[constants.EventStreamUserKeyName].Name { + userKey = fmt.Sprintf("%s%s", constants.EventStreamSourceSlotPrefix, constants.UserRoleNameSuffix) + userMap = c.systemUsers + } + } + pwdUser := userMap[userKey] secretName := util.NameFromMeta(secret.ObjectMeta) From 89375186b3fd3df2e787f9db5a85c13166729581 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Thu, 25 Aug 2022 18:29:54 +0200 Subject: [PATCH 13/77] use old LB DNS format when teamId prefix is disabled (#2011) * use old LB DNS format when teamId prefix is disabled * support both old and new format in external-dns * switch dns template from team to namespace --- .../crds/operatorconfigurations.yaml | 4 +- charts/postgres-operator/values.yaml | 4 +- docs/reference/operator_parameters.md | 8 +- docs/user.md | 4 +- manifests/configmap.yaml | 4 +- manifests/operatorconfiguration.crd.yaml | 4 +- ...gresql-operator-default-configuration.yaml | 4 +- pkg/cluster/cluster_test.go | 81 ++++++++++++------- pkg/cluster/k8sres.go | 7 +- pkg/cluster/util.go | 34 +++++++- pkg/controller/postgresql.go | 9 ++- pkg/util/config/config.go | 4 +- 12 files changed, 113 insertions(+), 54 deletions(-) diff --git a/charts/postgres-operator/crds/operatorconfigurations.yaml b/charts/postgres-operator/crds/operatorconfigurations.yaml index 1fca35e22..ca47ab3b8 100644 --- a/charts/postgres-operator/crds/operatorconfigurations.yaml +++ b/charts/postgres-operator/crds/operatorconfigurations.yaml @@ -424,10 +424,10 @@ spec: default: "Cluster" master_dns_name_format: type: string - default: "{cluster}.{team}.{hostedzone}" + default: "{cluster}.{namespace}.{hostedzone}" replica_dns_name_format: type: string - default: "{cluster}-repl.{team}.{hostedzone}" + default: "{cluster}-repl.{namespace}.{hostedzone}" aws_or_gcp: type: object properties: diff --git a/charts/postgres-operator/values.yaml b/charts/postgres-operator/values.yaml index 9e04f8d2e..9870c707f 100644 --- a/charts/postgres-operator/values.yaml +++ b/charts/postgres-operator/values.yaml @@ -269,9 +269,9 @@ configLoadBalancer: # define external traffic policy for the load balancer external_traffic_policy: "Cluster" # defines the DNS name string template for the master load balancer cluster - master_dns_name_format: "{cluster}.{team}.{hostedzone}" + master_dns_name_format: "{cluster}.{namespace}.{hostedzone}" # defines the DNS name string template for the replica load balancer cluster - replica_dns_name_format: "{cluster}-repl.{team}.{hostedzone}" + replica_dns_name_format: "{cluster}-repl.{namespace}.{hostedzone}" # options to aid debugging of the operator itself configDebug: diff --git a/docs/reference/operator_parameters.md b/docs/reference/operator_parameters.md index ba91146e7..a923ad71c 100644 --- a/docs/reference/operator_parameters.md +++ b/docs/reference/operator_parameters.md @@ -617,15 +617,15 @@ In the CRD-based configuration they are grouped under the `load_balancer` key. * **master_dns_name_format** defines the DNS name string template for the master load balancer cluster. The default is - `{cluster}.{team}.{hostedzone}`, where `{cluster}` is replaced by the cluster - name, `{team}` is replaced with the team name and `{hostedzone}` is replaced + `{cluster}.{namespace}.{hostedzone}`, where `{cluster}` is replaced by the cluster + name, `{namespace}` is replaced with the namespace and `{hostedzone}` is replaced with the hosted zone (the value of the `db_hosted_zone` parameter). No other placeholders are allowed. * **replica_dns_name_format** defines the DNS name string template for the replica load balancer cluster. The default is - `{cluster}-repl.{team}.{hostedzone}`, where `{cluster}` is replaced by the - cluster name, `{team}` is replaced with the team name and `{hostedzone}` is + `{cluster}-repl.{namespace}.{hostedzone}`, where `{cluster}` is replaced by the + cluster name, `{namespace}` is replaced with the namespace and `{hostedzone}` is replaced with the hosted zone (the value of the `db_hosted_zone` parameter). No other placeholders are allowed. diff --git a/docs/user.md b/docs/user.md index 43e729909..fc9099234 100644 --- a/docs/user.md +++ b/docs/user.md @@ -152,7 +152,7 @@ specified explicitly. The operator automatically generates a password for each manifest role and places it in the secret named -`{username}.{team}-{clustername}.credentials.postgresql.acid.zalan.do` in the +`{username}.{clustername}.credentials.postgresql.acid.zalan.do` in the same namespace as the cluster. This way, the application running in the K8s cluster and connecting to Postgres can obtain the password right from the secret, without ever sharing it outside of the cluster. @@ -182,7 +182,7 @@ be in the form of `namespace.username`. For such usernames, the secret is created in the given namespace and its name is of the following form, -`{namespace}.{username}.{team}-{clustername}.credentials.postgresql.acid.zalan.do` +`{namespace}.{username}.{clustername}.credentials.postgresql.acid.zalan.do` ### Infrastructure roles diff --git a/manifests/configmap.yaml b/manifests/configmap.yaml index 371cd5bd1..e8823ee0e 100644 --- a/manifests/configmap.yaml +++ b/manifests/configmap.yaml @@ -87,7 +87,7 @@ data: logical_backup_schedule: "30 00 * * *" major_version_upgrade_mode: "manual" # major_version_upgrade_team_allow_list: "" - master_dns_name_format: "{cluster}.{team}.{hostedzone}" + master_dns_name_format: "{cluster}.{namespace}.{hostedzone}" # master_pod_move_timeout: 20m # max_instances: "-1" # min_instances: "-1" @@ -124,7 +124,7 @@ data: ready_wait_interval: 3s ready_wait_timeout: 30s repair_period: 5m - replica_dns_name_format: "{cluster}-repl.{team}.{hostedzone}" + replica_dns_name_format: "{cluster}-repl.{namespace}.{hostedzone}" replication_username: standby resource_check_interval: 3s resource_check_timeout: 10m diff --git a/manifests/operatorconfiguration.crd.yaml b/manifests/operatorconfiguration.crd.yaml index 3d4892bab..1f6d48a4d 100644 --- a/manifests/operatorconfiguration.crd.yaml +++ b/manifests/operatorconfiguration.crd.yaml @@ -422,10 +422,10 @@ spec: default: "Cluster" master_dns_name_format: type: string - default: "{cluster}.{team}.{hostedzone}" + default: "{cluster}.{namespace}.{hostedzone}" replica_dns_name_format: type: string - default: "{cluster}-repl.{team}.{hostedzone}" + default: "{cluster}-repl.{namespace}.{hostedzone}" aws_or_gcp: type: object properties: diff --git a/manifests/postgresql-operator-default-configuration.yaml b/manifests/postgresql-operator-default-configuration.yaml index 645e5138b..7c8ac61ae 100644 --- a/manifests/postgresql-operator-default-configuration.yaml +++ b/manifests/postgresql-operator-default-configuration.yaml @@ -133,8 +133,8 @@ configuration: enable_replica_load_balancer: false enable_replica_pooler_load_balancer: false external_traffic_policy: "Cluster" - master_dns_name_format: "{cluster}.{team}.{hostedzone}" - replica_dns_name_format: "{cluster}-repl.{team}.{hostedzone}" + master_dns_name_format: "{cluster}.{namespace}.{hostedzone}" + replica_dns_name_format: "{cluster}-repl.{namespace}.{hostedzone}" aws_or_gcp: # additional_secret_mount: "some-secret-name" # additional_secret_mount_path: "/some/dir" diff --git a/pkg/cluster/cluster_test.go b/pkg/cluster/cluster_test.go index f36db09b0..e06df9176 100644 --- a/pkg/cluster/cluster_test.go +++ b/pkg/cluster/cluster_test.go @@ -450,6 +450,7 @@ func TestServiceAnnotations(t *testing.T) { enableMasterLoadBalancerOC bool enableReplicaLoadBalancerSpec *bool enableReplicaLoadBalancerOC bool + enableTeamIdClusterPrefix bool operatorAnnotations map[string]string clusterAnnotations map[string]string expect map[string]string @@ -460,6 +461,7 @@ func TestServiceAnnotations(t *testing.T) { role: "master", enableMasterLoadBalancerSpec: &disabled, enableMasterLoadBalancerOC: false, + enableTeamIdClusterPrefix: false, operatorAnnotations: make(map[string]string), clusterAnnotations: make(map[string]string), expect: make(map[string]string), @@ -469,10 +471,11 @@ func TestServiceAnnotations(t *testing.T) { role: "master", enableMasterLoadBalancerSpec: &enabled, enableMasterLoadBalancerOC: false, + enableTeamIdClusterPrefix: false, operatorAnnotations: make(map[string]string), clusterAnnotations: make(map[string]string), expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "test.acid.db.example.com", + "external-dns.alpha.kubernetes.io/hostname": "acid-test.test.db.example.com,test.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", }, }, @@ -481,6 +484,7 @@ func TestServiceAnnotations(t *testing.T) { role: "master", enableMasterLoadBalancerSpec: &disabled, enableMasterLoadBalancerOC: true, + enableTeamIdClusterPrefix: false, operatorAnnotations: make(map[string]string), clusterAnnotations: make(map[string]string), expect: make(map[string]string), @@ -489,10 +493,11 @@ func TestServiceAnnotations(t *testing.T) { about: "Master with no annotations and EnableMasterLoadBalancer defined only on operator config", role: "master", enableMasterLoadBalancerOC: true, + enableTeamIdClusterPrefix: false, operatorAnnotations: make(map[string]string), clusterAnnotations: make(map[string]string), expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "test.acid.db.example.com", + "external-dns.alpha.kubernetes.io/hostname": "acid-test.test.db.example.com,test.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", }, }, @@ -500,10 +505,11 @@ func TestServiceAnnotations(t *testing.T) { about: "Master with cluster annotations and load balancer enabled", role: "master", enableMasterLoadBalancerOC: true, + enableTeamIdClusterPrefix: false, operatorAnnotations: make(map[string]string), clusterAnnotations: map[string]string{"foo": "bar"}, expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "test.acid.db.example.com", + "external-dns.alpha.kubernetes.io/hostname": "acid-test.test.db.example.com,test.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", "foo": "bar", }, @@ -513,6 +519,7 @@ func TestServiceAnnotations(t *testing.T) { role: "master", enableMasterLoadBalancerSpec: &disabled, enableMasterLoadBalancerOC: true, + enableTeamIdClusterPrefix: false, operatorAnnotations: make(map[string]string), clusterAnnotations: map[string]string{"foo": "bar"}, expect: map[string]string{"foo": "bar"}, @@ -521,10 +528,11 @@ func TestServiceAnnotations(t *testing.T) { about: "Master with operator annotations and load balancer enabled", role: "master", enableMasterLoadBalancerOC: true, + enableTeamIdClusterPrefix: false, operatorAnnotations: map[string]string{"foo": "bar"}, clusterAnnotations: make(map[string]string), expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "test.acid.db.example.com", + "external-dns.alpha.kubernetes.io/hostname": "acid-test.test.db.example.com,test.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", "foo": "bar", }, @@ -533,12 +541,13 @@ func TestServiceAnnotations(t *testing.T) { about: "Master with operator annotations override default annotations", role: "master", enableMasterLoadBalancerOC: true, + enableTeamIdClusterPrefix: false, operatorAnnotations: map[string]string{ "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "1800", }, clusterAnnotations: make(map[string]string), expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "test.acid.db.example.com", + "external-dns.alpha.kubernetes.io/hostname": "acid-test.test.db.example.com,test.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "1800", }, }, @@ -546,12 +555,13 @@ func TestServiceAnnotations(t *testing.T) { about: "Master with cluster annotations override default annotations", role: "master", enableMasterLoadBalancerOC: true, + enableTeamIdClusterPrefix: false, operatorAnnotations: make(map[string]string), clusterAnnotations: map[string]string{ "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "1800", }, expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "test.acid.db.example.com", + "external-dns.alpha.kubernetes.io/hostname": "acid-test.test.db.example.com,test.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "1800", }, }, @@ -559,25 +569,25 @@ func TestServiceAnnotations(t *testing.T) { about: "Master with cluster annotations do not override external-dns annotations", role: "master", enableMasterLoadBalancerOC: true, + enableTeamIdClusterPrefix: false, operatorAnnotations: make(map[string]string), clusterAnnotations: map[string]string{ "external-dns.alpha.kubernetes.io/hostname": "wrong.external-dns-name.example.com", }, expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "test.acid.db.example.com", + "external-dns.alpha.kubernetes.io/hostname": "acid-test.test.db.example.com,test.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", }, }, { - about: "Master with operator annotations do not override external-dns annotations", + about: "Master with cluster name teamId prefix enabled", role: "master", enableMasterLoadBalancerOC: true, + enableTeamIdClusterPrefix: true, clusterAnnotations: make(map[string]string), - operatorAnnotations: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "wrong.external-dns-name.example.com", - }, + operatorAnnotations: make(map[string]string), expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "test.acid.db.example.com", + "external-dns.alpha.kubernetes.io/hostname": "test.test.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", }, }, @@ -587,6 +597,7 @@ func TestServiceAnnotations(t *testing.T) { role: "replica", enableReplicaLoadBalancerSpec: &disabled, enableReplicaLoadBalancerOC: false, + enableTeamIdClusterPrefix: false, operatorAnnotations: make(map[string]string), clusterAnnotations: make(map[string]string), expect: make(map[string]string), @@ -596,10 +607,11 @@ func TestServiceAnnotations(t *testing.T) { role: "replica", enableReplicaLoadBalancerSpec: &enabled, enableReplicaLoadBalancerOC: false, + enableTeamIdClusterPrefix: false, operatorAnnotations: make(map[string]string), clusterAnnotations: make(map[string]string), expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "test-repl.acid.db.example.com", + "external-dns.alpha.kubernetes.io/hostname": "acid-test-repl.test.db.example.com,test-repl.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", }, }, @@ -608,6 +620,7 @@ func TestServiceAnnotations(t *testing.T) { role: "replica", enableReplicaLoadBalancerSpec: &disabled, enableReplicaLoadBalancerOC: true, + enableTeamIdClusterPrefix: false, operatorAnnotations: make(map[string]string), clusterAnnotations: make(map[string]string), expect: make(map[string]string), @@ -616,10 +629,11 @@ func TestServiceAnnotations(t *testing.T) { about: "Replica with no annotations and EnableReplicaLoadBalancer defined only on operator config", role: "replica", enableReplicaLoadBalancerOC: true, + enableTeamIdClusterPrefix: false, operatorAnnotations: make(map[string]string), clusterAnnotations: make(map[string]string), expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "test-repl.acid.db.example.com", + "external-dns.alpha.kubernetes.io/hostname": "acid-test-repl.test.db.example.com,test-repl.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", }, }, @@ -627,10 +641,11 @@ func TestServiceAnnotations(t *testing.T) { about: "Replica with cluster annotations and load balancer enabled", role: "replica", enableReplicaLoadBalancerOC: true, + enableTeamIdClusterPrefix: false, operatorAnnotations: make(map[string]string), clusterAnnotations: map[string]string{"foo": "bar"}, expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "test-repl.acid.db.example.com", + "external-dns.alpha.kubernetes.io/hostname": "acid-test-repl.test.db.example.com,test-repl.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", "foo": "bar", }, @@ -640,6 +655,7 @@ func TestServiceAnnotations(t *testing.T) { role: "replica", enableReplicaLoadBalancerSpec: &disabled, enableReplicaLoadBalancerOC: true, + enableTeamIdClusterPrefix: false, operatorAnnotations: make(map[string]string), clusterAnnotations: map[string]string{"foo": "bar"}, expect: map[string]string{"foo": "bar"}, @@ -648,10 +664,11 @@ func TestServiceAnnotations(t *testing.T) { about: "Replica with operator annotations and load balancer enabled", role: "replica", enableReplicaLoadBalancerOC: true, + enableTeamIdClusterPrefix: false, operatorAnnotations: map[string]string{"foo": "bar"}, clusterAnnotations: make(map[string]string), expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "test-repl.acid.db.example.com", + "external-dns.alpha.kubernetes.io/hostname": "acid-test-repl.test.db.example.com,test-repl.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", "foo": "bar", }, @@ -660,12 +677,13 @@ func TestServiceAnnotations(t *testing.T) { about: "Replica with operator annotations override default annotations", role: "replica", enableReplicaLoadBalancerOC: true, + enableTeamIdClusterPrefix: false, operatorAnnotations: map[string]string{ "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "1800", }, clusterAnnotations: make(map[string]string), expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "test-repl.acid.db.example.com", + "external-dns.alpha.kubernetes.io/hostname": "acid-test-repl.test.db.example.com,test-repl.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "1800", }, }, @@ -673,12 +691,13 @@ func TestServiceAnnotations(t *testing.T) { about: "Replica with cluster annotations override default annotations", role: "replica", enableReplicaLoadBalancerOC: true, + enableTeamIdClusterPrefix: false, operatorAnnotations: make(map[string]string), clusterAnnotations: map[string]string{ "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "1800", }, expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "test-repl.acid.db.example.com", + "external-dns.alpha.kubernetes.io/hostname": "acid-test-repl.test.db.example.com,test-repl.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "1800", }, }, @@ -686,25 +705,25 @@ func TestServiceAnnotations(t *testing.T) { about: "Replica with cluster annotations do not override external-dns annotations", role: "replica", enableReplicaLoadBalancerOC: true, + enableTeamIdClusterPrefix: false, operatorAnnotations: make(map[string]string), clusterAnnotations: map[string]string{ "external-dns.alpha.kubernetes.io/hostname": "wrong.external-dns-name.example.com", }, expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "test-repl.acid.db.example.com", + "external-dns.alpha.kubernetes.io/hostname": "acid-test-repl.test.db.example.com,test-repl.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", }, }, { - about: "Replica with operator annotations do not override external-dns annotations", + about: "Replica with cluster name teamId prefix enabled", role: "replica", enableReplicaLoadBalancerOC: true, + enableTeamIdClusterPrefix: true, clusterAnnotations: make(map[string]string), - operatorAnnotations: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "wrong.external-dns-name.example.com", - }, + operatorAnnotations: make(map[string]string), expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "test-repl.acid.db.example.com", + "external-dns.alpha.kubernetes.io/hostname": "test-repl.test.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", }, }, @@ -713,6 +732,7 @@ func TestServiceAnnotations(t *testing.T) { about: "cluster annotations append to operator annotations", role: "replica", enableReplicaLoadBalancerOC: false, + enableTeamIdClusterPrefix: false, operatorAnnotations: map[string]string{"foo": "bar"}, clusterAnnotations: map[string]string{"post": "gres"}, expect: map[string]string{"foo": "bar", "post": "gres"}, @@ -721,6 +741,7 @@ func TestServiceAnnotations(t *testing.T) { about: "cluster annotations override operator annotations", role: "replica", enableReplicaLoadBalancerOC: false, + enableTeamIdClusterPrefix: false, operatorAnnotations: map[string]string{"foo": "bar", "post": "gres"}, clusterAnnotations: map[string]string{"post": "greSQL"}, expect: map[string]string{"foo": "bar", "post": "greSQL"}, @@ -729,14 +750,20 @@ func TestServiceAnnotations(t *testing.T) { for _, tt := range tests { t.Run(tt.about, func(t *testing.T) { + cl.OpConfig.EnableTeamIdClusternamePrefix = tt.enableTeamIdClusterPrefix + if tt.enableTeamIdClusterPrefix { + cl.Postgresql.Spec.ClusterName = "test" + } else { + cl.Postgresql.Spec.ClusterName = "acid-test" + } + cl.OpConfig.CustomServiceAnnotations = tt.operatorAnnotations cl.OpConfig.EnableMasterLoadBalancer = tt.enableMasterLoadBalancerOC cl.OpConfig.EnableReplicaLoadBalancer = tt.enableReplicaLoadBalancerOC - cl.OpConfig.MasterDNSNameFormat = "{cluster}.{team}.{hostedzone}" - cl.OpConfig.ReplicaDNSNameFormat = "{cluster}-repl.{team}.{hostedzone}" + cl.OpConfig.MasterDNSNameFormat = "{cluster}.{namespace}.{hostedzone}" + cl.OpConfig.ReplicaDNSNameFormat = "{cluster}-repl.{namespace}.{hostedzone}" cl.OpConfig.DbHostedZone = "db.example.com" - cl.Postgresql.Spec.ClusterName = "test" cl.Postgresql.Spec.TeamID = "acid" cl.Postgresql.Spec.ServiceAnnotations = tt.clusterAnnotations cl.Postgresql.Spec.EnableMasterLoadBalancer = tt.enableMasterLoadBalancerSpec diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index d14bc1572..337cd3a63 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -1814,12 +1814,7 @@ func (c *Cluster) generateServiceAnnotations(role PostgresRole, spec *acidv1.Pos } if c.shouldCreateLoadBalancerForService(role, spec) { - var dnsName string - if role == Master { - dnsName = c.masterDNSName() - } else { - dnsName = c.replicaDNSName() - } + dnsName := c.dnsName(role) // Just set ELB Timeout annotation with default value, if it does not // have a cutom value diff --git a/pkg/cluster/util.go b/pkg/cluster/util.go index 37dead012..f520e14ac 100644 --- a/pkg/cluster/util.go +++ b/pkg/cluster/util.go @@ -505,20 +505,50 @@ func (c *Cluster) roleLabelsSet(shouldAddExtraLabels bool, role PostgresRole) la return lbls } +func (c *Cluster) dnsName(role PostgresRole) string { + var dnsString string + if role == Master { + dnsString = c.masterDNSName() + } else { + dnsString = c.replicaDNSName() + } + + // when cluster name starts with teamId prefix create an extra DNS entry + // to support the old format when prefix contraint was enabled (but is disabled now) + if !c.OpConfig.EnableTeamIdClusternamePrefix { + clusterNameWithoutTeamPrefix, _ := acidv1.ExtractClusterName(c.Name, c.Spec.TeamID) + if clusterNameWithoutTeamPrefix != "" { + if role == Replica { + clusterNameWithoutTeamPrefix = fmt.Sprintf("%s-repl", clusterNameWithoutTeamPrefix) + } + dnsString = fmt.Sprintf("%s,%s", dnsString, c.oldDNSFormat(clusterNameWithoutTeamPrefix)) + } + } + + return dnsString +} + func (c *Cluster) masterDNSName() string { return strings.ToLower(c.OpConfig.MasterDNSNameFormat.Format( "cluster", c.Spec.ClusterName, - "team", c.teamName(), + "namespace", c.Namespace, "hostedzone", c.OpConfig.DbHostedZone)) } func (c *Cluster) replicaDNSName() string { return strings.ToLower(c.OpConfig.ReplicaDNSNameFormat.Format( "cluster", c.Spec.ClusterName, - "team", c.teamName(), + "namespace", c.Namespace, "hostedzone", c.OpConfig.DbHostedZone)) } +func (c *Cluster) oldDNSFormat(clusterName string) string { + return fmt.Sprintf("%s.%s.%s", + clusterName, + c.teamName(), + c.OpConfig.DbHostedZone) +} + func (c *Cluster) credentialSecretName(username string) string { return c.credentialSecretNameForCluster(username, c.Name) } diff --git a/pkg/controller/postgresql.go b/pkg/controller/postgresql.go index 861d95032..1333068e5 100644 --- a/pkg/controller/postgresql.go +++ b/pkg/controller/postgresql.go @@ -159,17 +159,24 @@ func (c *Controller) acquireInitialListOfClusters() error { } func (c *Controller) addCluster(lg *logrus.Entry, clusterName spec.NamespacedName, pgSpec *acidv1.Postgresql) (*cluster.Cluster, error) { + var ( + extractedClusterName string + err error + ) if c.opConfig.EnableTeamIdClusternamePrefix { - if _, err := acidv1.ExtractClusterName(clusterName.Name, pgSpec.Spec.TeamID); err != nil { + if extractedClusterName, err = acidv1.ExtractClusterName(clusterName.Name, pgSpec.Spec.TeamID); err != nil { c.KubeClient.SetPostgresCRDStatus(clusterName, acidv1.ClusterStatusInvalid) return nil, err } + } else { + extractedClusterName = clusterName.Name } cl := cluster.New(c.makeClusterConfig(), c.KubeClient, *pgSpec, lg, c.eventRecorder) cl.Run(c.stopCh) teamName := strings.ToLower(cl.Spec.TeamID) + cl.ClusterName = extractedClusterName defer c.clustersMu.Unlock() c.clustersMu.Lock() diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index 231929bb8..e1fde13f4 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -206,8 +206,8 @@ type Config struct { StorageResizeMode string `name:"storage_resize_mode" default:"pvc"` EnableLoadBalancer *bool `name:"enable_load_balancer"` // deprecated and kept for backward compatibility ExternalTrafficPolicy string `name:"external_traffic_policy" default:"Cluster"` - MasterDNSNameFormat StringTemplate `name:"master_dns_name_format" default:"{cluster}.{team}.{hostedzone}"` - ReplicaDNSNameFormat StringTemplate `name:"replica_dns_name_format" default:"{cluster}-repl.{team}.{hostedzone}"` + MasterDNSNameFormat StringTemplate `name:"master_dns_name_format" default:"{cluster}.{namespace}.{hostedzone}"` + ReplicaDNSNameFormat StringTemplate `name:"replica_dns_name_format" default:"{cluster}-repl.{namespace}.{hostedzone}"` PDBNameFormat StringTemplate `name:"pdb_name_format" default:"postgres-{cluster}-pdb"` EnablePodDisruptionBudget *bool `name:"enable_pod_disruption_budget" default:"true"` EnableInitContainers *bool `name:"enable_init_containers" default:"true"` From 4c07494ac731775d100d773987adb575c5b12581 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Mon, 29 Aug 2022 15:00:25 +0200 Subject: [PATCH 14/77] deprecate ClusterName field of Postgresql type and remove team from REST endpoints (#2015) * deprecate ClusterName field of Postgresql type * remove for teamId from operator API endpints /status /logs /history * update dns_format_string and yaml template in UI --- pkg/apis/acid.zalan.do/v1/postgresql_type.go | 4 +++- pkg/apiserver/apiserver.go | 22 ++++++++++---------- pkg/apiserver/apiserver_test.go | 6 +++--- pkg/cluster/cluster.go | 3 ++- pkg/cluster/cluster_test.go | 10 +++------ pkg/cluster/types.go | 1 + pkg/cluster/util.go | 22 ++++++++++---------- pkg/controller/logs_and_api.go | 12 +++++------ pkg/controller/postgresql.go | 10 +-------- ui/app/src/new.tag.pug | 13 ++++++------ ui/manifests/deployment.yaml | 2 +- ui/operator_ui/main.py | 12 ++--------- ui/run_local.sh | 2 +- 13 files changed, 51 insertions(+), 68 deletions(-) diff --git a/pkg/apis/acid.zalan.do/v1/postgresql_type.go b/pkg/apis/acid.zalan.do/v1/postgresql_type.go index 1e9245fb8..e46a43636 100644 --- a/pkg/apis/acid.zalan.do/v1/postgresql_type.go +++ b/pkg/apis/acid.zalan.do/v1/postgresql_type.go @@ -36,6 +36,9 @@ type PostgresSpec struct { TeamID string `json:"teamId"` DockerImage string `json:"dockerImage,omitempty"` + // deprecated field storing cluster name without teamId prefix + ClusterName string `json:"-"` + SpiloRunAsUser *int64 `json:"spiloRunAsUser,omitempty"` SpiloRunAsGroup *int64 `json:"spiloRunAsGroup,omitempty"` SpiloFSGroup *int64 `json:"spiloFSGroup,omitempty"` @@ -62,7 +65,6 @@ type PostgresSpec struct { NumberOfInstances int32 `json:"numberOfInstances"` MaintenanceWindows []MaintenanceWindow `json:"maintenanceWindows,omitempty"` Clone *CloneDescription `json:"clone,omitempty"` - ClusterName string `json:"-"` Databases map[string]string `json:"databases,omitempty"` PreparedDatabases map[string]PreparedDatabase `json:"preparedDatabases,omitempty"` SchedulerName *string `json:"schedulerName,omitempty"` diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index c0fa1f349..df13141d4 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -31,9 +31,9 @@ type controllerInformer interface { GetOperatorConfig() *config.Config GetStatus() *spec.ControllerStatus TeamClusterList() map[string][]spec.NamespacedName - ClusterStatus(team, namespace, cluster string) (*cluster.ClusterStatus, error) - ClusterLogs(team, namespace, cluster string) ([]*spec.LogEntry, error) - ClusterHistory(team, namespace, cluster string) ([]*spec.Diff, error) + ClusterStatus(namespace, cluster string) (*cluster.ClusterStatus, error) + ClusterLogs(namespace, cluster string) ([]*spec.LogEntry, error) + ClusterHistory(namespace, cluster string) ([]*spec.Diff, error) ClusterDatabasesMap() map[string][]string WorkerLogs(workerID uint32) ([]*spec.LogEntry, error) ListQueue(workerID uint32) (*spec.QueueDump, error) @@ -55,9 +55,9 @@ const ( ) var ( - clusterStatusRe = fmt.Sprintf(`^/clusters/%s/%s/%s/?$`, teamRe, namespaceRe, clusterRe) - clusterLogsRe = fmt.Sprintf(`^/clusters/%s/%s/%s/logs/?$`, teamRe, namespaceRe, clusterRe) - clusterHistoryRe = fmt.Sprintf(`^/clusters/%s/%s/%s/history/?$`, teamRe, namespaceRe, clusterRe) + clusterStatusRe = fmt.Sprintf(`^/clusters/%s/%s/?$`, namespaceRe, clusterRe) + clusterLogsRe = fmt.Sprintf(`^/clusters/%s/%s/logs/?$`, namespaceRe, clusterRe) + clusterHistoryRe = fmt.Sprintf(`^/clusters/%s/%s/history/?$`, namespaceRe, clusterRe) teamURLRe = fmt.Sprintf(`^/clusters/%s/?$`, teamRe) clusterStatusURL = regexp.MustCompile(clusterStatusRe) @@ -170,7 +170,7 @@ func (s *Server) clusters(w http.ResponseWriter, req *http.Request) { if matches := util.FindNamedStringSubmatch(clusterStatusURL, req.URL.Path); matches != nil { namespace := matches["namespace"] - resp, err = s.controller.ClusterStatus(matches["team"], namespace, matches["cluster"]) + resp, err = s.controller.ClusterStatus(namespace, matches["cluster"]) } else if matches := util.FindNamedStringSubmatch(teamURL, req.URL.Path); matches != nil { teamClusters := s.controller.TeamClusterList() clusters, found := teamClusters[matches["team"]] @@ -181,21 +181,21 @@ func (s *Server) clusters(w http.ResponseWriter, req *http.Request) { clusterNames := make([]string, 0) for _, cluster := range clusters { - clusterNames = append(clusterNames, cluster.Name[len(matches["team"])+1:]) + clusterNames = append(clusterNames, cluster.Name) } resp, err = clusterNames, nil } else if matches := util.FindNamedStringSubmatch(clusterLogsURL, req.URL.Path); matches != nil { namespace := matches["namespace"] - resp, err = s.controller.ClusterLogs(matches["team"], namespace, matches["cluster"]) + resp, err = s.controller.ClusterLogs(namespace, matches["cluster"]) } else if matches := util.FindNamedStringSubmatch(clusterHistoryURL, req.URL.Path); matches != nil { namespace := matches["namespace"] - resp, err = s.controller.ClusterHistory(matches["team"], namespace, matches["cluster"]) + resp, err = s.controller.ClusterHistory(namespace, matches["cluster"]) } else if req.URL.Path == clustersURL { clusterNamesPerTeam := make(map[string][]string) for team, clusters := range s.controller.TeamClusterList() { for _, cluster := range clusters { - clusterNamesPerTeam[team] = append(clusterNamesPerTeam[team], cluster.Name[len(team)+1:]) + clusterNamesPerTeam[team] = append(clusterNamesPerTeam[team], cluster.Name) } } resp, err = clusterNamesPerTeam, nil diff --git a/pkg/apiserver/apiserver_test.go b/pkg/apiserver/apiserver_test.go index fb6484d03..36571667c 100644 --- a/pkg/apiserver/apiserver_test.go +++ b/pkg/apiserver/apiserver_test.go @@ -5,9 +5,9 @@ import ( ) const ( - clusterStatusTest = "/clusters/test-id/test_namespace/testcluster/" - clusterStatusNumericTest = "/clusters/test-id-1/test_namespace/testcluster/" - clusterLogsTest = "/clusters/test-id/test_namespace/testcluster/logs/" + clusterStatusTest = "/clusters/test-namespace/testcluster/" + clusterStatusNumericTest = "/clusters/test-namespace-1/testcluster/" + clusterLogsTest = "/clusters/test-namespace/testcluster/logs/" teamTest = "/clusters/test-id/" ) diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index 611295f5f..93ae3ec35 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -1478,7 +1478,8 @@ func (c *Cluster) GetCurrentProcess() Process { // GetStatus provides status of the cluster func (c *Cluster) GetStatus() *ClusterStatus { status := &ClusterStatus{ - Cluster: c.Spec.ClusterName, + Cluster: c.Name, + Namespace: c.Namespace, Team: c.Spec.TeamID, Status: c.Status, Spec: c.Spec, diff --git a/pkg/cluster/cluster_test.go b/pkg/cluster/cluster_test.go index e06df9176..dc8e6a87c 100644 --- a/pkg/cluster/cluster_test.go +++ b/pkg/cluster/cluster_test.go @@ -587,7 +587,7 @@ func TestServiceAnnotations(t *testing.T) { clusterAnnotations: make(map[string]string), operatorAnnotations: make(map[string]string), expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "test.test.db.example.com", + "external-dns.alpha.kubernetes.io/hostname": "acid-test.test.db.example.com,test.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", }, }, @@ -723,7 +723,7 @@ func TestServiceAnnotations(t *testing.T) { clusterAnnotations: make(map[string]string), operatorAnnotations: make(map[string]string), expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "test-repl.test.db.example.com", + "external-dns.alpha.kubernetes.io/hostname": "acid-test-repl.test.db.example.com,test-repl.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", }, }, @@ -751,11 +751,6 @@ func TestServiceAnnotations(t *testing.T) { for _, tt := range tests { t.Run(tt.about, func(t *testing.T) { cl.OpConfig.EnableTeamIdClusternamePrefix = tt.enableTeamIdClusterPrefix - if tt.enableTeamIdClusterPrefix { - cl.Postgresql.Spec.ClusterName = "test" - } else { - cl.Postgresql.Spec.ClusterName = "acid-test" - } cl.OpConfig.CustomServiceAnnotations = tt.operatorAnnotations cl.OpConfig.EnableMasterLoadBalancer = tt.enableMasterLoadBalancerOC @@ -764,6 +759,7 @@ func TestServiceAnnotations(t *testing.T) { cl.OpConfig.ReplicaDNSNameFormat = "{cluster}-repl.{namespace}.{hostedzone}" cl.OpConfig.DbHostedZone = "db.example.com" + cl.Postgresql.Spec.ClusterName = "" cl.Postgresql.Spec.TeamID = "acid" cl.Postgresql.Spec.ServiceAnnotations = tt.clusterAnnotations cl.Postgresql.Spec.EnableMasterLoadBalancer = tt.enableMasterLoadBalancerSpec diff --git a/pkg/cluster/types.go b/pkg/cluster/types.go index 67b4ee395..31f27aa54 100644 --- a/pkg/cluster/types.go +++ b/pkg/cluster/types.go @@ -59,6 +59,7 @@ type WorkerStatus struct { type ClusterStatus struct { Team string Cluster string + Namespace string MasterService *v1.Service ReplicaService *v1.Service MasterEndpoint *v1.Endpoints diff --git a/pkg/cluster/util.go b/pkg/cluster/util.go index f520e14ac..2a8920803 100644 --- a/pkg/cluster/util.go +++ b/pkg/cluster/util.go @@ -507,22 +507,20 @@ func (c *Cluster) roleLabelsSet(shouldAddExtraLabels bool, role PostgresRole) la func (c *Cluster) dnsName(role PostgresRole) string { var dnsString string + if role == Master { dnsString = c.masterDNSName() } else { dnsString = c.replicaDNSName() } - // when cluster name starts with teamId prefix create an extra DNS entry - // to support the old format when prefix contraint was enabled (but is disabled now) - if !c.OpConfig.EnableTeamIdClusternamePrefix { - clusterNameWithoutTeamPrefix, _ := acidv1.ExtractClusterName(c.Name, c.Spec.TeamID) - if clusterNameWithoutTeamPrefix != "" { - if role == Replica { - clusterNameWithoutTeamPrefix = fmt.Sprintf("%s-repl", clusterNameWithoutTeamPrefix) - } - dnsString = fmt.Sprintf("%s,%s", dnsString, c.oldDNSFormat(clusterNameWithoutTeamPrefix)) + // if cluster name starts with teamID we might need to provide backwards compatibility + clusterNameWithoutTeamPrefix, _ := acidv1.ExtractClusterName(c.Name, c.Spec.TeamID) + if clusterNameWithoutTeamPrefix != "" { + if role == Replica { + clusterNameWithoutTeamPrefix = fmt.Sprintf("%s-repl", clusterNameWithoutTeamPrefix) } + dnsString = fmt.Sprintf("%s,%s", dnsString, c.oldDNSFormat(clusterNameWithoutTeamPrefix)) } return dnsString @@ -530,15 +528,17 @@ func (c *Cluster) dnsName(role PostgresRole) string { func (c *Cluster) masterDNSName() string { return strings.ToLower(c.OpConfig.MasterDNSNameFormat.Format( - "cluster", c.Spec.ClusterName, + "cluster", c.Name, "namespace", c.Namespace, + "team", c.teamName(), "hostedzone", c.OpConfig.DbHostedZone)) } func (c *Cluster) replicaDNSName() string { return strings.ToLower(c.OpConfig.ReplicaDNSNameFormat.Format( - "cluster", c.Spec.ClusterName, + "cluster", c.Name, "namespace", c.Namespace, + "team", c.teamName(), "hostedzone", c.OpConfig.DbHostedZone)) } diff --git a/pkg/controller/logs_and_api.go b/pkg/controller/logs_and_api.go index 24e73fabe..4af5e1f36 100644 --- a/pkg/controller/logs_and_api.go +++ b/pkg/controller/logs_and_api.go @@ -15,11 +15,11 @@ import ( ) // ClusterStatus provides status of the cluster -func (c *Controller) ClusterStatus(team, namespace, cluster string) (*cluster.ClusterStatus, error) { +func (c *Controller) ClusterStatus(namespace, cluster string) (*cluster.ClusterStatus, error) { clusterName := spec.NamespacedName{ Namespace: namespace, - Name: team + "-" + cluster, + Name: cluster, } c.clustersMu.RLock() @@ -92,11 +92,11 @@ func (c *Controller) GetStatus() *spec.ControllerStatus { } // ClusterLogs dumps cluster ring logs -func (c *Controller) ClusterLogs(team, namespace, name string) ([]*spec.LogEntry, error) { +func (c *Controller) ClusterLogs(namespace, name string) ([]*spec.LogEntry, error) { clusterName := spec.NamespacedName{ Namespace: namespace, - Name: team + "-" + name, + Name: name, } c.clustersMu.RLock() @@ -215,11 +215,11 @@ func (c *Controller) WorkerStatus(workerID uint32) (*cluster.WorkerStatus, error } // ClusterHistory dumps history of cluster changes -func (c *Controller) ClusterHistory(team, namespace, name string) ([]*spec.Diff, error) { +func (c *Controller) ClusterHistory(namespace, name string) ([]*spec.Diff, error) { clusterName := spec.NamespacedName{ Namespace: namespace, - Name: team + "-" + name, + Name: name, } c.clustersMu.RLock() diff --git a/pkg/controller/postgresql.go b/pkg/controller/postgresql.go index 1333068e5..ede7a99a3 100644 --- a/pkg/controller/postgresql.go +++ b/pkg/controller/postgresql.go @@ -159,24 +159,16 @@ func (c *Controller) acquireInitialListOfClusters() error { } func (c *Controller) addCluster(lg *logrus.Entry, clusterName spec.NamespacedName, pgSpec *acidv1.Postgresql) (*cluster.Cluster, error) { - var ( - extractedClusterName string - err error - ) - if c.opConfig.EnableTeamIdClusternamePrefix { - if extractedClusterName, err = acidv1.ExtractClusterName(clusterName.Name, pgSpec.Spec.TeamID); err != nil { + if _, err := acidv1.ExtractClusterName(clusterName.Name, pgSpec.Spec.TeamID); err != nil { c.KubeClient.SetPostgresCRDStatus(clusterName, acidv1.ClusterStatusInvalid) return nil, err } - } else { - extractedClusterName = clusterName.Name } cl := cluster.New(c.makeClusterConfig(), c.KubeClient, *pgSpec, lg, c.eventRecorder) cl.Run(c.stopCh) teamName := strings.ToLower(cl.Spec.TeamID) - cl.ClusterName = extractedClusterName defer c.clustersMu.Unlock() c.clustersMu.Lock() diff --git a/ui/app/src/new.tag.pug b/ui/app/src/new.tag.pug index a2b0506da..02955602b 100644 --- a/ui/app/src/new.tag.pug +++ b/ui/app/src/new.tag.pug @@ -64,7 +64,7 @@ new a.btn.btn-small.btn-warning( if='{ clusterExists }' - href='/#/status/{ namespace.state }/{ team }-{ name }' + href='/#/status/{ namespace.state }/{ name }' ) | Cluster exists (show status) @@ -137,10 +137,10 @@ new input.form-control( ref='name' type='text' - placeholder='new-cluster (can be { 53 - team.length - 1 } characters long)' + placeholder='new-cluster (can be 53 characters long)' title='Database cluster name, must be a valid hostname component' pattern='[a-z0-9]+[a-z0-9\-]+[a-z0-9]+' - maxlength='{ 53 - team.length - 1 }' + maxlength=53 required value='{ name }' onchange='{ nameChange }' @@ -520,7 +520,7 @@ new apiVersion: "acid.zalan.do/v1" metadata: - name: "{{ team }}-{{ name }}" + name: "{{ name }}" namespace: "{{ namespace.state }}" labels: team: {{ team }} @@ -670,13 +670,12 @@ new this.updateDNSName = () => { this.dnsName = this.config.dns_format_string.format( this.name, - this.team, this.namespace.state, ) } this.updateClusterName = () => { - this.clusterName = (this.team + '-' + this.name).toLowerCase() + this.clusterName = (this.name).toLowerCase() this.checkClusterExists() this.updateDNSName() } @@ -950,7 +949,7 @@ new this.team = '' } - this.clusterName = (this.name + '-' + this.team).toLowerCase() + this.clusterName = (this.name + '-').toLowerCase() this.volumeSize = 10 this.instanceCount = 1 this.ranges = {} diff --git a/ui/manifests/deployment.yaml b/ui/manifests/deployment.yaml index 60722fd92..e3e603b3e 100644 --- a/ui/manifests/deployment.yaml +++ b/ui/manifests/deployment.yaml @@ -55,7 +55,7 @@ spec: value: |- { "docs_link":"https://postgres-operator.readthedocs.io/en/latest/", - "dns_format_string": "{1}-{0}.{2}", + "dns_format_string": "{0}.{1}", "databases_visible": true, "master_load_balancer_visible": true, "nat_gateways_visible": false, diff --git a/ui/operator_ui/main.py b/ui/operator_ui/main.py index b671c4a01..f3854628a 100644 --- a/ui/operator_ui/main.py +++ b/ui/operator_ui/main.py @@ -321,7 +321,7 @@ DEFAULT_UI_CONFIG = { 'databases_visible': True, 'resources_visible': RESOURCES_VISIBLE, 'postgresql_versions': ['11','12','13','14'], - 'dns_format_string': '{0}.{1}.{2}', + 'dns_format_string': '{0}.{1}', 'pgui_link': '', 'static_network_whitelist': {}, 'read_only_mode': READ_ONLY_MODE, @@ -981,15 +981,7 @@ def get_operator_get_logs(worker: int): @app.route('/operator/clusters///logs') @authorize def get_operator_get_logs_per_cluster(namespace: str, cluster: str): - team, cluster_name = cluster.split('-', 1) - # team id might contain hyphens, try to find correct team name - user_teams = get_teams_for_user(session.get('user_name', '')) - for user_team in user_teams: - if cluster.find(user_team + '-') == 0: - team = cluster[:len(user_team)] - cluster_name = cluster[len(user_team + '-'):] - break - return proxy_operator(f'/clusters/{team}/{namespace}/{cluster_name}/logs/') + return proxy_operator(f'/clusters/{namespace}/{cluster}/logs/') @app.route('/login') diff --git a/ui/run_local.sh b/ui/run_local.sh index e23b43423..e2d9e2ea0 100755 --- a/ui/run_local.sh +++ b/ui/run_local.sh @@ -14,7 +14,7 @@ export TARGET_NAMESPACE="${TARGET_NAMESPACE-*}" default_operator_ui_config='{ "docs_link":"https://postgres-operator.readthedocs.io/en/latest/", - "dns_format_string": "{1}-{0}.{2}", + "dns_format_string": "{0}.{1}", "databases_visible": true, "nat_gateways_visible": false, "resources_visible": true, From d209612b18122ae42250bd0517533fd7cbad5c66 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Thu, 1 Sep 2022 10:58:42 +0200 Subject: [PATCH 15/77] use correct keys in updateSecret (#2029) --- manifests/fake-teams-api.yaml | 3 +++ pkg/cluster/sync.go | 4 ++-- pkg/cluster/sync_test.go | 35 ++++++++++++++++++++++++++++++----- 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/manifests/fake-teams-api.yaml b/manifests/fake-teams-api.yaml index 15f7c7576..5214c58ba 100644 --- a/manifests/fake-teams-api.yaml +++ b/manifests/fake-teams-api.yaml @@ -4,6 +4,9 @@ metadata: name: fake-teams-api spec: replicas: 1 + selector: + matchLabels: + name: fake-teams-api template: metadata: labels: diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index bb68eec1c..b68a36486 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -723,14 +723,14 @@ func (c *Cluster) updateSecret( // use system user when pooler is enabled and pooler user is specfied in manifest if _, exists := c.systemUsers[constants.ConnectionPoolerUserKeyName]; exists { if secretUsername == c.systemUsers[constants.ConnectionPoolerUserKeyName].Name { - userKey = constants.ConnectionPoolerUserName + userKey = constants.ConnectionPoolerUserKeyName userMap = c.systemUsers } } // use system user when streams are defined and fes_user is specfied in manifest if _, exists := c.systemUsers[constants.EventStreamUserKeyName]; exists { if secretUsername == c.systemUsers[constants.EventStreamUserKeyName].Name { - userKey = fmt.Sprintf("%s%s", constants.EventStreamSourceSlotPrefix, constants.UserRoleNameSuffix) + userKey = constants.EventStreamUserKeyName userMap = c.systemUsers } } diff --git a/pkg/cluster/sync_test.go b/pkg/cluster/sync_test.go index ea73fb97c..ff7a03103 100644 --- a/pkg/cluster/sync_test.go +++ b/pkg/cluster/sync_test.go @@ -286,6 +286,17 @@ func TestUpdateSecret(t *testing.T) { Databases: map[string]string{dbname: dbowner}, Users: map[string]acidv1.UserFlags{"foo": {}, dbowner: {}}, UsersWithInPlaceSecretRotation: []string{dbowner}, + Streams: []acidv1.Stream{ + { + ApplicationId: appId, + Database: dbname, + Tables: map[string]acidv1.StreamTable{ + "data.foo": acidv1.StreamTable{ + EventType: "stream-type-b", + }, + }, + }, + }, Volume: acidv1.Volume{ Size: "1Gi", }, @@ -297,6 +308,8 @@ func TestUpdateSecret(t *testing.T) { Config{ OpConfig: config.Config{ Auth: config.Auth{ + SuperUsername: "postgres", + ReplicationUsername: "standby", SecretNameTemplate: secretTemplate, EnablePasswordRotation: true, PasswordRotationInterval: 1, @@ -312,8 +325,9 @@ func TestUpdateSecret(t *testing.T) { cluster.Name = clusterName cluster.Namespace = namespace cluster.pgUsers = map[string]spec.PgUser{} - cluster.initRobotUsers() + // init all users + cluster.initUsers() // create secrets cluster.syncSecrets() // initialize rotation with current time @@ -321,22 +335,33 @@ func TestUpdateSecret(t *testing.T) { dayAfterTomorrow := time.Now().AddDate(0, 0, 2) - for username := range cluster.Spec.Users { - pgUser := cluster.pgUsers[username] + allUsers := make(map[string]spec.PgUser) + for userName, pgUser := range cluster.pgUsers { + allUsers[userName] = pgUser + } + for _, systemUser := range cluster.systemUsers { + allUsers[systemUser.Name] = systemUser + } + for username, pgUser := range allUsers { // first, get the secret - secret, err := cluster.KubeClient.Secrets(namespace).Get(context.TODO(), secretTemplate.Format("username", username, "cluster", clusterName), metav1.GetOptions{}) + secretName := cluster.credentialSecretName(username) + secret, err := cluster.KubeClient.Secrets(namespace).Get(context.TODO(), secretName, metav1.GetOptions{}) assert.NoError(t, err) secretPassword := string(secret.Data["password"]) // now update the secret setting a next rotation date (tomorrow + interval) cluster.updateSecret(username, secret, &rotationUsers, &retentionUsers, dayAfterTomorrow) - updatedSecret, err := cluster.KubeClient.Secrets(namespace).Get(context.TODO(), secretTemplate.Format("username", username, "cluster", clusterName), metav1.GetOptions{}) + updatedSecret, err := cluster.KubeClient.Secrets(namespace).Get(context.TODO(), secretName, metav1.GetOptions{}) assert.NoError(t, err) // check that passwords are different rotatedPassword := string(updatedSecret.Data["password"]) if secretPassword == rotatedPassword { + // passwords for system users should not have been rotated + if pgUser.Origin != spec.RoleOriginManifest { + continue + } t.Errorf("%s: password unchanged in updated secret for %s", testName, username) } From e0c4603057721f6b2425f96f0107cd37d7f490fd Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Mon, 19 Sep 2022 15:25:55 +0200 Subject: [PATCH 16/77] create streams only after postgres instances were restarted (#2034) * create streams only after postgres instances were restarted * checkAndSetGlobalPostgreSQLConfiguration returns if config has been patched * restart can be pending even without a config patch --- pkg/cluster/streams.go | 14 +++++++++----- pkg/cluster/sync.go | 41 +++++++++++++++++++++++----------------- pkg/cluster/sync_test.go | 25 +++++++++++++----------- 3 files changed, 47 insertions(+), 33 deletions(-) diff --git a/pkg/cluster/streams.go b/pkg/cluster/streams.go index 0236925ca..917073364 100644 --- a/pkg/cluster/streams.go +++ b/pkg/cluster/streams.go @@ -72,7 +72,7 @@ func gatherApplicationIds(streams []acidv1.Stream) []string { return appIds } -func (c *Cluster) syncPostgresConfig(requiredPatroniConfig acidv1.Patroni) error { +func (c *Cluster) syncPostgresConfig(requiredPatroniConfig acidv1.Patroni) (bool, error) { errorMsg := "no pods found to update config" // if streams are defined wal_level must be switched to logical @@ -91,17 +91,17 @@ func (c *Cluster) syncPostgresConfig(requiredPatroniConfig acidv1.Patroni) error continue } - _, err = c.checkAndSetGlobalPostgreSQLConfiguration(&pod, effectivePatroniConfig, requiredPatroniConfig, effectivePgParameters, requiredPgParameters) + configPatched, _, err := c.checkAndSetGlobalPostgreSQLConfiguration(&pod, effectivePatroniConfig, requiredPatroniConfig, effectivePgParameters, requiredPgParameters) if err != nil { errorMsg = fmt.Sprintf("could not set PostgreSQL configuration options for pod %s: %v", podName, err) continue } // Patroni's config endpoint is just a "proxy" to DCS. It is enough to patch it only once and it doesn't matter which pod is used - return nil + return configPatched, nil } - return fmt.Errorf(errorMsg) + return false, fmt.Errorf(errorMsg) } func (c *Cluster) syncPublication(publication, dbName string, tables map[string]acidv1.StreamTable) error { @@ -317,10 +317,14 @@ func (c *Cluster) syncStreams() error { // add extra logical slots to Patroni config c.logger.Debug("syncing Postgres config for logical decoding") - err = c.syncPostgresConfig(requiredPatroniConfig) + requiresRestart, err := c.syncPostgresConfig(requiredPatroniConfig) if err != nil { return fmt.Errorf("failed to snyc Postgres config for event streaming: %v", err) } + if requiresRestart { + c.logger.Debugf("updated Postgres config. Server will be restarted and streams will get created during next sync") + return nil + } // next, create publications to each created slot c.logger.Debug("syncing database publications") diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index b68a36486..cdb6fa77a 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -21,7 +21,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -var requireMasterRestartWhenDecreased = []string{ +var requirePrimaryRestartWhenDecreased = []string{ "max_connections", "max_prepared_transactions", "max_locks_per_transaction", @@ -278,8 +278,9 @@ func (c *Cluster) syncPodDisruptionBudget(isUpdate bool) error { func (c *Cluster) syncStatefulSet() error { var ( - restartWait uint32 - restartMasterFirst bool + restartWait uint32 + configPatched bool + restartPrimaryFirst bool ) podsToRecreate := make([]v1.Pod, 0) isSafeToRecreatePods := true @@ -420,22 +421,24 @@ func (c *Cluster) syncStatefulSet() error { // do not attempt a restart if !reflect.DeepEqual(patroniConfig, acidv1.Patroni{}) || len(pgParameters) > 0 { // compare config returned from Patroni with what is specified in the manifest - restartMasterFirst, err = c.checkAndSetGlobalPostgreSQLConfiguration(&pod, patroniConfig, c.Spec.Patroni, pgParameters, c.Spec.Parameters) + configPatched, restartPrimaryFirst, err = c.checkAndSetGlobalPostgreSQLConfiguration(&pod, patroniConfig, c.Spec.Patroni, pgParameters, c.Spec.Parameters) if err != nil { c.logger.Warningf("could not set PostgreSQL configuration options for pod %s: %v", pods[i].Name, err) continue } // it could take up to LoopWait to apply the config - time.Sleep(time.Duration(restartWait)*time.Second + time.Second*2) - break + if configPatched { + time.Sleep(time.Duration(restartWait)*time.Second + time.Second*2) + break + } } } - // restart instances if required + // restart instances if it is still pending remainingPods := make([]*v1.Pod, 0) skipRole := Master - if restartMasterFirst { + if restartPrimaryFirst { skipRole = Replica } for i, pod := range pods { @@ -474,6 +477,7 @@ func (c *Cluster) syncStatefulSet() error { c.logger.Warningf("postpone pod recreation until next sync") } } + return nil } @@ -533,10 +537,11 @@ func (c *Cluster) AnnotationsToPropagate(annotations map[string]string) map[stri // checkAndSetGlobalPostgreSQLConfiguration checks whether cluster-wide API parameters // (like max_connections) have changed and if necessary sets it via the Patroni API -func (c *Cluster) checkAndSetGlobalPostgreSQLConfiguration(pod *v1.Pod, effectivePatroniConfig, desiredPatroniConfig acidv1.Patroni, effectivePgParameters, desiredPgParameters map[string]string) (bool, error) { +func (c *Cluster) checkAndSetGlobalPostgreSQLConfiguration(pod *v1.Pod, effectivePatroniConfig, desiredPatroniConfig acidv1.Patroni, effectivePgParameters, desiredPgParameters map[string]string) (bool, bool, error) { configToSet := make(map[string]interface{}) parametersToSet := make(map[string]string) - restartMaster := make([]bool, 0) + restartPrimary := make([]bool, 0) + configPatched := false requiresMasterRestart := false // compare effective and desired Patroni config options @@ -581,22 +586,23 @@ func (c *Cluster) checkAndSetGlobalPostgreSQLConfiguration(pod *v1.Pod, effectiv effectiveValue := effectivePgParameters[desiredOption] if isBootstrapOnlyParameter(desiredOption) && (effectiveValue != desiredValue) { parametersToSet[desiredOption] = desiredValue - if util.SliceContains(requireMasterRestartWhenDecreased, desiredOption) { + if util.SliceContains(requirePrimaryRestartWhenDecreased, desiredOption) { effectiveValueNum, errConv := strconv.Atoi(effectiveValue) desiredValueNum, errConv2 := strconv.Atoi(desiredValue) if errConv != nil || errConv2 != nil { continue } if effectiveValueNum > desiredValueNum { - restartMaster = append(restartMaster, true) + restartPrimary = append(restartPrimary, true) continue } } - restartMaster = append(restartMaster, false) + restartPrimary = append(restartPrimary, false) } } - if !util.SliceContains(restartMaster, false) && len(configToSet) == 0 { + // check if there exist only config updates that require a restart of the primary + if !util.SliceContains(restartPrimary, false) && len(configToSet) == 0 { requiresMasterRestart = true } @@ -605,7 +611,7 @@ func (c *Cluster) checkAndSetGlobalPostgreSQLConfiguration(pod *v1.Pod, effectiv } if len(configToSet) == 0 { - return false, nil + return configPatched, requiresMasterRestart, nil } configToSetJson, err := json.Marshal(configToSet) @@ -619,10 +625,11 @@ func (c *Cluster) checkAndSetGlobalPostgreSQLConfiguration(pod *v1.Pod, effectiv c.logger.Debugf("patching Postgres config via Patroni API on pod %s with following options: %s", podName, configToSetJson) if err = c.patroni.SetConfig(pod, configToSet); err != nil { - return requiresMasterRestart, fmt.Errorf("could not patch postgres parameters within pod %s: %v", podName, err) + return configPatched, requiresMasterRestart, fmt.Errorf("could not patch postgres parameters within pod %s: %v", podName, err) } + configPatched = true - return requiresMasterRestart, nil + return configPatched, requiresMasterRestart, nil } func (c *Cluster) syncSecrets() error { diff --git a/pkg/cluster/sync_test.go b/pkg/cluster/sync_test.go index ff7a03103..cc7554b0e 100644 --- a/pkg/cluster/sync_test.go +++ b/pkg/cluster/sync_test.go @@ -204,10 +204,10 @@ func TestCheckAndSetGlobalPostgreSQLConfiguration(t *testing.T) { // simulate existing config that differs from cluster.Spec tests := []struct { - subtest string - patroni acidv1.Patroni - pgParams map[string]string - restartMaster bool + subtest string + patroni acidv1.Patroni + pgParams map[string]string + restartPrimary bool }{ { subtest: "Patroni and Postgresql.Parameters differ - restart replica first", @@ -218,7 +218,7 @@ func TestCheckAndSetGlobalPostgreSQLConfiguration(t *testing.T) { "log_min_duration_statement": "500", // desired 200 "max_connections": "100", // desired 50 }, - restartMaster: false, + restartPrimary: false, }, { subtest: "multiple Postgresql.Parameters differ - restart replica first", @@ -229,7 +229,7 @@ func TestCheckAndSetGlobalPostgreSQLConfiguration(t *testing.T) { "log_min_duration_statement": "500", // desired 200 "max_connections": "100", // desired 50 }, - restartMaster: false, + restartPrimary: false, }, { subtest: "desired max_connections bigger - restart replica first", @@ -240,7 +240,7 @@ func TestCheckAndSetGlobalPostgreSQLConfiguration(t *testing.T) { "log_min_duration_statement": "200", "max_connections": "30", // desired 50 }, - restartMaster: false, + restartPrimary: false, }, { subtest: "desired max_connections smaller - restart master first", @@ -251,15 +251,18 @@ func TestCheckAndSetGlobalPostgreSQLConfiguration(t *testing.T) { "log_min_duration_statement": "200", "max_connections": "100", // desired 50 }, - restartMaster: true, + restartPrimary: true, }, } for _, tt := range tests { - requireMasterRestart, err := cluster.checkAndSetGlobalPostgreSQLConfiguration(mockPod, tt.patroni, cluster.Spec.Patroni, tt.pgParams, cluster.Spec.Parameters) + configPatched, requirePrimaryRestart, err := cluster.checkAndSetGlobalPostgreSQLConfiguration(mockPod, tt.patroni, cluster.Spec.Patroni, tt.pgParams, cluster.Spec.Parameters) assert.NoError(t, err) - if requireMasterRestart != tt.restartMaster { - t.Errorf("%s - %s: unexpect master restart strategy, got %v, expected %v", testName, tt.subtest, requireMasterRestart, tt.restartMaster) + if configPatched != true { + t.Errorf("%s - %s: expected config update did not happen", testName, tt.subtest) + } + if requirePrimaryRestart != tt.restartPrimary { + t.Errorf("%s - %s: wrong master restart strategy, got restart %v, expected restart %v", testName, tt.subtest, requirePrimaryRestart, tt.restartPrimary) } } } From b48034d7625c3fe064574e62cbf83583f23b4c61 Mon Sep 17 00:00:00 2001 From: Jan Mussler Date: Wed, 21 Sep 2022 15:25:24 +0200 Subject: [PATCH 17/77] Fix major version upgrade return code (#2056) Fix major version upgrade return code --- pkg/cluster/majorversionupgrade.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/cluster/majorversionupgrade.go b/pkg/cluster/majorversionupgrade.go index d42f2c93d..986e95502 100644 --- a/pkg/cluster/majorversionupgrade.go +++ b/pkg/cluster/majorversionupgrade.go @@ -117,10 +117,10 @@ func (c *Cluster) majorVersionUpgrade() error { var result string if resultIdCheck != "0" { c.logger.Infof("User id was identified as: %s, hence default user is non-root already", resultIdCheck) - result, err = c.ExecCommand(podName, "/bin/bash", "-c", upgradeCommand) + result, err = c.ExecCommand(podName, "/bin/bash", "-o", "pipefail", "-c", upgradeCommand) } else { c.logger.Infof("User id was identified as: %s, using su to reach the postgres user", resultIdCheck) - result, err = c.ExecCommand(podName, "/bin/su", "postgres", "-c", upgradeCommand) + result, err = c.ExecCommand(podName, "/bin/su", "postgres", "-o", "pipefail", "-c", upgradeCommand) } if err != nil { c.eventRecorder.Eventf(c.GetReference(), v1.EventTypeWarning, "Major Version Upgrade", "Upgrade from %d to %d FAILED: %v", c.currentMajorVersion, desiredVersion, err) From a119772efbddb15d3eabcd7f057f3af909573f4a Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Wed, 5 Oct 2022 19:25:24 +0300 Subject: [PATCH 18/77] add toggle to turn off readiness probes (#2004) * add toggle to turn off readiness probes * include PodManagementPolicy and ReadinessProbe in stateful set comparison * add URI scheme to generated readiness probe --- .../crds/operatorconfigurations.yaml | 3 +++ charts/postgres-operator/values.yaml | 2 ++ docs/reference/operator_parameters.md | 8 ++++++++ manifests/configmap.yaml | 1 + manifests/operatorconfiguration.crd.yaml | 3 +++ .../postgresql-operator-default-configuration.yaml | 1 + pkg/apis/acid.zalan.do/v1/crds.go | 3 +++ .../acid.zalan.do/v1/operator_configuration_type.go | 1 + pkg/cluster/cluster.go | 7 +++++++ pkg/cluster/k8sres.go | 13 ++++++++----- pkg/controller/operator_config.go | 1 + pkg/util/config/config.go | 1 + 12 files changed, 39 insertions(+), 5 deletions(-) diff --git a/charts/postgres-operator/crds/operatorconfigurations.yaml b/charts/postgres-operator/crds/operatorconfigurations.yaml index ca47ab3b8..4ee1c6919 100644 --- a/charts/postgres-operator/crds/operatorconfigurations.yaml +++ b/charts/postgres-operator/crds/operatorconfigurations.yaml @@ -214,6 +214,9 @@ spec: enable_pod_disruption_budget: type: boolean default: true + enable_readiness_probe: + type: boolean + default: false enable_sidecars: type: boolean default: true diff --git a/charts/postgres-operator/values.yaml b/charts/postgres-operator/values.yaml index 9870c707f..0bcc5b7e0 100644 --- a/charts/postgres-operator/values.yaml +++ b/charts/postgres-operator/values.yaml @@ -129,6 +129,8 @@ configKubernetes: enable_pod_antiaffinity: false # toggles PDB to set to MinAvailabe 0 or 1 enable_pod_disruption_budget: true + # toogles readiness probe for database pods + enable_readiness_probe: false # enables sidecar containers to run alongside Spilo in the same pod enable_sidecars: true diff --git a/docs/reference/operator_parameters.md b/docs/reference/operator_parameters.md index a923ad71c..af09a5da4 100644 --- a/docs/reference/operator_parameters.md +++ b/docs/reference/operator_parameters.md @@ -489,6 +489,14 @@ configuration they are grouped under the `kubernetes` key. of stateful sets of PG clusters. The default is `ordered_ready`, the second possible value is `parallel`. +* **enable_readiness_probe** + the operator can set a readiness probe on the statefulset for the database + pods with `InitialDelaySeconds: 6`, `PeriodSeconds: 10`, `TimeoutSeconds: 5`, + `SuccessThreshold: 1` and `FailureThreshold: 3`. When enabling readiness + probes it is recommended to switch the `pod_management_policy` to `parallel` + to avoid unneccesary waiting times in case of multiple instances failing. + The default is `false`. + * **storage_resize_mode** defines how operator handles the difference between the requested volume size and the actual size. Available options are: diff --git a/manifests/configmap.yaml b/manifests/configmap.yaml index e8823ee0e..bd5f5af12 100644 --- a/manifests/configmap.yaml +++ b/manifests/configmap.yaml @@ -52,6 +52,7 @@ data: # 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" diff --git a/manifests/operatorconfiguration.crd.yaml b/manifests/operatorconfiguration.crd.yaml index 1f6d48a4d..0f0d47d63 100644 --- a/manifests/operatorconfiguration.crd.yaml +++ b/manifests/operatorconfiguration.crd.yaml @@ -212,6 +212,9 @@ spec: enable_pod_disruption_budget: type: boolean default: true + enable_readiness_probe: + type: boolean + default: false enable_sidecars: type: boolean default: true diff --git a/manifests/postgresql-operator-default-configuration.yaml b/manifests/postgresql-operator-default-configuration.yaml index 7c8ac61ae..070bbc4df 100644 --- a/manifests/postgresql-operator-default-configuration.yaml +++ b/manifests/postgresql-operator-default-configuration.yaml @@ -60,6 +60,7 @@ configuration: 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 diff --git a/pkg/apis/acid.zalan.do/v1/crds.go b/pkg/apis/acid.zalan.do/v1/crds.go index 7b06ee233..58186a5e5 100644 --- a/pkg/apis/acid.zalan.do/v1/crds.go +++ b/pkg/apis/acid.zalan.do/v1/crds.go @@ -1275,6 +1275,9 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{ "enable_pod_disruption_budget": { Type: "boolean", }, + "enable_readiness_probe": { + Type: "boolean", + }, "enable_sidecars": { Type: "boolean", }, diff --git a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go index de149033a..85ba25e34 100644 --- a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go +++ b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go @@ -98,6 +98,7 @@ type KubernetesMetaConfiguration struct { EnablePodAntiAffinity bool `json:"enable_pod_antiaffinity,omitempty"` PodAntiAffinityTopologyKey string `json:"pod_antiaffinity_topology_key,omitempty"` PodManagementPolicy string `json:"pod_management_policy,omitempty"` + EnableReadinessProbe bool `json:"enable_readiness_probe,omitempty"` EnableCrossNamespaceSecret bool `json:"enable_cross_namespace_secret,omitempty"` } diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index 93ae3ec35..d993cfa0d 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -389,6 +389,11 @@ func (c *Cluster) compareStatefulSetWith(statefulSet *appsv1.StatefulSet) *compa needsReplace = true reasons = append(reasons, "new statefulset's annotations do not match: "+reason) } + if c.Statefulset.Spec.PodManagementPolicy != statefulSet.Spec.PodManagementPolicy { + match = false + needsReplace = true + reasons = append(reasons, "new statefulset's pod management policy do not match") + } needsRollUpdate, reasons = c.compareContainers("initContainers", c.Statefulset.Spec.Template.Spec.InitContainers, statefulSet.Spec.Template.Spec.InitContainers, needsRollUpdate, reasons) needsRollUpdate, reasons = c.compareContainers("containers", c.Statefulset.Spec.Template.Spec.Containers, statefulSet.Spec.Template.Spec.Containers, needsRollUpdate, reasons) @@ -528,6 +533,8 @@ func (c *Cluster) compareContainers(description string, setA, setB []v1.Containe checks := []containerCheck{ newCheck("new statefulset %s's %s (index %d) name does not match the current one", func(a, b v1.Container) bool { return a.Name != b.Name }), + newCheck("new statefulset %s's %s (index %d) readiness probe does not match the current one", + func(a, b v1.Container) bool { return !reflect.DeepEqual(a.ReadinessProbe, b.ReadinessProbe) }), newCheck("new statefulset %s's %s (index %d) ports do not match the current one", func(a, b v1.Container) bool { return !comparePorts(a.Ports, b.Ports) }), newCheck("new statefulset %s's %s (index %d) resources do not match the current ones", diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index 337cd3a63..ac0ae59c6 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -1119,17 +1119,18 @@ func extractPgVersionFromBinPath(binPath string, template string) (string, error func generateSpiloReadinessProbe() *v1.Probe { return &v1.Probe{ + FailureThreshold: 3, Handler: v1.Handler{ HTTPGet: &v1.HTTPGetAction{ - Path: "/readiness", - Port: intstr.IntOrString{IntVal: patroni.ApiPort}, + Path: "/readiness", + Port: intstr.IntOrString{IntVal: patroni.ApiPort}, + Scheme: v1.URISchemeHTTP, }, }, InitialDelaySeconds: 6, PeriodSeconds: 10, - TimeoutSeconds: 5, SuccessThreshold: 1, - FailureThreshold: 3, + TimeoutSeconds: 5, } } @@ -1280,7 +1281,9 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef ) // Patroni responds 200 to probe only if it either owns the leader lock or postgres is running and DCS is accessible - spiloContainer.ReadinessProbe = generateSpiloReadinessProbe() + if c.OpConfig.EnableReadinessProbe { + spiloContainer.ReadinessProbe = generateSpiloReadinessProbe() + } // generate container specs for sidecars specified in the cluster manifest clusterSpecificSidecars := []v1.Container{} diff --git a/pkg/controller/operator_config.go b/pkg/controller/operator_config.go index d66ff7f11..b035573a0 100644 --- a/pkg/controller/operator_config.go +++ b/pkg/controller/operator_config.go @@ -118,6 +118,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur result.NodeReadinessLabelMerge = fromCRD.Kubernetes.NodeReadinessLabelMerge result.PodPriorityClassName = fromCRD.Kubernetes.PodPriorityClassName result.PodManagementPolicy = util.Coalesce(fromCRD.Kubernetes.PodManagementPolicy, "ordered_ready") + result.EnableReadinessProbe = fromCRD.Kubernetes.EnableReadinessProbe result.MasterPodMoveTimeout = util.CoalesceDuration(time.Duration(fromCRD.Kubernetes.MasterPodMoveTimeout), "10m") result.EnablePodAntiAffinity = fromCRD.Kubernetes.EnablePodAntiAffinity result.PodAntiAffinityTopologyKey = util.Coalesce(fromCRD.Kubernetes.PodAntiAffinityTopologyKey, "kubernetes.io/hostname") diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index e1fde13f4..447acbc92 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -219,6 +219,7 @@ type Config struct { TeamAPIRoleConfiguration map[string]string `name:"team_api_role_configuration" default:"log_statement:all"` PodTerminateGracePeriod time.Duration `name:"pod_terminate_grace_period" default:"5m"` PodManagementPolicy string `name:"pod_management_policy" default:"ordered_ready"` + EnableReadinessProbe bool `name:"enable_readiness_probe" default:"false"` ProtectedRoles []string `name:"protected_role_names" default:"admin,cron_admin"` PostgresSuperuserTeams []string `name:"postgres_superuser_teams" default:""` SetMemoryRequestToLimit bool `name:"set_memory_request_to_limit" default:"false"` From 2aa52094db3aef50df3265aaca7574ad15c58200 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Thu, 6 Oct 2022 10:43:17 +0300 Subject: [PATCH 19/77] switch to policy API v1 for PDBs (#2008) * switch to policy API v1 for PDBs * update e2e test dependencies * use kind 0.14.0 * bump K8s client in e2e docker image * bump e2e tests-runner --- e2e/Dockerfile | 2 +- e2e/Makefile | 2 +- e2e/exec_into_env.sh | 2 +- e2e/requirements.txt | 6 +++--- e2e/run.sh | 2 +- e2e/tests/k8s_api.py | 6 +++--- pkg/cluster/cluster.go | 4 ++-- pkg/cluster/k8sres.go | 8 ++++---- pkg/cluster/k8sres_test.go | 20 ++++++++++---------- pkg/cluster/resources.go | 8 ++++---- pkg/cluster/sync.go | 4 ++-- pkg/cluster/types.go | 4 ++-- pkg/cluster/util.go | 4 ++-- pkg/cluster/util_test.go | 2 +- pkg/util/k8sutil/k8sutil.go | 10 +++++----- 15 files changed, 42 insertions(+), 42 deletions(-) diff --git a/e2e/Dockerfile b/e2e/Dockerfile index eabd0dabe..b97f52dcb 100644 --- a/e2e/Dockerfile +++ b/e2e/Dockerfile @@ -16,7 +16,7 @@ RUN apt-get update \ curl \ vim \ && pip3 install --no-cache-dir -r requirements.txt \ - && curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.22.0/bin/linux/amd64/kubectl \ + && curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.24.3/bin/linux/amd64/kubectl \ && chmod +x ./kubectl \ && mv ./kubectl /usr/local/bin/kubectl \ && apt-get clean \ diff --git a/e2e/Makefile b/e2e/Makefile index b2da9c86e..a32017192 100644 --- a/e2e/Makefile +++ b/e2e/Makefile @@ -47,7 +47,7 @@ tools: # install pinned version of 'kind' # go install must run outside of a dir with a (module-based) Go project ! # otherwise go install updates project's dependencies and/or behaves differently - cd "/tmp" && GO111MODULE=on go install sigs.k8s.io/kind@v0.11.1 + cd "/tmp" && GO111MODULE=on go install sigs.k8s.io/kind@v0.14.0 e2etest: tools copy clean ./run.sh main diff --git a/e2e/exec_into_env.sh b/e2e/exec_into_env.sh index ef12ba18a..59acbeeb4 100755 --- a/e2e/exec_into_env.sh +++ b/e2e/exec_into_env.sh @@ -3,7 +3,7 @@ export cluster_name="postgres-operator-e2e-tests" export kubeconfig_path="/tmp/kind-config-${cluster_name}" export operator_image="registry.opensource.zalan.do/acid/postgres-operator:latest" -export e2e_test_runner_image="registry.opensource.zalan.do/acid/postgres-operator-e2e-tests-runner:0.3" +export e2e_test_runner_image="registry.opensource.zalan.do/acid/postgres-operator-e2e-tests-runner:0.4" docker run -it --entrypoint /bin/bash --network=host -e "TERM=xterm-256color" \ --mount type=bind,source="$(readlink -f ${kubeconfig_path})",target=/root/.kube/config \ diff --git a/e2e/requirements.txt b/e2e/requirements.txt index b276d2537..ea5405b56 100644 --- a/e2e/requirements.txt +++ b/e2e/requirements.txt @@ -1,3 +1,3 @@ -kubernetes==11.0.0 -timeout_decorator==0.4.1 -pyyaml==5.4.1 +kubernetes==24.2.0 +timeout_decorator==0.5.0 +pyyaml==6.0 diff --git a/e2e/run.sh b/e2e/run.sh index 39d85f072..f5ca56a05 100755 --- a/e2e/run.sh +++ b/e2e/run.sh @@ -9,7 +9,7 @@ IFS=$'\n\t' readonly cluster_name="postgres-operator-e2e-tests" readonly kubeconfig_path="/tmp/kind-config-${cluster_name}" readonly spilo_image="registry.opensource.zalan.do/acid/spilo-14-e2e:0.1" -readonly e2e_test_runner_image="registry.opensource.zalan.do/acid/postgres-operator-e2e-tests-runner:0.3" +readonly e2e_test_runner_image="registry.opensource.zalan.do/acid/postgres-operator-e2e-tests-runner:0.4" export GOPATH=${GOPATH-~/go} export PATH=${GOPATH}/bin:$PATH diff --git a/e2e/tests/k8s_api.py b/e2e/tests/k8s_api.py index cf8f47be4..c68e09d49 100644 --- a/e2e/tests/k8s_api.py +++ b/e2e/tests/k8s_api.py @@ -25,7 +25,7 @@ class K8sApi: self.apps_v1 = client.AppsV1Api() self.batch_v1_beta1 = client.BatchV1beta1Api() self.custom_objects_api = client.CustomObjectsApi() - self.policy_v1_beta1 = client.PolicyV1beta1Api() + self.policy_v1 = client.PolicyV1Api() self.storage_v1_api = client.StorageV1Api() @@ -179,7 +179,7 @@ class K8s: return len(self.api.apps_v1.list_namespaced_deployment(namespace, label_selector=labels).items) def count_pdbs_with_label(self, labels, namespace='default'): - return len(self.api.policy_v1_beta1.list_namespaced_pod_disruption_budget( + return len(self.api.policy_v1.list_namespaced_pod_disruption_budget( namespace, label_selector=labels).items) def count_running_pods(self, labels='application=spilo,cluster-name=acid-minimal-cluster', namespace='default'): @@ -471,7 +471,7 @@ class K8sBase: return len(self.api.apps_v1.list_namespaced_deployment(namespace, label_selector=labels).items) def count_pdbs_with_label(self, labels, namespace='default'): - return len(self.api.policy_v1_beta1.list_namespaced_pod_disruption_budget( + return len(self.api.policy_v1.list_namespaced_pod_disruption_budget( namespace, label_selector=labels).items) def count_running_pods(self, labels='application=spilo,cluster-name=acid-minimal-cluster', namespace='default'): diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index d993cfa0d..a75c03da2 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -29,7 +29,7 @@ import ( "github.com/zalando/postgres-operator/pkg/util/volumes" appsv1 "k8s.io/api/apps/v1" v1 "k8s.io/api/core/v1" - policybeta1 "k8s.io/api/policy/v1beta1" + policyv1 "k8s.io/api/policy/v1" rbacv1 "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -61,7 +61,7 @@ type kubeResources struct { Endpoints map[PostgresRole]*v1.Endpoints Secrets map[types.UID]*v1.Secret Statefulset *appsv1.StatefulSet - PodDisruptionBudget *policybeta1.PodDisruptionBudget + PodDisruptionBudget *policyv1.PodDisruptionBudget //Pods are treated separately //PVCs are treated separately } diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index ac0ae59c6..9dee64afb 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -13,7 +13,7 @@ import ( appsv1 "k8s.io/api/apps/v1" v1 "k8s.io/api/core/v1" - policybeta1 "k8s.io/api/policy/v1beta1" + policyv1 "k8s.io/api/policy/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -1983,7 +1983,7 @@ func (c *Cluster) generateStandbyEnvironment(description *acidv1.StandbyDescript return result } -func (c *Cluster) generatePodDisruptionBudget() *policybeta1.PodDisruptionBudget { +func (c *Cluster) generatePodDisruptionBudget() *policyv1.PodDisruptionBudget { minAvailable := intstr.FromInt(1) pdbEnabled := c.OpConfig.EnablePodDisruptionBudget @@ -1992,14 +1992,14 @@ func (c *Cluster) generatePodDisruptionBudget() *policybeta1.PodDisruptionBudget minAvailable = intstr.FromInt(0) } - return &policybeta1.PodDisruptionBudget{ + return &policyv1.PodDisruptionBudget{ ObjectMeta: metav1.ObjectMeta{ Name: c.podDisruptionBudgetName(), Namespace: c.Namespace, Labels: c.labelsSet(true), Annotations: c.annotationsSet(nil), }, - Spec: policybeta1.PodDisruptionBudgetSpec{ + Spec: policyv1.PodDisruptionBudgetSpec{ MinAvailable: &minAvailable, Selector: &metav1.LabelSelector{ MatchLabels: c.roleLabelsSet(false, Master), diff --git a/pkg/cluster/k8sres_test.go b/pkg/cluster/k8sres_test.go index 8e748f042..60be7dfaa 100644 --- a/pkg/cluster/k8sres_test.go +++ b/pkg/cluster/k8sres_test.go @@ -21,7 +21,7 @@ import ( appsv1 "k8s.io/api/apps/v1" v1 "k8s.io/api/core/v1" - policyv1beta1 "k8s.io/api/policy/v1beta1" + policyv1 "k8s.io/api/policy/v1" k8serrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -1979,7 +1979,7 @@ func TestSidecars(t *testing.T) { func TestGeneratePodDisruptionBudget(t *testing.T) { tests := []struct { c *Cluster - out policyv1beta1.PodDisruptionBudget + out policyv1.PodDisruptionBudget }{ // With multiple instances. { @@ -1991,13 +1991,13 @@ func TestGeneratePodDisruptionBudget(t *testing.T) { Spec: acidv1.PostgresSpec{TeamID: "myapp", NumberOfInstances: 3}}, logger, eventRecorder), - policyv1beta1.PodDisruptionBudget{ + policyv1.PodDisruptionBudget{ ObjectMeta: metav1.ObjectMeta{ Name: "postgres-myapp-database-pdb", Namespace: "myapp", Labels: map[string]string{"team": "myapp", "cluster-name": "myapp-database"}, }, - Spec: policyv1beta1.PodDisruptionBudgetSpec{ + Spec: policyv1.PodDisruptionBudgetSpec{ MinAvailable: util.ToIntStr(1), Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{"spilo-role": "master", "cluster-name": "myapp-database"}, @@ -2015,13 +2015,13 @@ func TestGeneratePodDisruptionBudget(t *testing.T) { Spec: acidv1.PostgresSpec{TeamID: "myapp", NumberOfInstances: 0}}, logger, eventRecorder), - policyv1beta1.PodDisruptionBudget{ + policyv1.PodDisruptionBudget{ ObjectMeta: metav1.ObjectMeta{ Name: "postgres-myapp-database-pdb", Namespace: "myapp", Labels: map[string]string{"team": "myapp", "cluster-name": "myapp-database"}, }, - Spec: policyv1beta1.PodDisruptionBudgetSpec{ + Spec: policyv1.PodDisruptionBudgetSpec{ MinAvailable: util.ToIntStr(0), Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{"spilo-role": "master", "cluster-name": "myapp-database"}, @@ -2039,13 +2039,13 @@ func TestGeneratePodDisruptionBudget(t *testing.T) { Spec: acidv1.PostgresSpec{TeamID: "myapp", NumberOfInstances: 3}}, logger, eventRecorder), - policyv1beta1.PodDisruptionBudget{ + policyv1.PodDisruptionBudget{ ObjectMeta: metav1.ObjectMeta{ Name: "postgres-myapp-database-pdb", Namespace: "myapp", Labels: map[string]string{"team": "myapp", "cluster-name": "myapp-database"}, }, - Spec: policyv1beta1.PodDisruptionBudgetSpec{ + Spec: policyv1.PodDisruptionBudgetSpec{ MinAvailable: util.ToIntStr(0), Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{"spilo-role": "master", "cluster-name": "myapp-database"}, @@ -2063,13 +2063,13 @@ func TestGeneratePodDisruptionBudget(t *testing.T) { Spec: acidv1.PostgresSpec{TeamID: "myapp", NumberOfInstances: 3}}, logger, eventRecorder), - policyv1beta1.PodDisruptionBudget{ + policyv1.PodDisruptionBudget{ ObjectMeta: metav1.ObjectMeta{ Name: "postgres-myapp-database-databass-budget", Namespace: "myapp", Labels: map[string]string{"team": "myapp", "cluster-name": "myapp-database"}, }, - Spec: policyv1beta1.PodDisruptionBudgetSpec{ + Spec: policyv1.PodDisruptionBudgetSpec{ MinAvailable: util.ToIntStr(1), Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{"spilo-role": "master", "cluster-name": "myapp-database"}, diff --git a/pkg/cluster/resources.go b/pkg/cluster/resources.go index 9a57051df..eea169ff3 100644 --- a/pkg/cluster/resources.go +++ b/pkg/cluster/resources.go @@ -9,7 +9,7 @@ import ( appsv1 "k8s.io/api/apps/v1" batchv1beta1 "k8s.io/api/batch/v1beta1" v1 "k8s.io/api/core/v1" - policybeta1 "k8s.io/api/policy/v1beta1" + policyv1 "k8s.io/api/policy/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -404,7 +404,7 @@ func (c *Cluster) generateEndpointSubsets(role PostgresRole) []v1.EndpointSubset return result } -func (c *Cluster) createPodDisruptionBudget() (*policybeta1.PodDisruptionBudget, error) { +func (c *Cluster) createPodDisruptionBudget() (*policyv1.PodDisruptionBudget, error) { podDisruptionBudgetSpec := c.generatePodDisruptionBudget() podDisruptionBudget, err := c.KubeClient. PodDisruptionBudgets(podDisruptionBudgetSpec.Namespace). @@ -418,7 +418,7 @@ func (c *Cluster) createPodDisruptionBudget() (*policybeta1.PodDisruptionBudget, return podDisruptionBudget, nil } -func (c *Cluster) updatePodDisruptionBudget(pdb *policybeta1.PodDisruptionBudget) error { +func (c *Cluster) updatePodDisruptionBudget(pdb *policyv1.PodDisruptionBudget) error { if c.PodDisruptionBudget == nil { return fmt.Errorf("there is no pod disruption budget in the cluster") } @@ -602,6 +602,6 @@ func (c *Cluster) GetStatefulSet() *appsv1.StatefulSet { } // GetPodDisruptionBudget returns cluster's kubernetes PodDisruptionBudget -func (c *Cluster) GetPodDisruptionBudget() *policybeta1.PodDisruptionBudget { +func (c *Cluster) GetPodDisruptionBudget() *policyv1.PodDisruptionBudget { return c.PodDisruptionBudget } diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index cdb6fa77a..8db772b07 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -17,7 +17,7 @@ import ( "github.com/zalando/postgres-operator/pkg/util/k8sutil" batchv1beta1 "k8s.io/api/batch/v1beta1" v1 "k8s.io/api/core/v1" - policybeta1 "k8s.io/api/policy/v1beta1" + policyv1 "k8s.io/api/policy/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -236,7 +236,7 @@ func (c *Cluster) syncEndpoint(role PostgresRole) error { func (c *Cluster) syncPodDisruptionBudget(isUpdate bool) error { var ( - pdb *policybeta1.PodDisruptionBudget + pdb *policyv1.PodDisruptionBudget err error ) if pdb, err = c.KubeClient.PodDisruptionBudgets(c.Namespace).Get(context.TODO(), c.podDisruptionBudgetName(), metav1.GetOptions{}); err == nil { diff --git a/pkg/cluster/types.go b/pkg/cluster/types.go index 31f27aa54..1b4d0f389 100644 --- a/pkg/cluster/types.go +++ b/pkg/cluster/types.go @@ -6,7 +6,7 @@ import ( acidv1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" appsv1 "k8s.io/api/apps/v1" v1 "k8s.io/api/core/v1" - policybeta1 "k8s.io/api/policy/v1beta1" + policyv1 "k8s.io/api/policy/v1" "k8s.io/apimachinery/pkg/types" ) @@ -65,7 +65,7 @@ type ClusterStatus struct { MasterEndpoint *v1.Endpoints ReplicaEndpoint *v1.Endpoints StatefulSet *appsv1.StatefulSet - PodDisruptionBudget *policybeta1.PodDisruptionBudget + PodDisruptionBudget *policyv1.PodDisruptionBudget CurrentProcess Process Worker uint32 diff --git a/pkg/cluster/util.go b/pkg/cluster/util.go index 2a8920803..fcaf8b9e6 100644 --- a/pkg/cluster/util.go +++ b/pkg/cluster/util.go @@ -14,7 +14,7 @@ import ( appsv1 "k8s.io/api/apps/v1" v1 "k8s.io/api/core/v1" - policybeta1 "k8s.io/api/policy/v1beta1" + policyv1 "k8s.io/api/policy/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" @@ -159,7 +159,7 @@ func metaAnnotationsPatch(annotations map[string]string) ([]byte, error) { }{&meta}) } -func (c *Cluster) logPDBChanges(old, new *policybeta1.PodDisruptionBudget, isUpdate bool, reason string) { +func (c *Cluster) logPDBChanges(old, new *policyv1.PodDisruptionBudget, isUpdate bool, reason string) { if isUpdate { c.logger.Infof("pod disruption budget %q has been changed", util.NameFromMeta(old.ObjectMeta)) } else { diff --git a/pkg/cluster/util_test.go b/pkg/cluster/util_test.go index 9ed644ea2..d04a24e1d 100644 --- a/pkg/cluster/util_test.go +++ b/pkg/cluster/util_test.go @@ -19,7 +19,7 @@ func newFakeK8sAnnotationsClient() (k8sutil.KubernetesClient, *k8sFake.Clientset acidClientSet := fakeacidv1.NewSimpleClientset() return k8sutil.KubernetesClient{ - PodDisruptionBudgetsGetter: clientSet.PolicyV1beta1(), + PodDisruptionBudgetsGetter: clientSet.PolicyV1(), ServicesGetter: clientSet.CoreV1(), StatefulSetsGetter: clientSet.AppsV1(), PostgresqlsGetter: acidClientSet.AcidV1(), diff --git a/pkg/util/k8sutil/k8sutil.go b/pkg/util/k8sutil/k8sutil.go index f4ea014f2..52491a365 100644 --- a/pkg/util/k8sutil/k8sutil.go +++ b/pkg/util/k8sutil/k8sutil.go @@ -18,7 +18,7 @@ import ( "github.com/zalando/postgres-operator/pkg/spec" apiappsv1 "k8s.io/api/apps/v1" v1 "k8s.io/api/core/v1" - policybeta1 "k8s.io/api/policy/v1beta1" + apipolicyv1 "k8s.io/api/policy/v1" apiextclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" apiextv1 "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -27,7 +27,7 @@ import ( "k8s.io/client-go/kubernetes" appsv1 "k8s.io/client-go/kubernetes/typed/apps/v1" corev1 "k8s.io/client-go/kubernetes/typed/core/v1" - policyv1beta1 "k8s.io/client-go/kubernetes/typed/policy/v1beta1" + policyv1 "k8s.io/client-go/kubernetes/typed/policy/v1" rbacv1 "k8s.io/client-go/kubernetes/typed/rbac/v1" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" @@ -61,7 +61,7 @@ type KubernetesClient struct { appsv1.StatefulSetsGetter appsv1.DeploymentsGetter rbacv1.RoleBindingsGetter - policyv1beta1.PodDisruptionBudgetsGetter + policyv1.PodDisruptionBudgetsGetter apiextv1.CustomResourceDefinitionsGetter clientbatchv1beta1.CronJobsGetter acidv1.OperatorConfigurationsGetter @@ -156,7 +156,7 @@ func NewFromConfig(cfg *rest.Config) (KubernetesClient, error) { kubeClient.NamespacesGetter = client.CoreV1() kubeClient.StatefulSetsGetter = client.AppsV1() kubeClient.DeploymentsGetter = client.AppsV1() - kubeClient.PodDisruptionBudgetsGetter = client.PolicyV1beta1() + kubeClient.PodDisruptionBudgetsGetter = client.PolicyV1() kubeClient.RESTClient = client.CoreV1().RESTClient() kubeClient.RoleBindingsGetter = client.RbacV1() kubeClient.CronJobsGetter = client.BatchV1beta1() @@ -214,7 +214,7 @@ func (client *KubernetesClient) SetPostgresCRDStatus(clusterName spec.Namespaced } // SamePDB compares the PodDisruptionBudgets -func SamePDB(cur, new *policybeta1.PodDisruptionBudget) (match bool, reason string) { +func SamePDB(cur, new *apipolicyv1.PodDisruptionBudget) (match bool, reason string) { //TODO: improve comparison match = reflect.DeepEqual(new.Spec, cur.Spec) if !match { From 84fe38a06908aa8adbe580041c133db84faf7121 Mon Sep 17 00:00:00 2001 From: Philipp B Date: Fri, 7 Oct 2022 11:27:58 +0200 Subject: [PATCH 20/77] switch to batch API v1 for Jobs (#2066) --- e2e/tests/k8s_api.py | 6 +++--- pkg/cluster/k8sres.go | 11 +++++------ pkg/cluster/resources.go | 4 ++-- pkg/cluster/sync.go | 6 +++--- pkg/util/k8sutil/k8sutil.go | 12 ++++++------ 5 files changed, 19 insertions(+), 20 deletions(-) diff --git a/e2e/tests/k8s_api.py b/e2e/tests/k8s_api.py index c68e09d49..82fed4c0b 100644 --- a/e2e/tests/k8s_api.py +++ b/e2e/tests/k8s_api.py @@ -23,7 +23,7 @@ class K8sApi: self.core_v1 = client.CoreV1Api() self.apps_v1 = client.AppsV1Api() - self.batch_v1_beta1 = client.BatchV1beta1Api() + self.batch_v1 = client.BatchV1Api() self.custom_objects_api = client.CustomObjectsApi() self.policy_v1 = client.PolicyV1Api() self.storage_v1_api = client.StorageV1Api() @@ -217,7 +217,7 @@ class K8s: time.sleep(self.RETRY_TIMEOUT_SEC) def get_logical_backup_job(self, namespace='default'): - return self.api.batch_v1_beta1.list_namespaced_cron_job(namespace, label_selector="application=spilo") + return self.api.batch_v1.list_namespaced_cron_job(namespace, label_selector="application=spilo") def wait_for_logical_backup_job(self, expected_num_of_jobs): while (len(self.get_logical_backup_job().items) != expected_num_of_jobs): @@ -499,7 +499,7 @@ class K8sBase: time.sleep(self.RETRY_TIMEOUT_SEC) def get_logical_backup_job(self, namespace='default'): - return self.api.batch_v1_beta1.list_namespaced_cron_job(namespace, label_selector="application=spilo") + return self.api.batch_v1.list_namespaced_cron_job(namespace, label_selector="application=spilo") def wait_for_logical_backup_job(self, expected_num_of_jobs): while (len(self.get_logical_backup_job().items) != expected_num_of_jobs): diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index 9dee64afb..437598cf0 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -29,7 +29,6 @@ import ( "github.com/zalando/postgres-operator/pkg/util/patroni" "github.com/zalando/postgres-operator/pkg/util/retryutil" batchv1 "k8s.io/api/batch/v1" - batchv1beta1 "k8s.io/api/batch/v1beta1" "k8s.io/apimachinery/pkg/labels" ) @@ -2017,7 +2016,7 @@ func (c *Cluster) getClusterServiceConnectionParameters(clusterName string) (hos return } -func (c *Cluster) generateLogicalBackupJob() (*batchv1beta1.CronJob, error) { +func (c *Cluster) generateLogicalBackupJob() (*batchv1.CronJob, error) { var ( err error @@ -2108,7 +2107,7 @@ func (c *Cluster) generateLogicalBackupJob() (*batchv1beta1.CronJob, error) { // configure a cron job - jobTemplateSpec := batchv1beta1.JobTemplateSpec{ + jobTemplateSpec := batchv1.JobTemplateSpec{ Spec: jobSpec, } @@ -2117,17 +2116,17 @@ func (c *Cluster) generateLogicalBackupJob() (*batchv1beta1.CronJob, error) { schedule = c.OpConfig.LogicalBackupSchedule } - cronJob := &batchv1beta1.CronJob{ + cronJob := &batchv1.CronJob{ ObjectMeta: metav1.ObjectMeta{ Name: c.getLogicalBackupJobName(), Namespace: c.Namespace, Labels: c.labelsSet(true), Annotations: c.annotationsSet(nil), }, - Spec: batchv1beta1.CronJobSpec{ + Spec: batchv1.CronJobSpec{ Schedule: schedule, JobTemplate: jobTemplateSpec, - ConcurrencyPolicy: batchv1beta1.ForbidConcurrent, + ConcurrencyPolicy: batchv1.ForbidConcurrent, }, } diff --git a/pkg/cluster/resources.go b/pkg/cluster/resources.go index eea169ff3..5cc428aca 100644 --- a/pkg/cluster/resources.go +++ b/pkg/cluster/resources.go @@ -7,7 +7,7 @@ import ( "strings" appsv1 "k8s.io/api/apps/v1" - batchv1beta1 "k8s.io/api/batch/v1beta1" + batchv1 "k8s.io/api/batch/v1" v1 "k8s.io/api/core/v1" policyv1 "k8s.io/api/policy/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -546,7 +546,7 @@ func (c *Cluster) createLogicalBackupJob() (err error) { return nil } -func (c *Cluster) patchLogicalBackupJob(newJob *batchv1beta1.CronJob) error { +func (c *Cluster) patchLogicalBackupJob(newJob *batchv1.CronJob) error { c.setProcessName("patching logical backup job") patchData, err := specPatch(newJob.Spec) diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index 8db772b07..3300f8724 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -15,7 +15,7 @@ import ( "github.com/zalando/postgres-operator/pkg/util" "github.com/zalando/postgres-operator/pkg/util/constants" "github.com/zalando/postgres-operator/pkg/util/k8sutil" - batchv1beta1 "k8s.io/api/batch/v1beta1" + batchv1 "k8s.io/api/batch/v1" v1 "k8s.io/api/core/v1" policyv1 "k8s.io/api/policy/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -1145,8 +1145,8 @@ func (c *Cluster) syncExtensions(extensions map[string]string) error { func (c *Cluster) syncLogicalBackupJob() error { var ( - job *batchv1beta1.CronJob - desiredJob *batchv1beta1.CronJob + job *batchv1.CronJob + desiredJob *batchv1.CronJob err error ) c.setProcessName("syncing the logical backup job") diff --git a/pkg/util/k8sutil/k8sutil.go b/pkg/util/k8sutil/k8sutil.go index 52491a365..dba589f6b 100644 --- a/pkg/util/k8sutil/k8sutil.go +++ b/pkg/util/k8sutil/k8sutil.go @@ -8,8 +8,8 @@ import ( b64 "encoding/base64" "encoding/json" - batchv1beta1 "k8s.io/api/batch/v1beta1" - clientbatchv1beta1 "k8s.io/client-go/kubernetes/typed/batch/v1beta1" + batchv1 "k8s.io/api/batch/v1" + clientbatchv1 "k8s.io/client-go/kubernetes/typed/batch/v1" apiacidv1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" zalandoclient "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned" @@ -63,7 +63,7 @@ type KubernetesClient struct { rbacv1.RoleBindingsGetter policyv1.PodDisruptionBudgetsGetter apiextv1.CustomResourceDefinitionsGetter - clientbatchv1beta1.CronJobsGetter + clientbatchv1.CronJobsGetter acidv1.OperatorConfigurationsGetter acidv1.PostgresTeamsGetter acidv1.PostgresqlsGetter @@ -159,7 +159,7 @@ func NewFromConfig(cfg *rest.Config) (KubernetesClient, error) { kubeClient.PodDisruptionBudgetsGetter = client.PolicyV1() kubeClient.RESTClient = client.CoreV1().RESTClient() kubeClient.RoleBindingsGetter = client.RbacV1() - kubeClient.CronJobsGetter = client.BatchV1beta1() + kubeClient.CronJobsGetter = client.BatchV1() kubeClient.EventsGetter = client.CoreV1() apiextClient, err := apiextclient.NewForConfig(cfg) @@ -224,12 +224,12 @@ func SamePDB(cur, new *apipolicyv1.PodDisruptionBudget) (match bool, reason stri return } -func getJobImage(cronJob *batchv1beta1.CronJob) string { +func getJobImage(cronJob *batchv1.CronJob) string { return cronJob.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Image } // SameLogicalBackupJob compares Specs of logical backup cron jobs -func SameLogicalBackupJob(cur, new *batchv1beta1.CronJob) (match bool, reason string) { +func SameLogicalBackupJob(cur, new *batchv1.CronJob) (match bool, reason string) { if cur.Spec.Schedule != new.Spec.Schedule { return false, fmt.Sprintf("new job's schedule %q does not match the current one %q", From ce8b009c6608524fd148eb91a52030cc0e6dbd7a Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Tue, 11 Oct 2022 18:02:41 +0200 Subject: [PATCH 21/77] fix team member deprecation (#2072) --- .github/workflows/run_e2e.yaml | 2 ++ e2e/tests/test_e2e.py | 53 ++++++++++++++++++++++++++++++---- pkg/cluster/database.go | 3 +- pkg/cluster/sync.go | 32 +++++++++++--------- pkg/util/users/users.go | 27 ++++++++++------- pkg/util/util_test.go | 2 ++ 6 files changed, 90 insertions(+), 29 deletions(-) diff --git a/.github/workflows/run_e2e.yaml b/.github/workflows/run_e2e.yaml index 5dcb195d2..cdfcf9b2b 100644 --- a/.github/workflows/run_e2e.yaml +++ b/.github/workflows/run_e2e.yaml @@ -17,6 +17,8 @@ jobs: go-version: "^1.17.4" - name: Make dependencies run: make deps mocks + - name: Code generation + run: make codegen - name: Compile run: make linux - name: Run unit tests diff --git a/e2e/tests/test_e2e.py b/e2e/tests/test_e2e.py index 4c37d2629..82e9cb39c 100644 --- a/e2e/tests/test_e2e.py +++ b/e2e/tests/test_e2e.py @@ -250,6 +250,8 @@ class EndToEndTestCase(unittest.TestCase): } k8s.update_config(enable_postgres_team_crd) + # add team and member to custom-team-membership + # contains already elephant user k8s.api.custom_objects_api.patch_namespaced_custom_object( 'acid.zalan.do', 'v1', 'default', 'postgresteams', 'custom-team-membership', @@ -300,6 +302,13 @@ class EndToEndTestCase(unittest.TestCase): self.eventuallyEqual(lambda: len(self.query_database(leader.metadata.name, "postgres", user_query)), 2, "Database role of replaced member in PostgresTeam not renamed", 10, 5) + # create fake deletion user so operator fails renaming + # but altering role to NOLOGIN will succeed + create_fake_deletion_user = """ + CREATE USER tester_delete_me NOLOGIN; + """ + self.query_database(leader.metadata.name, "postgres", create_fake_deletion_user) + # re-add additional member and check if the role is renamed back k8s.api.custom_objects_api.patch_namespaced_custom_object( 'acid.zalan.do', 'v1', 'default', @@ -317,11 +326,44 @@ class EndToEndTestCase(unittest.TestCase): user_query = """ SELECT rolname FROM pg_catalog.pg_roles - WHERE (rolname = 'kind' AND rolcanlogin) - OR (rolname = 'tester_delete_me' AND NOT rolcanlogin); + WHERE rolname = 'kind' AND rolcanlogin; + """ + self.eventuallyEqual(lambda: len(self.query_database(leader.metadata.name, "postgres", user_query)), 1, + "Database role of recreated member in PostgresTeam not renamed back to original name", 10, 5) + + user_query = """ + SELECT rolname + FROM pg_catalog.pg_roles + WHERE rolname IN ('tester','tester_delete_me') AND NOT rolcanlogin; """ self.eventuallyEqual(lambda: len(self.query_database(leader.metadata.name, "postgres", user_query)), 2, - "Database role of recreated member in PostgresTeam not renamed back to original name", 10, 5) + "Database role of replaced member in PostgresTeam not denied from login", 10, 5) + + # re-add other additional member, operator should grant LOGIN back to tester + # but nothing happens to deleted role + k8s.api.custom_objects_api.patch_namespaced_custom_object( + 'acid.zalan.do', 'v1', 'default', + 'postgresteams', 'custom-team-membership', + { + 'spec': { + 'additionalMembers': { + 'e2e': [ + 'kind', + 'tester' + ] + }, + } + }) + + user_query = """ + SELECT rolname + FROM pg_catalog.pg_roles + WHERE (rolname IN ('tester', 'kind') + AND rolcanlogin) + OR (rolname = 'tester_delete_me' AND NOT rolcanlogin); + """ + self.eventuallyEqual(lambda: len(self.query_database(leader.metadata.name, "postgres", user_query)), 3, + "Database role of deleted member in PostgresTeam not removed when recreated manually", 10, 5) # revert config change revert_resync = { @@ -1204,8 +1246,9 @@ class EndToEndTestCase(unittest.TestCase): self.eventuallyEqual(lambda: k8s.get_operator_state(), {"0": "idle"}, "Operator does not get in sync") # node affinity change should cause another rolling update and relocation of replica - k8s.wait_for_pod_start('spilo-role=replica,' + cluster_label) + k8s.wait_for_pod_failover(master_nodes, 'spilo-role=replica,' + cluster_label) k8s.wait_for_pod_start('spilo-role=master,' + cluster_label) + k8s.wait_for_pod_start('spilo-role=replica,' + cluster_label) except timeout_decorator.TimeoutError: print('Operator log: {}'.format(k8s.get_operator_log())) @@ -1956,4 +1999,4 @@ class EndToEndTestCase(unittest.TestCase): return result_set if __name__ == '__main__': - unittest.main() + unittest.main() \ No newline at end of file diff --git a/pkg/cluster/database.go b/pkg/cluster/database.go index 652f0d0ae..978b570b0 100644 --- a/pkg/cluster/database.go +++ b/pkg/cluster/database.go @@ -231,7 +231,8 @@ func (c *Cluster) readPgUsersFromDatabase(userNames []string) (users spec.PgUser parameters[fields[0]] = fields[1] } - if strings.HasSuffix(rolname, c.OpConfig.RoleDeletionSuffix) { + // consider NOLOGIN roles with deleted suffix as deprecated users + if strings.HasSuffix(rolname, c.OpConfig.RoleDeletionSuffix) && !rolcanlogin { roldeleted = true } diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index 3300f8724..68f00533a 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -104,18 +104,19 @@ func (c *Cluster) Sync(newSpec *acidv1.Postgresql) error { if !(c.databaseAccessDisabled() || c.getNumberOfInstances(&newSpec.Spec) <= 0 || c.Spec.StandbyCluster != nil) { c.logger.Debug("syncing roles") if err = c.syncRoles(); err != nil { - err = fmt.Errorf("could not sync roles: %v", err) - return err + // remember all cached users in c.pgUsers + for cachedUserName, cachedUser := range c.pgUsersCache { + c.pgUsers[cachedUserName] = cachedUser + } + c.logger.Errorf("could not sync roles: %v", err) } c.logger.Debug("syncing databases") if err = c.syncDatabases(); err != nil { - err = fmt.Errorf("could not sync databases: %v", err) - return err + c.logger.Errorf("could not sync databases: %v", err) } c.logger.Debug("syncing prepared databases with schemas") if err = c.syncPreparedDatabases(); err != nil { - err = fmt.Errorf("could not sync prepared database: %v", err) - return err + c.logger.Errorf("could not sync prepared database: %v", err) } } @@ -933,10 +934,7 @@ func (c *Cluster) syncRoles() (err error) { } } - // copy map for ProduceSyncRequests to include also system users - for userName, pgUser := range c.pgUsers { - newUsers[userName] = pgUser - } + // search also for system users for _, systemUser := range c.systemUsers { userNames = append(userNames, systemUser.Name) newUsers[systemUser.Name] = systemUser @@ -950,13 +948,21 @@ func (c *Cluster) syncRoles() (err error) { // update pgUsers where a deleted role was found // so that they are skipped in ProduceSyncRequests for _, dbUser := range dbUsers { - if originalUser, exists := deletedUsers[dbUser.Name]; exists { - recreatedUser := c.pgUsers[originalUser] + originalUsername, foundDeletedUser := deletedUsers[dbUser.Name] + // check if original user does not exist in dbUsers + _, originalUserAlreadyExists := dbUsers[originalUsername] + if foundDeletedUser && !originalUserAlreadyExists { + recreatedUser := c.pgUsers[originalUsername] recreatedUser.Deleted = true - c.pgUsers[originalUser] = recreatedUser + c.pgUsers[originalUsername] = recreatedUser } } + // last but not least copy pgUsers to newUsers to send to ProduceSyncRequests + for _, pgUser := range c.pgUsers { + newUsers[pgUser.Name] = pgUser + } + pgSyncRequests := c.userSyncStrategy.ProduceSyncRequests(dbUsers, newUsers) if err = c.userSyncStrategy.ExecuteSyncRequests(pgSyncRequests, c.pgDb); err != nil { return fmt.Errorf("error executing sync statements: %v", err) diff --git a/pkg/util/users/users.go b/pkg/util/users/users.go index b7f5c45c1..4d9a21f73 100644 --- a/pkg/util/users/users.go +++ b/pkg/util/users/users.go @@ -43,7 +43,8 @@ func (strategy DefaultUserSyncStrategy) ProduceSyncRequests(dbUsers spec.PgUserM var reqs []spec.PgSyncUserRequest for name, newUser := range newUsers { - // do not create user that exists in DB with deletion suffix + // do not create user when there exists a user with the same name plus deletion suffix + // instead request a renaming of the deleted user back to the original name (see * below) if newUser.Deleted { continue } @@ -82,22 +83,28 @@ func (strategy DefaultUserSyncStrategy) ProduceSyncRequests(dbUsers spec.PgUserM } } - // No existing roles are deleted or stripped of role membership/flags + // no existing roles are deleted or stripped of role membership/flags // but team roles will be renamed and denied from LOGIN for name, dbUser := range dbUsers { if _, exists := newUsers[name]; !exists { - // toggle LOGIN flag based on role deletion - userFlags := make([]string, len(dbUser.Flags)) - userFlags = append(userFlags, dbUser.Flags...) if dbUser.Deleted { - dbUser.Flags = util.StringSliceReplaceElement(dbUser.Flags, constants.RoleFlagNoLogin, constants.RoleFlagLogin) + // * user with deletion suffix and NOLOGIN found in database + // grant back LOGIN and rename only if original user is wanted and does not exist in database + originalName := strings.TrimSuffix(name, strategy.RoleDeletionSuffix) + _, originalUserWanted := newUsers[originalName] + _, originalUserAlreadyExists := dbUsers[originalName] + if !originalUserWanted || originalUserAlreadyExists { + continue + } + // a deleted dbUser has no NOLOGIN flag, so we can add the LOGIN flag + dbUser.Flags = append(dbUser.Flags, constants.RoleFlagLogin) } else { + // user found in database and not wanted in newUsers - replace LOGIN flag with NOLOGIN dbUser.Flags = util.StringSliceReplaceElement(dbUser.Flags, constants.RoleFlagLogin, constants.RoleFlagNoLogin) } - if !util.IsEqualIgnoreOrder(userFlags, dbUser.Flags) { - reqs = append(reqs, spec.PgSyncUserRequest{Kind: spec.PGsyncUserAlter, User: dbUser}) - } - + // request ALTER ROLE to grant or revoke LOGIN + reqs = append(reqs, spec.PgSyncUserRequest{Kind: spec.PGsyncUserAlter, User: dbUser}) + // request RENAME which will happen on behalf of the pgUser.Deleted field reqs = append(reqs, spec.PgSyncUserRequest{Kind: spec.PGSyncUserRename, User: dbUser}) } } diff --git a/pkg/util/util_test.go b/pkg/util/util_test.go index 75853c3d6..6444bb48f 100644 --- a/pkg/util/util_test.go +++ b/pkg/util/util_test.go @@ -62,6 +62,8 @@ var substractTest = []struct { }{ {[]string{"a", "b", "c", "d"}, []string{"a", "b", "c", "d"}, []string{}, true}, {[]string{"a", "b", "c", "d"}, []string{"a", "bb", "c", "d"}, []string{"b"}, false}, + {[]string{""}, []string{"b"}, []string{""}, false}, + {[]string{"a"}, []string{""}, []string{"a"}, false}, } var sliceContaintsTest = []struct { From e1de44561f8d72a8b795db2fbe043eaf8045ed09 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Wed, 12 Oct 2022 16:21:19 +0200 Subject: [PATCH 22/77] hello hughcapet (#2074) --- CODEOWNERS | 2 +- MAINTAINERS | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 512c4b253..daca96b42 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1,2 +1,2 @@ # global owners -* @sdudoladov @Jan-M @CyberDem0n @FxKu @jopadi @idanovinda +* @sdudoladov @Jan-M @FxKu @jopadi @idanovinda @hughcapet diff --git a/MAINTAINERS b/MAINTAINERS index 2af9b9e17..ea2a29ca0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2,4 +2,5 @@ Sergey Dudoladov Felix Kunde Jan Mussler Jociele Padilha -Ida Novindasari \ No newline at end of file +Ida Novindasari +Polina Bungina \ No newline at end of file From 4786f53f033154009051c2b1dc300be48c55790a Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Thu, 13 Oct 2022 11:33:26 +0200 Subject: [PATCH 23/77] Fix password rotation (#2043) * fix password rotation * test connection with rotation user in e2e test + minor changes --- e2e/tests/test_e2e.py | 29 +++++++++++++++++++++++- pkg/cluster/sync.go | 49 +++++++++++++--------------------------- pkg/cluster/sync_test.go | 13 +++++------ 3 files changed, 50 insertions(+), 41 deletions(-) diff --git a/e2e/tests/test_e2e.py b/e2e/tests/test_e2e.py index 82e9cb39c..d13a51c19 100644 --- a/e2e/tests/test_e2e.py +++ b/e2e/tests/test_e2e.py @@ -1442,6 +1442,10 @@ class EndToEndTestCase(unittest.TestCase): self.eventuallyEqual(lambda: len(self.query_database(leader.metadata.name, "postgres", user_query)), 3, "Found incorrect number of rotation users", 10, 5) + # test that rotation_user can connect to the database + self.eventuallyEqual(lambda: len(self.query_database_with_user(leader.metadata.name, "postgres", "SELECT 1", "foo_user")), 1, + "Could not connect to the database with rotation user {}".format(rotation_user), 10, 5) + # disable password rotation for all other users (foo_user) # and pick smaller intervals to see if the third fake rotation user is dropped enable_password_rotation = { @@ -1998,5 +2002,28 @@ class EndToEndTestCase(unittest.TestCase): return result_set + def query_database_with_user(self, pod_name, db_name, query, user_name): + ''' + Query database and return result as a list + ''' + k8s = self.k8s + result_set = [] + exec_query = r"PGPASSWORD={} psql -h localhost -U {} -tAq -c \"{}\" -d {}" + + try: + user_secret = k8s.get_secret(user_name) + secret_user = str(base64.b64decode(user_secret.data["username"]), 'utf-8') + secret_pw = str(base64.b64decode(user_secret.data["password"]), 'utf-8') + q = exec_query.format(secret_pw, secret_user, query, db_name) + q = "su postgres -c \"{}\"".format(q) + result = k8s.exec_with_kubectl(pod_name, q) + result_set = clean_list(result.stdout.split(b'\n')) + except Exception as ex: + print('Error on query execution: {}'.format(ex)) + print('Stdout: {}'.format(result.stdout)) + print('Stderr: {}'.format(result.stderr)) + + return result_set + if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index 68f00533a..cc795634f 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -638,7 +638,6 @@ func (c *Cluster) syncSecrets() error { c.logger.Info("syncing secrets") c.setProcessName("syncing secrets") generatedSecrets := c.generateUserSecrets() - rotationUsers := make(spec.PgUserMap) retentionUsers := make([]string, 0) currentTime := time.Now() @@ -650,7 +649,7 @@ func (c *Cluster) syncSecrets() error { continue } if k8sutil.ResourceAlreadyExists(err) { - if err = c.updateSecret(secretUsername, generatedSecret, &rotationUsers, &retentionUsers, currentTime); err != nil { + if err = c.updateSecret(secretUsername, generatedSecret, &retentionUsers, currentTime); err != nil { c.logger.Warningf("syncing secret %s failed: %v", util.NameFromMeta(secret.ObjectMeta), err) } } else { @@ -658,21 +657,6 @@ func (c *Cluster) syncSecrets() error { } } - // add new user with date suffix and use it in the secret of the original user - if len(rotationUsers) > 0 { - err := c.initDbConn() - if err != nil { - return fmt.Errorf("could not init db connection: %v", err) - } - pgSyncRequests := c.userSyncStrategy.ProduceSyncRequests(spec.PgUserMap{}, rotationUsers) - if err = c.userSyncStrategy.ExecuteSyncRequests(pgSyncRequests, c.pgDb); err != nil { - return fmt.Errorf("error creating database roles for password rotation: %v", err) - } - if err := c.closeDbConn(); err != nil { - c.logger.Errorf("could not close database connection after creating users for password rotation: %v", err) - } - } - // remove rotation users that exceed the retention interval if len(retentionUsers) > 0 { err := c.initDbConn() @@ -698,7 +682,6 @@ func (c *Cluster) getNextRotationDate(currentDate time.Time) (time.Time, string) func (c *Cluster) updateSecret( secretUsername string, generatedSecret *v1.Secret, - rotationUsers *spec.PgUserMap, retentionUsers *[]string, currentTime time.Time) error { var ( @@ -757,7 +740,7 @@ func (c *Cluster) updateSecret( rotationAllowed := !pwdUser.IsDbOwner && util.SliceContains(allowedRoleTypes, pwdUser.Origin) if (c.OpConfig.EnablePasswordRotation && rotationAllowed) || rotationEnabledInManifest { - updateSecretMsg, err = c.rotatePasswordInSecret(secret, pwdUser, secretUsername, currentTime, rotationUsers, retentionUsers) + updateSecretMsg, err = c.rotatePasswordInSecret(secret, secretUsername, pwdUser.Origin, currentTime, retentionUsers) if err != nil { c.logger.Warnf("password rotation failed for user %s: %v", secretUsername, err) } @@ -782,8 +765,13 @@ func (c *Cluster) updateSecret( updateSecret = true updateSecretMsg = fmt.Sprintf("updating the secret %s from the infrastructure roles", secretName) } else { - // for non-infrastructure role - update the role with the password from the secret + // for non-infrastructure role - update the role with username and password from secret + pwdUser.Name = string(secret.Data["username"]) pwdUser.Password = string(secret.Data["password"]) + // update membership if we deal with a rotation user + if secretUsername != pwdUser.Name { + pwdUser.MemberOf = []string{secretUsername} + } userMap[userKey] = pwdUser } @@ -800,10 +788,9 @@ func (c *Cluster) updateSecret( func (c *Cluster) rotatePasswordInSecret( secret *v1.Secret, - secretPgUser spec.PgUser, secretUsername string, + roleOrigin spec.RoleOrigin, currentTime time.Time, - rotationUsers *spec.PgUserMap, retentionUsers *[]string) (string, error) { var ( err error @@ -833,18 +820,14 @@ func (c *Cluster) rotatePasswordInSecret( if currentTime.After(nextRotationDate) { // create rotation user if role is not listed for in-place password update if !util.SliceContains(c.Spec.UsersWithInPlaceSecretRotation, secretUsername) { - rotationUser := secretPgUser - newRotationUsername := fmt.Sprintf("%s%s", secretUsername, currentTime.Format("060102")) - rotationUser.Name = newRotationUsername - rotationUser.MemberOf = []string{secretUsername} - (*rotationUsers)[newRotationUsername] = rotationUser - secret.Data["username"] = []byte(newRotationUsername) - + rotationUsername := fmt.Sprintf("%s%s", secretUsername, currentTime.Format("060102")) + secret.Data["username"] = []byte(rotationUsername) + c.logger.Infof("updating username in secret %s and creating rotation user %s in the database", secretName, rotationUsername) // whenever there is a rotation, check if old rotation users can be deleted *retentionUsers = append(*retentionUsers, secretUsername) } else { // when passwords of system users are rotated in place, pods have to be replaced - if secretPgUser.Origin == spec.RoleOriginSystem { + if roleOrigin == spec.RoleOriginSystem { pods, err := c.listPods() if err != nil { return "", fmt.Errorf("could not list pods of the statefulset: %v", err) @@ -858,7 +841,7 @@ func (c *Cluster) rotatePasswordInSecret( } // when password of connection pooler is rotated in place, pooler pods have to be replaced - if secretPgUser.Origin == spec.RoleOriginConnectionPooler { + if roleOrigin == spec.RoleOriginConnectionPooler { listOptions := metav1.ListOptions{ LabelSelector: c.poolerLabelsSet(true).String(), } @@ -875,8 +858,8 @@ func (c *Cluster) rotatePasswordInSecret( } // when password of stream user is rotated in place, it should trigger rolling update in FES deployment - if secretPgUser.Origin == spec.RoleOriginStream { - c.logger.Warnf("secret of stream user %s changed", constants.EventStreamSourceSlotPrefix+constants.UserRoleNameSuffix) + if roleOrigin == spec.RoleOriginStream { + c.logger.Warnf("password in secret of stream user %s changed", constants.EventStreamSourceSlotPrefix+constants.UserRoleNameSuffix) } } secret.Data["password"] = []byte(util.RandomPassword(constants.PasswordLength)) diff --git a/pkg/cluster/sync_test.go b/pkg/cluster/sync_test.go index cc7554b0e..785a7cca2 100644 --- a/pkg/cluster/sync_test.go +++ b/pkg/cluster/sync_test.go @@ -276,7 +276,6 @@ func TestUpdateSecret(t *testing.T) { dbname := "app" dbowner := "appowner" secretTemplate := config.StringTemplate("{username}.{cluster}.credentials") - rotationUsers := make(spec.PgUserMap) retentionUsers := make([]string, 0) // define manifest users and enable rotation for dbowner @@ -339,8 +338,8 @@ func TestUpdateSecret(t *testing.T) { dayAfterTomorrow := time.Now().AddDate(0, 0, 2) allUsers := make(map[string]spec.PgUser) - for userName, pgUser := range cluster.pgUsers { - allUsers[userName] = pgUser + for _, pgUser := range cluster.pgUsers { + allUsers[pgUser.Name] = pgUser } for _, systemUser := range cluster.systemUsers { allUsers[systemUser.Name] = systemUser @@ -354,7 +353,7 @@ func TestUpdateSecret(t *testing.T) { secretPassword := string(secret.Data["password"]) // now update the secret setting a next rotation date (tomorrow + interval) - cluster.updateSecret(username, secret, &rotationUsers, &retentionUsers, dayAfterTomorrow) + cluster.updateSecret(username, secret, &retentionUsers, dayAfterTomorrow) updatedSecret, err := cluster.KubeClient.Secrets(namespace).Get(context.TODO(), secretName, metav1.GetOptions{}) assert.NoError(t, err) @@ -386,9 +385,9 @@ func TestUpdateSecret(t *testing.T) { if secretUsername != rotatedUsername { t.Errorf("%s: updated secret does not contain correct username: expected %s, got %s", testName, rotatedUsername, secretUsername) } - - if len(rotationUsers) != 1 && len(retentionUsers) != 1 { - t.Errorf("%s: unexpected number of users to rotate - expected only %s, found %d", testName, username, len(rotationUsers)) + // whenever there's a rotation the retentionUsers list is extended or updated + if len(retentionUsers) != 1 { + t.Errorf("%s: unexpected number of users to drop - expected only %s, found %d", testName, username, len(retentionUsers)) } } } From 78bd4fac6109e95c59661bee073d37a414a1dfcd Mon Sep 17 00:00:00 2001 From: Jairo Llopis <973709+yajo@users.noreply.github.com> Date: Thu, 13 Oct 2022 13:54:30 +0200 Subject: [PATCH 24/77] docs: fix code block rendering (#2041) This code block was wrongly rendered in docs. --- docs/reference/operator_parameters.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/reference/operator_parameters.md b/docs/reference/operator_parameters.md index af09a5da4..625d5699c 100644 --- a/docs/reference/operator_parameters.md +++ b/docs/reference/operator_parameters.md @@ -28,6 +28,7 @@ configuration. and change it. To test the CRD-based configuration locally, use the following + ```bash kubectl create -f manifests/operatorconfiguration.crd.yaml # registers the CRD kubectl create -f manifests/postgresql-operator-default-configuration.yaml From a85023ff1067bc032e2def7a5fe07820e6033588 Mon Sep 17 00:00:00 2001 From: Dmitry Volodin Date: Thu, 13 Oct 2022 14:54:58 +0300 Subject: [PATCH 25/77] Cluster env variables should be reflected for StatefulSet update (#2045) * Cluster env variables should be reflected for StatefulSet update * Add unit test for comparing StatefulSet's --- pkg/cluster/cluster_test.go | 112 ++++++++++++++++++++++-------------- pkg/cluster/k8sres.go | 23 +++----- pkg/cluster/k8sres_test.go | 9 ++- 3 files changed, 84 insertions(+), 60 deletions(-) diff --git a/pkg/cluster/cluster_test.go b/pkg/cluster/cluster_test.go index dc8e6a87c..b53725923 100644 --- a/pkg/cluster/cluster_test.go +++ b/pkg/cluster/cluster_test.go @@ -6,9 +6,8 @@ import ( "strings" "testing" - "github.com/stretchr/testify/assert" - "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" acidv1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" fakeacidv1 "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/fake" "github.com/zalando/postgres-operator/pkg/spec" @@ -61,7 +60,6 @@ var cl = New( ) func TestStatefulSetAnnotations(t *testing.T) { - testName := "CheckStatefulsetAnnotations" spec := acidv1.PostgresSpec{ TeamID: "myapp", NumberOfInstances: 1, Resources: &acidv1.Resources{ @@ -74,19 +72,59 @@ func TestStatefulSetAnnotations(t *testing.T) { } ss, err := cl.generateStatefulSet(&spec) if err != nil { - t.Errorf("in %s no statefulset created %v", testName, err) + t.Errorf("in %s no statefulset created %v", t.Name(), err) } if ss != nil { annotation := ss.ObjectMeta.GetAnnotations() if _, ok := annotation["downscaler/downtime_replicas"]; !ok { - t.Errorf("in %s respective annotation not found on sts", testName) + t.Errorf("in %s respective annotation not found on sts", t.Name()) } } +} +func TestStatefulSetUpdateWithEnv(t *testing.T) { + oldSpec := &acidv1.PostgresSpec{ + TeamID: "myapp", NumberOfInstances: 1, + Resources: &acidv1.Resources{ + ResourceRequests: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, + ResourceLimits: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, + }, + Volume: acidv1.Volume{ + Size: "1G", + }, + } + oldSS, err := cl.generateStatefulSet(oldSpec) + if err != nil { + t.Errorf("in %s no StatefulSet created %v", t.Name(), err) + } + + newSpec := oldSpec.DeepCopy() + newSS, err := cl.generateStatefulSet(newSpec) + if err != nil { + t.Errorf("in %s no StatefulSet created %v", t.Name(), err) + } + + if !reflect.DeepEqual(oldSS, newSS) { + t.Errorf("in %s StatefulSet's must be equal", t.Name()) + } + + newSpec.Env = []v1.EnvVar{ + { + Name: "CUSTOM_ENV_VARIABLE", + Value: "data", + }, + } + newSS, err = cl.generateStatefulSet(newSpec) + if err != nil { + t.Errorf("in %s no StatefulSet created %v", t.Name(), err) + } + + if reflect.DeepEqual(oldSS, newSS) { + t.Errorf("in %s StatefulSet's must be not equal", t.Name()) + } } func TestInitRobotUsers(t *testing.T) { - testName := "TestInitRobotUsers" tests := []struct { manifestUsers map[string]acidv1.UserFlags infraRoles map[string]spec.PgUser @@ -130,22 +168,20 @@ func TestInitRobotUsers(t *testing.T) { cl.pgUsers = tt.infraRoles if err := cl.initRobotUsers(); err != nil { if tt.err == nil { - t.Errorf("%s got an unexpected error: %v", testName, err) + t.Errorf("%s got an unexpected error: %v", t.Name(), err) } if err.Error() != tt.err.Error() { - t.Errorf("%s expected error %v, got %v", testName, tt.err, err) + t.Errorf("%s expected error %v, got %v", t.Name(), tt.err, err) } } else { if !reflect.DeepEqual(cl.pgUsers, tt.result) { - t.Errorf("%s expected: %#v, got %#v", testName, tt.result, cl.pgUsers) + t.Errorf("%s expected: %#v, got %#v", t.Name(), tt.result, cl.pgUsers) } } } } func TestInitAdditionalOwnerRoles(t *testing.T) { - testName := "TestInitAdditionalOwnerRoles" - manifestUsers := map[string]acidv1.UserFlags{"foo_owner": {}, "bar_owner": {}, "app_user": {}} expectedUsers := map[string]spec.PgUser{ "foo_owner": {Origin: spec.RoleOriginManifest, Name: "foo_owner", Namespace: cl.Namespace, Password: "f123", Flags: []string{"LOGIN"}, IsDbOwner: true, MemberOf: []string{"cron_admin", "part_man"}}, @@ -158,7 +194,7 @@ func TestInitAdditionalOwnerRoles(t *testing.T) { // this should set IsDbOwner field for manifest users if err := cl.initRobotUsers(); err != nil { - t.Errorf("%s could not init manifest users", testName) + t.Errorf("%s could not init manifest users", t.Name()) } // now assign additional roles to owners @@ -169,7 +205,7 @@ func TestInitAdditionalOwnerRoles(t *testing.T) { expectedPgUser := expectedUsers[username] if !util.IsEqualIgnoreOrder(expectedPgUser.MemberOf, existingPgUser.MemberOf) { t.Errorf("%s unexpected membership of user %q: expected member of %#v, got member of %#v", - testName, username, expectedPgUser.MemberOf, existingPgUser.MemberOf) + t.Name(), username, expectedPgUser.MemberOf, existingPgUser.MemberOf) } } } @@ -195,11 +231,9 @@ func (m *mockTeamsAPIClient) setMembers(members []string) { // Test adding a member of a product team owning a particular DB cluster func TestInitHumanUsers(t *testing.T) { - var mockTeamsAPI mockTeamsAPIClient cl.oauthTokenGetter = &mockOAuthTokenGetter{} cl.teamsAPIClient = &mockTeamsAPI - testName := "TestInitHumanUsers" // members of a product team are granted superuser rights for DBs of their team cl.OpConfig.EnableTeamSuperuser = true @@ -232,11 +266,11 @@ func TestInitHumanUsers(t *testing.T) { cl.pgUsers = tt.existingRoles mockTeamsAPI.setMembers(tt.teamRoles) if err := cl.initHumanUsers(); err != nil { - t.Errorf("%s got an unexpected error %v", testName, err) + t.Errorf("%s got an unexpected error %v", t.Name(), err) } if !reflect.DeepEqual(cl.pgUsers, tt.result) { - t.Errorf("%s expects %#v, got %#v", testName, tt.result, cl.pgUsers) + t.Errorf("%s expects %#v, got %#v", t.Name(), tt.result, cl.pgUsers) } } } @@ -264,12 +298,10 @@ func (m *mockTeamsAPIClientMultipleTeams) TeamInfo(teamID, token string) (tm *te // Test adding members of maintenance teams that get superuser rights for all PG databases func TestInitHumanUsersWithSuperuserTeams(t *testing.T) { - var mockTeamsAPI mockTeamsAPIClientMultipleTeams cl.oauthTokenGetter = &mockOAuthTokenGetter{} cl.teamsAPIClient = &mockTeamsAPI cl.OpConfig.EnableTeamSuperuser = false - testName := "TestInitHumanUsersWithSuperuserTeams" cl.OpConfig.EnableTeamsAPI = true cl.OpConfig.PamRoleName = "zalandos" @@ -371,17 +403,16 @@ func TestInitHumanUsersWithSuperuserTeams(t *testing.T) { cl.OpConfig.PostgresSuperuserTeams = tt.superuserTeams if err := cl.initHumanUsers(); err != nil { - t.Errorf("%s got an unexpected error %v", testName, err) + t.Errorf("%s got an unexpected error %v", t.Name(), err) } if !reflect.DeepEqual(cl.pgUsers, tt.result) { - t.Errorf("%s expects %#v, got %#v", testName, tt.result, cl.pgUsers) + t.Errorf("%s expects %#v, got %#v", t.Name(), tt.result, cl.pgUsers) } } } func TestPodAnnotations(t *testing.T) { - testName := "TestPodAnnotations" tests := []struct { subTest string operator map[string]string @@ -428,13 +459,13 @@ func TestPodAnnotations(t *testing.T) { for k, v := range annotations { if observed, expected := v, tt.merged[k]; observed != expected { t.Errorf("%v expects annotation value %v for key %v, but found %v", - testName+"/"+tt.subTest, expected, observed, k) + t.Name()+"/"+tt.subTest, expected, observed, k) } } for k, v := range tt.merged { if observed, expected := annotations[k], v; observed != expected { t.Errorf("%v expects annotation value %v for key %v, but found %v", - testName+"/"+tt.subTest, expected, observed, k) + t.Name()+"/"+tt.subTest, expected, observed, k) } } } @@ -780,22 +811,20 @@ func TestServiceAnnotations(t *testing.T) { } func TestInitSystemUsers(t *testing.T) { - testName := "Test system users initialization" - // default cluster without connection pooler and event streams cl.initSystemUsers() if _, exist := cl.systemUsers[constants.ConnectionPoolerUserKeyName]; exist { - t.Errorf("%s, connection pooler user is present", testName) + t.Errorf("%s, connection pooler user is present", t.Name()) } if _, exist := cl.systemUsers[constants.EventStreamUserKeyName]; exist { - t.Errorf("%s, stream user is present", testName) + t.Errorf("%s, stream user is present", t.Name()) } // cluster with connection pooler cl.Spec.EnableConnectionPooler = boolToPointer(true) cl.initSystemUsers() if _, exist := cl.systemUsers[constants.ConnectionPoolerUserKeyName]; !exist { - t.Errorf("%s, connection pooler user is not present", testName) + t.Errorf("%s, connection pooler user is not present", t.Name()) } // superuser is not allowed as connection pool user @@ -807,7 +836,7 @@ func TestInitSystemUsers(t *testing.T) { cl.initSystemUsers() if _, exist := cl.systemUsers["pooler"]; !exist { - t.Errorf("%s, Superuser is not allowed to be a connection pool user", testName) + t.Errorf("%s, Superuser is not allowed to be a connection pool user", t.Name()) } // neither protected users are @@ -819,7 +848,7 @@ func TestInitSystemUsers(t *testing.T) { cl.initSystemUsers() if _, exist := cl.systemUsers["pooler"]; !exist { - t.Errorf("%s, Protected user are not allowed to be a connection pool user", testName) + t.Errorf("%s, Protected user are not allowed to be a connection pool user", t.Name()) } delete(cl.systemUsers, "pooler") @@ -829,7 +858,7 @@ func TestInitSystemUsers(t *testing.T) { cl.initSystemUsers() if _, exist := cl.systemUsers["pooler"]; !exist { - t.Errorf("%s, System users are not allowed to be a connection pool user", testName) + t.Errorf("%s, System users are not allowed to be a connection pool user", t.Name()) } // using stream user in manifest but no streams defined should be treated like normal robot user @@ -837,7 +866,7 @@ func TestInitSystemUsers(t *testing.T) { cl.Spec.Users = map[string]acidv1.UserFlags{streamUser: []string{}} cl.initSystemUsers() if _, exist := cl.systemUsers[constants.EventStreamUserKeyName]; exist { - t.Errorf("%s, stream user is present", testName) + t.Errorf("%s, stream user is present", t.Name()) } // cluster with streams @@ -846,7 +875,7 @@ func TestInitSystemUsers(t *testing.T) { ApplicationId: "test-app", Database: "test_db", Tables: map[string]acidv1.StreamTable{ - "data.test_table": acidv1.StreamTable{ + "data.test_table": { EventType: "test_event", }, }, @@ -854,24 +883,22 @@ func TestInitSystemUsers(t *testing.T) { } cl.initSystemUsers() if _, exist := cl.systemUsers[constants.EventStreamUserKeyName]; !exist { - t.Errorf("%s, stream user is not present", testName) + t.Errorf("%s, stream user is not present", t.Name()) } } func TestPreparedDatabases(t *testing.T) { - testName := "TestDefaultPreparedDatabase" - cl.Spec.PreparedDatabases = map[string]acidv1.PreparedDatabase{} cl.initPreparedDatabaseRoles() for _, role := range []string{"acid_test_owner", "acid_test_reader", "acid_test_writer", "acid_test_data_owner", "acid_test_data_reader", "acid_test_data_writer"} { if _, exist := cl.pgUsers[role]; !exist { - t.Errorf("%s, default role %q for prepared database not present", testName, role) + t.Errorf("%s, default role %q for prepared database not present", t.Name(), role) } } - testName = "TestPreparedDatabaseWithSchema" + testName := "TestPreparedDatabaseWithSchema" cl.Spec.PreparedDatabases = map[string]acidv1.PreparedDatabase{ "foo": { @@ -1109,7 +1136,6 @@ func newService(ann map[string]string, svcT v1.ServiceType, lbSr []string) *v1.S } func TestCompareServices(t *testing.T) { - testName := "TestCompareServices" cluster := Cluster{ Config: Config{ OpConfig: config.Config{ @@ -1410,16 +1436,16 @@ func TestCompareServices(t *testing.T) { match, reason := cluster.compareServices(tt.current, tt.new) if match && !tt.match { t.Logf("match=%v current=%v, old=%v reason=%s", match, tt.current.Annotations, tt.new.Annotations, reason) - t.Errorf("%s - expected services to do not match: %q and %q", testName, tt.current, tt.new) + t.Errorf("%s - expected services to do not match: %q and %q", t.Name(), tt.current, tt.new) return } if !match && tt.match { - t.Errorf("%s - expected services to be the same: %q and %q", testName, tt.current, tt.new) + t.Errorf("%s - expected services to be the same: %q and %q", t.Name(), tt.current, tt.new) return } if !match && !tt.match { if !strings.HasPrefix(reason, tt.reason) { - t.Errorf("%s - expected reason prefix %s, found %s", testName, tt.reason, reason) + t.Errorf("%s - expected reason prefix %s, found %s", t.Name(), tt.reason, reason) return } } diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index 437598cf0..76e00c7d3 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -797,10 +797,9 @@ func (c *Cluster) generatePodTemplate( // generatePodEnvVars generates environment variables for the Spilo Pod func (c *Cluster) generateSpiloPodEnvVars( + spec *acidv1.PostgresSpec, uid types.UID, - spiloConfiguration string, - cloneDescription *acidv1.CloneDescription, - standbyDescription *acidv1.StandbyDescription) []v1.EnvVar { + spiloConfiguration string) []v1.EnvVar { // hard-coded set of environment variables we need // to guarantee core functionality of the operator @@ -906,17 +905,17 @@ func (c *Cluster) generateSpiloPodEnvVars( envVars = append(envVars, v1.EnvVar{Name: "KUBERNETES_USE_CONFIGMAPS", Value: "true"}) } - if cloneDescription != nil && cloneDescription.ClusterName != "" { - envVars = append(envVars, c.generateCloneEnvironment(cloneDescription)...) + if spec.Clone != nil && spec.Clone.ClusterName != "" { + envVars = append(envVars, c.generateCloneEnvironment(spec.Clone)...) } - if standbyDescription != nil { - envVars = append(envVars, c.generateStandbyEnvironment(standbyDescription)...) + if spec.StandbyCluster != nil { + envVars = append(envVars, c.generateStandbyEnvironment(spec.StandbyCluster)...) } // fetch cluster-specific variables that will override all subsequent global variables - if len(c.Spec.Env) > 0 { - envVars = appendEnvVars(envVars, c.Spec.Env...) + if len(spec.Env) > 0 { + envVars = appendEnvVars(envVars, spec.Env...) } // fetch variables from custom environment Secret @@ -1186,11 +1185,7 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef } // generate environment variables for the spilo container - spiloEnvVars := c.generateSpiloPodEnvVars( - c.Postgresql.GetUID(), - spiloConfiguration, - spec.Clone, - spec.StandbyCluster) + spiloEnvVars := c.generateSpiloPodEnvVars(spec, c.Postgresql.GetUID(), spiloConfiguration) // pickup the docker image for the spilo container effectiveDockerImage := util.Coalesce(spec.DockerImage, c.OpConfig.DockerImage) diff --git a/pkg/cluster/k8sres_test.go b/pkg/cluster/k8sres_test.go index 60be7dfaa..4ae952fe2 100644 --- a/pkg/cluster/k8sres_test.go +++ b/pkg/cluster/k8sres_test.go @@ -836,9 +836,12 @@ func TestGenerateSpiloPodEnvVars(t *testing.T) { for _, tt := range tests { c := newMockCluster(tt.opConfig) - c.Postgresql = tt.pgsql - actualEnvs := c.generateSpiloPodEnvVars( - types.UID(dummyUUID), exampleSpiloConfig, tt.cloneDescription, tt.standbyDescription) + pgsql := tt.pgsql + pgsql.Spec.Clone = tt.cloneDescription + pgsql.Spec.StandbyCluster = tt.standbyDescription + c.Postgresql = pgsql + + actualEnvs := c.generateSpiloPodEnvVars(&pgsql.Spec, types.UID(dummyUUID), exampleSpiloConfig) for _, ev := range tt.expectedValues { env := actualEnvs[ev.envIndex] From acb3ffd70282e06fd7c9b229fe2a622d58d577eb Mon Sep 17 00:00:00 2001 From: Polina Bungina Date: Mon, 17 Oct 2022 17:36:02 +0200 Subject: [PATCH 26/77] Fix upgrade command (#2075) Upgrade run using postgres user used to be broken by an invalid option. Move -o pipefail directly to upgradeCommand. + minor formatting corrections --- pkg/cluster/majorversionupgrade.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pkg/cluster/majorversionupgrade.go b/pkg/cluster/majorversionupgrade.go index 986e95502..abe015959 100644 --- a/pkg/cluster/majorversionupgrade.go +++ b/pkg/cluster/majorversionupgrade.go @@ -57,10 +57,10 @@ func (c *Cluster) isUpgradeAllowedForTeam(owningTeam string) bool { } /* - Execute upgrade when mode is set to manual or full or when the owning team is allowed for upgrade (and mode is "off"). +Execute upgrade when mode is set to manual or full or when the owning team is allowed for upgrade (and mode is "off"). - Manual upgrade means, it is triggered by the user via manifest version change - Full upgrade means, operator also determines the minimal version used accross all clusters and upgrades violators. +Manual upgrade means, it is triggered by the user via manifest version change +Full upgrade means, operator also determines the minimal version used accross all clusters and upgrades violators. */ func (c *Cluster) majorVersionUpgrade() error { @@ -105,8 +105,8 @@ func (c *Cluster) majorVersionUpgrade() error { podName := &spec.NamespacedName{Namespace: masterPod.Namespace, Name: masterPod.Name} c.logger.Infof("triggering major version upgrade on pod %s of %d pods", masterPod.Name, numberOfPods) c.eventRecorder.Eventf(c.GetReference(), v1.EventTypeNormal, "Major Version Upgrade", "Starting major version upgrade on pod %s of %d pods", masterPod.Name, numberOfPods) - upgradeCommand := fmt.Sprintf("/usr/bin/python3 /scripts/inplace_upgrade.py %d 2>&1 | tee last_upgrade.log", numberOfPods) - + upgradeCommand := fmt.Sprintf("set -o pipefail && /usr/bin/python3 /scripts/inplace_upgrade.py %d 2>&1 | tee last_upgrade.log", numberOfPods) + c.logger.Debugf("checking if the spilo image runs with root or non-root (check for user id=0)") resultIdCheck, errIdCheck := c.ExecCommand(podName, "/bin/bash", "-c", "/usr/bin/id -u") if errIdCheck != nil { @@ -117,10 +117,10 @@ func (c *Cluster) majorVersionUpgrade() error { var result string if resultIdCheck != "0" { c.logger.Infof("User id was identified as: %s, hence default user is non-root already", resultIdCheck) - result, err = c.ExecCommand(podName, "/bin/bash", "-o", "pipefail", "-c", upgradeCommand) + result, err = c.ExecCommand(podName, "/bin/bash", "-c", upgradeCommand) } else { c.logger.Infof("User id was identified as: %s, using su to reach the postgres user", resultIdCheck) - result, err = c.ExecCommand(podName, "/bin/su", "postgres", "-o", "pipefail", "-c", upgradeCommand) + result, err = c.ExecCommand(podName, "/bin/su", "postgres", "-c", upgradeCommand) } if err != nil { c.eventRecorder.Eventf(c.GetReference(), v1.EventTypeWarning, "Major Version Upgrade", "Upgrade from %d to %d FAILED: %v", c.currentMajorVersion, desiredVersion, err) From 03ccb429ff57742f943010798abd59b87825d784 Mon Sep 17 00:00:00 2001 From: dogaakcinar Date: Tue, 18 Oct 2022 11:54:47 +0300 Subject: [PATCH 27/77] fix azure setup in administrator docs (#2059) in the OperatorConfiguration CRD "pod_environment_secret" and "pod_environment_configmap" properties is located under "kubernetes" object. aws_or_gcp object does not have these properties. --- docs/administrator.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/administrator.md b/docs/administrator.md index 4d18f2f61..373e691a8 100644 --- a/docs/administrator.md +++ b/docs/administrator.md @@ -1099,9 +1099,10 @@ and `pod-env-overrides` resources applied to your cluster, ensure that the opera is set up like the following: ```yml ... -aws_or_gcp: +kubernetes: pod_environment_secret: "psql-backup-creds" pod_environment_configmap: "postgres-operator-system/pod-env-overrides" +aws_or_gcp: wal_az_storage_account: "postgresbackupsbucket28302F2" # name of storage account to save the WAL-G logs ... ``` From 640581fb4661dbab852dfc0995b5e9f6140fd8f8 Mon Sep 17 00:00:00 2001 From: machine424 Date: Tue, 18 Oct 2022 10:55:25 +0200 Subject: [PATCH 28/77] Fix the Operator rolling update statefulsets unnecessary whien Kube API down (#2031) (#2064) --- pkg/cluster/k8sres.go | 13 ++++++++----- pkg/cluster/k8sres_test.go | 3 ++- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index 76e00c7d3..aa3229848 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -799,7 +799,7 @@ func (c *Cluster) generatePodTemplate( func (c *Cluster) generateSpiloPodEnvVars( spec *acidv1.PostgresSpec, uid types.UID, - spiloConfiguration string) []v1.EnvVar { + spiloConfiguration string) ([]v1.EnvVar, error) { // hard-coded set of environment variables we need // to guarantee core functionality of the operator @@ -922,7 +922,7 @@ func (c *Cluster) generateSpiloPodEnvVars( // that will override all subsequent global variables secretEnvVarsList, err := c.getPodEnvironmentSecretVariables() if err != nil { - c.logger.Warningf("%v", err) + return nil, err } envVars = appendEnvVars(envVars, secretEnvVarsList...) @@ -930,7 +930,7 @@ func (c *Cluster) generateSpiloPodEnvVars( // that will override all subsequent global variables configMapEnvVarsList, err := c.getPodEnvironmentConfigMapVariables() if err != nil { - c.logger.Warningf("%v", err) + return nil, err } envVars = appendEnvVars(envVars, configMapEnvVarsList...) @@ -966,7 +966,7 @@ func (c *Cluster) generateSpiloPodEnvVars( envVars = appendEnvVars(envVars, opConfigEnvVars...) - return envVars + return envVars, nil } func appendEnvVars(envs []v1.EnvVar, appEnv ...v1.EnvVar) []v1.EnvVar { @@ -1185,7 +1185,10 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef } // generate environment variables for the spilo container - spiloEnvVars := c.generateSpiloPodEnvVars(spec, c.Postgresql.GetUID(), spiloConfiguration) + spiloEnvVars, err := c.generateSpiloPodEnvVars(spec, c.Postgresql.GetUID(), spiloConfiguration) + if err != nil { + return nil, fmt.Errorf("could not generate Spilo env vars: %v", err) + } // pickup the docker image for the spilo container effectiveDockerImage := util.Coalesce(spec.DockerImage, c.OpConfig.DockerImage) diff --git a/pkg/cluster/k8sres_test.go b/pkg/cluster/k8sres_test.go index 4ae952fe2..797c1426c 100644 --- a/pkg/cluster/k8sres_test.go +++ b/pkg/cluster/k8sres_test.go @@ -841,7 +841,8 @@ func TestGenerateSpiloPodEnvVars(t *testing.T) { pgsql.Spec.StandbyCluster = tt.standbyDescription c.Postgresql = pgsql - actualEnvs := c.generateSpiloPodEnvVars(&pgsql.Spec, types.UID(dummyUUID), exampleSpiloConfig) + actualEnvs, err := c.generateSpiloPodEnvVars(&pgsql.Spec, types.UID(dummyUUID), exampleSpiloConfig) + assert.NoError(t, err) for _, ev := range tt.expectedValues { env := actualEnvs[ev.envIndex] From 920f3dee3e4fa0fdf244a12312a219061ed38ba8 Mon Sep 17 00:00:00 2001 From: Philip Haberkern <59010269+thedatabaseme@users.noreply.github.com> Date: Tue, 18 Oct 2022 11:02:04 +0200 Subject: [PATCH 29/77] bumped to alpine base image 3.15 (#2027) * Bumped Alpine to 3.15 Co-authored-by: Philip Haberkern --- docker/DebugDockerfile | 2 +- docker/Dockerfile | 2 +- ui/Dockerfile | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docker/DebugDockerfile b/docker/DebugDockerfile index 1981dfc82..7c7ee8aee 100644 --- a/docker/DebugDockerfile +++ b/docker/DebugDockerfile @@ -1,4 +1,4 @@ -FROM registry.opensource.zalan.do/library/alpine-3.12:latest +FROM registry.opensource.zalan.do/library/alpine-3.15:latest LABEL maintainer="Team ACID @ Zalando " # We need root certificates to deal with teams api over https diff --git a/docker/Dockerfile b/docker/Dockerfile index c1b87caf7..becfcf308 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM registry.opensource.zalan.do/library/alpine-3.12:latest +FROM registry.opensource.zalan.do/library/alpine-3.15:latest LABEL maintainer="Team ACID @ Zalando " # We need root certificates to deal with teams api over https diff --git a/ui/Dockerfile b/ui/Dockerfile index ad775ece2..63e8817e6 100644 --- a/ui/Dockerfile +++ b/ui/Dockerfile @@ -1,4 +1,4 @@ -FROM registry.opensource.zalan.do/library/alpine-3.12:latest +FROM registry.opensource.zalan.do/library/alpine-3.15:latest LABEL maintainer="Team ACID @ Zalando " EXPOSE 8081 From d55e74e1e751c67e6cf9e9534a9f67731e105cdd Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Fri, 21 Oct 2022 14:31:13 +0200 Subject: [PATCH 30/77] create publication before creating logical replication slot (#2085) --- pkg/cluster/streams.go | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/pkg/cluster/streams.go b/pkg/cluster/streams.go index 917073364..4c75d07cf 100644 --- a/pkg/cluster/streams.go +++ b/pkg/cluster/streams.go @@ -315,6 +315,17 @@ func (c *Cluster) syncStreams() error { return nil } + // create publications to each created slot + c.logger.Debug("syncing database publications") + for publication, tables := range publications { + // but first check for existing publications + dbName := slots[publication]["database"] + err = c.syncPublication(publication, dbName, tables) + if err != nil { + c.logger.Warningf("could not sync publication %q in database %q: %v", publication, dbName, err) + } + } + // add extra logical slots to Patroni config c.logger.Debug("syncing Postgres config for logical decoding") requiresRestart, err := c.syncPostgresConfig(requiredPatroniConfig) @@ -326,17 +337,7 @@ func (c *Cluster) syncStreams() error { return nil } - // next, create publications to each created slot - c.logger.Debug("syncing database publications") - for publication, tables := range publications { - // but first check for existing publications - dbName := slots[publication]["database"] - err = c.syncPublication(publication, dbName, tables) - if err != nil { - c.logger.Warningf("could not sync publication %q in database %q: %v", publication, dbName, err) - } - } - + // after Postgres was restarted we can create stream CRDs err = c.createOrUpdateStreams() if err != nil { return err From 70f3ee8e36bb00e7a23fedd1f0354f7faa8b8b5a Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Fri, 21 Oct 2022 17:50:14 +0200 Subject: [PATCH 31/77] skip db sync on failed initUsers during UPDATE (#2083) * skip db sync on failed initUsers during UPDATE * provide unit test for teams API being unavailable * add test for 404 case --- pkg/cluster/cluster.go | 87 +++++++++++++++----------------- pkg/cluster/cluster_test.go | 55 +++++++++++++++++--- pkg/cluster/connection_pooler.go | 31 ++++++++++++ pkg/cluster/sync.go | 4 -- 4 files changed, 119 insertions(+), 58 deletions(-) diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index a75c03da2..812965854 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -227,6 +227,10 @@ func (c *Cluster) initUsers() error { } if err := c.initHumanUsers(); err != nil { + // remember all cached users in c.pgUsers + for cachedUserName, cachedUser := range c.pgUsersCache { + c.pgUsers[cachedUserName] = cachedUser + } return fmt.Errorf("could not init human users: %v", err) } @@ -748,6 +752,7 @@ func (c *Cluster) compareServices(old, new *v1.Service) (bool, string) { // for a cluster that had no such job before. In this case a missing job is not an error. func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error { updateFailed := false + userInitFailed := false syncStatefulSet := false c.mu.Lock() @@ -785,32 +790,39 @@ func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error { } } - // check if users need to be synced - sameUsers := reflect.DeepEqual(oldSpec.Spec.Users, newSpec.Spec.Users) && - reflect.DeepEqual(oldSpec.Spec.PreparedDatabases, newSpec.Spec.PreparedDatabases) - sameRotatedUsers := reflect.DeepEqual(oldSpec.Spec.UsersWithSecretRotation, newSpec.Spec.UsersWithSecretRotation) && - reflect.DeepEqual(oldSpec.Spec.UsersWithInPlaceSecretRotation, newSpec.Spec.UsersWithInPlaceSecretRotation) + // Users + func() { + // check if users need to be synced during update + sameUsers := reflect.DeepEqual(oldSpec.Spec.Users, newSpec.Spec.Users) && + reflect.DeepEqual(oldSpec.Spec.PreparedDatabases, newSpec.Spec.PreparedDatabases) + sameRotatedUsers := reflect.DeepEqual(oldSpec.Spec.UsersWithSecretRotation, newSpec.Spec.UsersWithSecretRotation) && + reflect.DeepEqual(oldSpec.Spec.UsersWithInPlaceSecretRotation, newSpec.Spec.UsersWithInPlaceSecretRotation) - // connection pooler needs one system user created, which is done in - // initUsers. Check if it needs to be called. - needConnectionPooler := needMasterConnectionPoolerWorker(&newSpec.Spec) || - needReplicaConnectionPoolerWorker(&newSpec.Spec) + // connection pooler needs one system user created who is initialized in initUsers + // only when disabled in oldSpec and enabled in newSpec + needPoolerUser := c.needConnectionPoolerUser(&oldSpec.Spec, &newSpec.Spec) - if !sameUsers || !sameRotatedUsers || needConnectionPooler { - c.logger.Debugf("initialize users") - if err := c.initUsers(); err != nil { - c.logger.Errorf("could not init users: %v", err) - updateFailed = true + // streams new replication user created who is initialized in initUsers + // only when streams were not specified in oldSpec but in newSpec + needStreamUser := len(oldSpec.Spec.Streams) == 0 && len(newSpec.Spec.Streams) > 0 + + if !sameUsers || !sameRotatedUsers || needPoolerUser || needStreamUser { + c.logger.Debugf("initialize users") + if err := c.initUsers(); err != nil { + c.logger.Errorf("could not init users - skipping sync of secrets and databases: %v", err) + userInitFailed = true + updateFailed = true + return + } + + c.logger.Debugf("syncing secrets") + //TODO: mind the secrets of the deleted/new users + if err := c.syncSecrets(); err != nil { + c.logger.Errorf("could not sync secrets: %v", err) + updateFailed = true + } } - - c.logger.Debugf("syncing secrets") - - //TODO: mind the secrets of the deleted/new users - if err := c.syncSecrets(); err != nil { - c.logger.Errorf("could not sync secrets: %v", err) - updateFailed = true - } - } + }() // Volume if c.OpConfig.StorageResizeMode != "off" { @@ -892,7 +904,7 @@ func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error { }() // Roles and Databases - if !(c.databaseAccessDisabled() || c.getNumberOfInstances(&c.Spec) <= 0 || c.Spec.StandbyCluster != nil) { + if !userInitFailed && !(c.databaseAccessDisabled() || c.getNumberOfInstances(&c.Spec) <= 0 || c.Spec.StandbyCluster != nil) { c.logger.Debugf("syncing roles") if err := c.syncRoles(); err != nil { c.logger.Errorf("could not sync roles: %v", err) @@ -920,13 +932,12 @@ func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error { // need to process. In the future we may want to do this more careful and // check which databases we need to process, but even repeating the whole // installation process should be good enough. - if _, err := c.syncConnectionPooler(oldSpec, newSpec, c.installLookupFunction); err != nil { c.logger.Errorf("could not sync connection pooler: %v", err) updateFailed = true } - if len(c.Spec.Streams) > 0 { + if len(newSpec.Spec.Streams) > 0 { if err := c.syncStreams(); err != nil { c.logger.Errorf("could not sync streams: %v", err) updateFailed = true @@ -1094,28 +1105,10 @@ func (c *Cluster) initSystemUsers() { Password: util.RandomPassword(constants.PasswordLength), } - // Connection pooler user is an exception, if requested it's going to be - // created by operator as a normal pgUser + // Connection pooler user is an exception + // if requested it's going to be created by operator if needConnectionPooler(&c.Spec) { - connectionPoolerSpec := c.Spec.ConnectionPooler - if connectionPoolerSpec == nil { - connectionPoolerSpec = &acidv1.ConnectionPooler{} - } - - // Using superuser as pooler user is not a good idea. First of all it's - // not going to be synced correctly with the current implementation, - // and second it's a bad practice. - username := c.OpConfig.ConnectionPooler.User - - isSuperUser := connectionPoolerSpec.User == c.OpConfig.SuperUsername - isProtectedUser := c.shouldAvoidProtectedOrSystemRole( - connectionPoolerSpec.User, "connection pool role") - - if !isSuperUser && !isProtectedUser { - username = util.Coalesce( - connectionPoolerSpec.User, - c.OpConfig.ConnectionPooler.User) - } + username := c.poolerUser(&c.Spec) // connection pooler application should be able to login with this role connectionPoolerUser := spec.PgUser{ diff --git a/pkg/cluster/cluster_test.go b/pkg/cluster/cluster_test.go index b53725923..24994cfc7 100644 --- a/pkg/cluster/cluster_test.go +++ b/pkg/cluster/cluster_test.go @@ -2,6 +2,7 @@ package cluster import ( "fmt" + "net/http" "reflect" "strings" "testing" @@ -222,7 +223,14 @@ type mockTeamsAPIClient struct { } func (m *mockTeamsAPIClient) TeamInfo(teamID, token string) (tm *teams.Team, statusCode int, err error) { - return &teams.Team{Members: m.members}, statusCode, nil + if len(m.members) > 0 { + return &teams.Team{Members: m.members}, http.StatusOK, nil + } + + // when members are not set handle this as an error for this mock API + // makes it easier to test behavior when teams API is unavailable + return nil, http.StatusInternalServerError, + fmt.Errorf("mocked %d error of mock Teams API for team %q", http.StatusInternalServerError, teamID) } func (m *mockTeamsAPIClient) setMembers(members []string) { @@ -237,32 +245,53 @@ func TestInitHumanUsers(t *testing.T) { // members of a product team are granted superuser rights for DBs of their team cl.OpConfig.EnableTeamSuperuser = true - cl.OpConfig.EnableTeamsAPI = true + cl.OpConfig.EnableTeamMemberDeprecation = true cl.OpConfig.PamRoleName = "zalandos" cl.Spec.TeamID = "test" + cl.Spec.Users = map[string]acidv1.UserFlags{"bar": []string{}} tests := []struct { existingRoles map[string]spec.PgUser teamRoles []string result map[string]spec.PgUser + err error }{ { existingRoles: map[string]spec.PgUser{"foo": {Name: "foo", Origin: spec.RoleOriginTeamsAPI, - Flags: []string{"NOLOGIN"}}, "bar": {Name: "bar", Flags: []string{"NOLOGIN"}}}, + Flags: []string{"LOGIN"}}, "bar": {Name: "bar", Flags: []string{"LOGIN"}}}, teamRoles: []string{"foo"}, result: map[string]spec.PgUser{"foo": {Name: "foo", Origin: spec.RoleOriginTeamsAPI, MemberOf: []string{cl.OpConfig.PamRoleName}, Flags: []string{"LOGIN", "SUPERUSER"}}, - "bar": {Name: "bar", Flags: []string{"NOLOGIN"}}}, + "bar": {Name: "bar", Flags: []string{"LOGIN"}}}, + err: fmt.Errorf("could not init human users: cannot initialize members for team %q who owns the Postgres cluster: could not get list of team members for team %q: could not get team info for team %q: mocked %d error of mock Teams API for team %q", + cl.Spec.TeamID, cl.Spec.TeamID, cl.Spec.TeamID, http.StatusInternalServerError, cl.Spec.TeamID), }, { existingRoles: map[string]spec.PgUser{}, teamRoles: []string{"admin", replicationUserName}, result: map[string]spec.PgUser{}, + err: nil, }, } for _, tt := range tests { + // set pgUsers so that initUsers sets up pgUsersCache with team roles + cl.pgUsers = tt.existingRoles + + // initUsers calls initHumanUsers which should fail + // because no members are set for mocked teams API + if err := cl.initUsers(); err != nil { + // check that at least team roles are remembered in c.pgUsers + if len(cl.pgUsers) < len(tt.teamRoles) { + t.Errorf("%s unexpected size of pgUsers: expected at least %d, got %d", t.Name(), len(tt.teamRoles), len(cl.pgUsers)) + } + if err.Error() != tt.err.Error() { + t.Errorf("%s expected error %v, got %v", t.Name(), err, tt.err) + } + } + + // set pgUsers again to test initHumanUsers with working teams API cl.pgUsers = tt.existingRoles mockTeamsAPI.setMembers(tt.teamRoles) if err := cl.initHumanUsers(); err != nil { @@ -288,12 +317,14 @@ type mockTeamsAPIClientMultipleTeams struct { func (m *mockTeamsAPIClientMultipleTeams) TeamInfo(teamID, token string) (tm *teams.Team, statusCode int, err error) { for _, team := range m.teams { if team.teamID == teamID { - return &teams.Team{Members: team.members}, statusCode, nil + return &teams.Team{Members: team.members}, http.StatusOK, nil } } - // should not be reached if a slice with teams is populated correctly - return nil, statusCode, nil + // when given teamId is not found in teams return StatusNotFound + // the operator should only return a warning in this case and not error out (#1842) + return nil, http.StatusNotFound, + fmt.Errorf("mocked %d error of mock Teams API for team %q", http.StatusNotFound, teamID) } // Test adding members of maintenance teams that get superuser rights for all PG databases @@ -392,6 +423,16 @@ func TestInitHumanUsersWithSuperuserTeams(t *testing.T) { "postgres_superuser": userA, }, }, + // case 4: the team does not exist which should not return an error + { + ownerTeam: "acid", + existingRoles: map[string]spec.PgUser{}, + superuserTeams: []string{"postgres_superusers"}, + teams: []mockTeam{teamA, teamB, teamTest}, + result: map[string]spec.PgUser{ + "postgres_superuser": userA, + }, + }, } for _, tt := range tests { diff --git a/pkg/cluster/connection_pooler.go b/pkg/cluster/connection_pooler.go index 340f6a3ae..a8214dff4 100644 --- a/pkg/cluster/connection_pooler.go +++ b/pkg/cluster/connection_pooler.go @@ -75,6 +75,37 @@ func needReplicaConnectionPoolerWorker(spec *acidv1.PostgresSpec) bool { *spec.EnableReplicaConnectionPooler } +func (c *Cluster) needConnectionPoolerUser(oldSpec, newSpec *acidv1.PostgresSpec) bool { + // return true if pooler is needed AND was not disabled before OR user name differs + return (needMasterConnectionPoolerWorker(newSpec) || needReplicaConnectionPoolerWorker(newSpec)) && + ((!needMasterConnectionPoolerWorker(oldSpec) && + !needReplicaConnectionPoolerWorker(oldSpec)) || + c.poolerUser(oldSpec) != c.poolerUser(newSpec)) +} + +func (c *Cluster) poolerUser(spec *acidv1.PostgresSpec) string { + connectionPoolerSpec := spec.ConnectionPooler + if connectionPoolerSpec == nil { + connectionPoolerSpec = &acidv1.ConnectionPooler{} + } + // Using superuser as pooler user is not a good idea. First of all it's + // not going to be synced correctly with the current implementation, + // and second it's a bad practice. + username := c.OpConfig.ConnectionPooler.User + + isSuperUser := connectionPoolerSpec.User == c.OpConfig.SuperUsername + isProtectedUser := c.shouldAvoidProtectedOrSystemRole( + connectionPoolerSpec.User, "connection pool role") + + if !isSuperUser && !isProtectedUser { + username = util.Coalesce( + connectionPoolerSpec.User, + c.OpConfig.ConnectionPooler.User) + } + + return username +} + // when listing pooler k8s objects func (c *Cluster) poolerLabelsSet(addExtraLabels bool) labels.Set { poolerLabels := c.labelsSet(addExtraLabels) diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index cc795634f..ed1511311 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -104,10 +104,6 @@ func (c *Cluster) Sync(newSpec *acidv1.Postgresql) error { if !(c.databaseAccessDisabled() || c.getNumberOfInstances(&newSpec.Spec) <= 0 || c.Spec.StandbyCluster != nil) { c.logger.Debug("syncing roles") if err = c.syncRoles(); err != nil { - // remember all cached users in c.pgUsers - for cachedUserName, cachedUser := range c.pgUsersCache { - c.pgUsers[cachedUserName] = cachedUser - } c.logger.Errorf("could not sync roles: %v", err) } c.logger.Debug("syncing databases") From 529cdfc0b67377bebdc56523fa8850e53d225612 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Tue, 25 Oct 2022 14:51:29 +0200 Subject: [PATCH 32/77] skip slots where publication sync failed (#2091) --- pkg/cluster/streams.go | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/pkg/cluster/streams.go b/pkg/cluster/streams.go index 4c75d07cf..51014b7b8 100644 --- a/pkg/cluster/streams.go +++ b/pkg/cluster/streams.go @@ -279,6 +279,7 @@ func (c *Cluster) syncStreams() error { c.streamApplications = appIds slots := make(map[string]map[string]string) + slotsToSync := make(map[string]map[string]string) publications := make(map[string]map[string]acidv1.StreamTable) requiredPatroniConfig := c.Spec.Patroni @@ -308,13 +309,6 @@ func (c *Cluster) syncStreams() error { } } - // no slots = no streams defined - if len(slots) > 0 { - requiredPatroniConfig.Slots = slots - } else { - return nil - } - // create publications to each created slot c.logger.Debug("syncing database publications") for publication, tables := range publications { @@ -323,7 +317,16 @@ func (c *Cluster) syncStreams() error { err = c.syncPublication(publication, dbName, tables) if err != nil { c.logger.Warningf("could not sync publication %q in database %q: %v", publication, dbName, err) + continue } + slotsToSync[publication] = slots[publication] + } + + // no slots to sync = no streams defined or publications created + if len(slotsToSync) > 0 { + requiredPatroniConfig.Slots = slotsToSync + } else { + return nil } // add extra logical slots to Patroni config From 18908e6097535712d2de01759469e5fd3cdba506 Mon Sep 17 00:00:00 2001 From: idanovinda Date: Wed, 26 Oct 2022 12:19:48 +0200 Subject: [PATCH 33/77] [UI] include load balancer in the monthly cost calculation (#1977) --- ui/app/src/edit.tag.pug | 3 + ui/app/src/new.tag.pug | 125 +++++++++++++++++++++++++-------- ui/app/src/postgresql.tag.pug | 12 ++-- ui/app/src/postgresqls.tag.pug | 15 ++-- ui/operator_ui/main.py | 57 +++++---------- 5 files changed, 131 insertions(+), 81 deletions(-) diff --git a/ui/app/src/edit.tag.pug b/ui/app/src/edit.tag.pug index bb536a8e4..ddcaa4653 100644 --- a/ui/app/src/edit.tag.pug +++ b/ui/app/src/edit.tag.pug @@ -139,6 +139,9 @@ edit o.spec.enableMasterLoadBalancer = i.spec.enableMasterLoadBalancer || false o.spec.enableReplicaLoadBalancer = i.spec.enableReplicaLoadBalancer || false o.spec.enableConnectionPooler = i.spec.enableConnectionPooler || false + o.spec.enableReplicaConnectionPooler = i.spec.enableReplicaConnectionPooler || false + o.spec.enableMasterPoolerLoadBalancer = i.spec.enableMasterPoolerLoadBalancer || false + o.spec.enableReplicaPoolerLoadBalancer = i.spec.enableReplicaPoolerLoadBalancer || false o.spec.volume = { size: i.spec.volume.size, diff --git a/ui/app/src/new.tag.pug b/ui/app/src/new.tag.pug index 02955602b..c066ad4f4 100644 --- a/ui/app/src/new.tag.pug +++ b/ui/app/src/new.tag.pug @@ -216,40 +216,73 @@ new ) tr(if='{ [undefined, true].includes(config.master_load_balancer_visible) }') - td Master load balancer + td Enable load balancer td - label - input( - type='checkbox' - value='{ enableMasterLoadBalancer }' - onchange='{ toggleEnableMasterLoadBalancer }' - ) - | - | Enable master ELB + ul.ips + li + label + input( + type='checkbox' + value='{ enableMasterLoadBalancer }' + onchange='{ toggleEnableMasterLoadBalancer }' + ) + | + | Master + li(if='{ [undefined, true].includes(config.replica_load_balancer_visible) && instanceCount > 1 }') + label + input( + type='checkbox' + value='{ enableReplicaLoadBalancer }' + onchange='{ toggleEnableReplicaLoadBalancer }' + ) + | + | Replica - tr(if='{ [undefined, true].includes(config.replica_load_balancer_visible) }') - td Replica load balancer + tr(if='{ [undefined, true].includes(config.connection_pooler_visible) }') + td Enable connection pooler td - label - input( - type='checkbox' - value='{ enableReplicaLoadBalancer }' - onchange='{ toggleEnableReplicaLoadBalancer }' - ) - | - | Enable replica ELB + ul.ips + li + label + input( + type='checkbox' + value='{ enableConnectionPooler }' + onchange='{ toggleEnableConnectionPooler }' + ) + | + | Master + li(if='{ [undefined, true].includes(config.replica_connection_pooler_visible) && instanceCount > 1 }') + label + input( + type='checkbox' + value='{ enableReplicaConnectionPooler }' + onchange='{ toggleEnableReplicaConnectionPooler }' + ) + | + | Replica - tr - td Enable Connection Pool + tr(if='{ [undefined, true].includes(config.master_pooler_load_balancer_visible) }') + td Enable connection pooler load balancer td - label - input( - type='checkbox' - value='{ enableConnectionPooler }' - onchange='{ toggleEnableConnectionPooler }' - ) - | - | Enable Connection Pool (using PGBouncer) + ul.ips + li + label + input( + type='checkbox' + value='{ enableMasterPoolerLoadBalancer }' + onchange='{ toggleEnableMasterPoolerLoadBalancer }' + ) + | + | Master + li(if='{ [undefined, true].includes(config.replica_pooler_load_balancer_visible) && instanceCount > 1 }') + label + input( + type='checkbox' + value='{ enableReplicaPoolerLoadBalancer }' + onchange='{ toggleEnableReplicaPoolerLoadBalancer }' + ) + | + | Replica tr td Volume size @@ -267,7 +300,7 @@ new .input-group-addon .input-units Gi tr - td + td td Specify Iops and Throughput only if you need more than the default 3000 Iops and 125Mb/s EBS provides. tr @@ -539,6 +572,15 @@ new {{#if enableConnectionPooler}} enableConnectionPooler: true {{/if}} + {{#if enableReplicaConnectionPooler}} + enableReplicaConnectionPooler: true + {{/if}} + {{#if enableMasterPoolerLoadBalancer}} + enableMasterPoolerLoadBalancer: true + {{/if}} + {{#if enableReplicaPoolerLoadBalancer}} + enableReplicaPoolerLoadBalancer: true + {{/if}} volume: size: "{{ volumeSize }}Gi"{{#if iops}} iops: {{ iops }}{{/if}}{{#if throughput}} @@ -592,6 +634,9 @@ new enableMasterLoadBalancer: this.enableMasterLoadBalancer, enableReplicaLoadBalancer: this.enableReplicaLoadBalancer, enableConnectionPooler: this.enableConnectionPooler, + enableReplicaConnectionPooler: this.enableReplicaConnectionPooler, + enableMasterPoolerLoadBalancer: this.enableMasterPoolerLoadBalancer, + enableReplicaPoolerLoadBalancer: this.enableReplicaPoolerLoadBalancer, volumeSize: this.volumeSize, iops: this.iops, throughput: this.throughput, @@ -655,6 +700,18 @@ new this.enableConnectionPooler = !this.enableConnectionPooler } + this.toggleEnableReplicaConnectionPooler = e => { + this.enableReplicaConnectionPooler = !this.enableReplicaConnectionPooler + } + + this.toggleEnableMasterPoolerLoadBalancer = e => { + this.enableMasterPoolerLoadBalancer = !this.enableMasterPoolerLoadBalancer + } + + this.toggleEnableReplicaPoolerLoadBalancer = e => { + this.enableReplicaPoolerLoadBalancer = !this.enableReplicaPoolerLoadBalancer + } + this.volumeChange = e => { this.volumeSize = +e.target.value } @@ -692,6 +749,11 @@ new this.instanceCountChange = e => { this.instanceCount = +e.target.value + if (this.instanceCount < 2) { + this.enableReplicaLoadBalancer = false + this.enableReplicaConnectionPooler = false + this.enableReplicaPoolerLoadBalancer = false + } } this.checkClusterExists = () => ( @@ -957,6 +1019,9 @@ new this.enableMasterLoadBalancer = false this.enableReplicaLoadBalancer = false this.enableConnectionPooler = false + this.enableReplicaConnectionPooler = false + this.enableMasterPoolerLoadBalancer = false + this.enableReplicaPoolerLoadBalancer = false this.postgresqlVersion = this.postgresqlVersion = ( this.config.postgresql_versions[0] diff --git a/ui/app/src/postgresql.tag.pug b/ui/app/src/postgresql.tag.pug index c557e4da8..f1b15ca81 100644 --- a/ui/app/src/postgresql.tag.pug +++ b/ui/app/src/postgresql.tag.pug @@ -168,11 +168,13 @@ postgresql this.progress.dnsName = data.metadata.name + '.' + data.metadata.namespace } - jQuery.get('/pooler/' + this.cluster_path).done(data => { - this.progress.pooler = {"url": ""} - this.update() - }) - + if (this.progress.poolerEnabled == true) { + jQuery.get('/pooler/' + this.cluster_path).done(data => { + this.progress.pooler = {"url": ""} + this.update() + }) + } + this.update() }) }) diff --git a/ui/app/src/postgresqls.tag.pug b/ui/app/src/postgresqls.tag.pug index 742bb2968..6b60b9f8a 100644 --- a/ui/app/src/postgresqls.tag.pug +++ b/ui/app/src/postgresqls.tag.pug @@ -68,6 +68,8 @@ postgresqls | IOPS (-3000 baseline): 0.006$ br | Throughput (-125 baseline): 0.0476$ + br + | 1 ELB: 21.96$ th(stlye='width: 120px') tbody @@ -87,7 +89,7 @@ postgresqls td { volume_size } td { iops } td { throughput } - td { calcCosts(nodes, cpu, memory, volume_size, iops, throughput) }$ + td { calcCosts(nodes, cpu, memory, volume_size, iops, throughput, num_elb) }$ td @@ -167,7 +169,9 @@ postgresqls | IOPS (-3000 baseline): 0.006$ br | Throughput (-125 baseline): 0.0476$ - th(stlye='width: 120px') + br + | 1 ELB: 21.96$ + th(style='width: 120px') tbody tr( @@ -188,7 +192,7 @@ postgresqls td { volume_size } td { iops } td { throughput } - td { calcCosts(nodes, cpu, memory, volume_size, iops, throughput) }$ + td { calcCosts(nodes, cpu, memory, volume_size, iops, throughput, num_elb) }$ td @@ -263,10 +267,11 @@ postgresqls + '/' + encodeURI(cluster.name) ) - const calcCosts = this.calcCosts = (nodes, cpu, memory, disk, iops, throughput) => { + const calcCosts = this.calcCosts = (nodes, cpu, memory, disk, iops, throughput, num_elb) => { podcount = Math.max(nodes, opts.config.min_pods) corecost = toCores(cpu) * opts.config.cost_core * 30.5 * 24 memorycost = toMemory(memory) * opts.config.cost_memory * 30.5 * 24 + elbcost = num_elb * opts.config.cost_elb * 30.5 * 24 diskcost = toDisk(disk) * opts.config.cost_ebs iopscost = 0 if (iops !== undefined && iops > opts.config.free_iops) { @@ -283,7 +288,7 @@ postgresqls throughputcost = (throughput - opts.config.free_throughput) * opts.config.cost_throughput } - costs = podcount * (Math.max(corecost, memorycost) + diskcost + iopscost + throughputcost) + costs = podcount * (Math.max(corecost, memorycost) + diskcost + iopscost + throughputcost) + elbcost return costs.toFixed(2) } diff --git a/ui/operator_ui/main.py b/ui/operator_ui/main.py index f3854628a..72fb7d4e6 100644 --- a/ui/operator_ui/main.py +++ b/ui/operator_ui/main.py @@ -101,6 +101,7 @@ COST_THROUGHPUT = float(getenv('COST_THROUGHPUT', 0.0476)) # MB/s per month abo # compute costs, i.e. https://www.ec2instances.info/?region=eu-central-1&selected=m5.2xlarge COST_CORE = float(getenv('COST_CORE', 0.0575)) # Core per hour m5.2xlarge / 8. COST_MEMORY = float(getenv('COST_MEMORY', 0.014375)) # Memory GB m5.2xlarge / 32. +COST_ELB = float(getenv('COST_ELB', 0.03)) # per hour # maximum and limitation of IOPS and throughput FREE_IOPS = float(getenv('FREE_IOPS', 3000)) @@ -334,6 +335,7 @@ DEFAULT_UI_CONFIG = { 'cost_throughput': COST_THROUGHPUT, 'cost_core': COST_CORE, 'cost_memory': COST_MEMORY, + 'cost_elb': COST_ELB, 'min_pods': MIN_PODS, 'free_iops': FREE_IOPS, 'free_throughput': FREE_THROUGHPUT, @@ -523,6 +525,8 @@ def get_postgresqls(): 'namespaced_name': namespace + '/' + name, 'full_name': namespace + '/' + name + ('/' + uid if uid else ''), 'status': status, + 'num_elb': spec.get('enableMasterLoadBalancer', 0) + spec.get('enableReplicaLoadBalancer', 0) + \ + spec.get('enableMasterPoolerLoadBalancer', 0) + spec.get('enableReplicaPoolerLoadBalancer', 0), } for cluster in these( read_postgresqls( @@ -662,49 +666,20 @@ def update_postgresql(namespace: str, cluster: str): spec['volume']['throughput'] = throughput - if 'enableConnectionPooler' in postgresql['spec']: - cp = postgresql['spec']['enableConnectionPooler'] - if not cp: - if 'enableConnectionPooler' in o['spec']: - del o['spec']['enableConnectionPooler'] - else: - spec['enableConnectionPooler'] = True - else: - if 'enableConnectionPooler' in o['spec']: - del o['spec']['enableConnectionPooler'] + additional_specs = ['enableMasterLoadBalancer', + 'enableReplicaLoadBalancer', + 'enableConnectionPooler', + 'enableReplicaConnectionPooler', + 'enableMasterPoolerLoadBalancer', + 'enableReplicaPoolerLoadBalancer', + ] - if 'enableReplicaConnectionPooler' in postgresql['spec']: - cp = postgresql['spec']['enableReplicaConnectionPooler'] - if not cp: - if 'enableReplicaConnectionPooler' in o['spec']: - del o['spec']['enableReplicaConnectionPooler'] + for var in additional_specs: + if postgresql['spec'].get(var): + spec[var] = True else: - spec['enableReplicaConnectionPooler'] = True - else: - if 'enableReplicaConnectionPooler' in o['spec']: - del o['spec']['enableReplicaConnectionPooler'] - - if 'enableReplicaLoadBalancer' in postgresql['spec']: - rlb = postgresql['spec']['enableReplicaLoadBalancer'] - if not rlb: - if 'enableReplicaLoadBalancer' in o['spec']: - del o['spec']['enableReplicaLoadBalancer'] - else: - spec['enableReplicaLoadBalancer'] = True - else: - if 'enableReplicaLoadBalancer' in o['spec']: - del o['spec']['enableReplicaLoadBalancer'] - - if 'enableMasterLoadBalancer' in postgresql['spec']: - rlb = postgresql['spec']['enableMasterLoadBalancer'] - if not rlb: - if 'enableMasterLoadBalancer' in o['spec']: - del o['spec']['enableMasterLoadBalancer'] - else: - spec['enableMasterLoadBalancer'] = True - else: - if 'enableMasterLoadBalancer' in o['spec']: - del o['spec']['enableMasterLoadBalancer'] + if var in o['spec']: + del o['spec'][var] if 'users' in postgresql['spec']: spec['users'] = postgresql['spec']['users'] From c895e8f61fc394602bc3151c5b8f00954dcf5d8e Mon Sep 17 00:00:00 2001 From: yoshihikoueno <38683757+yoshihikoueno@users.noreply.github.com> Date: Thu, 3 Nov 2022 18:57:33 +0900 Subject: [PATCH 34/77] Bumped Spilo 14 image tag to 2.1-p7 (#2096) * Bumped Spilo 14 image tag to 2.1-p7 Co-authored-by: yoshihiko --- README.md | 2 +- charts/postgres-operator/crds/operatorconfigurations.yaml | 2 +- charts/postgres-operator/values.yaml | 2 +- manifests/complete-postgres-manifest.yaml | 2 +- manifests/configmap.yaml | 2 +- manifests/operatorconfiguration.crd.yaml | 2 +- manifests/postgresql-operator-default-configuration.yaml | 2 +- pkg/controller/operator_config.go | 2 +- pkg/util/config/config.go | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index a67a9a8cf..25839e965 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ We introduce the major version into the backup path to smoothen the [major versi The new operator configuration can set a compatibility flag *enable_spilo_wal_path_compat* to make Spilo look for wal segments in the current path but also old format paths. This comes at potential performance costs and should be disabled after a few days. -The newest Spilo image is: `registry.opensource.zalan.do/acid/spilo-14:2.1-p6` +The newest Spilo image is: `registry.opensource.zalan.do/acid/spilo-14:2.1-p7` The last Spilo 12 image is: `registry.opensource.zalan.do/acid/spilo-12:1.6-p5` diff --git a/charts/postgres-operator/crds/operatorconfigurations.yaml b/charts/postgres-operator/crds/operatorconfigurations.yaml index 4ee1c6919..02fab645f 100644 --- a/charts/postgres-operator/crds/operatorconfigurations.yaml +++ b/charts/postgres-operator/crds/operatorconfigurations.yaml @@ -68,7 +68,7 @@ spec: type: string docker_image: type: string - default: "registry.opensource.zalan.do/acid/spilo-14:2.1-p6" + default: "registry.opensource.zalan.do/acid/spilo-14:2.1-p7" enable_crd_registration: type: boolean default: true diff --git a/charts/postgres-operator/values.yaml b/charts/postgres-operator/values.yaml index 0bcc5b7e0..9dc6960b7 100644 --- a/charts/postgres-operator/values.yaml +++ b/charts/postgres-operator/values.yaml @@ -38,7 +38,7 @@ configGeneral: # etcd connection string for Patroni. Empty uses K8s-native DCS. etcd_host: "" # Spilo docker image - docker_image: registry.opensource.zalan.do/acid/spilo-14:2.1-p6 + docker_image: registry.opensource.zalan.do/acid/spilo-14:2.1-p7 # key name for annotation to ignore globally configured instance limits # ignore_instance_limits_annotation_key: "" diff --git a/manifests/complete-postgres-manifest.yaml b/manifests/complete-postgres-manifest.yaml index 2b4412b79..cb86dc272 100644 --- a/manifests/complete-postgres-manifest.yaml +++ b/manifests/complete-postgres-manifest.yaml @@ -10,7 +10,7 @@ metadata: # "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: registry.opensource.zalan.do/acid/spilo-14:2.1-p6 + dockerImage: registry.opensource.zalan.do/acid/spilo-14:2.1-p7 teamId: "acid" numberOfInstances: 2 users: # Application/Robot users diff --git a/manifests/configmap.yaml b/manifests/configmap.yaml index bd5f5af12..94b7c5f1c 100644 --- a/manifests/configmap.yaml +++ b/manifests/configmap.yaml @@ -34,7 +34,7 @@ data: # default_memory_request: 100Mi # delete_annotation_date_key: delete-date # delete_annotation_name_key: delete-clustername - docker_image: registry.opensource.zalan.do/acid/spilo-14:2.1-p6 + docker_image: registry.opensource.zalan.do/acid/spilo-14:2.1-p7 # downscaler_annotations: "deployment-time,downscaler/*" # enable_admin_role_for_users: "true" # enable_crd_registration: "true" diff --git a/manifests/operatorconfiguration.crd.yaml b/manifests/operatorconfiguration.crd.yaml index 0f0d47d63..5a4383e82 100644 --- a/manifests/operatorconfiguration.crd.yaml +++ b/manifests/operatorconfiguration.crd.yaml @@ -66,7 +66,7 @@ spec: type: string docker_image: type: string - default: "registry.opensource.zalan.do/acid/spilo-14:2.1-p6" + default: "registry.opensource.zalan.do/acid/spilo-14:2.1-p7" enable_crd_registration: type: boolean default: true diff --git a/manifests/postgresql-operator-default-configuration.yaml b/manifests/postgresql-operator-default-configuration.yaml index 070bbc4df..80643b767 100644 --- a/manifests/postgresql-operator-default-configuration.yaml +++ b/manifests/postgresql-operator-default-configuration.yaml @@ -3,7 +3,7 @@ kind: OperatorConfiguration metadata: name: postgresql-operator-default-configuration configuration: - docker_image: registry.opensource.zalan.do/acid/spilo-14:2.1-p6 + docker_image: registry.opensource.zalan.do/acid/spilo-14:2.1-p7 # enable_crd_registration: true # crd_categories: # - all diff --git a/pkg/controller/operator_config.go b/pkg/controller/operator_config.go index b035573a0..57c3c7110 100644 --- a/pkg/controller/operator_config.go +++ b/pkg/controller/operator_config.go @@ -39,7 +39,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur result.EnableTeamIdClusternamePrefix = fromCRD.EnableTeamIdClusternamePrefix result.EtcdHost = fromCRD.EtcdHost result.KubernetesUseConfigMaps = fromCRD.KubernetesUseConfigMaps - result.DockerImage = util.Coalesce(fromCRD.DockerImage, "registry.opensource.zalan.do/acid/spilo-14:2.1-p6") + result.DockerImage = util.Coalesce(fromCRD.DockerImage, "registry.opensource.zalan.do/acid/spilo-14:2.1-p7") result.Workers = util.CoalesceUInt32(fromCRD.Workers, 8) result.MinInstances = fromCRD.MinInstances result.MaxInstances = fromCRD.MaxInstances diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index 447acbc92..8e51dd020 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -165,7 +165,7 @@ type Config struct { WatchedNamespace string `name:"watched_namespace"` // special values: "*" means 'watch all namespaces', the empty string "" means 'watch a namespace where operator is deployed to' KubernetesUseConfigMaps bool `name:"kubernetes_use_configmaps" default:"false"` EtcdHost string `name:"etcd_host" default:""` // special values: the empty string "" means Patroni will use K8s as a DCS - DockerImage string `name:"docker_image" default:"registry.opensource.zalan.do/acid/spilo-14:2.1-p6"` + DockerImage string `name:"docker_image" default:"registry.opensource.zalan.do/acid/spilo-14:2.1-p7"` SidecarImages map[string]string `name:"sidecar_docker_images"` // deprecated in favour of SidecarContainers SidecarContainers []v1.Container `name:"sidecars"` PodServiceAccountName string `name:"pod_service_account_name" default:"postgres-pod"` From 2a54e49e9fe7c638d1ef516616aa875698715de6 Mon Sep 17 00:00:00 2001 From: adriannieto-attechnest <88320765+adriannieto-attechnest@users.noreply.github.com> Date: Tue, 15 Nov 2022 09:21:26 +0100 Subject: [PATCH 35/77] Fixed tiny typo in values.yaml (#2100) --- charts/postgres-operator/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/postgres-operator/values.yaml b/charts/postgres-operator/values.yaml index 9dc6960b7..99e1092be 100644 --- a/charts/postgres-operator/values.yaml +++ b/charts/postgres-operator/values.yaml @@ -299,7 +299,7 @@ configAwsOrGcp: # Path to mount the above Secret in the filesystem of the container(s) # additional_secret_mount_path: "/some/dir" - # AWS region used to store ESB volumes + # AWS region used to store EBS volumes aws_region: eu-central-1 # enable automatic migration on AWS from gp2 to gp3 volumes From 528bb81a782d3417aa6fbe6c5255ada4fd5c949a Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Mon, 28 Nov 2022 16:37:34 +0100 Subject: [PATCH 36/77] first sync wal_level then publications (#2109) --- pkg/cluster/database.go | 2 +- pkg/cluster/streams.go | 19 ++++++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/pkg/cluster/database.go b/pkg/cluster/database.go index 978b570b0..f1ea736ce 100644 --- a/pkg/cluster/database.go +++ b/pkg/cluster/database.go @@ -48,7 +48,7 @@ const ( getPublicationsSQL = `SELECT p.pubname, string_agg(pt.schemaname || '.' || pt.tablename, ', ' ORDER BY pt.schemaname, pt.tablename) FROM pg_publication p - JOIN pg_publication_tables pt ON pt.pubname = p.pubname + LEFT JOIN pg_publication_tables pt ON pt.pubname = p.pubname GROUP BY p.pubname;` createPublicationSQL = `CREATE PUBLICATION "%s" FOR TABLE %s WITH (publish = 'insert, update');` alterPublicationSQL = `ALTER PUBLICATION "%s" SET TABLE %s;` diff --git a/pkg/cluster/streams.go b/pkg/cluster/streams.go index 51014b7b8..d911e6a83 100644 --- a/pkg/cluster/streams.go +++ b/pkg/cluster/streams.go @@ -273,6 +273,17 @@ func (c *Cluster) syncStreams() error { return nil } + // update config to set wal_level: logical + requiredPatroniConfig := c.Spec.Patroni + requiresRestart, err := c.syncPostgresConfig(requiredPatroniConfig) + if err != nil { + return fmt.Errorf("failed to snyc Postgres config for event streaming: %v", err) + } + if requiresRestart { + c.logger.Debugf("updated Postgres config. Server will be restarted and streams will get created during next sync") + return nil + } + // fetch different application IDs from streams section // there will be a separate event stream resource for each ID appIds := gatherApplicationIds(c.Spec.Streams) @@ -282,7 +293,6 @@ func (c *Cluster) syncStreams() error { slotsToSync := make(map[string]map[string]string) publications := make(map[string]map[string]acidv1.StreamTable) - requiredPatroniConfig := c.Spec.Patroni if len(requiredPatroniConfig.Slots) > 0 { slots = requiredPatroniConfig.Slots } @@ -330,15 +340,10 @@ func (c *Cluster) syncStreams() error { } // add extra logical slots to Patroni config - c.logger.Debug("syncing Postgres config for logical decoding") - requiresRestart, err := c.syncPostgresConfig(requiredPatroniConfig) + _, err = c.syncPostgresConfig(requiredPatroniConfig) if err != nil { return fmt.Errorf("failed to snyc Postgres config for event streaming: %v", err) } - if requiresRestart { - c.logger.Debugf("updated Postgres config. Server will be restarted and streams will get created during next sync") - return nil - } // after Postgres was restarted we can create stream CRDs err = c.createOrUpdateStreams() From 0e3cbcd1804c68c342df3a167f141d5b6f0d2d21 Mon Sep 17 00:00:00 2001 From: Dmitry Shurupov Date: Mon, 28 Nov 2022 22:37:57 +0700 Subject: [PATCH 37/77] Updating Flant blog link (#2113) --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index fde3f0ba2..673819267 100644 --- a/docs/index.md +++ b/docs/index.md @@ -91,7 +91,7 @@ Please, report any issues discovered to https://github.com/zalando/postgres-oper - "Getting started with the Zalando Operator for PostgreSQL" by Daniel Westermann on [dbi services blog](https://blog.dbi-services.com/getting-started-with-the-zalando-operator-for-postgresql/), Mar. 2021. -- "Our experience with Postgres Operator for Kubernetes by Zalando" by Nikolay Bogdanov on [flant blog](https://blog.flant.com/our-experience-with-postgres-operator-for-kubernetes-by-zalando/), Feb. 2021. +- "Our experience with Postgres Operator for Kubernetes by Zalando" by Nikolay Bogdanov on [Palark blog](https://blog.palark.com/our-experience-with-postgres-operator-for-kubernetes-by-zalando/), Feb. 2021. - "How to set up continuous backups and monitoring" by Pål Kristensen on [GitHub](https://github.com/zalando/postgres-operator/issues/858#issuecomment-608136253), Mar. 2020. From 4a386691b7f069978e13131d8a73e871b8a8c39d Mon Sep 17 00:00:00 2001 From: Alfred Schmid Date: Mon, 28 Nov 2022 16:39:23 +0100 Subject: [PATCH 38/77] use tpl in value objects containing arrays (#2115) using tpl in value objects containing arrays allows us to override objects in arrays. This is not possible via helm --set due to limitations in helm --- .../postgres-operator/templates/operatorconfiguration.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/charts/postgres-operator/templates/operatorconfiguration.yaml b/charts/postgres-operator/templates/operatorconfiguration.yaml index 4e380f448..b548ce462 100644 --- a/charts/postgres-operator/templates/operatorconfiguration.yaml +++ b/charts/postgres-operator/templates/operatorconfiguration.yaml @@ -12,7 +12,7 @@ metadata: configuration: {{ toYaml .Values.configGeneral | indent 2 }} users: -{{ toYaml .Values.configUsers | indent 4 }} +{{ tpl (toYaml .Values.configUsers) . | indent 4 }} major_version_upgrade: {{ toYaml .Values.configMajorVersionUpgrade | indent 4 }} kubernetes: @@ -21,7 +21,7 @@ configuration: {{- end }} pod_service_account_name: {{ include "postgres-pod.serviceAccountName" . }} oauth_token_secret_name: {{ template "postgres-operator.fullname" . }} -{{ toYaml .Values.configKubernetes | indent 4 }} +{{ tpl (toYaml .Values.configKubernetes) . | indent 4 }} postgres_pod_resources: {{ toYaml .Values.configPostgresPodResources | indent 4 }} timeouts: @@ -35,7 +35,7 @@ configuration: debug: {{ toYaml .Values.configDebug | indent 4 }} teams_api: -{{ toYaml .Values.configTeamsApi | indent 4 }} +{{ tpl (toYaml .Values.configTeamsApi) . | indent 4 }} logging_rest_api: {{ toYaml .Values.configLoggingRestApi | indent 4 }} connection_pooler: From 1d44dd4694f21154140f636403e960e92fda3c9b Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Fri, 2 Dec 2022 10:09:19 +0100 Subject: [PATCH 39/77] delete secret resource from map (#2123) --- pkg/cluster/resources.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cluster/resources.go b/pkg/cluster/resources.go index 5cc428aca..eb68e9fb2 100644 --- a/pkg/cluster/resources.go +++ b/pkg/cluster/resources.go @@ -518,7 +518,7 @@ func (c *Cluster) deleteSecret(uid types.UID, secret v1.Secret) error { return fmt.Errorf("could not delete secret %q: %v", secretName, err) } c.logger.Infof("secret %q has been deleted", secretName) - c.Secrets[uid] = nil + delete(c.Secrets, uid) return nil } From 4d585250dbaa1003c4b8f61068e4bb97765dcf20 Mon Sep 17 00:00:00 2001 From: Polina Bungina Date: Fri, 2 Dec 2022 13:33:02 +0100 Subject: [PATCH 40/77] Add Patroni failsafe_mode parameter (#2076) This commit adds support of a not-yet-released Patroni feature that allows postgres to run as primary in case of a failed leader lock update. * Add Patroni 'failsafe_mode' local parameter (enable for a single PG cluster) * Allow configuring Patroni 'failsafe_mode' parameter globally --- .../crds/operatorconfigurations.yaml | 6 + .../postgres-operator/crds/postgresqls.yaml | 2 + charts/postgres-operator/values.yaml | 4 + docs/reference/cluster_manifest.md | 3 + e2e/tests/test_e2e.py | 5 +- manifests/complete-postgres-manifest.yaml | 1 + manifests/configmap.yaml | 1 + manifests/operatorconfiguration.crd.yaml | 6 + ...gresql-operator-default-configuration.yaml | 2 + manifests/postgresql.crd.yaml | 2 + pkg/apis/acid.zalan.do/v1/crds.go | 11 ++ .../v1/operator_configuration_type.go | 8 +- pkg/apis/acid.zalan.do/v1/postgresql_type.go | 1 + .../acid.zalan.do/v1/zz_generated.deepcopy.go | 27 ++++ pkg/cluster/k8sres.go | 14 +- pkg/cluster/k8sres_test.go | 74 ++++++++-- pkg/cluster/sync.go | 15 ++ pkg/cluster/sync_test.go | 133 +++++++++++++++--- pkg/controller/operator_config.go | 3 + .../clientset/versioned/fake/register.go | 14 +- .../clientset/versioned/scheme/register.go | 14 +- pkg/util/config/config.go | 1 + 22 files changed, 297 insertions(+), 50 deletions(-) diff --git a/charts/postgres-operator/crds/operatorconfigurations.yaml b/charts/postgres-operator/crds/operatorconfigurations.yaml index 02fab645f..e6305fada 100644 --- a/charts/postgres-operator/crds/operatorconfigurations.yaml +++ b/charts/postgres-operator/crds/operatorconfigurations.yaml @@ -633,6 +633,12 @@ spec: type: string pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' default: "100Mi" + patroni: + type: object + properties: + failsafe_mode: + type: boolean + default: false status: type: object additionalProperties: diff --git a/charts/postgres-operator/crds/postgresqls.yaml b/charts/postgres-operator/crds/postgresqls.yaml index d2ad89da6..d78bdb2fb 100644 --- a/charts/postgres-operator/crds/postgresqls.yaml +++ b/charts/postgres-operator/crds/postgresqls.yaml @@ -320,6 +320,8 @@ spec: patroni: type: object properties: + failsafe_mode: + type: boolean initdb: type: object additionalProperties: diff --git a/charts/postgres-operator/values.yaml b/charts/postgres-operator/values.yaml index 99e1092be..1ca429a99 100644 --- a/charts/postgres-operator/values.yaml +++ b/charts/postgres-operator/values.yaml @@ -409,6 +409,10 @@ configConnectionPooler: connection_pooler_default_cpu_limit: "1" connection_pooler_default_memory_limit: 100Mi +configPatroni: + # enable Patroni DCS failsafe_mode feature + failsafe_mode: false + # Zalando's internal CDC stream feature enableStreams: false diff --git a/docs/reference/cluster_manifest.md b/docs/reference/cluster_manifest.md index ba4006f64..718479d50 100644 --- a/docs/reference/cluster_manifest.md +++ b/docs/reference/cluster_manifest.md @@ -316,6 +316,9 @@ explanation of `ttl` and `loop_wait` parameters. * **synchronous_node_count** Patroni `synchronous_node_count` parameter value. Note, this option is only available for Spilo images with Patroni 2.0+. The default is set to `1`. Optional. + +* **failsafe_mode** + Patroni `failsafe_mode` parameter value. If enabled, allows Patroni to cope with DCS outages and avoid leader demotion. Note, this option is currently not included in any Patroni release. The default is set to `false`. Optional. ## Postgres container resources diff --git a/e2e/tests/test_e2e.py b/e2e/tests/test_e2e.py index d13a51c19..638cd05b2 100644 --- a/e2e/tests/test_e2e.py +++ b/e2e/tests/test_e2e.py @@ -407,7 +407,8 @@ class EndToEndTestCase(unittest.TestCase): "ttl": 29, "loop_wait": 9, "retry_timeout": 9, - "synchronous_mode": True + "synchronous_mode": True, + "failsafe_mode": True, } } } @@ -434,6 +435,8 @@ class EndToEndTestCase(unittest.TestCase): "retry_timeout not updated") self.assertEqual(desired_config["synchronous_mode"], effective_config["synchronous_mode"], "synchronous_mode not updated") + self.assertEqual(desired_config["failsafe_mode"], effective_config["failsafe_mode"], + "failsafe_mode not updated") return True # check if Patroni config has been updated diff --git a/manifests/complete-postgres-manifest.yaml b/manifests/complete-postgres-manifest.yaml index cb86dc272..bc8309684 100644 --- a/manifests/complete-postgres-manifest.yaml +++ b/manifests/complete-postgres-manifest.yaml @@ -109,6 +109,7 @@ spec: cpu: 500m memory: 500Mi patroni: + failsafe_mode: false initdb: encoding: "UTF8" locale: "en_US.UTF-8" diff --git a/manifests/configmap.yaml b/manifests/configmap.yaml index 94b7c5f1c..6eb391ba4 100644 --- a/manifests/configmap.yaml +++ b/manifests/configmap.yaml @@ -47,6 +47,7 @@ data: 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" diff --git a/manifests/operatorconfiguration.crd.yaml b/manifests/operatorconfiguration.crd.yaml index 5a4383e82..2be301c84 100644 --- a/manifests/operatorconfiguration.crd.yaml +++ b/manifests/operatorconfiguration.crd.yaml @@ -631,6 +631,12 @@ spec: type: string pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' default: "100Mi" + patroni: + type: object + properties: + failsafe_mode: + type: boolean + default: false status: type: object additionalProperties: diff --git a/manifests/postgresql-operator-default-configuration.yaml b/manifests/postgresql-operator-default-configuration.yaml index 80643b767..e02ef8bae 100644 --- a/manifests/postgresql-operator-default-configuration.yaml +++ b/manifests/postgresql-operator-default-configuration.yaml @@ -198,3 +198,5 @@ configuration: connection_pooler_number_of_instances: 2 # connection_pooler_schema: "pooler" # connection_pooler_user: "pooler" + patroni: + # failsafe_mode: "false" diff --git a/manifests/postgresql.crd.yaml b/manifests/postgresql.crd.yaml index b113c849f..ae92a756a 100644 --- a/manifests/postgresql.crd.yaml +++ b/manifests/postgresql.crd.yaml @@ -318,6 +318,8 @@ spec: patroni: type: object properties: + failsafe_mode: + type: boolean initdb: type: object additionalProperties: diff --git a/pkg/apis/acid.zalan.do/v1/crds.go b/pkg/apis/acid.zalan.do/v1/crds.go index 58186a5e5..cbff1e3ad 100644 --- a/pkg/apis/acid.zalan.do/v1/crds.go +++ b/pkg/apis/acid.zalan.do/v1/crds.go @@ -503,6 +503,9 @@ var PostgresCRDResourceValidation = apiextv1.CustomResourceValidation{ "patroni": { Type: "object", Properties: map[string]apiextv1.JSONSchemaProps{ + "failsafe_mode": { + Type: "boolean", + }, "initdb": { Type: "object", AdditionalProperties: &apiextv1.JSONSchemaPropsOrBool{ @@ -1458,6 +1461,14 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{ }, }, }, + "patroni": { + Type: "object", + Properties: map[string]apiextv1.JSONSchemaProps{ + "failsafe_mode": { + Type: "boolean", + }, + }, + }, "postgres_pod_resources": { Type: "object", Properties: map[string]apiextv1.JSONSchemaProps{ diff --git a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go index 85ba25e34..fc5746413 100644 --- a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go +++ b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go @@ -227,6 +227,11 @@ type OperatorLogicalBackupConfiguration struct { JobPrefix string `json:"logical_backup_job_prefix,omitempty"` } +// PatroniConfiguration defines configuration for Patroni +type PatroniConfiguration struct { + FailsafeMode *bool `json:"failsafe_mode,omitempty"` +} + // OperatorConfigurationData defines the operation config type OperatorConfigurationData struct { EnableCRDRegistration *bool `json:"enable_crd_registration,omitempty"` @@ -259,11 +264,12 @@ type OperatorConfigurationData struct { Scalyr ScalyrConfiguration `json:"scalyr"` LogicalBackup OperatorLogicalBackupConfiguration `json:"logical_backup"` ConnectionPooler ConnectionPoolerConfiguration `json:"connection_pooler"` + Patroni PatroniConfiguration `json:"patroni"` MinInstances int32 `json:"min_instances,omitempty"` MaxInstances int32 `json:"max_instances,omitempty"` IgnoreInstanceLimitsAnnotationKey string `json:"ignore_instance_limits_annotation_key,omitempty"` } -//Duration shortens this frequently used name +// Duration shortens this frequently used name type Duration time.Duration diff --git a/pkg/apis/acid.zalan.do/v1/postgresql_type.go b/pkg/apis/acid.zalan.do/v1/postgresql_type.go index e46a43636..dde47622c 100644 --- a/pkg/apis/acid.zalan.do/v1/postgresql_type.go +++ b/pkg/apis/acid.zalan.do/v1/postgresql_type.go @@ -171,6 +171,7 @@ type Patroni struct { SynchronousMode bool `json:"synchronous_mode,omitempty"` SynchronousModeStrict bool `json:"synchronous_mode_strict,omitempty"` SynchronousNodeCount uint32 `json:"synchronous_node_count,omitempty" defaults:"1"` + FailsafeMode *bool `json:"failsafe_mode,omitempty"` } // StandbyDescription contains remote primary config or s3/gs wal path diff --git a/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go b/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go index b338421a2..2cae5743e 100644 --- a/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go +++ b/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go @@ -423,6 +423,7 @@ func (in *OperatorConfigurationData) DeepCopyInto(out *OperatorConfigurationData out.Scalyr = in.Scalyr out.LogicalBackup = in.LogicalBackup in.ConnectionPooler.DeepCopyInto(&out.ConnectionPooler) + in.Patroni.DeepCopyInto(&out.Patroni) return } @@ -549,6 +550,11 @@ func (in *Patroni) DeepCopyInto(out *Patroni) { (*out)[key] = outVal } } + if in.FailsafeMode != nil { + in, out := &in.FailsafeMode, &out.FailsafeMode + *out = new(bool) + **out = **in + } return } @@ -562,6 +568,27 @@ func (in *Patroni) DeepCopy() *Patroni { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PatroniConfiguration) DeepCopyInto(out *PatroniConfiguration) { + *out = *in + if in.FailsafeMode != nil { + in, out := &in.FailsafeMode, &out.FailsafeMode + *out = new(bool) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PatroniConfiguration. +func (in *PatroniConfiguration) DeepCopy() *PatroniConfiguration { + if in == nil { + return nil + } + out := new(PatroniConfiguration) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PostgresPodResourcesDefaults) DeepCopyInto(out *PostgresPodResourcesDefaults) { *out = *in diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index aa3229848..73f4090d8 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -59,6 +59,7 @@ type patroniDCS struct { SynchronousNodeCount uint32 `json:"synchronous_node_count,omitempty"` PGBootstrapConfiguration map[string]interface{} `json:"postgresql,omitempty"` Slots map[string]map[string]string `json:"slots,omitempty"` + FailsafeMode *bool `json:"failsafe_mode,omitempty"` } type pgBootstrap struct { @@ -296,7 +297,7 @@ func (c *Cluster) generateResourceRequirements( return &result, nil } -func generateSpiloJSONConfiguration(pg *acidv1.PostgresqlParam, patroni *acidv1.Patroni, pamRoleName string, EnablePgVersionEnvVar bool, logger *logrus.Entry) (string, error) { +func generateSpiloJSONConfiguration(pg *acidv1.PostgresqlParam, patroni *acidv1.Patroni, opConfig *config.Config, logger *logrus.Entry) (string, error) { config := spiloConfiguration{} config.Bootstrap = pgBootstrap{} @@ -378,6 +379,11 @@ PatroniInitDBParams: if patroni.SynchronousNodeCount >= 1 { config.Bootstrap.DCS.SynchronousNodeCount = patroni.SynchronousNodeCount } + if patroni.FailsafeMode != nil { + config.Bootstrap.DCS.FailsafeMode = patroni.FailsafeMode + } else if opConfig.EnablePatroniFailsafeMode != nil { + config.Bootstrap.DCS.FailsafeMode = opConfig.EnablePatroniFailsafeMode + } config.PgLocalConfiguration = make(map[string]interface{}) @@ -385,7 +391,7 @@ PatroniInitDBParams: // setting postgresq.bin_dir in the SPILO_CONFIGURATION still works and takes precedence over PGVERSION // so we add postgresq.bin_dir only if PGVERSION is unused // see PR 222 in Spilo - if !EnablePgVersionEnvVar { + if !opConfig.EnablePgVersionEnvVar { config.PgLocalConfiguration[patroniPGBinariesParameterName] = fmt.Sprintf(pgBinariesLocationTemplate, pg.PgVersion) } if len(pg.Parameters) > 0 { @@ -407,7 +413,7 @@ PatroniInitDBParams: } config.Bootstrap.Users = map[string]pgUser{ - pamRoleName: { + opConfig.PamRoleName: { Password: "", Options: []string{constants.RoleFlagCreateDB, constants.RoleFlagNoLogin}, }, @@ -1179,7 +1185,7 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef } } - spiloConfiguration, err := generateSpiloJSONConfiguration(&spec.PostgresqlParam, &spec.Patroni, c.OpConfig.PamRoleName, c.OpConfig.EnablePgVersionEnvVar, c.logger) + spiloConfiguration, err := generateSpiloJSONConfiguration(&spec.PostgresqlParam, &spec.Patroni, &c.OpConfig, c.logger) if err != nil { return nil, fmt.Errorf("could not generate Spilo JSON configuration: %v", err) } diff --git a/pkg/cluster/k8sres_test.go b/pkg/cluster/k8sres_test.go index 797c1426c..4781bfabc 100644 --- a/pkg/cluster/k8sres_test.go +++ b/pkg/cluster/k8sres_test.go @@ -69,17 +69,19 @@ func TestGenerateSpiloJSONConfiguration(t *testing.T) { subtest string pgParam *acidv1.PostgresqlParam patroni *acidv1.Patroni - role string - opConfig config.Config + opConfig *config.Config result string }{ { - subtest: "Patroni default configuration", - pgParam: &acidv1.PostgresqlParam{PgVersion: "9.6"}, - patroni: &acidv1.Patroni{}, - role: "zalandos", - opConfig: config.Config{}, - result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/9.6/bin"},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"}],"users":{"zalandos":{"password":"","options":["CREATEDB","NOLOGIN"]}},"dcs":{}}}`, + subtest: "Patroni default configuration", + pgParam: &acidv1.PostgresqlParam{PgVersion: "9.6"}, + patroni: &acidv1.Patroni{}, + opConfig: &config.Config{ + Auth: config.Auth{ + PamRoleName: "zalandos", + }, + }, + result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/9.6/bin"},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"}],"users":{"zalandos":{"password":"","options":["CREATEDB","NOLOGIN"]}},"dcs":{}}}`, }, { subtest: "Patroni configured", @@ -99,21 +101,65 @@ func TestGenerateSpiloJSONConfiguration(t *testing.T) { SynchronousModeStrict: true, SynchronousNodeCount: 1, Slots: map[string]map[string]string{"permanent_logical_1": {"type": "logical", "database": "foo", "plugin": "pgoutput"}}, + FailsafeMode: util.True(), }, - role: "zalandos", - opConfig: config.Config{}, - result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/11/bin","pg_hba":["hostssl all all 0.0.0.0/0 md5","host all all 0.0.0.0/0 md5"]},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"},"data-checksums",{"encoding":"UTF8"},{"locale":"en_US.UTF-8"}],"users":{"zalandos":{"password":"","options":["CREATEDB","NOLOGIN"]}},"dcs":{"ttl":30,"loop_wait":10,"retry_timeout":10,"maximum_lag_on_failover":33554432,"synchronous_mode":true,"synchronous_mode_strict":true,"synchronous_node_count":1,"slots":{"permanent_logical_1":{"database":"foo","plugin":"pgoutput","type":"logical"}}}}}`, + opConfig: &config.Config{ + Auth: config.Auth{ + PamRoleName: "zalandos", + }, + }, + result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/11/bin","pg_hba":["hostssl all all 0.0.0.0/0 md5","host all all 0.0.0.0/0 md5"]},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"},"data-checksums",{"encoding":"UTF8"},{"locale":"en_US.UTF-8"}],"users":{"zalandos":{"password":"","options":["CREATEDB","NOLOGIN"]}},"dcs":{"ttl":30,"loop_wait":10,"retry_timeout":10,"maximum_lag_on_failover":33554432,"synchronous_mode":true,"synchronous_mode_strict":true,"synchronous_node_count":1,"slots":{"permanent_logical_1":{"database":"foo","plugin":"pgoutput","type":"logical"}},"failsafe_mode":true}}}`, + }, + { + subtest: "Patroni failsafe_mode configured globally", + pgParam: &acidv1.PostgresqlParam{PgVersion: "14"}, + patroni: &acidv1.Patroni{}, + opConfig: &config.Config{ + Auth: config.Auth{ + PamRoleName: "zalandos", + }, + EnablePatroniFailsafeMode: util.True(), + }, + result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/14/bin"},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"}],"users":{"zalandos":{"password":"","options":["CREATEDB","NOLOGIN"]}},"dcs":{"failsafe_mode":true}}}`, + }, + { + subtest: "Patroni failsafe_mode configured globally, disabled for cluster", + pgParam: &acidv1.PostgresqlParam{PgVersion: "14"}, + patroni: &acidv1.Patroni{ + FailsafeMode: util.False(), + }, + opConfig: &config.Config{ + Auth: config.Auth{ + PamRoleName: "zalandos", + }, + EnablePatroniFailsafeMode: util.True(), + }, + result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/14/bin"},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"}],"users":{"zalandos":{"password":"","options":["CREATEDB","NOLOGIN"]}},"dcs":{"failsafe_mode":false}}}`, + }, + { + subtest: "Patroni failsafe_mode disabled globally, configured for cluster", + pgParam: &acidv1.PostgresqlParam{PgVersion: "14"}, + patroni: &acidv1.Patroni{ + FailsafeMode: util.True(), + }, + opConfig: &config.Config{ + Auth: config.Auth{ + PamRoleName: "zalandos", + }, + EnablePatroniFailsafeMode: util.False(), + }, + result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/14/bin"},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"}],"users":{"zalandos":{"password":"","options":["CREATEDB","NOLOGIN"]}},"dcs":{"failsafe_mode":true}}}`, }, } for _, tt := range tests { - cluster.OpConfig = tt.opConfig - result, err := generateSpiloJSONConfiguration(tt.pgParam, tt.patroni, tt.role, false, logger) + cluster.OpConfig = *tt.opConfig + result, err := generateSpiloJSONConfiguration(tt.pgParam, tt.patroni, tt.opConfig, logger) if err != nil { t.Errorf("Unexpected error: %v", err) } if tt.result != result { t.Errorf("%s %s: Spilo Config is %v, expected %v for role %#v and param %#v", - testName, tt.subtest, result, tt.result, tt.role, tt.pgParam) + testName, tt.subtest, result, tt.result, tt.opConfig.Auth.PamRoleName, tt.pgParam) } } } diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index ed1511311..76c9fd12a 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -564,6 +564,21 @@ func (c *Cluster) checkAndSetGlobalPostgreSQLConfiguration(pod *v1.Pod, effectiv configToSet["ttl"] = desiredPatroniConfig.TTL } + var desiredFailsafe *bool + if desiredPatroniConfig.FailsafeMode != nil { + desiredFailsafe = desiredPatroniConfig.FailsafeMode + } else if c.OpConfig.EnablePatroniFailsafeMode != nil { + desiredFailsafe = c.OpConfig.EnablePatroniFailsafeMode + } + + effectiveFailsafe := effectivePatroniConfig.FailsafeMode + + if desiredFailsafe != nil { + if effectiveFailsafe == nil || *desiredFailsafe != *effectiveFailsafe { + configToSet["failsafe_mode"] = *desiredFailsafe + } + } + // check if specified slots exist in config and if they differ slotsToSet := make(map[string]map[string]string) for slotName, desiredSlot := range desiredPatroniConfig.Slots { diff --git a/pkg/cluster/sync_test.go b/pkg/cluster/sync_test.go index 785a7cca2..4d50b791f 100644 --- a/pkg/cluster/sync_test.go +++ b/pkg/cluster/sync_test.go @@ -20,6 +20,7 @@ import ( acidv1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" fakeacidv1 "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/fake" "github.com/zalando/postgres-operator/pkg/spec" + "github.com/zalando/postgres-operator/pkg/util" "github.com/zalando/postgres-operator/pkg/util/config" "github.com/zalando/postgres-operator/pkg/util/k8sutil" "github.com/zalando/postgres-operator/pkg/util/patroni" @@ -147,20 +148,23 @@ func TestCheckAndSetGlobalPostgreSQLConfiguration(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() + defaultPgParameters := map[string]string{ + "log_min_duration_statement": "200", + "max_connections": "50", + } + defaultPatroniParameters := acidv1.Patroni{ + TTL: 20, + } + pg := acidv1.Postgresql{ ObjectMeta: metav1.ObjectMeta{ Name: clusterName, Namespace: namespace, }, Spec: acidv1.PostgresSpec{ - Patroni: acidv1.Patroni{ - TTL: 20, - }, + Patroni: defaultPatroniParameters, PostgresqlParam: acidv1.PostgresqlParam{ - Parameters: map[string]string{ - "log_min_duration_statement": "200", - "max_connections": "50", - }, + Parameters: defaultPgParameters, }, Volume: acidv1.Volume{ Size: "1Gi", @@ -222,9 +226,7 @@ func TestCheckAndSetGlobalPostgreSQLConfiguration(t *testing.T) { }, { subtest: "multiple Postgresql.Parameters differ - restart replica first", - patroni: acidv1.Patroni{ - TTL: 20, - }, + patroni: defaultPatroniParameters, pgParams: map[string]string{ "log_min_duration_statement": "500", // desired 200 "max_connections": "100", // desired 50 @@ -233,9 +235,7 @@ func TestCheckAndSetGlobalPostgreSQLConfiguration(t *testing.T) { }, { subtest: "desired max_connections bigger - restart replica first", - patroni: acidv1.Patroni{ - TTL: 20, - }, + patroni: defaultPatroniParameters, pgParams: map[string]string{ "log_min_duration_statement": "200", "max_connections": "30", // desired 50 @@ -244,9 +244,7 @@ func TestCheckAndSetGlobalPostgreSQLConfiguration(t *testing.T) { }, { subtest: "desired max_connections smaller - restart master first", - patroni: acidv1.Patroni{ - TTL: 20, - }, + patroni: defaultPatroniParameters, pgParams: map[string]string{ "log_min_duration_statement": "200", "max_connections": "100", // desired 50 @@ -265,6 +263,109 @@ func TestCheckAndSetGlobalPostgreSQLConfiguration(t *testing.T) { t.Errorf("%s - %s: wrong master restart strategy, got restart %v, expected restart %v", testName, tt.subtest, requirePrimaryRestart, tt.restartPrimary) } } + + testsFailsafe := []struct { + subtest string + operatorVal *bool + effectiveVal *bool + desiredVal bool + shouldBePatched bool + restartPrimary bool + }{ + { + subtest: "Not set in operator config, not set for pg cluster. Set to true in the pg config.", + operatorVal: nil, + effectiveVal: nil, + desiredVal: true, + shouldBePatched: true, + restartPrimary: false, + }, + { + subtest: "Not set in operator config, disabled for pg cluster. Set to true in the pg config.", + operatorVal: nil, + effectiveVal: util.False(), + desiredVal: true, + shouldBePatched: true, + restartPrimary: false, + }, + { + subtest: "Not set in operator config, not set for pg cluster. Set to false in the pg config.", + operatorVal: nil, + effectiveVal: nil, + desiredVal: false, + shouldBePatched: true, + restartPrimary: false, + }, + { + subtest: "Not set in operator config, enabled for pg cluster. Set to false in the pg config.", + operatorVal: nil, + effectiveVal: util.True(), + desiredVal: false, + shouldBePatched: true, + restartPrimary: false, + }, + { + subtest: "Enabled in operator config, not set for pg cluster. Set to false in the pg config.", + operatorVal: util.True(), + effectiveVal: nil, + desiredVal: false, + shouldBePatched: true, + restartPrimary: false, + }, + { + subtest: "Enabled in operator config, disabled for pg cluster. Set to true in the pg config.", + operatorVal: util.True(), + effectiveVal: util.False(), + desiredVal: true, + shouldBePatched: true, + restartPrimary: false, + }, + { + subtest: "Disabled in operator config, not set for pg cluster. Set to true in the pg config.", + operatorVal: util.False(), + effectiveVal: nil, + desiredVal: true, + shouldBePatched: true, + restartPrimary: false, + }, + { + subtest: "Disabled in operator config, enabled for pg cluster. Set to false in the pg config.", + operatorVal: util.False(), + effectiveVal: util.True(), + desiredVal: false, + shouldBePatched: true, + restartPrimary: false, + }, + { + subtest: "Disabled in operator config, enabled for pg cluster. Set to true in the pg config.", + operatorVal: util.False(), + effectiveVal: util.True(), + desiredVal: true, + shouldBePatched: false, // should not require patching + restartPrimary: true, + }, + } + + for _, tt := range testsFailsafe { + patroniConf := defaultPatroniParameters + + if tt.operatorVal != nil { + cluster.OpConfig.EnablePatroniFailsafeMode = tt.operatorVal + } + if tt.effectiveVal != nil { + patroniConf.FailsafeMode = tt.effectiveVal + } + cluster.Spec.Patroni.FailsafeMode = &tt.desiredVal + + configPatched, requirePrimaryRestart, err := cluster.checkAndSetGlobalPostgreSQLConfiguration(mockPod, patroniConf, cluster.Spec.Patroni, defaultPgParameters, cluster.Spec.Parameters) + assert.NoError(t, err) + if configPatched != tt.shouldBePatched { + t.Errorf("%s - %s: expected update went wrong", testName, tt.subtest) + } + if requirePrimaryRestart != tt.restartPrimary { + t.Errorf("%s - %s: wrong master restart strategy, got restart %v, expected restart %v", testName, tt.subtest, requirePrimaryRestart, tt.restartPrimary) + } + } } func TestUpdateSecret(t *testing.T) { diff --git a/pkg/controller/operator_config.go b/pkg/controller/operator_config.go index 57c3c7110..03afee358 100644 --- a/pkg/controller/operator_config.go +++ b/pkg/controller/operator_config.go @@ -216,6 +216,9 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur result.ScalyrCPULimit = fromCRD.Scalyr.ScalyrCPULimit result.ScalyrMemoryLimit = fromCRD.Scalyr.ScalyrMemoryLimit + // Patroni config + result.EnablePatroniFailsafeMode = util.CoalesceBool(fromCRD.Patroni.FailsafeMode, util.False()) + // Connection pooler. Looks like we can't use defaulting in CRD before 1.17, // so ensure default values here. result.ConnectionPooler.NumberOfInstances = util.CoalesceInt32( diff --git a/pkg/generated/clientset/versioned/fake/register.go b/pkg/generated/clientset/versioned/fake/register.go index 313eeacc2..a5d94de6b 100644 --- a/pkg/generated/clientset/versioned/fake/register.go +++ b/pkg/generated/clientset/versioned/fake/register.go @@ -45,14 +45,14 @@ var localSchemeBuilder = runtime.SchemeBuilder{ // AddToScheme adds all types of this clientset into the given scheme. This allows composition // of clientsets, like in: // -// import ( -// "k8s.io/client-go/kubernetes" -// clientsetscheme "k8s.io/client-go/kubernetes/scheme" -// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" -// ) +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) // -// kclientset, _ := kubernetes.NewForConfig(c) -// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) // // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types // correctly. diff --git a/pkg/generated/clientset/versioned/scheme/register.go b/pkg/generated/clientset/versioned/scheme/register.go index 823909bcb..96ab81670 100644 --- a/pkg/generated/clientset/versioned/scheme/register.go +++ b/pkg/generated/clientset/versioned/scheme/register.go @@ -45,14 +45,14 @@ var localSchemeBuilder = runtime.SchemeBuilder{ // AddToScheme adds all types of this clientset into the given scheme. This allows composition // of clientsets, like in: // -// import ( -// "k8s.io/client-go/kubernetes" -// clientsetscheme "k8s.io/client-go/kubernetes/scheme" -// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" -// ) +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) // -// kclientset, _ := kubernetes.NewForConfig(c) -// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) // // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types // correctly. diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index 8e51dd020..234486c24 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -234,6 +234,7 @@ type Config struct { TargetMajorVersion string `name:"target_major_version" default:"14"` PatroniAPICheckInterval time.Duration `name:"patroni_api_check_interval" default:"1s"` PatroniAPICheckTimeout time.Duration `name:"patroni_api_check_timeout" default:"5s"` + EnablePatroniFailsafeMode *bool `name:"enable_patroni_failsafe_mode" default:"false"` } // MustMarshal marshals the config or panics From 0bef3b325f54ed648b569d09bf33e81fb44eeabb Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Fri, 9 Dec 2022 12:42:10 +0100 Subject: [PATCH 41/77] fix migration of single-node clusters (#2134) --- pkg/cluster/pod.go | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/pkg/cluster/pod.go b/pkg/cluster/pod.go index 73f077058..098fdc057 100644 --- a/pkg/cluster/pod.go +++ b/pkg/cluster/pod.go @@ -214,19 +214,16 @@ func (c *Cluster) movePodFromEndOfLifeNode(pod *v1.Pod) (*v1.Pod, error) { // MigrateMasterPod migrates master pod via failover to a replica func (c *Cluster) MigrateMasterPod(podName spec.NamespacedName) error { var ( - masterCandidateName spec.NamespacedName - err error - eol bool + err error + eol bool ) oldMaster, err := c.KubeClient.Pods(podName.Namespace).Get(context.TODO(), podName.Name, metav1.GetOptions{}) - if err != nil { - return fmt.Errorf("could not get pod: %v", err) + return fmt.Errorf("could not get master pod: %v", err) } c.logger.Infof("starting process to migrate master pod %q", podName) - if eol, err = c.podIsEndOfLife(oldMaster); err != nil { return fmt.Errorf("could not get node %q: %v", oldMaster.Spec.NodeName, err) } @@ -250,21 +247,21 @@ func (c *Cluster) MigrateMasterPod(podName spec.NamespacedName) error { } c.Statefulset = sset } - // We may not have a cached statefulset if the initial cluster sync has aborted, revert to the spec in that case. + // we may not have a cached statefulset if the initial cluster sync has aborted, revert to the spec in that case + masterCandidateName := podName + masterCandidatePod := oldMaster if *c.Statefulset.Spec.Replicas > 1 { if masterCandidateName, err = c.getSwitchoverCandidate(oldMaster); err != nil { return fmt.Errorf("could not find suitable replica pod as candidate for failover: %v", err) } + masterCandidatePod, err = c.KubeClient.Pods(masterCandidateName.Namespace).Get(context.TODO(), masterCandidateName.Name, metav1.GetOptions{}) + if err != nil { + return fmt.Errorf("could not get master candidate pod: %v", err) + } } else { c.logger.Warningf("migrating single pod cluster %q, this will cause downtime of the Postgres cluster until pod is back", c.clusterName()) } - masterCandidatePod, err := c.KubeClient.Pods(masterCandidateName.Namespace).Get(context.TODO(), masterCandidateName.Name, metav1.GetOptions{}) - - if err != nil { - return fmt.Errorf("could not get master candidate pod: %v", err) - } - // there are two cases for each postgres cluster that has its master pod on the node to migrate from: // - the cluster has some replicas - migrate one of those if necessary and failover to it // - there are no replicas - just terminate the master and wait until it respawns From 81e17e4af1e114e2da82224cbaba323a7f4b8381 Mon Sep 17 00:00:00 2001 From: Dmitry Volodin Date: Fri, 9 Dec 2022 17:33:06 +0300 Subject: [PATCH 42/77] Remove namespace and fix errors in example manifests files (#2131) * Remove namespace and fix errors in example manifests files * Comment required section in default configuration --- manifests/e2e-storage-class.yaml | 1 - manifests/fake-teams-api.yaml | 2 -- manifests/infrastructure-roles-new.yaml | 1 - manifests/infrastructure-roles.yaml | 1 - manifests/minimal-postgres-manifest-12.yaml | 1 - manifests/minimal-postgres-manifest.yaml | 1 - manifests/platform-credentials.yaml | 1 - manifests/postgresql-operator-default-configuration.yaml | 2 +- manifests/standby-manifest.yaml | 1 - 9 files changed, 1 insertion(+), 10 deletions(-) diff --git a/manifests/e2e-storage-class.yaml b/manifests/e2e-storage-class.yaml index c8d941341..3c70c4020 100644 --- a/manifests/e2e-storage-class.yaml +++ b/manifests/e2e-storage-class.yaml @@ -1,7 +1,6 @@ apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: - namespace: kube-system name: standard annotations: storageclass.kubernetes.io/is-default-class: "true" diff --git a/manifests/fake-teams-api.yaml b/manifests/fake-teams-api.yaml index 5214c58ba..33f7c6013 100644 --- a/manifests/fake-teams-api.yaml +++ b/manifests/fake-teams-api.yaml @@ -40,8 +40,6 @@ metadata: name: postgresql-operator namespace: default type: Opaque -data: -apiVersion: v1 data: read-only-token-secret: dGVzdHRva2Vu read-only-token-type: QmVhcmVy diff --git a/manifests/infrastructure-roles-new.yaml b/manifests/infrastructure-roles-new.yaml index 64b854c6a..d6b5fa9ad 100644 --- a/manifests/infrastructure-roles-new.yaml +++ b/manifests/infrastructure-roles-new.yaml @@ -8,5 +8,4 @@ data: kind: Secret metadata: name: postgresql-infrastructure-roles-new - namespace: default type: Opaque diff --git a/manifests/infrastructure-roles.yaml b/manifests/infrastructure-roles.yaml index c66d79139..975a02827 100644 --- a/manifests/infrastructure-roles.yaml +++ b/manifests/infrastructure-roles.yaml @@ -21,5 +21,4 @@ data: kind: Secret metadata: name: postgresql-infrastructure-roles - namespace: default type: Opaque diff --git a/manifests/minimal-postgres-manifest-12.yaml b/manifests/minimal-postgres-manifest-12.yaml index 3f89b765d..d578ac46d 100644 --- a/manifests/minimal-postgres-manifest-12.yaml +++ b/manifests/minimal-postgres-manifest-12.yaml @@ -2,7 +2,6 @@ apiVersion: "acid.zalan.do/v1" kind: postgresql metadata: name: acid-upgrade-test - namespace: default spec: teamId: "acid" volume: diff --git a/manifests/minimal-postgres-manifest.yaml b/manifests/minimal-postgres-manifest.yaml index f0c5ff4b5..b2a8fa2c8 100644 --- a/manifests/minimal-postgres-manifest.yaml +++ b/manifests/minimal-postgres-manifest.yaml @@ -2,7 +2,6 @@ apiVersion: "acid.zalan.do/v1" kind: postgresql metadata: name: acid-minimal-cluster - namespace: default spec: teamId: "acid" volume: diff --git a/manifests/platform-credentials.yaml b/manifests/platform-credentials.yaml index 0a320b838..44ecf7f24 100644 --- a/manifests/platform-credentials.yaml +++ b/manifests/platform-credentials.yaml @@ -2,7 +2,6 @@ apiVersion: "zalando.org/v1" kind: PlatformCredentialsSet metadata: name: postgresql-operator - namespace: acid spec: application: postgresql-operator tokens: diff --git a/manifests/postgresql-operator-default-configuration.yaml b/manifests/postgresql-operator-default-configuration.yaml index e02ef8bae..3f925f032 100644 --- a/manifests/postgresql-operator-default-configuration.yaml +++ b/manifests/postgresql-operator-default-configuration.yaml @@ -198,5 +198,5 @@ configuration: connection_pooler_number_of_instances: 2 # connection_pooler_schema: "pooler" # connection_pooler_user: "pooler" - patroni: + # patroni: # failsafe_mode: "false" diff --git a/manifests/standby-manifest.yaml b/manifests/standby-manifest.yaml index 66f515518..ac94fe897 100644 --- a/manifests/standby-manifest.yaml +++ b/manifests/standby-manifest.yaml @@ -2,7 +2,6 @@ apiVersion: "acid.zalan.do/v1" kind: postgresql metadata: name: acid-standby-cluster - namespace: default spec: teamId: "acid" volume: From 3e148ea57e25e02aa598bbda6d5bef8488ef3eba Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Thu, 15 Dec 2022 12:17:27 +0100 Subject: [PATCH 43/77] enable operator support for pg15 and drop support for 9.5 and 9.6 (#2140) * enable operator support for pg15 and drop support for 9.5 and 9.6 * not offer 15 in UI before spilo-15 is available --- README.md | 4 +-- .../crds/operatorconfigurations.yaml | 2 +- .../postgres-operator/crds/postgresqls.yaml | 3 +- charts/postgres-operator/values.yaml | 2 +- docker/logical-backup/Dockerfile | 3 +- docs/administrator.md | 2 +- docs/reference/cluster_manifest.md | 4 +-- docs/reference/operator_parameters.md | 2 +- docs/user.md | 6 ++-- manifests/configmap.yaml | 2 +- manifests/operatorconfiguration.crd.yaml | 2 +- ...gresql-operator-default-configuration.yaml | 2 +- manifests/postgresql.crd.yaml | 3 +- pkg/apis/acid.zalan.do/v1/crds.go | 9 ++---- .../v1/operator_configuration_type.go | 2 +- pkg/apis/acid.zalan.do/v1/util_test.go | 10 +++---- pkg/cluster/k8sres_test.go | 30 +++++++++---------- pkg/cluster/majorversionupgrade.go | 15 +++++----- pkg/controller/operator_config.go | 2 +- pkg/util/config/config.go | 2 +- ui/manifests/deployment.yaml | 1 + ui/operator_ui/main.py | 2 +- ui/operator_ui/spiloutils.py | 2 +- ui/run_local.sh | 1 + 24 files changed, 54 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index 25839e965..99be3845a 100644 --- a/README.md +++ b/README.md @@ -29,13 +29,13 @@ pipelines with no access to Kubernetes API directly, promoting infrastructure as ### PostgreSQL features -* Supports PostgreSQL 14, starting from 9.6+ +* Supports PostgreSQL 14, starting from 10+ * Streaming replication cluster via Patroni * Point-In-Time-Recovery with [pg_basebackup](https://www.postgresql.org/docs/11/app-pgbasebackup.html) / [WAL-E](https://github.com/wal-e/wal-e) via [Spilo](https://github.com/zalando/spilo) * Preload libraries: [bg_mon](https://github.com/CyberDem0n/bg_mon), -[pg_stat_statements](https://www.postgresql.org/docs/14/pgstatstatements.html), +[pg_stat_statements](https://www.postgresql.org/docs/15/pgstatstatements.html), [pgextwlist](https://github.com/dimitri/pgextwlist), [pg_auth_mon](https://github.com/RafiaSabih/pg_auth_mon) * Incl. popular Postgres extensions such as diff --git a/charts/postgres-operator/crds/operatorconfigurations.yaml b/charts/postgres-operator/crds/operatorconfigurations.yaml index e6305fada..472f15720 100644 --- a/charts/postgres-operator/crds/operatorconfigurations.yaml +++ b/charts/postgres-operator/crds/operatorconfigurations.yaml @@ -167,7 +167,7 @@ spec: type: string minimal_major_version: type: string - default: "9.6" + default: "11" target_major_version: type: string default: "14" diff --git a/charts/postgres-operator/crds/postgresqls.yaml b/charts/postgres-operator/crds/postgresqls.yaml index d78bdb2fb..9d56a6ee3 100644 --- a/charts/postgres-operator/crds/postgresqls.yaml +++ b/charts/postgres-operator/crds/postgresqls.yaml @@ -367,13 +367,12 @@ spec: version: type: string enum: - - "9.5" - - "9.6" - "10" - "11" - "12" - "13" - "14" + - "15" parameters: type: object additionalProperties: diff --git a/charts/postgres-operator/values.yaml b/charts/postgres-operator/values.yaml index 1ca429a99..37dc9c2e4 100644 --- a/charts/postgres-operator/values.yaml +++ b/charts/postgres-operator/values.yaml @@ -89,7 +89,7 @@ configMajorVersionUpgrade: # - acid # minimal Postgres major version that will not automatically be upgraded - minimal_major_version: "9.6" + minimal_major_version: "11" # target Postgres major version when upgrading clusters automatically target_major_version: "14" diff --git a/docker/logical-backup/Dockerfile b/docker/logical-backup/Dockerfile index 5c1ee6e39..184e3be1c 100644 --- a/docker/logical-backup/Dockerfile +++ b/docker/logical-backup/Dockerfile @@ -23,13 +23,12 @@ RUN apt-get update \ && curl --silent https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - \ && apt-get update \ && apt-get install --no-install-recommends -y \ + postgresql-client-15 \ postgresql-client-14 \ postgresql-client-13 \ postgresql-client-12 \ postgresql-client-11 \ postgresql-client-10 \ - postgresql-client-9.6 \ - postgresql-client-9.5 \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* diff --git a/docs/administrator.md b/docs/administrator.md index 373e691a8..1a0f730b8 100644 --- a/docs/administrator.md +++ b/docs/administrator.md @@ -1111,7 +1111,7 @@ aws_or_gcp: If cluster members have to be (re)initialized restoring physical backups happens automatically either from the backup location or by running -[pg_basebackup](https://www.postgresql.org/docs/13/app-pgbasebackup.html) +[pg_basebackup](https://www.postgresql.org/docs/15/app-pgbasebackup.html) on one of the other running instances (preferably replicas if they do not lag behind). You can test restoring backups by [cloning](user.md#how-to-clone-an-existing-postgresql-cluster) clusters. diff --git a/docs/reference/cluster_manifest.md b/docs/reference/cluster_manifest.md index 718479d50..321a70d0b 100644 --- a/docs/reference/cluster_manifest.md +++ b/docs/reference/cluster_manifest.md @@ -558,7 +558,7 @@ Those parameters are grouped under the `tls` top-level key. ## Change data capture streams This sections enables change data capture (CDC) streams via Postgres' -[logical decoding](https://www.postgresql.org/docs/14/logicaldecoding.html) +[logical decoding](https://www.postgresql.org/docs/15/logicaldecoding.html) feature and `pgoutput` plugin. While the Postgres operator takes responsibility for providing the setup to publish change events, it relies on external tools to consume them. At Zalando, we are using a workflow based on @@ -590,7 +590,7 @@ can have the following properties: and `payloadColumn`). The CDC operator is following the [outbox pattern](https://debezium.io/blog/2019/02/19/reliable-microservices-data-exchange-with-the-outbox-pattern/). The application is responsible for putting events into a (JSON/B or VARCHAR) payload column of the outbox table in the structure of the specified target - event type. The operator will create a [PUBLICATION](https://www.postgresql.org/docs/14/logical-replication-publication.html) + event type. The operator will create a [PUBLICATION](https://www.postgresql.org/docs/15/logical-replication-publication.html) in Postgres for all tables specified for one `database` and `applicationId`. The CDC operator will consume from it shortly after transactions are committed to the outbox table. The `idColumn` will be used in telemetry for diff --git a/docs/reference/operator_parameters.md b/docs/reference/operator_parameters.md index 625d5699c..763f9da75 100644 --- a/docs/reference/operator_parameters.md +++ b/docs/reference/operator_parameters.md @@ -246,7 +246,7 @@ CRD-configuration, they are grouped under the `major_version_upgrade` key. * **minimal_major_version** The minimal Postgres major version that will not automatically be upgraded - when `major_version_upgrade_mode` is set to `"full"`. The default is `"9.6"`. + when `major_version_upgrade_mode` is set to `"full"`. The default is `"11"`. * **target_major_version** The target Postgres major version when upgrading clusters automatically diff --git a/docs/user.md b/docs/user.md index fc9099234..a80d984f6 100644 --- a/docs/user.md +++ b/docs/user.md @@ -517,7 +517,7 @@ Postgres Operator will create the following NOLOGIN roles: The `_owner` role is the database owner and should be used when creating new database objects. All members of the `admin` role, e.g. teams API roles, can -become the owner with the `SET ROLE` command. [Default privileges](https://www.postgresql.org/docs/12/sql-alterdefaultprivileges.html) +become the owner with the `SET ROLE` command. [Default privileges](https://www.postgresql.org/docs/15/sql-alterdefaultprivileges.html) are configured for the owner role so that the `_reader` role automatically gets read-access (SELECT) to new tables and sequences and the `_writer` receives write-access (INSERT, UPDATE, DELETE on tables, @@ -592,7 +592,7 @@ spec: ### Schema `search_path` for default roles -The schema [`search_path`](https://www.postgresql.org/docs/13/ddl-schemas.html#DDL-SCHEMAS-PATH) +The schema [`search_path`](https://www.postgresql.org/docs/15/ddl-schemas.html#DDL-SCHEMAS-PATH) for each role will include the role name and the schemas, this role should have access to. So `foo_bar_writer` does not have to schema-qualify tables from schemas `foo_bar_writer, bar`, while `foo_writer` can look up `foo_writer` and @@ -812,7 +812,7 @@ spec: ### Clone directly Another way to get a fresh copy of your source DB cluster is via -[pg_basebackup](https://www.postgresql.org/docs/13/app-pgbasebackup.html). To +[pg_basebackup](https://www.postgresql.org/docs/15/app-pgbasebackup.html). To use this feature simply leave out the timestamp field from the clone section. The operator will connect to the service of the source cluster by name. If the cluster is called test, then the connection string will look like host=test diff --git a/manifests/configmap.yaml b/manifests/configmap.yaml index 6eb391ba4..aad09ca02 100644 --- a/manifests/configmap.yaml +++ b/manifests/configmap.yaml @@ -97,7 +97,7 @@ data: # max_memory_request: 4Gi # min_cpu_limit: 250m # min_memory_limit: 250Mi - # minimal_major_version: "9.6" + # minimal_major_version: "11" # node_readiness_label: "status:ready" # node_readiness_label_merge: "OR" # oauth_token_secret_name: postgresql-operator diff --git a/manifests/operatorconfiguration.crd.yaml b/manifests/operatorconfiguration.crd.yaml index 2be301c84..4402602a3 100644 --- a/manifests/operatorconfiguration.crd.yaml +++ b/manifests/operatorconfiguration.crd.yaml @@ -165,7 +165,7 @@ spec: type: string minimal_major_version: type: string - default: "9.6" + default: "11" target_major_version: type: string default: "14" diff --git a/manifests/postgresql-operator-default-configuration.yaml b/manifests/postgresql-operator-default-configuration.yaml index 3f925f032..aba77e848 100644 --- a/manifests/postgresql-operator-default-configuration.yaml +++ b/manifests/postgresql-operator-default-configuration.yaml @@ -39,7 +39,7 @@ configuration: major_version_upgrade_mode: "off" # major_version_upgrade_team_allow_list: # - acid - minimal_major_version: "9.6" + minimal_major_version: "11" target_major_version: "14" kubernetes: # additional_pod_capabilities: diff --git a/manifests/postgresql.crd.yaml b/manifests/postgresql.crd.yaml index ae92a756a..b2178a246 100644 --- a/manifests/postgresql.crd.yaml +++ b/manifests/postgresql.crd.yaml @@ -365,13 +365,12 @@ spec: version: type: string enum: - - "9.5" - - "9.6" - "10" - "11" - "12" - "13" - "14" + - "15" parameters: type: object additionalProperties: diff --git a/pkg/apis/acid.zalan.do/v1/crds.go b/pkg/apis/acid.zalan.do/v1/crds.go index cbff1e3ad..f9af7eae5 100644 --- a/pkg/apis/acid.zalan.do/v1/crds.go +++ b/pkg/apis/acid.zalan.do/v1/crds.go @@ -580,12 +580,6 @@ var PostgresCRDResourceValidation = apiextv1.CustomResourceValidation{ "version": { Type: "string", Enum: []apiextv1.JSON{ - { - Raw: []byte(`"9.5"`), - }, - { - Raw: []byte(`"9.6"`), - }, { Raw: []byte(`"10"`), }, @@ -601,6 +595,9 @@ var PostgresCRDResourceValidation = apiextv1.CustomResourceValidation{ { Raw: []byte(`"14"`), }, + { + Raw: []byte(`"15"`), + }, }, }, "parameters": { diff --git a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go index fc5746413..e49b8d7ae 100644 --- a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go +++ b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go @@ -49,7 +49,7 @@ type PostgresUsersConfiguration struct { type MajorVersionUpgradeConfiguration struct { MajorVersionUpgradeMode string `json:"major_version_upgrade_mode" default:"off"` // off - no actions, manual - manifest triggers action, full - manifest and minimal version violation trigger upgrade MajorVersionUpgradeTeamAllowList []string `json:"major_version_upgrade_team_allow_list,omitempty"` - MinimalMajorVersion string `json:"minimal_major_version" default:"9.6"` + MinimalMajorVersion string `json:"minimal_major_version" default:"11"` TargetMajorVersion string `json:"target_major_version" default:"14"` } diff --git a/pkg/apis/acid.zalan.do/v1/util_test.go b/pkg/apis/acid.zalan.do/v1/util_test.go index 8c570650e..71a629f31 100644 --- a/pkg/apis/acid.zalan.do/v1/util_test.go +++ b/pkg/apis/acid.zalan.do/v1/util_test.go @@ -213,7 +213,7 @@ var unmarshalCluster = []struct { "127.0.0.1/32" ], "postgresql": { - "version": "9.6", + "version": "15", "parameters": { "shared_buffers": "32MB", "max_connections": "10", @@ -273,7 +273,7 @@ var unmarshalCluster = []struct { }, Spec: PostgresSpec{ PostgresqlParam: PostgresqlParam{ - PgVersion: "9.6", + PgVersion: "15", Parameters: map[string]string{ "shared_buffers": "32MB", "max_connections": "10", @@ -333,7 +333,7 @@ var unmarshalCluster = []struct { }, Error: "", }, - marshal: []byte(`{"kind":"Postgresql","apiVersion":"acid.zalan.do/v1","metadata":{"name":"acid-testcluster1","creationTimestamp":null},"spec":{"postgresql":{"version":"9.6","parameters":{"log_statement":"all","max_connections":"10","shared_buffers":"32MB"}},"pod_priority_class_name":"spilo-pod-priority","volume":{"size":"5Gi","storageClass":"SSD", "subPath": "subdir"},"enableShmVolume":false,"patroni":{"initdb":{"data-checksums":"true","encoding":"UTF8","locale":"en_US.UTF-8"},"pg_hba":["hostssl all all 0.0.0.0/0 md5","host all all 0.0.0.0/0 md5"],"ttl":30,"loop_wait":10,"retry_timeout":10,"maximum_lag_on_failover":33554432,"slots":{"permanent_logical_1":{"database":"foo","plugin":"pgoutput","type":"logical"}}},"resources":{"requests":{"cpu":"10m","memory":"50Mi"},"limits":{"cpu":"300m","memory":"3000Mi"}},"teamId":"acid","allowedSourceRanges":["127.0.0.1/32"],"numberOfInstances":2,"users":{"zalando":["superuser","createdb"]},"maintenanceWindows":["Mon:01:00-06:00","Sat:00:00-04:00","05:00-05:15"],"clone":{"cluster":"acid-batman"}},"status":{"PostgresClusterStatus":""}}`), + marshal: []byte(`{"kind":"Postgresql","apiVersion":"acid.zalan.do/v1","metadata":{"name":"acid-testcluster1","creationTimestamp":null},"spec":{"postgresql":{"version":"15","parameters":{"log_statement":"all","max_connections":"10","shared_buffers":"32MB"}},"pod_priority_class_name":"spilo-pod-priority","volume":{"size":"5Gi","storageClass":"SSD", "subPath": "subdir"},"enableShmVolume":false,"patroni":{"initdb":{"data-checksums":"true","encoding":"UTF8","locale":"en_US.UTF-8"},"pg_hba":["hostssl all all 0.0.0.0/0 md5","host all all 0.0.0.0/0 md5"],"ttl":30,"loop_wait":10,"retry_timeout":10,"maximum_lag_on_failover":33554432,"slots":{"permanent_logical_1":{"database":"foo","plugin":"pgoutput","type":"logical"}}},"resources":{"requests":{"cpu":"10m","memory":"50Mi"},"limits":{"cpu":"300m","memory":"3000Mi"}},"teamId":"acid","allowedSourceRanges":["127.0.0.1/32"],"numberOfInstances":2,"users":{"zalando":["superuser","createdb"]},"maintenanceWindows":["Mon:01:00-06:00","Sat:00:00-04:00","05:00-05:15"],"clone":{"cluster":"acid-batman"}},"status":{"PostgresClusterStatus":""}}`), err: nil}, { about: "example with clone", @@ -398,7 +398,7 @@ var postgresqlList = []struct { out PostgresqlList err error }{ - {"expect success", []byte(`{"apiVersion":"v1","items":[{"apiVersion":"acid.zalan.do/v1","kind":"Postgresql","metadata":{"labels":{"team":"acid"},"name":"acid-testcluster42","namespace":"default","resourceVersion":"30446957","selfLink":"/apis/acid.zalan.do/v1/namespaces/default/postgresqls/acid-testcluster42","uid":"857cd208-33dc-11e7-b20a-0699041e4b03"},"spec":{"allowedSourceRanges":["185.85.220.0/22"],"numberOfInstances":1,"postgresql":{"version":"9.6"},"teamId":"acid","volume":{"size":"10Gi"}},"status":{"PostgresClusterStatus":"Running"}}],"kind":"List","metadata":{},"resourceVersion":"","selfLink":""}`), + {"expect success", []byte(`{"apiVersion":"v1","items":[{"apiVersion":"acid.zalan.do/v1","kind":"Postgresql","metadata":{"labels":{"team":"acid"},"name":"acid-testcluster42","namespace":"default","resourceVersion":"30446957","selfLink":"/apis/acid.zalan.do/v1/namespaces/default/postgresqls/acid-testcluster42","uid":"857cd208-33dc-11e7-b20a-0699041e4b03"},"spec":{"allowedSourceRanges":["185.85.220.0/22"],"numberOfInstances":1,"postgresql":{"version":"15"},"teamId":"acid","volume":{"size":"10Gi"}},"status":{"PostgresClusterStatus":"Running"}}],"kind":"List","metadata":{},"resourceVersion":"","selfLink":""}`), PostgresqlList{ TypeMeta: metav1.TypeMeta{ Kind: "List", @@ -419,7 +419,7 @@ var postgresqlList = []struct { }, Spec: PostgresSpec{ ClusterName: "testcluster42", - PostgresqlParam: PostgresqlParam{PgVersion: "9.6"}, + PostgresqlParam: PostgresqlParam{PgVersion: "15"}, Volume: Volume{Size: "10Gi"}, TeamID: "acid", AllowedSourceRanges: []string{"185.85.220.0/22"}, diff --git a/pkg/cluster/k8sres_test.go b/pkg/cluster/k8sres_test.go index 4781bfabc..223fdb787 100644 --- a/pkg/cluster/k8sres_test.go +++ b/pkg/cluster/k8sres_test.go @@ -74,18 +74,18 @@ func TestGenerateSpiloJSONConfiguration(t *testing.T) { }{ { subtest: "Patroni default configuration", - pgParam: &acidv1.PostgresqlParam{PgVersion: "9.6"}, + pgParam: &acidv1.PostgresqlParam{PgVersion: "15"}, patroni: &acidv1.Patroni{}, opConfig: &config.Config{ Auth: config.Auth{ PamRoleName: "zalandos", }, }, - result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/9.6/bin"},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"}],"users":{"zalandos":{"password":"","options":["CREATEDB","NOLOGIN"]}},"dcs":{}}}`, + result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/15/bin"},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"}],"users":{"zalandos":{"password":"","options":["CREATEDB","NOLOGIN"]}},"dcs":{}}}`, }, { subtest: "Patroni configured", - pgParam: &acidv1.PostgresqlParam{PgVersion: "11"}, + pgParam: &acidv1.PostgresqlParam{PgVersion: "15"}, patroni: &acidv1.Patroni{ InitDB: map[string]string{ "encoding": "UTF8", @@ -108,11 +108,11 @@ func TestGenerateSpiloJSONConfiguration(t *testing.T) { PamRoleName: "zalandos", }, }, - result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/11/bin","pg_hba":["hostssl all all 0.0.0.0/0 md5","host all all 0.0.0.0/0 md5"]},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"},"data-checksums",{"encoding":"UTF8"},{"locale":"en_US.UTF-8"}],"users":{"zalandos":{"password":"","options":["CREATEDB","NOLOGIN"]}},"dcs":{"ttl":30,"loop_wait":10,"retry_timeout":10,"maximum_lag_on_failover":33554432,"synchronous_mode":true,"synchronous_mode_strict":true,"synchronous_node_count":1,"slots":{"permanent_logical_1":{"database":"foo","plugin":"pgoutput","type":"logical"}},"failsafe_mode":true}}}`, + result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/15/bin","pg_hba":["hostssl all all 0.0.0.0/0 md5","host all all 0.0.0.0/0 md5"]},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"},"data-checksums",{"encoding":"UTF8"},{"locale":"en_US.UTF-8"}],"users":{"zalandos":{"password":"","options":["CREATEDB","NOLOGIN"]}},"dcs":{"ttl":30,"loop_wait":10,"retry_timeout":10,"maximum_lag_on_failover":33554432,"synchronous_mode":true,"synchronous_mode_strict":true,"synchronous_node_count":1,"slots":{"permanent_logical_1":{"database":"foo","plugin":"pgoutput","type":"logical"}},"failsafe_mode":true}}}`, }, { subtest: "Patroni failsafe_mode configured globally", - pgParam: &acidv1.PostgresqlParam{PgVersion: "14"}, + pgParam: &acidv1.PostgresqlParam{PgVersion: "15"}, patroni: &acidv1.Patroni{}, opConfig: &config.Config{ Auth: config.Auth{ @@ -120,11 +120,11 @@ func TestGenerateSpiloJSONConfiguration(t *testing.T) { }, EnablePatroniFailsafeMode: util.True(), }, - result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/14/bin"},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"}],"users":{"zalandos":{"password":"","options":["CREATEDB","NOLOGIN"]}},"dcs":{"failsafe_mode":true}}}`, + result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/15/bin"},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"}],"users":{"zalandos":{"password":"","options":["CREATEDB","NOLOGIN"]}},"dcs":{"failsafe_mode":true}}}`, }, { subtest: "Patroni failsafe_mode configured globally, disabled for cluster", - pgParam: &acidv1.PostgresqlParam{PgVersion: "14"}, + pgParam: &acidv1.PostgresqlParam{PgVersion: "15"}, patroni: &acidv1.Patroni{ FailsafeMode: util.False(), }, @@ -134,11 +134,11 @@ func TestGenerateSpiloJSONConfiguration(t *testing.T) { }, EnablePatroniFailsafeMode: util.True(), }, - result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/14/bin"},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"}],"users":{"zalandos":{"password":"","options":["CREATEDB","NOLOGIN"]}},"dcs":{"failsafe_mode":false}}}`, + result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/15/bin"},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"}],"users":{"zalandos":{"password":"","options":["CREATEDB","NOLOGIN"]}},"dcs":{"failsafe_mode":false}}}`, }, { subtest: "Patroni failsafe_mode disabled globally, configured for cluster", - pgParam: &acidv1.PostgresqlParam{PgVersion: "14"}, + pgParam: &acidv1.PostgresqlParam{PgVersion: "15"}, patroni: &acidv1.Patroni{ FailsafeMode: util.True(), }, @@ -148,7 +148,7 @@ func TestGenerateSpiloJSONConfiguration(t *testing.T) { }, EnablePatroniFailsafeMode: util.False(), }, - result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/14/bin"},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"}],"users":{"zalandos":{"password":"","options":["CREATEDB","NOLOGIN"]}},"dcs":{"failsafe_mode":true}}}`, + result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/15/bin"},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"}],"users":{"zalandos":{"password":"","options":["CREATEDB","NOLOGIN"]}},"dcs":{"failsafe_mode":true}}}`, }, } for _, tt := range tests { @@ -180,15 +180,15 @@ func TestExtractPgVersionFromBinPath(t *testing.T) { }, { subTest: "test current bin path against hard coded template", - binPath: "/usr/lib/postgresql/12/bin", + binPath: "/usr/lib/postgresql/15/bin", template: pgBinariesLocationTemplate, - expected: "12", + expected: "15", }, { subTest: "test alternative bin path against a matching template", - binPath: "/usr/pgsql-12/bin", + binPath: "/usr/pgsql-15/bin", template: "/usr/pgsql-%v/bin", - expected: "12", + expected: "15", }, } @@ -1849,7 +1849,7 @@ func TestSidecars(t *testing.T) { spec = acidv1.PostgresSpec{ PostgresqlParam: acidv1.PostgresqlParam{ - PgVersion: "12.1", + PgVersion: "15", Parameters: map[string]string{ "max_connections": "100", }, diff --git a/pkg/cluster/majorversionupgrade.go b/pkg/cluster/majorversionupgrade.go index abe015959..7a63da32f 100644 --- a/pkg/cluster/majorversionupgrade.go +++ b/pkg/cluster/majorversionupgrade.go @@ -11,13 +11,12 @@ import ( // VersionMap Map of version numbers var VersionMap = map[string]int{ - "9.5": 90500, - "9.6": 90600, - "10": 100000, - "11": 110000, - "12": 120000, - "13": 130000, - "14": 140000, + "10": 100000, + "11": 110000, + "12": 120000, + "13": 130000, + "14": 140000, + "15": 150000, } // IsBiggerPostgresVersion Compare two Postgres version numbers @@ -36,7 +35,7 @@ func (c *Cluster) GetDesiredMajorVersionAsInt() int { func (c *Cluster) GetDesiredMajorVersion() string { if c.Config.OpConfig.MajorVersionUpgradeMode == "full" { - // e.g. current is 9.6, minimal is 11 allowing 11 to 14 clusters, everything below is upgraded + // e.g. current is 10, minimal is 11 allowing 11 to 14 clusters, everything below is upgraded if IsBiggerPostgresVersion(c.Spec.PgVersion, c.Config.OpConfig.MinimalMajorVersion) { c.logger.Infof("overwriting configured major version %s to %s", c.Spec.PgVersion, c.Config.OpConfig.TargetMajorVersion) return c.Config.OpConfig.TargetMajorVersion diff --git a/pkg/controller/operator_config.go b/pkg/controller/operator_config.go index 03afee358..e0107d18c 100644 --- a/pkg/controller/operator_config.go +++ b/pkg/controller/operator_config.go @@ -62,7 +62,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur // major version upgrade config result.MajorVersionUpgradeMode = util.Coalesce(fromCRD.MajorVersionUpgrade.MajorVersionUpgradeMode, "off") result.MajorVersionUpgradeTeamAllowList = fromCRD.MajorVersionUpgrade.MajorVersionUpgradeTeamAllowList - result.MinimalMajorVersion = util.Coalesce(fromCRD.MajorVersionUpgrade.MinimalMajorVersion, "9.6") + result.MinimalMajorVersion = util.Coalesce(fromCRD.MajorVersionUpgrade.MinimalMajorVersion, "11") result.TargetMajorVersion = util.Coalesce(fromCRD.MajorVersionUpgrade.TargetMajorVersion, "14") // kubernetes config diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index 234486c24..374e26292 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -230,7 +230,7 @@ type Config struct { EnableTeamIdClusternamePrefix bool `name:"enable_team_id_clustername_prefix" default:"false"` MajorVersionUpgradeMode string `name:"major_version_upgrade_mode" default:"off"` MajorVersionUpgradeTeamAllowList []string `name:"major_version_upgrade_team_allow_list" default:""` - MinimalMajorVersion string `name:"minimal_major_version" default:"9.6"` + MinimalMajorVersion string `name:"minimal_major_version" default:"11"` TargetMajorVersion string `name:"target_major_version" default:"14"` PatroniAPICheckInterval time.Duration `name:"patroni_api_check_interval" default:"1s"` PatroniAPICheckTimeout time.Duration `name:"patroni_api_check_timeout" default:"5s"` diff --git a/ui/manifests/deployment.yaml b/ui/manifests/deployment.yaml index e3e603b3e..bff1a3bc9 100644 --- a/ui/manifests/deployment.yaml +++ b/ui/manifests/deployment.yaml @@ -72,6 +72,7 @@ spec: "limit_iops": 16000, "limit_throughput": 1000, "postgresql_versions": [ + "15", "14", "13", "12", diff --git a/ui/operator_ui/main.py b/ui/operator_ui/main.py index 72fb7d4e6..fdb61cd39 100644 --- a/ui/operator_ui/main.py +++ b/ui/operator_ui/main.py @@ -321,7 +321,7 @@ DEFAULT_UI_CONFIG = { 'users_visible': True, 'databases_visible': True, 'resources_visible': RESOURCES_VISIBLE, - 'postgresql_versions': ['11','12','13','14'], + 'postgresql_versions': ['11', '12', '13', '14'], 'dns_format_string': '{0}.{1}', 'pgui_link': '', 'static_network_whitelist': {}, diff --git a/ui/operator_ui/spiloutils.py b/ui/operator_ui/spiloutils.py index b9c599c44..9bbc4e3ba 100644 --- a/ui/operator_ui/spiloutils.py +++ b/ui/operator_ui/spiloutils.py @@ -308,7 +308,7 @@ def read_versions( if uid == 'wal' or defaulting(lambda: UUID(uid)) ] -BACKUP_VERSION_PREFIXES = ['', '9.5/', '9.6/', '10/', '11/', '12/', '13/', '14/'] +BACKUP_VERSION_PREFIXES = ['', '9.6/', '10/', '11/', '12/', '13/', '14/', '15/'] def read_basebackups( pg_cluster, diff --git a/ui/run_local.sh b/ui/run_local.sh index e2d9e2ea0..bee5c3e55 100755 --- a/ui/run_local.sh +++ b/ui/run_local.sh @@ -25,6 +25,7 @@ default_operator_ui_config='{ "cost_core": 0.0575, "cost_memory": 0.014375, "postgresql_versions": [ + "15", "14", "13", "12", From e80cccb93b30c27d0bfb3eade0874208be2fa70f Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Tue, 27 Dec 2022 16:52:01 +0100 Subject: [PATCH 44/77] use random short name for stream CRDs (#2137) * use random short name for stream CRDs --- e2e/Makefile | 2 +- pkg/cluster/cluster.go | 1 - pkg/cluster/streams.go | 87 ++++++++++++++++++++++--------------- pkg/cluster/streams_test.go | 39 +++++++++-------- 4 files changed, 74 insertions(+), 55 deletions(-) diff --git a/e2e/Makefile b/e2e/Makefile index a32017192..9b1b5ea11 100644 --- a/e2e/Makefile +++ b/e2e/Makefile @@ -32,7 +32,7 @@ clean: copy: clean mkdir manifests - cp ../manifests -r . + cp -r ../manifests . docker: scm-source.json docker build -t "$(IMAGE):$(TAG)" . diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index 812965854..f18cf8053 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -91,7 +91,6 @@ type Cluster struct { currentProcess Process processMu sync.RWMutex // protects the current operation for reporting, no need to hold the master mutex specMu sync.RWMutex // protects the spec for reporting, no need to hold the master mutex - streamApplications []string ConnectionPooler map[PostgresRole]*ConnectionPoolerObjects EBSVolumes map[string]volumes.VolumeProperties VolumeResizer volumes.VolumeResizer diff --git a/pkg/cluster/streams.go b/pkg/cluster/streams.go index d911e6a83..2f403ab23 100644 --- a/pkg/cluster/streams.go +++ b/pkg/cluster/streams.go @@ -15,15 +15,16 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func (c *Cluster) createStreams(appId string) error { +func (c *Cluster) createStreams(appId string) (*zalandov1.FabricEventStream, error) { c.setProcessName("creating streams") fes := c.generateFabricEventStream(appId) - if _, err := c.KubeClient.FabricEventStreams(c.Namespace).Create(context.TODO(), fes, metav1.CreateOptions{}); err != nil { - return err + streamCRD, err := c.KubeClient.FabricEventStreams(c.Namespace).Create(context.TODO(), fes, metav1.CreateOptions{}) + if err != nil { + return nil, err } - return nil + return streamCRD, nil } func (c *Cluster) updateStreams(newEventStreams *zalandov1.FabricEventStream) error { @@ -46,11 +47,17 @@ func (c *Cluster) deleteStreams() error { } errors := make([]string, 0) - for _, appId := range c.streamApplications { - fesName := fmt.Sprintf("%s-%s", c.Name, appId) - err = c.KubeClient.FabricEventStreams(c.Namespace).Delete(context.TODO(), fesName, metav1.DeleteOptions{}) + listOptions := metav1.ListOptions{ + LabelSelector: c.labelsSet(true).String(), + } + streams, err := c.KubeClient.FabricEventStreams(c.Namespace).List(context.TODO(), listOptions) + if err != nil { + return fmt.Errorf("could not list of FabricEventStreams: %v", err) + } + for _, stream := range streams.Items { + err = c.KubeClient.FabricEventStreams(stream.Namespace).Delete(context.TODO(), stream.Name, metav1.DeleteOptions{}) if err != nil { - errors = append(errors, fmt.Sprintf("could not delete event stream %q: %v", fesName, err)) + errors = append(errors, fmt.Sprintf("could not delete event stream %q: %v", stream.Name, err)) } } @@ -184,8 +191,10 @@ func (c *Cluster) generateFabricEventStream(appId string) *zalandov1.FabricEvent Kind: constants.EventStreamCRDKind, }, ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("%s-%s", c.Name, appId), + // max length for cluster name is 58 so we can only add 5 more characters / numbers + Name: fmt.Sprintf("%s-%s", c.Name, util.RandomPassword(5)), Namespace: c.Namespace, + Labels: c.labelsSet(true), Annotations: c.AnnotationsToPropagate(c.annotationsSet(nil)), // make cluster StatefulSet the owner (like with connection pooler objects) OwnerReferences: c.ownerReferences(), @@ -284,11 +293,6 @@ func (c *Cluster) syncStreams() error { return nil } - // fetch different application IDs from streams section - // there will be a separate event stream resource for each ID - appIds := gatherApplicationIds(c.Spec.Streams) - c.streamApplications = appIds - slots := make(map[string]map[string]string) slotsToSync := make(map[string]map[string]string) publications := make(map[string]map[string]acidv1.StreamTable) @@ -355,32 +359,43 @@ func (c *Cluster) syncStreams() error { } func (c *Cluster) createOrUpdateStreams() error { - for _, appId := range c.streamApplications { - fesName := fmt.Sprintf("%s-%s", c.Name, appId) - effectiveStreams, err := c.KubeClient.FabricEventStreams(c.Namespace).Get(context.TODO(), fesName, metav1.GetOptions{}) - if err != nil { - if !k8sutil.ResourceNotFound(err) { - return fmt.Errorf("failed reading event stream %s: %v", fesName, err) - } - c.logger.Infof("event streams do not exist, create it") - err = c.createStreams(appId) - if err != nil { - return fmt.Errorf("failed creating event stream %s: %v", fesName, err) - } - c.logger.Infof("event stream %q has been successfully created", fesName) - } else { - desiredStreams := c.generateFabricEventStream(appId) - if match, reason := sameStreams(effectiveStreams.Spec.EventStreams, desiredStreams.Spec.EventStreams); !match { - c.logger.Debugf("updating event streams: %s", reason) - desiredStreams.ObjectMeta.ResourceVersion = effectiveStreams.ObjectMeta.ResourceVersion - err = c.updateStreams(desiredStreams) - if err != nil { - return fmt.Errorf("failed updating event stream %s: %v", fesName, err) + // fetch different application IDs from streams section + // there will be a separate event stream resource for each ID + appIds := gatherApplicationIds(c.Spec.Streams) + + // list all existing stream CRDs + listOptions := metav1.ListOptions{ + LabelSelector: c.labelsSet(true).String(), + } + streams, err := c.KubeClient.FabricEventStreams(c.Namespace).List(context.TODO(), listOptions) + if err != nil { + return fmt.Errorf("could not list of FabricEventStreams: %v", err) + } + + for _, appId := range appIds { + // update stream when it exists and EventStreams array differs + for _, stream := range streams.Items { + if appId == stream.Spec.ApplicationId { + desiredStreams := c.generateFabricEventStream(appId) + if match, reason := sameStreams(stream.Spec.EventStreams, desiredStreams.Spec.EventStreams); !match { + c.logger.Debugf("updating event streams: %s", reason) + desiredStreams.ObjectMeta.ResourceVersion = stream.ObjectMeta.ResourceVersion + err = c.updateStreams(desiredStreams) + if err != nil { + return fmt.Errorf("failed updating event stream %s: %v", stream.Name, err) + } + c.logger.Infof("event stream %q has been successfully updated", stream.Name) } - c.logger.Infof("event stream %q has been successfully updated", fesName) + continue } } + c.logger.Infof("event streams with applicationId %s do not exist, create it", appId) + streamCRD, err := c.createStreams(appId) + if err != nil { + return fmt.Errorf("failed creating event streams with applicationId %s: %v", appId, err) + } + c.logger.Infof("event streams %q have been successfully created", streamCRD.Name) } return nil diff --git a/pkg/cluster/streams_test.go b/pkg/cluster/streams_test.go index 674d2738d..271942ac9 100644 --- a/pkg/cluster/streams_test.go +++ b/pkg/cluster/streams_test.go @@ -41,7 +41,6 @@ var ( appId string = "test-app" dbName string = "foo" fesUser string = fmt.Sprintf("%s%s", constants.EventStreamSourceSlotPrefix, constants.UserRoleNameSuffix) - fesName string = fmt.Sprintf("%s-%s", clusterName, appId) slotName string = fmt.Sprintf("%s_%s_%s", constants.EventStreamSourceSlotPrefix, dbName, strings.Replace(appId, "-", "_", -1)) pg = acidv1.Postgresql{ @@ -77,6 +76,7 @@ var ( BatchSize: k8sutil.UInt32ToPointer(uint32(100)), }, }, + TeamID: "acid", Volume: acidv1.Volume{ Size: "1Gi", }, @@ -89,7 +89,7 @@ var ( Kind: constants.EventStreamCRDKind, }, ObjectMeta: metav1.ObjectMeta{ - Name: fesName, + Name: fmt.Sprintf("%s-12345", clusterName), Namespace: namespace, OwnerReferences: []metav1.OwnerReference{ metav1.OwnerReference{ @@ -196,9 +196,6 @@ func TestGenerateFabricEventStream(t *testing.T) { _, err := cluster.createStatefulSet() assert.NoError(t, err) - // createOrUpdateStreams will loop over existing apps - cluster.streamApplications = []string{appId} - // create the streams err = cluster.createOrUpdateStreams() assert.NoError(t, err) @@ -209,11 +206,14 @@ func TestGenerateFabricEventStream(t *testing.T) { t.Errorf("malformed FabricEventStream, expected %#v, got %#v", fes, result) } - // compare stream resturned from API with expected stream - streamCRD, err := cluster.KubeClient.FabricEventStreams(namespace).Get(context.TODO(), fesName, metav1.GetOptions{}) + // compare stream returned from API with expected stream + listOptions := metav1.ListOptions{ + LabelSelector: cluster.labelsSet(true).String(), + } + streams, err := cluster.KubeClient.FabricEventStreams(namespace).List(context.TODO(), listOptions) assert.NoError(t, err) - if match, _ := sameStreams(streamCRD.Spec.EventStreams, fes.Spec.EventStreams); !match { - t.Errorf("malformed FabricEventStream returned from API, expected %#v, got %#v", fes, streamCRD) + if match, _ := sameStreams(streams.Items[0].Spec.EventStreams, fes.Spec.EventStreams); !match { + t.Errorf("malformed FabricEventStream returned from API, expected %#v, got %#v", fes, streams.Items[0]) } // sync streams once again @@ -221,10 +221,10 @@ func TestGenerateFabricEventStream(t *testing.T) { assert.NoError(t, err) // compare stream resturned from API with generated stream - streamCRD, err = cluster.KubeClient.FabricEventStreams(namespace).Get(context.TODO(), fesName, metav1.GetOptions{}) + streams, err = cluster.KubeClient.FabricEventStreams(namespace).List(context.TODO(), listOptions) assert.NoError(t, err) - if match, _ := sameStreams(streamCRD.Spec.EventStreams, result.Spec.EventStreams); !match { - t.Errorf("returned FabricEventStream differs from generated one, expected %#v, got %#v", result, streamCRD) + if match, _ := sameStreams(streams.Items[0].Spec.EventStreams, result.Spec.EventStreams); !match { + t.Errorf("returned FabricEventStream differs from generated one, expected %#v, got %#v", result, streams.Items[0]) } } @@ -331,8 +331,9 @@ func TestUpdateFabricEventStream(t *testing.T) { context.TODO(), &pg, metav1.CreateOptions{}) assert.NoError(t, err) - // createOrUpdateStreams will loop over existing apps - cluster.streamApplications = []string{appId} + // create statefulset to have ownerReference for streams + _, err = cluster.createStatefulSet() + assert.NoError(t, err) err = cluster.createOrUpdateStreams() assert.NoError(t, err) @@ -365,11 +366,15 @@ func TestUpdateFabricEventStream(t *testing.T) { err = cluster.createOrUpdateStreams() assert.NoError(t, err) - streamCRD, err := cluster.KubeClient.FabricEventStreams(namespace).Get(context.TODO(), fesName, metav1.GetOptions{}) + // compare stream returned from API with expected stream + listOptions := metav1.ListOptions{ + LabelSelector: cluster.labelsSet(true).String(), + } + streams, err := cluster.KubeClient.FabricEventStreams(namespace).List(context.TODO(), listOptions) assert.NoError(t, err) result := cluster.generateFabricEventStream(appId) - if !reflect.DeepEqual(result, streamCRD) { - t.Errorf("Malformed FabricEventStream, expected %#v, got %#v", streamCRD, result) + if !reflect.DeepEqual(result.Spec.EventStreams, streams.Items[0].Spec.EventStreams) { + t.Errorf("Malformed FabricEventStream, expected %#v, got %#v", streams.Items[0], result) } } From 20113675252239fe51006a2b8e0e292f87de91de Mon Sep 17 00:00:00 2001 From: chengyou tang Date: Thu, 29 Dec 2022 22:22:22 +0800 Subject: [PATCH 45/77] feat: [ui] Relative navigation; add storageClass (#1835) - Change all ui request from absolute path to relative path. Can access ui web like ex.com/pgo/ with nginx spec config relative issue: zalando#1613 - Add storageClass field relative-nav Co-authored-by: Felix Kunde --- ui/app/src/app.js | 6 +++--- ui/app/src/app.tag.pug | 16 +++++++-------- ui/app/src/edit.tag.pug | 13 ++++++++----- ui/app/src/logs.tag.pug | 10 +++++----- ui/app/src/new.tag.pug | 30 ++++++++++++++++++++++++----- ui/app/src/postgresql.tag.pug | 22 ++++++++++----------- ui/app/src/postgresqls.tag.pug | 26 ++++++++++++------------- ui/app/src/restore.tag.pug | 14 +++++++------- ui/app/src/status.tag.pug | 16 +++++++-------- ui/operator_ui/templates/index.html | 10 +++++----- 10 files changed, 93 insertions(+), 70 deletions(-) diff --git a/ui/app/src/app.js b/ui/app/src/app.js index 9eb7569e6..20c92e63e 100644 --- a/ui/app/src/app.js +++ b/ui/app/src/app.js @@ -214,13 +214,13 @@ const delete_cluster = (namespace, clustername) => { jQuery.ajax({ type: 'DELETE', url: ( - '/postgresqls/' + './postgresqls/' + encodeURI(namespace) + '/' + encodeURI(clustername) ), dataType: 'text', - success: () => location.assign('/#/list'), - error: (r, status, error) => location.assign('/#/list'), // TODO: show error + success: () => location.assign('./#/list'), + error: (r, status, error) => location.assign('./#/list'), // TODO: show error }) }, }, diff --git a/ui/app/src/app.tag.pug b/ui/app/src/app.tag.pug index 365371e63..f53d425da 100644 --- a/ui/app/src/app.tag.pug +++ b/ui/app/src/app.tag.pug @@ -4,23 +4,23 @@ app .container .navbar-header - a.navbar-brand(href='/') + a.navbar-brand(href='./') | PostgreSQL Operator UI #navbar.navbar-collapse.collapse ul.nav.navbar-nav li(class='{ active: ["EDIT", "LIST", "LOGS", "STATUS"].includes(activenav) }') - a(href='/#/list') PostgreSQL clusters + a(href='./#/list') PostgreSQL clusters li(class='{ active: "BACKUPS" === activenav }') - a(href='/#/backups') Backups + a(href='./#/backups') Backups li(class='{ active: "OPERATOR" === activenav }') - a(href='/#/operator') Status + a(href='./#/operator') Status li(class='{ active: "NEW" === activenav }') - a(href='/#/new') New cluster + a(href='./#/new') New cluster li(if='{ config }') a(href='{ config.docs_link }' target='_blank') Documentation @@ -55,7 +55,7 @@ app | | or | - a(href="/") start over + a(href="./") start over | . div(if='{ config }') @@ -152,12 +152,12 @@ app ;( jQuery - .get('/config') + .get('./config') .done(config => { this.config = config ;( jQuery - .get('/teams') + .get('./teams') .done(teams => { this.teams = teams.sort() this.team = this.teams[0] diff --git a/ui/app/src/edit.tag.pug b/ui/app/src/edit.tag.pug index ddcaa4653..5b03c06da 100644 --- a/ui/app/src/edit.tag.pug +++ b/ui/app/src/edit.tag.pug @@ -6,18 +6,18 @@ edit ol.breadcrumb li.breadcrumb-item - a(href='/#/list') + a(href='./#/list') | PostgreSQL clusters li.breadcrumb-item(if='{ cluster_path }') - a(href='/#/status/{ cluster_path }') + a(href='./#/status/{ cluster_path }') | { qname } li.breadcrumb-item.active( aria-current='page' if='{ cluster_path }' ) - a(href='/#/edit/{ cluster_path }') + a(href='./#/edit/{ cluster_path }') | Edit .row(if='{ cluster_path }') @@ -92,7 +92,7 @@ edit jQuery.ajax({ type: 'POST', - url: '/postgresqls/' + this.cluster_path, + url: './postgresqls/' + this.cluster_path, contentType:"application/json", data: jsonPayload, processData: false, @@ -113,7 +113,7 @@ edit this.pollProgress = () => { jQuery.get( - '/postgresqls/' + this.cluster_path, + './postgresqls/' + this.cluster_path, ).then(data => { // Input data: @@ -148,6 +148,9 @@ edit throughput: i.spec.volume.throughput || 125, iops: i.spec.volume.iops || 3000 } + if ('storageClass' in i.spec.volume) { + o.spec.volume.storageClass=i.spec.volume.storageClass + } o.spec.postgresql = {} o.spec.postgresql.version = i.spec.postgresql.version diff --git a/ui/app/src/logs.tag.pug b/ui/app/src/logs.tag.pug index e25cc979e..be79168af 100644 --- a/ui/app/src/logs.tag.pug +++ b/ui/app/src/logs.tag.pug @@ -5,15 +5,15 @@ logs ol.breadcrumb li.breadcrumb-item - a(href='/#/list') + a(href='./#/list') | PostgreSQL clusters li.breadcrumb-item - a(href='/#/status/{ cluster_path }') + a(href='./#/status/{ cluster_path }') | { qname } li.breadcrumb-item - a(href='/#/logs/{ cluster_path }') + a(href='./#/logs/{ cluster_path }') | Logs .sk-spinner-pulse(if='{ logs === undefined }') @@ -26,7 +26,7 @@ logs | | or | - a(href="/") start over + a(href="./") start over | . .container-fluid(if='{ logs }') @@ -72,7 +72,7 @@ logs ) ;( jQuery - .get(`/operator/clusters/${cluster_path}/logs`) + .get(`./operator/clusters/${cluster_path}/logs`) .done(logs => this.logs = logs.reverse()) .fail(() => this.logs = null) .always(() => this.update()) diff --git a/ui/app/src/new.tag.pug b/ui/app/src/new.tag.pug index c066ad4f4..5c21f93c7 100644 --- a/ui/app/src/new.tag.pug +++ b/ui/app/src/new.tag.pug @@ -18,7 +18,7 @@ new ol.breadcrumb li.breadcrumb-item - a(href='/#/new') + a(href='./#/new') | New PostgreSQL cluster .row.text-center(if='{ !creating }') @@ -64,7 +64,7 @@ new a.btn.btn-small.btn-warning( if='{ clusterExists }' - href='/#/status/{ namespace.state }/{ name }' + href='./#/status/{ namespace.state }/{ name }' ) | Cluster exists (show status) @@ -299,8 +299,21 @@ new ) .input-group-addon .input-units Gi + tr + td storageClass td + .input-group + input.form-control( + ref='volumeStorageClass' + type='text' + value='{ volumeStorageClass }' + onchange='{ storageClassChange }' + onkeyup='{ storageClassChange }' + ) + + tr + td td Specify Iops and Throughput only if you need more than the default 3000 Iops and 125Mb/s EBS provides. tr @@ -582,7 +595,8 @@ new enableReplicaPoolerLoadBalancer: true {{/if}} volume: - size: "{{ volumeSize }}Gi"{{#if iops}} + size: "{{ volumeSize }}Gi"{{#if volumeStorageClass}} + storageClass: "{{ volumeStorageClass }}"{{/if}}{{#if iops}} iops: {{ iops }}{{/if}}{{#if throughput}} throughput: {{ throughput }}{{/if}} {{#if users}} @@ -638,6 +652,7 @@ new enableMasterPoolerLoadBalancer: this.enableMasterPoolerLoadBalancer, enableReplicaPoolerLoadBalancer: this.enableReplicaPoolerLoadBalancer, volumeSize: this.volumeSize, + volumeStorageClass: this.volumeStorageClass, iops: this.iops, throughput: this.throughput, users: this.users.valids, @@ -716,6 +731,10 @@ new this.volumeSize = +e.target.value } + this.storageClassChange = e => { + this.volumeStorageClass = e.target.value + } + this.iopsChange = e => { this.iops = +e.target.value } @@ -759,7 +778,7 @@ new this.checkClusterExists = () => ( jQuery .get( - '/postgresqls/' + './postgresqls/' + this.namespace.state + '/' + this.clusterName @@ -781,7 +800,7 @@ new jQuery.ajax({ type: 'POST', - url: '/create-cluster', + url: './create-cluster', contentType:'application/json', data: jsonPayload, processData: false, @@ -1013,6 +1032,7 @@ new this.clusterName = (this.name + '-').toLowerCase() this.volumeSize = 10 + this.volumeStorageClass = '' this.instanceCount = 1 this.ranges = {} this.odd = '' diff --git a/ui/app/src/postgresql.tag.pug b/ui/app/src/postgresql.tag.pug index f1b15ca81..e40315bb5 100644 --- a/ui/app/src/postgresql.tag.pug +++ b/ui/app/src/postgresql.tag.pug @@ -6,11 +6,11 @@ postgresql ol.breadcrumb li.breadcrumb-item - a(href='/#/list') + a(href='./#/list') | PostgreSQL clusters li.breadcrumb-item(if='{ cluster_path }') - a(href='/#/status/{ cluster_path }') + a(href='./#/status/{ cluster_path }') | { qname } .row(if='{ cluster_path }') @@ -39,20 +39,20 @@ postgresql ) a.btn.btn-info( - href='/#/logs/{ cluster_path }' + href='./#/logs/{ cluster_path }' ) | Logs a.btn( class='btn-{ opts.read_write ? "primary" : "info" }' if='{ progress.postgresql }' - href='/#/clone/{ clustername }/{ uid }/{ encodeURI(new Date().toISOString()) }' + href='./#/clone/{ clustername }/{ uid }/{ encodeURI(new Date().toISOString()) }' ) | Clone a.btn( class='btn-{ opts.read_write ? "warning" : "info" }' - href='/#/edit/{ cluster_path }' + href='./#/edit/{ cluster_path }' ) | Edit @@ -124,7 +124,7 @@ postgresql this.pollProgress = () => { jQuery.get( - '/postgresqls/' + this.cluster_path, + './postgresqls/' + this.cluster_path, ).done(data => { this.progress.pooler = false this.progress.postgresql = true @@ -137,13 +137,13 @@ postgresql this.update() jQuery.get( - '/statefulsets/' + this.cluster_path, + './statefulsets/' + this.cluster_path, ).done(data => { this.progress.statefulSet = true this.update() jQuery.get( - '/statefulsets/' + this.cluster_path + '/pods', + './statefulsets/' + this.cluster_path + '/pods', ).done(data => { if (data.length > 0) { this.progress.containerFirst = true @@ -157,7 +157,7 @@ postgresql this.update() jQuery.get( - '/services/' + this.cluster_path, + './services/' + this.cluster_path, ).done(data => { if (data.metadata && data.metadata.annotations && 'zalando.org/dnsname' in data.metadata.annotations) { this.progress.dnsName = data.metadata.annotations['zalando.org/dnsname'] @@ -169,12 +169,12 @@ postgresql } if (this.progress.poolerEnabled == true) { - jQuery.get('/pooler/' + this.cluster_path).done(data => { + jQuery.get('./pooler/' + this.cluster_path).done(data => { this.progress.pooler = {"url": ""} this.update() }) } - + this.update() }) }) diff --git a/ui/app/src/postgresqls.tag.pug b/ui/app/src/postgresqls.tag.pug index 6b60b9f8a..aed0d21b0 100644 --- a/ui/app/src/postgresqls.tag.pug +++ b/ui/app/src/postgresqls.tag.pug @@ -6,7 +6,7 @@ postgresqls ol.breadcrumb li.breadcrumb-item - a(href='/#/list') + a(href='./#/list') | PostgreSQL clusters .sk-spinner-pulse( @@ -20,7 +20,7 @@ postgresqls | | or | - a(href='/') start over + a(href='./') start over | . div( @@ -81,7 +81,7 @@ postgresqls td(style='white-space: pre') | { namespace } td - a(href='/#/status/{ cluster_path(this) }') { name } + a(href='./#/status/{ cluster_path(this) }') { name } btn.btn-danger(if='{status.PostgresClusterStatus == "CreateFailed"}') Create Failed td { nodes } td { cpu } / { cpu_limit } @@ -101,7 +101,7 @@ postgresqls ) a.btn.btn-info( - href='/#/status/{ cluster_path(this) }' + href='./#/status/{ cluster_path(this) }' ) i.fa.fa-check-circle.regular | Status @@ -114,21 +114,21 @@ postgresqls | Pgview a.btn.btn-info( - href='/#/logs/{ cluster_path(this) }' + href='./#/logs/{ cluster_path(this) }' ) i.fa.fa-align-justify | Logs a.btn( class='btn-{ opts.read_write ? "primary" : "info" }' - href='/#/clone/{ encodeURI(name) }/{ encodeURI(uid) }/{ encodeURI(new Date().toISOString()) }' + href='./#/clone/{ encodeURI(name) }/{ encodeURI(uid) }/{ encodeURI(new Date().toISOString()) }' ) i.fa.fa-clone.regular | Clone a.btn( class='btn-{ opts.read_write ? "warning" : "info" }' - href='/#/edit/{ cluster_path(this) }' + href='./#/edit/{ cluster_path(this) }' ) | Edit @@ -183,7 +183,7 @@ postgresqls | { namespace } td a( - href='/#/status/{ cluster_path(this) }' + href='./#/status/{ cluster_path(this) }' ) | { name } td { nodes } @@ -203,7 +203,7 @@ postgresqls ) a.btn.btn-info( - href='/#/status/{ cluster_path(this) }' + href='./#/status/{ cluster_path(this) }' ) i.fa.fa-check-circle.regular | Status @@ -217,21 +217,21 @@ postgresqls | Pgview a.btn.btn-info( - href='/#/logs/{ cluster_path(this) }' + href='./#/logs/{ cluster_path(this) }' ) i.fa.fa-align-justify | Logs a.btn( class='btn-{ opts.read_write ? "primary" : "info" }' - href='/#/clone/{ encodeURI(name) }/{ encodeURI(uid) }/{ encodeURI(new Date().toISOString()) }' + href='./#/clone/{ encodeURI(name) }/{ encodeURI(uid) }/{ encodeURI(new Date().toISOString()) }' ) i.fa.fa-clone.regular | Clone a.btn( class='btn-{ opts.read_write ? "warning" : "info" }' - href='/#/edit/{ cluster_path(this) }' + href='./#/edit/{ cluster_path(this) }' ) | Edit @@ -343,7 +343,7 @@ postgresqls this.on('mount', () => jQuery - .get('/postgresqls') + .get('./postgresqls') .done(clusters => { this.my_clusters = [] this.other_clusters = [] diff --git a/ui/app/src/restore.tag.pug b/ui/app/src/restore.tag.pug index 3a322df3f..523dbfbed 100644 --- a/ui/app/src/restore.tag.pug +++ b/ui/app/src/restore.tag.pug @@ -10,7 +10,7 @@ restore | | or | - a(href="/") start over + a(href="./") start over | . p(if='{ stored_clusters && stored_clusters.length === 0 }') @@ -23,7 +23,7 @@ restore ol.breadcrumb li.breadcrumb-item - a(href='/#/backups') + a(href='./#/backups') | PostgreSQL cluster backups ({ stored_clusters.length }) p @@ -63,7 +63,7 @@ restore p(if='{ versions === null }') | Error loading backups. Please try again or | - a(href="/") start over + a(href="./") start over | . p(if='{ versions && versions.length === 0 }') @@ -96,7 +96,7 @@ restore p(if='{ basebackups === null }') | Error loading snapshots. Please try again or | - a(href="/") start over + a(href="./") start over | . p(if='{ basebackups && basebackups.length === 0 }') @@ -312,7 +312,7 @@ restore get_subresources_once({ parent_resource: this, key: 'stored_clusters', - url: '/stored_clusters', + url: './stored_clusters', build_subresource: stored_cluster_name => ({ id: 'stored-cluster-' + stored_cluster_name, name: stored_cluster_name, @@ -324,7 +324,7 @@ restore body: stored_cluster => get_subresources_once({ parent_resource: stored_cluster, key: 'versions', - url: '/stored_clusters/' + stored_cluster.name, + url: './stored_clusters/' + stored_cluster.name, build_subresource: version_name => ({ id: stored_cluster.id + '-version-' + version_name, name: version_name, @@ -340,7 +340,7 @@ restore parent_resource: version, key: 'basebackups', url: ( - '/stored_clusters/' + stored_cluster.name + './stored_clusters/' + stored_cluster.name + '/' + version.name ), build_subresource: basebackup => Object.assign(basebackup, { diff --git a/ui/app/src/status.tag.pug b/ui/app/src/status.tag.pug index eae63fbdb..8a35703d9 100644 --- a/ui/app/src/status.tag.pug +++ b/ui/app/src/status.tag.pug @@ -6,19 +6,19 @@ status ol.breadcrumb li.breadcrumb-item - a(href='/#/operator') + a(href='./#/operator') | Workers virtual(if='{ operatorShowLogs && logs }') li.breadcrumb-item { worker_id } li.breadcrumb-item - a(href='/#/operator/worker/{ worker_id }/logs') + a(href='./#/operator/worker/{ worker_id }/logs') | Logs virtual(if='{ operatorShowQueue && queue }') li.breadcrumb-item { worker_id } li.breadcrumb-item - a(href='/#/operator/worker/{ worker_id }/queue') + a(href='./#/operator/worker/{ worker_id }/queue') | Queue div(if='{ status }') @@ -44,12 +44,12 @@ status ) a.btn.btn-info( - href='/#/operator/worker/{ worker_id }/logs' + href='./#/operator/worker/{ worker_id }/logs' ) | Logs a.btn.btn-info( - href='/#/operator/worker/{ worker_id }/queue' + href='./#/operator/worker/{ worker_id }/queue' ) | Queue @@ -96,7 +96,7 @@ status this.pollStatus = () => { jQuery.get( - '/operator/status', + './operator/status', ).done(data => { this.update({status: data}) }) @@ -111,7 +111,7 @@ status this.pollLogs = id => { jQuery.get( - '/operator/workers/' + id + '/logs', + './operator/workers/' + id + '/logs', ).done(data => { data.reverse() this.update({logs: data}) @@ -120,7 +120,7 @@ status this.pollQueue = id => { jQuery.get( - '/operator/workers/' + id + '/queue', + './operator/workers/' + id + '/queue', ).done(data => this.update({queue: data.List}) ) diff --git a/ui/operator_ui/templates/index.html b/ui/operator_ui/templates/index.html index 6b4689079..7307c8a3a 100644 --- a/ui/operator_ui/templates/index.html +++ b/ui/operator_ui/templates/index.html @@ -62,9 +62,9 @@ - - - + + + @@ -168,8 +168,8 @@ - - + + {% if google_analytics %} From c1657ec4843f30957d968f0092589a41443558c6 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Thu, 29 Dec 2022 16:55:18 +0100 Subject: [PATCH 46/77] stream resource name must be lower case (#2149) --- pkg/cluster/streams.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cluster/streams.go b/pkg/cluster/streams.go index 2f403ab23..bf1c2b742 100644 --- a/pkg/cluster/streams.go +++ b/pkg/cluster/streams.go @@ -192,7 +192,7 @@ func (c *Cluster) generateFabricEventStream(appId string) *zalandov1.FabricEvent }, ObjectMeta: metav1.ObjectMeta{ // max length for cluster name is 58 so we can only add 5 more characters / numbers - Name: fmt.Sprintf("%s-%s", c.Name, util.RandomPassword(5)), + Name: fmt.Sprintf("%s-%s", c.Name, strings.ToLower(util.RandomPassword(5))), Namespace: c.Namespace, Labels: c.labelsSet(true), Annotations: c.AnnotationsToPropagate(c.annotationsSet(nil)), From 4534a4cd9e4701286dde554aa914770e7355d745 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Fri, 30 Dec 2022 13:09:15 +0100 Subject: [PATCH 47/77] fix syncing of stream CRDs (#2152) * fix syncing of stream CRDs and improve corresponding unit tests --- pkg/cluster/streams.go | 18 ++++++++---- pkg/cluster/streams_test.go | 57 +++++++++++++++++++++++-------------- 2 files changed, 48 insertions(+), 27 deletions(-) diff --git a/pkg/cluster/streams.go b/pkg/cluster/streams.go index bf1c2b742..bb03c2e36 100644 --- a/pkg/cluster/streams.go +++ b/pkg/cluster/streams.go @@ -374,13 +374,16 @@ func (c *Cluster) createOrUpdateStreams() error { } for _, appId := range appIds { + streamExists := false + // update stream when it exists and EventStreams array differs for _, stream := range streams.Items { if appId == stream.Spec.ApplicationId { + streamExists = true desiredStreams := c.generateFabricEventStream(appId) if match, reason := sameStreams(stream.Spec.EventStreams, desiredStreams.Spec.EventStreams); !match { c.logger.Debugf("updating event streams: %s", reason) - desiredStreams.ObjectMeta.ResourceVersion = stream.ObjectMeta.ResourceVersion + desiredStreams.ObjectMeta = stream.ObjectMeta err = c.updateStreams(desiredStreams) if err != nil { return fmt.Errorf("failed updating event stream %s: %v", stream.Name, err) @@ -390,12 +393,15 @@ func (c *Cluster) createOrUpdateStreams() error { continue } } - c.logger.Infof("event streams with applicationId %s do not exist, create it", appId) - streamCRD, err := c.createStreams(appId) - if err != nil { - return fmt.Errorf("failed creating event streams with applicationId %s: %v", appId, err) + + if !streamExists { + c.logger.Infof("event streams with applicationId %s do not exist, create it", appId) + streamCRD, err := c.createStreams(appId) + if err != nil { + return fmt.Errorf("failed creating event streams with applicationId %s: %v", appId, err) + } + c.logger.Infof("event streams %q have been successfully created", streamCRD.Name) } - c.logger.Infof("event streams %q have been successfully created", streamCRD.Name) } return nil diff --git a/pkg/cluster/streams_test.go b/pkg/cluster/streams_test.go index 271942ac9..1e59e0f86 100644 --- a/pkg/cluster/streams_test.go +++ b/pkg/cluster/streams_test.go @@ -1,7 +1,6 @@ package cluster import ( - "encoding/json" "fmt" "reflect" "strings" @@ -167,6 +166,15 @@ var ( } ) +func TestGatherApplicationIds(t *testing.T) { + testAppIds := []string{appId} + appIds := gatherApplicationIds(pg.Spec.Streams) + + if !util.IsEqualIgnoreOrder(testAppIds, appIds) { + t.Errorf("gathered applicationIds do not match, expected %#v, got %#v", testAppIds, appIds) + } +} + func TestGenerateFabricEventStream(t *testing.T) { client, _ := newFakeK8sStreamClient() @@ -206,12 +214,18 @@ func TestGenerateFabricEventStream(t *testing.T) { t.Errorf("malformed FabricEventStream, expected %#v, got %#v", fes, result) } - // compare stream returned from API with expected stream listOptions := metav1.ListOptions{ LabelSelector: cluster.labelsSet(true).String(), } streams, err := cluster.KubeClient.FabricEventStreams(namespace).List(context.TODO(), listOptions) assert.NoError(t, err) + + // check if there is only one stream + if len(streams.Items) > 1 { + t.Errorf("too many stream CRDs found: got %d, but expected only one", len(streams.Items)) + } + + // compare stream returned from API with expected stream if match, _ := sameStreams(streams.Items[0].Spec.EventStreams, fes.Spec.EventStreams); !match { t.Errorf("malformed FabricEventStream returned from API, expected %#v, got %#v", fes, streams.Items[0]) } @@ -220,9 +234,15 @@ func TestGenerateFabricEventStream(t *testing.T) { err = cluster.createOrUpdateStreams() assert.NoError(t, err) - // compare stream resturned from API with generated stream streams, err = cluster.KubeClient.FabricEventStreams(namespace).List(context.TODO(), listOptions) assert.NoError(t, err) + + // check if there is still only one stream + if len(streams.Items) > 1 { + t.Errorf("too many stream CRDs found after sync: got %d, but expected only one", len(streams.Items)) + } + + // compare stream resturned from API with generated stream if match, _ := sameStreams(streams.Items[0].Spec.EventStreams, result.Spec.EventStreams); !match { t.Errorf("returned FabricEventStream differs from generated one, expected %#v, got %#v", result, streams.Items[0]) } @@ -335,31 +355,26 @@ func TestUpdateFabricEventStream(t *testing.T) { _, err = cluster.createStatefulSet() assert.NoError(t, err) + // now create the stream err = cluster.createOrUpdateStreams() assert.NoError(t, err) - var pgSpec acidv1.PostgresSpec - pgSpec.Streams = []acidv1.Stream{ - { - ApplicationId: appId, - Database: dbName, - Tables: map[string]acidv1.StreamTable{ - "data.bar": acidv1.StreamTable{ - EventType: "stream-type-c", - IdColumn: k8sutil.StringToPointer("b_id"), - PayloadColumn: k8sutil.StringToPointer("b_payload"), - }, - }, - BatchSize: k8sutil.UInt32ToPointer(uint32(250)), - }, + // change specs of streams and patch CRD + for i, stream := range pg.Spec.Streams { + if stream.ApplicationId == appId { + streamTable := stream.Tables["data.bar"] + streamTable.EventType = "stream-type-c" + stream.Tables["data.bar"] = streamTable + stream.BatchSize = k8sutil.UInt32ToPointer(uint32(250)) + pg.Spec.Streams[i] = stream + } } - patch, err := json.Marshal(struct { - PostgresSpec interface{} `json:"spec"` - }{&pgSpec}) + + patchData, err := specPatch(pg.Spec) assert.NoError(t, err) pgPatched, err := cluster.KubeClient.Postgresqls(namespace).Patch( - context.TODO(), cluster.Name, types.MergePatchType, patch, metav1.PatchOptions{}, "spec") + context.TODO(), cluster.Name, types.MergePatchType, patchData, metav1.PatchOptions{}, "spec") assert.NoError(t, err) cluster.Postgresql.Spec = pgPatched.Spec From 44f08bc21720d2040dfd0512bc835944ec62b511 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilo=20Sch=C3=B6ningh?= <92592425+flyingcamilo@users.noreply.github.com> Date: Fri, 30 Dec 2022 14:45:14 +0100 Subject: [PATCH 48/77] fix: add missing json tags to the MaintenanceWindow type struct. (#2148) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If you import the zalando postgresql v1 api with kubebuilder, it complains about the missing tags. ``` ❯ make manifests test -s /.../bin/controller-gen && /.../bin/controller-gen --version | grep -q v0.10.0 || \ GOBIN=/.../bin go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.10.0 /.../bin/controller-gen rbac:roleName=manager-role crd:allowDangerousTypes=true webhook paths="./..." output:crd:artifacts:config=config/crd/bases /.../go/pkg/mod/github.com/zalando/postgres-operator@v1.8.2/pkg/apis/acid.zalan.do/v1/postgresql_type.go:116:2: encountered struct field "Everyday" without JSON tag in type "MaintenanceWindow" /.../go/pkg/mod/github.com/zalando/postgres-operator@v1.8.2/pkg/apis/acid.zalan.do/v1/postgresql_type.go:117:2: encountered struct field "Weekday" without JSON tag in type "MaintenanceWindow" /.../go/pkg/mod/github.com/zalando/postgres-operator@v1.8.2/pkg/apis/acid.zalan.do/v1/postgresql_type.go:118:2: encountered struct field "StartTime" without JSON tag in type "MaintenanceWindow" /.../go/pkg/mod/github.com/zalando/postgres-operator@v1.8.2/pkg/apis/acid.zalan.do/v1/postgresql_type.go:119:2: encountered struct field "EndTime" without JSON tag in type "MaintenanceWindow" Error: not all generators ran successfully run `controller-gen rbac:roleName=manager-role crd:allowDangerousTypes=true webhook paths=./... output:crd:artifacts:config=config/crd/bases -w` to see all available markers, or `controller-gen rbac:roleName=manager-role crd:allowDangerousTypes=true webhook paths=./... output:crd:artifacts:config=config/crd/bases -h` for usage make: *** [manifests] Error 1 ``` --- pkg/apis/acid.zalan.do/v1/postgresql_type.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/apis/acid.zalan.do/v1/postgresql_type.go b/pkg/apis/acid.zalan.do/v1/postgresql_type.go index dde47622c..19e2f685f 100644 --- a/pkg/apis/acid.zalan.do/v1/postgresql_type.go +++ b/pkg/apis/acid.zalan.do/v1/postgresql_type.go @@ -115,10 +115,10 @@ type PreparedSchema struct { // MaintenanceWindow describes the time window when the operator is allowed to do maintenance on a cluster. type MaintenanceWindow struct { - Everyday bool - Weekday time.Weekday - StartTime metav1.Time // Start time - EndTime metav1.Time // End time + Everyday bool `json:"everyday,omitempty"` + Weekday time.Weekday `json:"weekday,omitempty"` + StartTime metav1.Time `json:"startTime,omitempty"` + EndTime metav1.Time `json:"endTime,omitempty"` } // Volume describes a single volume in the manifest. From a6a49fafc964f6e0a877911f384bb2b31bb5e33a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20Austerm=C3=BChle?= Date: Fri, 30 Dec 2022 18:09:07 +0100 Subject: [PATCH 49/77] Fix parameter name for data-checksums (#2136) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stephan Austermühle --- docs/reference/cluster_manifest.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/cluster_manifest.md b/docs/reference/cluster_manifest.md index 321a70d0b..9e5789d59 100644 --- a/docs/reference/cluster_manifest.md +++ b/docs/reference/cluster_manifest.md @@ -268,7 +268,7 @@ documentation](https://patroni.readthedocs.io/en/latest/SETTINGS.html) for the explanation of `ttl` and `loop_wait` parameters. * **initdb** - a map of key-value pairs describing initdb parameters. For `data-checksum`, + a map of key-value pairs describing initdb parameters. For `data-checksums`, `debug`, `no-locale`, `noclean`, `nosync` and `sync-only` parameters use `true` as the value if you want to set them. Changes to this option do not affect the already initialized clusters. Optional. From 024aab1f13ea04c3accaba236cf7eb5fcad42334 Mon Sep 17 00:00:00 2001 From: Christian Rohmann Date: Mon, 2 Jan 2023 12:57:36 +0100 Subject: [PATCH 50/77] Add config switch to share pg_socket in /var/run/postgresql via an emptyDir with the sidecar containers (#962) --- docs/reference/operator_parameters.md | 6 ++++ docs/user.md | 10 ++++++- manifests/operatorconfiguration.crd.yaml | 3 ++ pkg/apis/acid.zalan.do/v1/crds.go | 3 ++ .../v1/operator_configuration_type.go | 1 + .../acid.zalan.do/v1/zz_generated.deepcopy.go | 5 ++++ pkg/cluster/k8sres.go | 29 +++++++++++++++++++ pkg/controller/operator_config.go | 1 + pkg/util/config/config.go | 1 + 9 files changed, 58 insertions(+), 1 deletion(-) diff --git a/docs/reference/operator_parameters.md b/docs/reference/operator_parameters.md index 763f9da75..42193660d 100644 --- a/docs/reference/operator_parameters.md +++ b/docs/reference/operator_parameters.md @@ -344,6 +344,12 @@ configuration they are grouped under the `kubernetes` key. to run alongside Spilo on the same pod. Globally defined sidecars are always enabled. Default is true. +* **share_pg_socket_with_sidecars** + global option to create an emptyDir volume named `postgresql-run`. This is + mounted by all containers at `/var/run/postgresql` sharing the unix socket of + PostgreSQL (`pg_socket`) with the sidecars this way. + Default is `false`. + * **secret_name_template** a template for the name of the database user secrets generated by the operator. `{namespace}` is replaced with name of the namespace if diff --git a/docs/user.md b/docs/user.md index a80d984f6..e2fa59380 100644 --- a/docs/user.md +++ b/docs/user.md @@ -1006,6 +1006,14 @@ option must be set to `true`. If you want to add a sidecar to every cluster managed by the operator, you can specify it in the [operator configuration](administrator.md#sidecars-for-postgres-clusters) instead. +### Accessing the PostgreSQL socket from sidecars + +If enabled by the `share_pg_socket_with_sidecars` option in the operator +configuration the PostgreSQL socket is placed in a volume of type +`emptyDir` named `postgresql-run`. +To allow access to the socket from any sidecar container simply add a +VolumeMount to this volume to your sidecar spec. + ## InitContainers Support Each cluster can specify arbitrary init containers to run. These containers can @@ -1049,7 +1057,7 @@ When using AWS with gp3 volumes you should set the mode to `mixed` because it will also adjust the IOPS and throughput that can be defined in the manifest. Check the [AWS docs](https://aws.amazon.com/ebs/general-purpose/) to learn about default and maximum values. Keep in mind that AWS rate-limits updating -volume specs to no more than once every 6 hours. +volume specs to no more than once every 6 hours. ```yaml spec: diff --git a/manifests/operatorconfiguration.crd.yaml b/manifests/operatorconfiguration.crd.yaml index 4402602a3..0597c692a 100644 --- a/manifests/operatorconfiguration.crd.yaml +++ b/manifests/operatorconfiguration.crd.yaml @@ -222,6 +222,9 @@ spec: type: array items: type: string + share_pg_socket_with_sidecars: + type: boolean + default: false infrastructure_roles_secret_name: type: string infrastructure_roles_secrets: diff --git a/pkg/apis/acid.zalan.do/v1/crds.go b/pkg/apis/acid.zalan.do/v1/crds.go index f9af7eae5..645a30883 100644 --- a/pkg/apis/acid.zalan.do/v1/crds.go +++ b/pkg/apis/acid.zalan.do/v1/crds.go @@ -1289,6 +1289,9 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{ }, }, }, + "share_pg_socket_with_sidecars": { + Type: "boolean", + }, "infrastructure_roles_secret_name": { Type: "string", }, diff --git a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go index e49b8d7ae..4f6ef96af 100644 --- a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go +++ b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go @@ -72,6 +72,7 @@ type KubernetesMetaConfiguration struct { StorageResizeMode string `json:"storage_resize_mode,omitempty"` EnableInitContainers *bool `json:"enable_init_containers,omitempty"` EnableSidecars *bool `json:"enable_sidecars,omitempty"` + SharePGSocketWithSidecars *bool `json:"share_pgsocket_with_sidecars,omitempty"` SecretNameTemplate config.StringTemplate `json:"secret_name_template,omitempty"` ClusterDomain string `json:"cluster_domain,omitempty"` OAuthTokenSecretName spec.NamespacedName `json:"oauth_token_secret_name,omitempty"` diff --git a/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go b/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go index 2cae5743e..a37ae6c3c 100644 --- a/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go +++ b/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go @@ -193,6 +193,11 @@ func (in *KubernetesMetaConfiguration) DeepCopyInto(out *KubernetesMetaConfigura *out = new(bool) **out = **in } + if in.SharePGSocketWithSidecars != nil { + in, out := &in.SharePGSocketWithSidecars, &out.SharePGSocketWithSidecars + *out = new(bool) + **out = **in + } out.OAuthTokenSecretName = in.OAuthTokenSecretName out.InfrastructureRolesSecretName = in.InfrastructureRolesSecretName if in.InfrastructureRolesDefs != nil { diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index 73f4090d8..6d2b2bdfe 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -713,6 +713,7 @@ func (c *Cluster) generatePodTemplate( spiloContainer *v1.Container, initContainers []v1.Container, sidecarContainers []v1.Container, + sharePGSocketWithSidecars *bool, tolerationsSpec *[]v1.Toleration, spiloRunAsUser *int64, spiloRunAsGroup *int64, @@ -775,6 +776,10 @@ func (c *Cluster) generatePodTemplate( podSpec.PriorityClassName = priorityClassName } + if sharePGSocketWithSidecars != nil && *sharePGSocketWithSidecars { + addVarRunVolume(&podSpec) + } + if additionalSecretMount != "" { addSecretVolume(&podSpec, additionalSecretMount, additionalSecretMountPath) } @@ -1357,6 +1362,7 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef spiloContainer, initContainers, sidecarContainers, + c.OpConfig.SharePGSocketWithSidecars, &tolerationSpec, effectiveRunAsUser, effectiveRunAsGroup, @@ -1550,6 +1556,28 @@ func addShmVolume(podSpec *v1.PodSpec) { podSpec.Volumes = volumes } +func addVarRunVolume(podSpec *v1.PodSpec) { + volumes := append(podSpec.Volumes, v1.Volume{ + Name: "postgresql-run", + VolumeSource: v1.VolumeSource{ + EmptyDir: &v1.EmptyDirVolumeSource{ + Medium: "Memory", + }, + }, + }) + + for i := range podSpec.Containers { + mounts := append(podSpec.Containers[i].VolumeMounts, + v1.VolumeMount{ + Name: "postgresql-run", + MountPath: "/var/run/postgresql", + }) + podSpec.Containers[i].VolumeMounts = mounts + } + + podSpec.Volumes = volumes +} + func addSecretVolume(podSpec *v1.PodSpec, additionalSecretMount string, additionalSecretMountPath string) { volumes := append(podSpec.Volumes, v1.Volume{ Name: additionalSecretMount, @@ -2080,6 +2108,7 @@ func (c *Cluster) generateLogicalBackupJob() (*batchv1.CronJob, error) { logicalBackupContainer, []v1.Container{}, []v1.Container{}, + util.False(), &[]v1.Toleration{}, nil, nil, diff --git a/pkg/controller/operator_config.go b/pkg/controller/operator_config.go index e0107d18c..379a92be5 100644 --- a/pkg/controller/operator_config.go +++ b/pkg/controller/operator_config.go @@ -86,6 +86,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur result.StorageResizeMode = util.Coalesce(fromCRD.Kubernetes.StorageResizeMode, "pvc") result.EnableInitContainers = util.CoalesceBool(fromCRD.Kubernetes.EnableInitContainers, util.True()) result.EnableSidecars = util.CoalesceBool(fromCRD.Kubernetes.EnableSidecars, util.True()) + result.SharePGSocketWithSidecars = util.CoalesceBool(fromCRD.Kubernetes.SharePGSocketWithSidecars, util.False()) result.SecretNameTemplate = fromCRD.Kubernetes.SecretNameTemplate result.OAuthTokenSecretName = fromCRD.Kubernetes.OAuthTokenSecretName result.EnableCrossNamespaceSecret = fromCRD.Kubernetes.EnableCrossNamespaceSecret diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index 374e26292..fcf1f607b 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -212,6 +212,7 @@ type Config struct { EnablePodDisruptionBudget *bool `name:"enable_pod_disruption_budget" default:"true"` EnableInitContainers *bool `name:"enable_init_containers" default:"true"` EnableSidecars *bool `name:"enable_sidecars" default:"true"` + SharePGSocketWithSidecars *bool `name:"share_pg_socket_with_sidecars" default:"false"` Workers uint32 `name:"workers" default:"8"` APIPort int `name:"api_port" default:"8080"` RingLogLines int `name:"ring_log_lines" default:"100"` From 93a253bde12b167797f82137f77977dc61ad0039 Mon Sep 17 00:00:00 2001 From: Dmitry Volodin Date: Mon, 2 Jan 2023 18:00:17 +0300 Subject: [PATCH 51/77] Bump k8s api to v0.23.5 for operator v1.9.0 (#1854) * Bump k8s api to v0.23.5 for operator v1.8.0 * Update k8s version in Makefile --- Makefile | 2 +- go.mod | 39 ++-- go.sum | 189 ++++++++++++++---- pkg/cluster/connection_pooler.go | 2 +- pkg/cluster/k8sres.go | 2 +- .../clientset/versioned/clientset.go | 37 +++- .../acid.zalan.do/v1/acid.zalan.do_client.go | 20 +- .../acid.zalan.do/v1/fake/fake_postgresql.go | 2 +- .../v1/fake/fake_postgresteam.go | 2 +- .../v1/fake/fake_fabriceventstream.go | 2 +- .../zalando.org/v1/zalando.org_client.go | 20 +- 11 files changed, 240 insertions(+), 77 deletions(-) diff --git a/Makefile b/Makefile index 030a8088c..5478854a3 100644 --- a/Makefile +++ b/Makefile @@ -85,7 +85,7 @@ mocks: GO111MODULE=on go generate ./... tools: - GO111MODULE=on go get -d k8s.io/client-go@kubernetes-1.22.4 + GO111MODULE=on go get -d k8s.io/client-go@kubernetes-1.23.5 GO111MODULE=on go install github.com/golang/mock/mockgen@v1.6.0 GO111MODULE=on go mod tidy diff --git a/go.mod b/go.mod index 26e629beb..79c82a10c 100644 --- a/go.mod +++ b/go.mod @@ -13,11 +13,11 @@ require ( github.com/stretchr/testify v1.7.0 golang.org/x/crypto v0.0.0-20211202192323-5770296d904e gopkg.in/yaml.v2 v2.4.0 - k8s.io/api v0.22.4 - k8s.io/apiextensions-apiserver v0.22.4 - k8s.io/apimachinery v0.22.4 - k8s.io/client-go v0.22.4 - k8s.io/code-generator v0.22.4 + k8s.io/api v0.23.5 + k8s.io/apiextensions-apiserver v0.23.5 + k8s.io/apimachinery v0.23.5 + k8s.io/client-go v0.23.5 + k8s.io/code-generator v0.23.5 ) require ( @@ -25,8 +25,8 @@ require ( github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/emicklei/go-restful v2.9.5+incompatible // indirect - github.com/evanphx/json-patch v4.11.0+incompatible // indirect - github.com/go-logr/logr v0.4.0 // indirect + github.com/evanphx/json-patch v4.12.0+incompatible // indirect + github.com/go-logr/logr v1.2.0 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.19.5 // indirect github.com/go-openapi/swag v0.19.14 // indirect @@ -39,31 +39,32 @@ require ( github.com/imdario/mergo v0.3.5 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect - github.com/json-iterator/go v1.1.11 // indirect + github.com/json-iterator/go v1.1.12 // indirect github.com/kr/text v0.2.0 // indirect github.com/mailru/easyjson v0.7.6 // indirect github.com/moby/spdystream v0.2.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.1 // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect golang.org/x/mod v0.5.1 // indirect - golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect - golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602 // indirect + golang.org/x/net v0.0.0-20211209124913-491a49abca63 // indirect + golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f // indirect golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 // indirect - golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect - golang.org/x/text v0.3.6 // indirect + golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b // indirect + golang.org/x/text v0.3.7 // indirect golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect golang.org/x/tools v0.1.7 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.26.0 // indirect + google.golang.org/protobuf v1.27.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect - k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027 // indirect - k8s.io/klog/v2 v2.9.0 // indirect - k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c // indirect - k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect + k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c // indirect + k8s.io/klog/v2 v2.30.0 // indirect + k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 // indirect + k8s.io/utils v0.0.0-20211116205334-6203023598ed // indirect + sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect sigs.k8s.io/yaml v1.2.0 // indirect ) diff --git a/go.sum b/go.sum index fda15408b..1b68e78c2 100644 --- a/go.sum +++ b/go.sum @@ -13,6 +13,11 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -56,6 +61,7 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210826220005-b48c857c3a0e/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= @@ -63,11 +69,13 @@ github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:l github.com/aws/aws-sdk-go v1.42.18 h1:2f/cDNwQ3e+yHxtPn1si0to3GalbNHwkRm461IjwRiM= github.com/aws/aws-sdk-go v1.42.18/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= +github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= @@ -79,7 +87,9 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= @@ -108,11 +118,13 @@ github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch v4.11.0+incompatible h1:glyUF9yIYtMHzn8xaKw5rMhdWcwsYV8dZHIq5567/xs= -github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= +github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= @@ -120,6 +132,7 @@ github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoD github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -133,8 +146,9 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc= -github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v1.2.0 h1:QK40JKJyMdUDz+h+xvCsru/bJhvG0UxvePV0ufL/AcE= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/zapr v1.2.0/go.mod h1:Qa4Bsj2Vb+FAVeAKsLD8RLQ+YRJB8YDmOAKxaBQf7Ro= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= @@ -152,6 +166,7 @@ github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -165,6 +180,7 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -188,6 +204,8 @@ github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/cel-go v0.9.0/go.mod h1:U7ayypeSkw23szu4GaQTPJGx66c20mx8JklMSxrmI1w= +github.com/google/cel-spec v0.6.0/go.mod h1:Nwjgxy5CbjlPrtCWjeDjUyKMl8w41YBYGjsyDdqk0xA= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -195,6 +213,8 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -203,6 +223,7 @@ github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -210,8 +231,11 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= @@ -220,6 +244,7 @@ github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2c github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw= github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= @@ -249,6 +274,7 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= @@ -263,8 +289,9 @@ github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFF github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= @@ -276,6 +303,7 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= @@ -287,6 +315,7 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= @@ -304,6 +333,7 @@ github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS4 github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= @@ -311,8 +341,9 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/motomux/pretty v0.0.0-20161209205251-b2aad2c9a95d h1:LznySqW8MqVeFh+pW6rOkFdld9QQ7jRydBKKM6jyPVI= github.com/motomux/pretty v0.0.0-20161209205251-b2aad2c9a95d/go.mod h1:u3hJ0kqCQu/cPpsu3RbCOPZ0d7V3IjPjv1adNRleM9I= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= @@ -337,11 +368,13 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= @@ -360,6 +393,7 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.28.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= @@ -388,14 +422,18 @@ github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= +github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -430,6 +468,8 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4= @@ -449,15 +489,17 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/ go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211202192323-5770296d904e h1:MUP6MR3rJ7Gk9LEia0LP2ytiH6MuCfs7qYz+47jGdD8= golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -482,6 +524,7 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= @@ -491,6 +534,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= @@ -529,20 +574,36 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE= +golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211209124913-491a49abca63 h1:iocB37TsdFuN6IBRZ+ry36wrkoV51/tl5vOWqkcPGvY= +golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602 h1:0Ja1LBD+yisY6RWM/BH7TJVXWsSjs2VwBSmvSX4HdBc= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f h1:Qmd2pbz05z7z6lm0DrgQVVPuBm92jqujBKMHMOlOQEw= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -595,10 +656,18 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -607,20 +676,22 @@ golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 h1:TyHqChC80pFkXWraUUf6RuB5IqFdQieMLwwCJokV2pc= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE= -golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE= +golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -675,9 +746,17 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.6-0.20210820212750-d4cc65f0b2ff/go.mod h1:YD9qOF0M9xpSpdWTBbzEl5e/RnCefISl8E5Noe10jFM= golang.org/x/tools v0.1.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -701,6 +780,12 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -740,8 +825,20 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201102152239-715cce707fb0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -754,10 +851,16 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -769,8 +872,9 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -782,6 +886,7 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= @@ -810,35 +915,37 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.22.4 h1:UvyHW0ezB2oIgHAxlYoo6UJQObYXU7awuNarwoHEOjw= -k8s.io/api v0.22.4/go.mod h1:Rgs+9gIGYC5laXQSZZ9JqT5NevNgoGiOdVWi1BAB3qk= -k8s.io/apiextensions-apiserver v0.22.4 h1:2iGpcVyw4MnAyyXVJU2Xg6ZsbIxAOfRHo0LF5A5J0RA= -k8s.io/apiextensions-apiserver v0.22.4/go.mod h1:kH9lxD8dbJ+k0ZizGET55lFgdGjO8t45fgZnCVdZEpw= -k8s.io/apimachinery v0.22.4 h1:9uwcvPpukBw/Ri0EUmWz+49cnFtaoiyEhQTK+xOe7Ck= -k8s.io/apimachinery v0.22.4/go.mod h1:yU6oA6Gnax9RrxGzVvPFFJ+mpnW6PBSqp0sx0I0HHW0= -k8s.io/apiserver v0.22.4/go.mod h1:38WmcUZiiy41A7Aty8/VorWRa8vDGqoUzDf2XYlku0E= -k8s.io/client-go v0.22.4 h1:aAQ1Wk+I3bjCNk35YWUqbaueqrIonkfDPJSPDDe8Kfg= -k8s.io/client-go v0.22.4/go.mod h1:Yzw4e5e7h1LNHA4uqnMVrpEpUs1hJOiuBsJKIlRCHDA= -k8s.io/code-generator v0.22.4 h1:h7lBa5IuEUC4OQ45q/gIip/a0iQcML2iwrRmXksau30= -k8s.io/code-generator v0.22.4/go.mod h1:qjYl54pQ/emhkT0UxbufbREYJMWsHNNV/jSVwhYZQGw= -k8s.io/component-base v0.22.4/go.mod h1:MrSaQy4a3tFVViff8TZL6JHYSewNCLshZCwHYM58v5A= -k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027 h1:Uusb3oh8XcdzDF/ndlI4ToKTYVlkCSJP39SRY2mfRAw= -k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/api v0.23.5 h1:zno3LUiMubxD/V1Zw3ijyKO3wxrhbUF1Ck+VjBvfaoA= +k8s.io/api v0.23.5/go.mod h1:Na4XuKng8PXJ2JsploYYrivXrINeTaycCGcYgF91Xm8= +k8s.io/apiextensions-apiserver v0.23.5 h1:5SKzdXyvIJKu+zbfPc3kCbWpbxi+O+zdmAJBm26UJqI= +k8s.io/apiextensions-apiserver v0.23.5/go.mod h1:ntcPWNXS8ZPKN+zTXuzYMeg731CP0heCTl6gYBxLcuQ= +k8s.io/apimachinery v0.23.5 h1:Va7dwhp8wgkUPWsEXk6XglXWU4IKYLKNlv8VkX7SDM0= +k8s.io/apimachinery v0.23.5/go.mod h1:BEuFMMBaIbcOqVIJqNZJXGFTP4W6AycEpb5+m/97hrM= +k8s.io/apiserver v0.23.5/go.mod h1:7wvMtGJ42VRxzgVI7jkbKvMbuCbVbgsWFT7RyXiRNTw= +k8s.io/client-go v0.23.5 h1:zUXHmEuqx0RY4+CsnkOn5l0GU+skkRXKGJrhmE2SLd8= +k8s.io/client-go v0.23.5/go.mod h1:flkeinTO1CirYgzMPRWxUCnV0G4Fbu2vLhYCObnt/r4= +k8s.io/code-generator v0.23.5 h1:xn3a6J5pUL49AoH6SPrOFtnB5cvdMl76f/bEY176R3c= +k8s.io/code-generator v0.23.5/go.mod h1:S0Q1JVA+kSzTI1oUvbKAxZY/DYbA/ZUb4Uknog12ETk= +k8s.io/component-base v0.23.5/go.mod h1:c5Nq44KZyt1aLl0IpHX82fhsn84Sb0jjzwjpcA42bY0= +k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c h1:GohjlNKauSai7gN4wsJkeZ3WAJx4Sh+oT/b5IYn5suA= +k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.9.0 h1:D7HV+n1V57XeZ0m6tdRkfknthUaM06VFbWldOFh8kzM= -k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= -k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c h1:jvamsI1tn9V0S8jicyX82qaFC0H/NKxv2e5mbqsgR80= -k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a h1:8dYfu/Fc9Gz2rNJKB9IQRGgQOh2clmRzNIPPY1xLY5g= -k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/klog/v2 v2.30.0 h1:bUO6drIvCIsvZ/XFgfxoGFQU/a4Qkh0iAlvUR7vlHJw= +k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 h1:E3J9oCLlaobFUqsjG9DfKbP2BmgwBL2p7pn0A3dG9W4= +k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk= +k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20211116205334-6203023598ed h1:ck1fRPWPJWsMd8ZRFsWc6mh/zHp5fZ/shhbrgPUxDAE= +k8s.io/utils v0.0.0-20211116205334-6203023598ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.30/go.mod h1:fEO7lRTdivWO2qYVCVG7dEADOMo/MLDCVr8So2g88Uw= +sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 h1:fD1pz4yfdADVNfFmcP2aBEtudwUQ1AlLnRBALr33v3s= +sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno= -sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= +sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y= +sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/pkg/cluster/connection_pooler.go b/pkg/cluster/connection_pooler.go index a8214dff4..c83c41233 100644 --- a/pkg/cluster/connection_pooler.go +++ b/pkg/cluster/connection_pooler.go @@ -325,7 +325,7 @@ func (c *Cluster) generateConnectionPoolerPodTemplate(role PostgresRole) ( }, Env: envVars, ReadinessProbe: &v1.Probe{ - Handler: v1.Handler{ + ProbeHandler: v1.ProbeHandler{ TCPSocket: &v1.TCPSocketAction{ Port: intstr.IntOrString{IntVal: pgPort}, }, diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index 6d2b2bdfe..8836e2e5f 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -1129,7 +1129,7 @@ func extractPgVersionFromBinPath(binPath string, template string) (string, error func generateSpiloReadinessProbe() *v1.Probe { return &v1.Probe{ FailureThreshold: 3, - Handler: v1.Handler{ + ProbeHandler: v1.ProbeHandler{ HTTPGet: &v1.HTTPGetAction{ Path: "/readiness", Port: intstr.IntOrString{IntVal: patroni.ApiPort}, diff --git a/pkg/generated/clientset/versioned/clientset.go b/pkg/generated/clientset/versioned/clientset.go index 50f0ac841..ccda2baff 100644 --- a/pkg/generated/clientset/versioned/clientset.go +++ b/pkg/generated/clientset/versioned/clientset.go @@ -26,6 +26,7 @@ package versioned import ( "fmt" + "net/http" acidv1 "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1" zalandov1 "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/zalando.org/v1" @@ -69,26 +70,45 @@ func (c *Clientset) Discovery() discovery.DiscoveryInterface { // NewForConfig creates a new Clientset for the given config. // If config's RateLimiter is not set and QPS and Burst are acceptable, // NewForConfig will generate a rate-limiter in configShallowCopy. +// NewForConfig is equivalent to NewForConfigAndClient(c, httpClient), +// where httpClient was generated with rest.HTTPClientFor(c). func NewForConfig(c *rest.Config) (*Clientset, error) { configShallowCopy := *c + + // share the transport between all clients + httpClient, err := rest.HTTPClientFor(&configShallowCopy) + if err != nil { + return nil, err + } + + return NewForConfigAndClient(&configShallowCopy, httpClient) +} + +// NewForConfigAndClient creates a new Clientset for the given config and http client. +// Note the http client provided takes precedence over the configured transport values. +// If config's RateLimiter is not set and QPS and Burst are acceptable, +// NewForConfigAndClient will generate a rate-limiter in configShallowCopy. +func NewForConfigAndClient(c *rest.Config, httpClient *http.Client) (*Clientset, error) { + configShallowCopy := *c if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { if configShallowCopy.Burst <= 0 { return nil, fmt.Errorf("burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0") } configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) } + var cs Clientset var err error - cs.acidV1, err = acidv1.NewForConfig(&configShallowCopy) + cs.acidV1, err = acidv1.NewForConfigAndClient(&configShallowCopy, httpClient) if err != nil { return nil, err } - cs.zalandoV1, err = zalandov1.NewForConfig(&configShallowCopy) + cs.zalandoV1, err = zalandov1.NewForConfigAndClient(&configShallowCopy, httpClient) if err != nil { return nil, err } - cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy) + cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfigAndClient(&configShallowCopy, httpClient) if err != nil { return nil, err } @@ -98,12 +118,11 @@ func NewForConfig(c *rest.Config) (*Clientset, error) { // NewForConfigOrDie creates a new Clientset for the given config and // panics if there is an error in the config. func NewForConfigOrDie(c *rest.Config) *Clientset { - var cs Clientset - cs.acidV1 = acidv1.NewForConfigOrDie(c) - cs.zalandoV1 = zalandov1.NewForConfigOrDie(c) - - cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c) - return &cs + cs, err := NewForConfig(c) + if err != nil { + panic(err) + } + return cs } // New creates a new Clientset for the given RESTClient. diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/acid.zalan.do_client.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/acid.zalan.do_client.go index d36078922..0d7c2c7ed 100644 --- a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/acid.zalan.do_client.go +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/acid.zalan.do_client.go @@ -25,6 +25,8 @@ SOFTWARE. package v1 import ( + "net/http" + v1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/scheme" rest "k8s.io/client-go/rest" @@ -55,12 +57,28 @@ func (c *AcidV1Client) Postgresqls(namespace string) PostgresqlInterface { } // NewForConfig creates a new AcidV1Client for the given config. +// NewForConfig is equivalent to NewForConfigAndClient(c, httpClient), +// where httpClient was generated with rest.HTTPClientFor(c). func NewForConfig(c *rest.Config) (*AcidV1Client, error) { config := *c if err := setConfigDefaults(&config); err != nil { return nil, err } - client, err := rest.RESTClientFor(&config) + httpClient, err := rest.HTTPClientFor(&config) + if err != nil { + return nil, err + } + return NewForConfigAndClient(&config, httpClient) +} + +// NewForConfigAndClient creates a new AcidV1Client for the given config and http client. +// Note the http client provided takes precedence over the configured transport values. +func NewForConfigAndClient(c *rest.Config, h *http.Client) (*AcidV1Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + client, err := rest.RESTClientForConfigAndClient(&config, h) if err != nil { return nil, err } diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_postgresql.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_postgresql.go index cb39ea290..c9000a90c 100644 --- a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_postgresql.go +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_postgresql.go @@ -123,7 +123,7 @@ func (c *FakePostgresqls) UpdateStatus(ctx context.Context, postgresql *acidzala // Delete takes name of the postgresql and deletes it. Returns an error if one occurs. func (c *FakePostgresqls) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { _, err := c.Fake. - Invokes(testing.NewDeleteAction(postgresqlsResource, c.ns, name), &acidzalandov1.Postgresql{}) + Invokes(testing.NewDeleteActionWithOptions(postgresqlsResource, c.ns, name, opts), &acidzalandov1.Postgresql{}) return err } diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_postgresteam.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_postgresteam.go index 59cf600aa..c70b00e94 100644 --- a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_postgresteam.go +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_postgresteam.go @@ -111,7 +111,7 @@ func (c *FakePostgresTeams) Update(ctx context.Context, postgresTeam *acidzaland // Delete takes name of the postgresTeam and deletes it. Returns an error if one occurs. func (c *FakePostgresTeams) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { _, err := c.Fake. - Invokes(testing.NewDeleteAction(postgresteamsResource, c.ns, name), &acidzalandov1.PostgresTeam{}) + Invokes(testing.NewDeleteActionWithOptions(postgresteamsResource, c.ns, name, opts), &acidzalandov1.PostgresTeam{}) return err } diff --git a/pkg/generated/clientset/versioned/typed/zalando.org/v1/fake/fake_fabriceventstream.go b/pkg/generated/clientset/versioned/typed/zalando.org/v1/fake/fake_fabriceventstream.go index 8d76e06de..b0c592b92 100644 --- a/pkg/generated/clientset/versioned/typed/zalando.org/v1/fake/fake_fabriceventstream.go +++ b/pkg/generated/clientset/versioned/typed/zalando.org/v1/fake/fake_fabriceventstream.go @@ -111,7 +111,7 @@ func (c *FakeFabricEventStreams) Update(ctx context.Context, fabricEventStream * // Delete takes name of the fabricEventStream and deletes it. Returns an error if one occurs. func (c *FakeFabricEventStreams) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { _, err := c.Fake. - Invokes(testing.NewDeleteAction(fabriceventstreamsResource, c.ns, name), &zalandoorgv1.FabricEventStream{}) + Invokes(testing.NewDeleteActionWithOptions(fabriceventstreamsResource, c.ns, name, opts), &zalandoorgv1.FabricEventStream{}) return err } diff --git a/pkg/generated/clientset/versioned/typed/zalando.org/v1/zalando.org_client.go b/pkg/generated/clientset/versioned/typed/zalando.org/v1/zalando.org_client.go index 937fd6b7e..1937dd78f 100644 --- a/pkg/generated/clientset/versioned/typed/zalando.org/v1/zalando.org_client.go +++ b/pkg/generated/clientset/versioned/typed/zalando.org/v1/zalando.org_client.go @@ -25,6 +25,8 @@ SOFTWARE. package v1 import ( + "net/http" + v1 "github.com/zalando/postgres-operator/pkg/apis/zalando.org/v1" "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/scheme" rest "k8s.io/client-go/rest" @@ -45,12 +47,28 @@ func (c *ZalandoV1Client) FabricEventStreams(namespace string) FabricEventStream } // NewForConfig creates a new ZalandoV1Client for the given config. +// NewForConfig is equivalent to NewForConfigAndClient(c, httpClient), +// where httpClient was generated with rest.HTTPClientFor(c). func NewForConfig(c *rest.Config) (*ZalandoV1Client, error) { config := *c if err := setConfigDefaults(&config); err != nil { return nil, err } - client, err := rest.RESTClientFor(&config) + httpClient, err := rest.HTTPClientFor(&config) + if err != nil { + return nil, err + } + return NewForConfigAndClient(&config, httpClient) +} + +// NewForConfigAndClient creates a new ZalandoV1Client for the given config and http client. +// Note the http client provided takes precedence over the configured transport values. +func NewForConfigAndClient(c *rest.Config, h *http.Client) (*ZalandoV1Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + client, err := rest.RESTClientForConfigAndClient(&config, h) if err != nil { return nil, err } From be7b52db92d774c196ddb62a9de6fcbcfac7228e Mon Sep 17 00:00:00 2001 From: Francois Parquet Date: Mon, 2 Jan 2023 18:22:47 +0100 Subject: [PATCH 52/77] add preferred during scheduling pod anti affinity (#2048) * add preferred during scheduling pod anti affinity Co-authored-by: Felix Kunde --- .../crds/operatorconfigurations.yaml | 3 + charts/postgres-operator/values.yaml | 2 + docs/administrator.md | 7 +- pkg/apis/acid.zalan.do/v1/crds.go | 3 + .../v1/operator_configuration_type.go | 21 +-- pkg/cluster/connection_pooler.go | 7 +- pkg/cluster/k8sres.go | 38 +++-- pkg/cluster/k8sres_test.go | 89 ++++++++++++ pkg/controller/operator_config.go | 1 + pkg/util/config/config.go | 133 +++++++++--------- 10 files changed, 215 insertions(+), 89 deletions(-) diff --git a/charts/postgres-operator/crds/operatorconfigurations.yaml b/charts/postgres-operator/crds/operatorconfigurations.yaml index 472f15720..d0c2dca66 100644 --- a/charts/postgres-operator/crds/operatorconfigurations.yaml +++ b/charts/postgres-operator/crds/operatorconfigurations.yaml @@ -281,6 +281,9 @@ spec: pod_antiaffinity_topology_key: type: string default: "kubernetes.io/hostname" + pod_antiaffinity_preferred_during_scheduling: + type: boolean + default: false pod_environment_configmap: type: string pod_environment_secret: diff --git a/charts/postgres-operator/values.yaml b/charts/postgres-operator/values.yaml index 37dc9c2e4..50977bd8a 100644 --- a/charts/postgres-operator/values.yaml +++ b/charts/postgres-operator/values.yaml @@ -167,6 +167,8 @@ configKubernetes: pdb_name_format: "postgres-{cluster}-pdb" # override topology key for pod anti affinity pod_antiaffinity_topology_key: "kubernetes.io/hostname" + # switches pod anti affinity type to `preferredDuringSchedulingIgnoredDuringExecution` + # pod_antiaffinity_preferred_during_scheduling: true # namespaced name of the ConfigMap with environment variables to populate on every pod # pod_environment_configmap: "default/my-custom-config" # name of the Secret (in cluster namespace) with environment variables to populate on every pod diff --git a/docs/administrator.md b/docs/administrator.md index 1a0f730b8..cd56289b4 100644 --- a/docs/administrator.md +++ b/docs/administrator.md @@ -15,7 +15,7 @@ CRDs set `enable_crd_registration` config option to `false`. CRDs are defined with a `openAPIV3Schema` structural schema against which new manifests of [`postgresql`](https://github.com/zalando/postgres-operator/blob/master/manifests/postgresql.crd.yaml) or [`OperatorConfiguration`](https://github.com/zalando/postgres-operator/blob/master/manifests/operatorconfiguration.crd.yaml) -resources will be validated. On creation you can bypass the validation with +resources will be validated. On creation you can bypass the validation with `kubectl create --validate=false`. By default, the operator will register the CRDs in the `all` category so @@ -516,6 +516,9 @@ configuration: enable_pod_antiaffinity: true ``` +By default the type of pod anti affinity is `requiredDuringSchedulingIgnoredDuringExecution`, +you can switch to `preferredDuringSchedulingIgnoredDuringExecution` by setting `pod_antiaffinity_preferred_during_scheduling: true`. + By default the topology key for the pod anti affinity is set to `kubernetes.io/hostname`, you can set another topology key e.g. `failure-domain.beta.kubernetes.io/zone`. See [built-in node labels](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#interlude-built-in-node-labels) for available topology keys. @@ -1091,7 +1094,7 @@ data: USE_WALG_BACKUP: "true" USE_WALG_RESTORE: "true" CLONE_USE_WALG_RESTORE: "true" - WALG_AZ_PREFIX: "azure://container-name/$(SCOPE)/$(PGVERSION)" # Enables Azure Backups (SCOPE = Cluster name) (PGVERSION = Postgres version) + WALG_AZ_PREFIX: "azure://container-name/$(SCOPE)/$(PGVERSION)" # Enables Azure Backups (SCOPE = Cluster name) (PGVERSION = Postgres version) ``` 3. Setup your operator configuration values. With the `psql-backup-creds` diff --git a/pkg/apis/acid.zalan.do/v1/crds.go b/pkg/apis/acid.zalan.do/v1/crds.go index 645a30883..84028bb20 100644 --- a/pkg/apis/acid.zalan.do/v1/crds.go +++ b/pkg/apis/acid.zalan.do/v1/crds.go @@ -1378,6 +1378,9 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{ "pod_antiaffinity_topology_key": { Type: "string", }, + "pod_antiaffinity_preferred_during_scheduling": { + Type: "boolean", + }, "pod_environment_configmap": { Type: "string", }, diff --git a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go index 4f6ef96af..b3251b217 100644 --- a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go +++ b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go @@ -91,16 +91,17 @@ type KubernetesMetaConfiguration struct { NodeReadinessLabelMerge string `json:"node_readiness_label_merge,omitempty"` CustomPodAnnotations map[string]string `json:"custom_pod_annotations,omitempty"` // TODO: use a proper toleration structure? - PodToleration map[string]string `json:"toleration,omitempty"` - PodEnvironmentConfigMap spec.NamespacedName `json:"pod_environment_configmap,omitempty"` - PodEnvironmentSecret string `json:"pod_environment_secret,omitempty"` - PodPriorityClassName string `json:"pod_priority_class_name,omitempty"` - MasterPodMoveTimeout Duration `json:"master_pod_move_timeout,omitempty"` - EnablePodAntiAffinity bool `json:"enable_pod_antiaffinity,omitempty"` - PodAntiAffinityTopologyKey string `json:"pod_antiaffinity_topology_key,omitempty"` - PodManagementPolicy string `json:"pod_management_policy,omitempty"` - EnableReadinessProbe bool `json:"enable_readiness_probe,omitempty"` - EnableCrossNamespaceSecret bool `json:"enable_cross_namespace_secret,omitempty"` + PodToleration map[string]string `json:"toleration,omitempty"` + PodEnvironmentConfigMap spec.NamespacedName `json:"pod_environment_configmap,omitempty"` + PodEnvironmentSecret string `json:"pod_environment_secret,omitempty"` + PodPriorityClassName string `json:"pod_priority_class_name,omitempty"` + MasterPodMoveTimeout Duration `json:"master_pod_move_timeout,omitempty"` + EnablePodAntiAffinity bool `json:"enable_pod_antiaffinity,omitempty"` + PodAntiAffinityTopologyKey string `json:"pod_antiaffinity_topology_key,omitempty"` + PodAntiAffinityPreferredDuringScheduling bool `json:"pod_antiaffinity_preferred_during_scheduling,omitempty"` + PodManagementPolicy string `json:"pod_management_policy,omitempty"` + EnableReadinessProbe bool `json:"enable_readiness_probe,omitempty"` + EnableCrossNamespaceSecret bool `json:"enable_cross_namespace_secret,omitempty"` } // PostgresPodResourcesDefaults defines the spec of default resources diff --git a/pkg/cluster/connection_pooler.go b/pkg/cluster/connection_pooler.go index c83c41233..fd8b9251c 100644 --- a/pkg/cluster/connection_pooler.go +++ b/pkg/cluster/connection_pooler.go @@ -354,7 +354,12 @@ func (c *Cluster) generateConnectionPoolerPodTemplate(role PostgresRole) ( nodeAffinity := c.nodeAffinity(c.OpConfig.NodeReadinessLabel, spec.NodeAffinity) if c.OpConfig.EnablePodAntiAffinity { labelsSet := labels.Set(c.connectionPoolerLabels(role, false).MatchLabels) - podTemplate.Spec.Affinity = generatePodAffinity(labelsSet, c.OpConfig.PodAntiAffinityTopologyKey, nodeAffinity) + podTemplate.Spec.Affinity = generatePodAffinity( + labelsSet, + c.OpConfig.PodAntiAffinityTopologyKey, + nodeAffinity, + c.OpConfig.PodAntiAffinityPreferredDuringScheduling, + ) } else if nodeAffinity != nil { podTemplate.Spec.Affinity = nodeAffinity } diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index 8836e2e5f..6b7688816 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -495,17 +495,27 @@ func (c *Cluster) nodeAffinity(nodeReadinessLabel map[string]string, nodeAffinit } } -func generatePodAffinity(labels labels.Set, topologyKey string, nodeAffinity *v1.Affinity) *v1.Affinity { +func generatePodAffinity(labels labels.Set, topologyKey string, nodeAffinity *v1.Affinity, preferredDuringScheduling bool) *v1.Affinity { // generate pod anti-affinity to avoid multiple pods of the same Postgres cluster in the same topology , e.g. node - podAffinity := v1.Affinity{ - PodAntiAffinity: &v1.PodAntiAffinity{ - RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{{ - LabelSelector: &metav1.LabelSelector{ - MatchLabels: labels, - }, - TopologyKey: topologyKey, - }}, + + podAffinityTerm := v1.PodAffinityTerm{ + LabelSelector: &metav1.LabelSelector{ + MatchLabels: labels, }, + TopologyKey: topologyKey, + } + + podAffinity := v1.Affinity{ + PodAntiAffinity: &v1.PodAntiAffinity{}, + } + + if preferredDuringScheduling { + podAffinity.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution = []v1.WeightedPodAffinityTerm{{ + Weight: 1, + PodAffinityTerm: podAffinityTerm, + }} + } else { + podAffinity.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution = []v1.PodAffinityTerm{podAffinityTerm} } if nodeAffinity != nil && nodeAffinity.NodeAffinity != nil { @@ -727,6 +737,7 @@ func (c *Cluster) generatePodTemplate( shmVolume *bool, podAntiAffinity bool, podAntiAffinityTopologyKey string, + podAntiAffinityPreferredDuringScheduling bool, additionalSecretMount string, additionalSecretMountPath string, additionalVolumes []acidv1.AdditionalVolume, @@ -767,7 +778,12 @@ func (c *Cluster) generatePodTemplate( } if podAntiAffinity { - podSpec.Affinity = generatePodAffinity(labels, podAntiAffinityTopologyKey, nodeAffinity) + podSpec.Affinity = generatePodAffinity( + labels, + podAntiAffinityTopologyKey, + nodeAffinity, + podAntiAffinityPreferredDuringScheduling, + ) } else if nodeAffinity != nil { podSpec.Affinity = nodeAffinity } @@ -1376,6 +1392,7 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef mountShmVolumeNeeded(c.OpConfig, spec), c.OpConfig.EnablePodAntiAffinity, c.OpConfig.PodAntiAffinityTopologyKey, + c.OpConfig.PodAntiAffinityPreferredDuringScheduling, c.OpConfig.AdditionalSecretMount, c.OpConfig.AdditionalSecretMountPath, additionalVolumes) @@ -2122,6 +2139,7 @@ func (c *Cluster) generateLogicalBackupJob() (*batchv1.CronJob, error) { util.False(), false, "", + false, c.OpConfig.AdditionalSecretMount, c.OpConfig.AdditionalSecretMountPath, []acidv1.AdditionalVolume{}); err != nil { diff --git a/pkg/cluster/k8sres_test.go b/pkg/cluster/k8sres_test.go index 223fdb787..cd8bf14e7 100644 --- a/pkg/cluster/k8sres_test.go +++ b/pkg/cluster/k8sres_test.go @@ -1360,6 +1360,95 @@ func TestNodeAffinity(t *testing.T) { assert.Equal(t, s.Spec.Template.Spec.Affinity.NodeAffinity, nodeAff, "cluster template has correct node affinity") } +func TestPodAntiAffinityrRequiredDuringScheduling(t *testing.T) { + var err error + var spiloRunAsUser = int64(101) + var spiloRunAsGroup = int64(103) + var spiloFSGroup = int64(103) + + spec := acidv1.PostgresSpec{ + TeamID: "myapp", NumberOfInstances: 1, + Resources: &acidv1.Resources{ + ResourceRequests: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, + ResourceLimits: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, + }, + Volume: acidv1.Volume{ + Size: "1G", + }, + } + + cluster := New( + Config{ + OpConfig: config.Config{ + PodManagementPolicy: "ordered_ready", + ProtectedRoles: []string{"admin"}, + Auth: config.Auth{ + SuperUsername: superUserName, + ReplicationUsername: replicationUserName, + }, + Resources: config.Resources{ + SpiloRunAsUser: &spiloRunAsUser, + SpiloRunAsGroup: &spiloRunAsGroup, + SpiloFSGroup: &spiloFSGroup, + }, + EnablePodAntiAffinity: true, + }, + }, k8sutil.KubernetesClient{}, acidv1.Postgresql{}, logger, eventRecorder) + + s, err := cluster.generateStatefulSet(&spec) + if err != nil { + assert.NoError(t, err) + } + + assert.Nil(t, s.Spec.Template.Spec.Affinity.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution, "pod anti-affinity should not use preferredDuringScheduling") + assert.NotNil(t, s.Spec.Template.Spec.Affinity.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution, "pod anti-affinity should use requiredDuringScheduling") +} + +func TestPodAntiAffinityPreferredDuringScheduling(t *testing.T) { + var err error + var spiloRunAsUser = int64(101) + var spiloRunAsGroup = int64(103) + var spiloFSGroup = int64(103) + + spec := acidv1.PostgresSpec{ + TeamID: "myapp", NumberOfInstances: 1, + Resources: &acidv1.Resources{ + ResourceRequests: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, + ResourceLimits: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, + }, + Volume: acidv1.Volume{ + Size: "1G", + }, + } + + cluster := New( + Config{ + OpConfig: config.Config{ + PodManagementPolicy: "ordered_ready", + ProtectedRoles: []string{"admin"}, + Auth: config.Auth{ + SuperUsername: superUserName, + ReplicationUsername: replicationUserName, + }, + Resources: config.Resources{ + SpiloRunAsUser: &spiloRunAsUser, + SpiloRunAsGroup: &spiloRunAsGroup, + SpiloFSGroup: &spiloFSGroup, + }, + EnablePodAntiAffinity: true, + PodAntiAffinityPreferredDuringScheduling: true, + }, + }, k8sutil.KubernetesClient{}, acidv1.Postgresql{}, logger, eventRecorder) + + s, err := cluster.generateStatefulSet(&spec) + if err != nil { + assert.NoError(t, err) + } + + assert.NotNil(t, s.Spec.Template.Spec.Affinity.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution, "pod anti-affinity should use preferredDuringScheduling") + assert.Nil(t, s.Spec.Template.Spec.Affinity.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution, "pod anti-affinity should not use requiredDuringScheduling") +} + func testDeploymentOwnerReference(cluster *Cluster, deployment *appsv1.Deployment) error { owner := deployment.ObjectMeta.OwnerReferences[0] diff --git a/pkg/controller/operator_config.go b/pkg/controller/operator_config.go index 379a92be5..4e347b079 100644 --- a/pkg/controller/operator_config.go +++ b/pkg/controller/operator_config.go @@ -123,6 +123,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur result.MasterPodMoveTimeout = util.CoalesceDuration(time.Duration(fromCRD.Kubernetes.MasterPodMoveTimeout), "10m") result.EnablePodAntiAffinity = fromCRD.Kubernetes.EnablePodAntiAffinity result.PodAntiAffinityTopologyKey = util.Coalesce(fromCRD.Kubernetes.PodAntiAffinityTopologyKey, "kubernetes.io/hostname") + result.PodAntiAffinityPreferredDuringScheduling = fromCRD.Kubernetes.PodAntiAffinityPreferredDuringScheduling result.PodToleration = fromCRD.Kubernetes.PodToleration // Postgres Pod resources diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index fcf1f607b..75c7043cb 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -170,72 +170,73 @@ type Config struct { SidecarContainers []v1.Container `name:"sidecars"` PodServiceAccountName string `name:"pod_service_account_name" default:"postgres-pod"` // value of this string must be valid JSON or YAML; see initPodServiceAccount - PodServiceAccountDefinition string `name:"pod_service_account_definition" default:""` - PodServiceAccountRoleBindingDefinition string `name:"pod_service_account_role_binding_definition" default:""` - MasterPodMoveTimeout time.Duration `name:"master_pod_move_timeout" default:"20m"` - DbHostedZone string `name:"db_hosted_zone" default:"db.example.com"` - AWSRegion string `name:"aws_region" default:"eu-central-1"` - WALES3Bucket string `name:"wal_s3_bucket"` - LogS3Bucket string `name:"log_s3_bucket"` - KubeIAMRole string `name:"kube_iam_role"` - WALGSBucket string `name:"wal_gs_bucket"` - GCPCredentials string `name:"gcp_credentials"` - WALAZStorageAccount string `name:"wal_az_storage_account"` - AdditionalSecretMount string `name:"additional_secret_mount"` - AdditionalSecretMountPath string `name:"additional_secret_mount_path" default:"/meta/credentials"` - EnableEBSGp3Migration bool `name:"enable_ebs_gp3_migration" default:"false"` - EnableEBSGp3MigrationMaxSize int64 `name:"enable_ebs_gp3_migration_max_size" default:"1000"` - DebugLogging bool `name:"debug_logging" default:"true"` - EnableDBAccess bool `name:"enable_database_access" default:"true"` - EnableTeamsAPI bool `name:"enable_teams_api" default:"true"` - EnableTeamSuperuser bool `name:"enable_team_superuser" default:"false"` - TeamAdminRole string `name:"team_admin_role" default:"admin"` - RoleDeletionSuffix string `name:"role_deletion_suffix" default:"_deleted"` - EnableTeamMemberDeprecation bool `name:"enable_team_member_deprecation" default:"false"` - EnableAdminRoleForUsers bool `name:"enable_admin_role_for_users" default:"true"` - EnablePostgresTeamCRD bool `name:"enable_postgres_team_crd" default:"false"` - EnablePostgresTeamCRDSuperusers bool `name:"enable_postgres_team_crd_superusers" default:"false"` - EnableMasterLoadBalancer bool `name:"enable_master_load_balancer" default:"true"` - EnableMasterPoolerLoadBalancer bool `name:"enable_master_pooler_load_balancer" default:"false"` - EnableReplicaLoadBalancer bool `name:"enable_replica_load_balancer" default:"false"` - EnableReplicaPoolerLoadBalancer bool `name:"enable_replica_pooler_load_balancer" default:"false"` - CustomServiceAnnotations map[string]string `name:"custom_service_annotations"` - CustomPodAnnotations map[string]string `name:"custom_pod_annotations"` - EnablePodAntiAffinity bool `name:"enable_pod_antiaffinity" default:"false"` - PodAntiAffinityTopologyKey string `name:"pod_antiaffinity_topology_key" default:"kubernetes.io/hostname"` - StorageResizeMode string `name:"storage_resize_mode" default:"pvc"` - EnableLoadBalancer *bool `name:"enable_load_balancer"` // deprecated and kept for backward compatibility - ExternalTrafficPolicy string `name:"external_traffic_policy" default:"Cluster"` - MasterDNSNameFormat StringTemplate `name:"master_dns_name_format" default:"{cluster}.{namespace}.{hostedzone}"` - ReplicaDNSNameFormat StringTemplate `name:"replica_dns_name_format" default:"{cluster}-repl.{namespace}.{hostedzone}"` - PDBNameFormat StringTemplate `name:"pdb_name_format" default:"postgres-{cluster}-pdb"` - EnablePodDisruptionBudget *bool `name:"enable_pod_disruption_budget" default:"true"` - EnableInitContainers *bool `name:"enable_init_containers" default:"true"` - EnableSidecars *bool `name:"enable_sidecars" default:"true"` - SharePGSocketWithSidecars *bool `name:"share_pg_socket_with_sidecars" default:"false"` - Workers uint32 `name:"workers" default:"8"` - APIPort int `name:"api_port" default:"8080"` - RingLogLines int `name:"ring_log_lines" default:"100"` - ClusterHistoryEntries int `name:"cluster_history_entries" default:"1000"` - TeamAPIRoleConfiguration map[string]string `name:"team_api_role_configuration" default:"log_statement:all"` - PodTerminateGracePeriod time.Duration `name:"pod_terminate_grace_period" default:"5m"` - PodManagementPolicy string `name:"pod_management_policy" default:"ordered_ready"` - EnableReadinessProbe bool `name:"enable_readiness_probe" default:"false"` - ProtectedRoles []string `name:"protected_role_names" default:"admin,cron_admin"` - PostgresSuperuserTeams []string `name:"postgres_superuser_teams" default:""` - SetMemoryRequestToLimit bool `name:"set_memory_request_to_limit" default:"false"` - EnableLazySpiloUpgrade bool `name:"enable_lazy_spilo_upgrade" default:"false"` - EnableCrossNamespaceSecret bool `name:"enable_cross_namespace_secret" default:"false"` - EnablePgVersionEnvVar bool `name:"enable_pgversion_env_var" default:"true"` - EnableSpiloWalPathCompat bool `name:"enable_spilo_wal_path_compat" default:"false"` - EnableTeamIdClusternamePrefix bool `name:"enable_team_id_clustername_prefix" default:"false"` - MajorVersionUpgradeMode string `name:"major_version_upgrade_mode" default:"off"` - MajorVersionUpgradeTeamAllowList []string `name:"major_version_upgrade_team_allow_list" default:""` - MinimalMajorVersion string `name:"minimal_major_version" default:"11"` - TargetMajorVersion string `name:"target_major_version" default:"14"` - PatroniAPICheckInterval time.Duration `name:"patroni_api_check_interval" default:"1s"` - PatroniAPICheckTimeout time.Duration `name:"patroni_api_check_timeout" default:"5s"` - EnablePatroniFailsafeMode *bool `name:"enable_patroni_failsafe_mode" default:"false"` + PodServiceAccountDefinition string `name:"pod_service_account_definition" default:""` + PodServiceAccountRoleBindingDefinition string `name:"pod_service_account_role_binding_definition" default:""` + MasterPodMoveTimeout time.Duration `name:"master_pod_move_timeout" default:"20m"` + DbHostedZone string `name:"db_hosted_zone" default:"db.example.com"` + AWSRegion string `name:"aws_region" default:"eu-central-1"` + WALES3Bucket string `name:"wal_s3_bucket"` + LogS3Bucket string `name:"log_s3_bucket"` + KubeIAMRole string `name:"kube_iam_role"` + WALGSBucket string `name:"wal_gs_bucket"` + GCPCredentials string `name:"gcp_credentials"` + WALAZStorageAccount string `name:"wal_az_storage_account"` + AdditionalSecretMount string `name:"additional_secret_mount"` + AdditionalSecretMountPath string `name:"additional_secret_mount_path" default:"/meta/credentials"` + EnableEBSGp3Migration bool `name:"enable_ebs_gp3_migration" default:"false"` + EnableEBSGp3MigrationMaxSize int64 `name:"enable_ebs_gp3_migration_max_size" default:"1000"` + DebugLogging bool `name:"debug_logging" default:"true"` + EnableDBAccess bool `name:"enable_database_access" default:"true"` + EnableTeamsAPI bool `name:"enable_teams_api" default:"true"` + EnableTeamSuperuser bool `name:"enable_team_superuser" default:"false"` + TeamAdminRole string `name:"team_admin_role" default:"admin"` + RoleDeletionSuffix string `name:"role_deletion_suffix" default:"_deleted"` + EnableTeamMemberDeprecation bool `name:"enable_team_member_deprecation" default:"false"` + EnableAdminRoleForUsers bool `name:"enable_admin_role_for_users" default:"true"` + EnablePostgresTeamCRD bool `name:"enable_postgres_team_crd" default:"false"` + EnablePostgresTeamCRDSuperusers bool `name:"enable_postgres_team_crd_superusers" default:"false"` + EnableMasterLoadBalancer bool `name:"enable_master_load_balancer" default:"true"` + EnableMasterPoolerLoadBalancer bool `name:"enable_master_pooler_load_balancer" default:"false"` + EnableReplicaLoadBalancer bool `name:"enable_replica_load_balancer" default:"false"` + EnableReplicaPoolerLoadBalancer bool `name:"enable_replica_pooler_load_balancer" default:"false"` + CustomServiceAnnotations map[string]string `name:"custom_service_annotations"` + CustomPodAnnotations map[string]string `name:"custom_pod_annotations"` + EnablePodAntiAffinity bool `name:"enable_pod_antiaffinity" default:"false"` + PodAntiAffinityTopologyKey string `name:"pod_antiaffinity_topology_key" default:"kubernetes.io/hostname"` + PodAntiAffinityPreferredDuringScheduling bool `name:"pod_antiaffinity_preferred_during_scheduling" default:"false"` + StorageResizeMode string `name:"storage_resize_mode" default:"pvc"` + EnableLoadBalancer *bool `name:"enable_load_balancer"` // deprecated and kept for backward compatibility + ExternalTrafficPolicy string `name:"external_traffic_policy" default:"Cluster"` + MasterDNSNameFormat StringTemplate `name:"master_dns_name_format" default:"{cluster}.{namespace}.{hostedzone}"` + ReplicaDNSNameFormat StringTemplate `name:"replica_dns_name_format" default:"{cluster}-repl.{namespace}.{hostedzone}"` + PDBNameFormat StringTemplate `name:"pdb_name_format" default:"postgres-{cluster}-pdb"` + EnablePodDisruptionBudget *bool `name:"enable_pod_disruption_budget" default:"true"` + EnableInitContainers *bool `name:"enable_init_containers" default:"true"` + EnableSidecars *bool `name:"enable_sidecars" default:"true"` + SharePGSocketWithSidecars *bool `name:"share_pg_socket_with_sidecars" default:"false"` + Workers uint32 `name:"workers" default:"8"` + APIPort int `name:"api_port" default:"8080"` + RingLogLines int `name:"ring_log_lines" default:"100"` + ClusterHistoryEntries int `name:"cluster_history_entries" default:"1000"` + TeamAPIRoleConfiguration map[string]string `name:"team_api_role_configuration" default:"log_statement:all"` + PodTerminateGracePeriod time.Duration `name:"pod_terminate_grace_period" default:"5m"` + PodManagementPolicy string `name:"pod_management_policy" default:"ordered_ready"` + EnableReadinessProbe bool `name:"enable_readiness_probe" default:"false"` + ProtectedRoles []string `name:"protected_role_names" default:"admin,cron_admin"` + PostgresSuperuserTeams []string `name:"postgres_superuser_teams" default:""` + SetMemoryRequestToLimit bool `name:"set_memory_request_to_limit" default:"false"` + EnableLazySpiloUpgrade bool `name:"enable_lazy_spilo_upgrade" default:"false"` + EnableCrossNamespaceSecret bool `name:"enable_cross_namespace_secret" default:"false"` + EnablePgVersionEnvVar bool `name:"enable_pgversion_env_var" default:"true"` + EnableSpiloWalPathCompat bool `name:"enable_spilo_wal_path_compat" default:"false"` + EnableTeamIdClusternamePrefix bool `name:"enable_team_id_clustername_prefix" default:"false"` + MajorVersionUpgradeMode string `name:"major_version_upgrade_mode" default:"off"` + MajorVersionUpgradeTeamAllowList []string `name:"major_version_upgrade_team_allow_list" default:""` + MinimalMajorVersion string `name:"minimal_major_version" default:"9.6"` + TargetMajorVersion string `name:"target_major_version" default:"14"` + PatroniAPICheckInterval time.Duration `name:"patroni_api_check_interval" default:"1s"` + PatroniAPICheckTimeout time.Duration `name:"patroni_api_check_timeout" default:"5s"` + EnablePatroniFailsafeMode *bool `name:"enable_patroni_failsafe_mode" default:"false"` } // MustMarshal marshals the config or panics From d7e1fb57f1baa37507c7ace2f6d12b6405a54b43 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Mon, 2 Jan 2023 18:28:48 +0100 Subject: [PATCH 53/77] polish global config about sharing postgresql-run socket (#2155) * polish global config about sharing postgresql-run socket --- .../crds/operatorconfigurations.yaml | 3 + charts/postgres-operator/values.yaml | 3 + docs/reference/operator_parameters.md | 2 +- docs/user.md | 38 ++++- manifests/configmap.yaml | 1 + manifests/operatorconfiguration.crd.yaml | 6 +- pkg/apis/acid.zalan.do/v1/crds.go | 6 +- .../v1/operator_configuration_type.go | 2 +- .../acid.zalan.do/v1/zz_generated.deepcopy.go | 4 +- pkg/cluster/k8sres.go | 10 +- pkg/cluster/k8sres_test.go | 134 +++++++++++------- pkg/controller/operator_config.go | 2 +- .../clientset/versioned/fake/register.go | 14 +- .../clientset/versioned/scheme/register.go | 14 +- pkg/util/config/config.go | 6 +- pkg/util/constants/postgresql.go | 3 + 16 files changed, 159 insertions(+), 89 deletions(-) diff --git a/charts/postgres-operator/crds/operatorconfigurations.yaml b/charts/postgres-operator/crds/operatorconfigurations.yaml index d0c2dca66..5c5da0474 100644 --- a/charts/postgres-operator/crds/operatorconfigurations.yaml +++ b/charts/postgres-operator/crds/operatorconfigurations.yaml @@ -314,6 +314,9 @@ spec: 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 diff --git a/charts/postgres-operator/values.yaml b/charts/postgres-operator/values.yaml index 50977bd8a..613ee50d5 100644 --- a/charts/postgres-operator/values.yaml +++ b/charts/postgres-operator/values.yaml @@ -191,9 +191,12 @@ configKubernetes: # if the user is in different namespace than cluster and cross namespace secrets # are enabled via `enable_cross_namespace_secret` flag in the configuration. secret_name_template: "{username}.{cluster}.credentials.{tprkind}.{tprgroup}" + # sharing unix socket of PostgreSQL (`pg_socket`) with the sidecars + share_pgsocket_with_sidecars: false # set user and group for the spilo container (required to run Spilo as non-root process) # spilo_runasuser: 101 # spilo_runasgroup: 103 + # group ID with write-access to volumes (required to run Spilo as non-root process) # spilo_fsgroup: 103 diff --git a/docs/reference/operator_parameters.md b/docs/reference/operator_parameters.md index 42193660d..16ec7320f 100644 --- a/docs/reference/operator_parameters.md +++ b/docs/reference/operator_parameters.md @@ -344,7 +344,7 @@ configuration they are grouped under the `kubernetes` key. to run alongside Spilo on the same pod. Globally defined sidecars are always enabled. Default is true. -* **share_pg_socket_with_sidecars** +* **share_pgsocket_with_sidecars** global option to create an emptyDir volume named `postgresql-run`. This is mounted by all containers at `/var/run/postgresql` sharing the unix socket of PostgreSQL (`pg_socket`) with the sidecars this way. diff --git a/docs/user.md b/docs/user.md index e2fa59380..c5fcfda87 100644 --- a/docs/user.md +++ b/docs/user.md @@ -1008,11 +1008,39 @@ If you want to add a sidecar to every cluster managed by the operator, you can s ### Accessing the PostgreSQL socket from sidecars -If enabled by the `share_pg_socket_with_sidecars` option in the operator -configuration the PostgreSQL socket is placed in a volume of type -`emptyDir` named `postgresql-run`. -To allow access to the socket from any sidecar container simply add a -VolumeMount to this volume to your sidecar spec. +If enabled by the `share_pgsocket_with_sidecars` option in the operator +configuration the PostgreSQL socket is placed in a volume of type `emptyDir` +named `postgresql-run`. To allow access to the socket from any sidecar +container simply add a VolumeMount to this volume to your sidecar spec. + +```yaml + - name: "container-name" + image: "company/image:tag" + volumeMounts: + - mountPath: /var/run + name: postgresql-run +``` + +If you do not want to globally enable this feature and only use it for single +Postgres clusters, specify an `EmptyDir` volume under `additionalVolumes` in +the manifest: + +```yaml +spec: + additionalVolumes: + - name: postgresql-run + mountPath: /var/run/postgresql + targetContainers: + - all + volumeSource: + emptyDir: {} + sidecars: + - name: "container-name" + image: "company/image:tag" + volumeMounts: + - mountPath: /var/run + name: postgresql-run +``` ## InitContainers Support diff --git a/manifests/configmap.yaml b/manifests/configmap.yaml index aad09ca02..5aebddb93 100644 --- a/manifests/configmap.yaml +++ b/manifests/configmap.yaml @@ -134,6 +134,7 @@ data: 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" diff --git a/manifests/operatorconfiguration.crd.yaml b/manifests/operatorconfiguration.crd.yaml index 0597c692a..58a27bc54 100644 --- a/manifests/operatorconfiguration.crd.yaml +++ b/manifests/operatorconfiguration.crd.yaml @@ -222,9 +222,6 @@ spec: type: array items: type: string - share_pg_socket_with_sidecars: - type: boolean - default: false infrastructure_roles_secret_name: type: string infrastructure_roles_secrets: @@ -312,6 +309,9 @@ spec: 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 diff --git a/pkg/apis/acid.zalan.do/v1/crds.go b/pkg/apis/acid.zalan.do/v1/crds.go index 84028bb20..5fdb87f21 100644 --- a/pkg/apis/acid.zalan.do/v1/crds.go +++ b/pkg/apis/acid.zalan.do/v1/crds.go @@ -1289,9 +1289,6 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{ }, }, }, - "share_pg_socket_with_sidecars": { - Type: "boolean", - }, "infrastructure_roles_secret_name": { Type: "string", }, @@ -1419,6 +1416,9 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{ "secret_name_template": { Type: "string", }, + "share_pgsocket_with_sidecars": { + Type: "boolean", + }, "spilo_runasuser": { Type: "integer", }, diff --git a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go index b3251b217..1279ba43b 100644 --- a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go +++ b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go @@ -72,7 +72,7 @@ type KubernetesMetaConfiguration struct { StorageResizeMode string `json:"storage_resize_mode,omitempty"` EnableInitContainers *bool `json:"enable_init_containers,omitempty"` EnableSidecars *bool `json:"enable_sidecars,omitempty"` - SharePGSocketWithSidecars *bool `json:"share_pgsocket_with_sidecars,omitempty"` + SharePgSocketWithSidecars *bool `json:"share_pgsocket_with_sidecars,omitempty"` SecretNameTemplate config.StringTemplate `json:"secret_name_template,omitempty"` ClusterDomain string `json:"cluster_domain,omitempty"` OAuthTokenSecretName spec.NamespacedName `json:"oauth_token_secret_name,omitempty"` diff --git a/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go b/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go index a37ae6c3c..3ad388318 100644 --- a/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go +++ b/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go @@ -193,8 +193,8 @@ func (in *KubernetesMetaConfiguration) DeepCopyInto(out *KubernetesMetaConfigura *out = new(bool) **out = **in } - if in.SharePGSocketWithSidecars != nil { - in, out := &in.SharePGSocketWithSidecars, &out.SharePGSocketWithSidecars + if in.SharePgSocketWithSidecars != nil { + in, out := &in.SharePgSocketWithSidecars, &out.SharePgSocketWithSidecars *out = new(bool) **out = **in } diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index 6b7688816..250d3156b 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -723,7 +723,7 @@ func (c *Cluster) generatePodTemplate( spiloContainer *v1.Container, initContainers []v1.Container, sidecarContainers []v1.Container, - sharePGSocketWithSidecars *bool, + sharePgSocketWithSidecars *bool, tolerationsSpec *[]v1.Toleration, spiloRunAsUser *int64, spiloRunAsGroup *int64, @@ -792,7 +792,7 @@ func (c *Cluster) generatePodTemplate( podSpec.PriorityClassName = priorityClassName } - if sharePGSocketWithSidecars != nil && *sharePGSocketWithSidecars { + if sharePgSocketWithSidecars != nil && *sharePgSocketWithSidecars { addVarRunVolume(&podSpec) } @@ -1378,7 +1378,7 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef spiloContainer, initContainers, sidecarContainers, - c.OpConfig.SharePGSocketWithSidecars, + c.OpConfig.SharePgSocketWithSidecars, &tolerationSpec, effectiveRunAsUser, effectiveRunAsGroup, @@ -1586,8 +1586,8 @@ func addVarRunVolume(podSpec *v1.PodSpec) { for i := range podSpec.Containers { mounts := append(podSpec.Containers[i].VolumeMounts, v1.VolumeMount{ - Name: "postgresql-run", - MountPath: "/var/run/postgresql", + Name: constants.RunVolumeName, + MountPath: constants.RunVolumePath, }) podSpec.Containers[i].VolumeMounts = mounts } diff --git a/pkg/cluster/k8sres_test.go b/pkg/cluster/k8sres_test.go index cd8bf14e7..4f62d59be 100644 --- a/pkg/cluster/k8sres_test.go +++ b/pkg/cluster/k8sres_test.go @@ -64,7 +64,6 @@ func TestGenerateSpiloJSONConfiguration(t *testing.T) { }, }, k8sutil.KubernetesClient{}, acidv1.Postgresql{}, logger, eventRecorder) - testName := "TestGenerateSpiloConfig" tests := []struct { subtest string pgParam *acidv1.PostgresqlParam @@ -159,13 +158,12 @@ func TestGenerateSpiloJSONConfiguration(t *testing.T) { } if tt.result != result { t.Errorf("%s %s: Spilo Config is %v, expected %v for role %#v and param %#v", - testName, tt.subtest, result, tt.result, tt.opConfig.Auth.PamRoleName, tt.pgParam) + t.Name(), tt.subtest, result, tt.result, tt.opConfig.Auth.PamRoleName, tt.pgParam) } } } func TestExtractPgVersionFromBinPath(t *testing.T) { - testName := "TestExtractPgVersionFromBinPath" tests := []struct { subTest string binPath string @@ -199,7 +197,7 @@ func TestExtractPgVersionFromBinPath(t *testing.T) { } if pgVersion != tt.expected { t.Errorf("%s %s: Expected version %s, have %s instead", - testName, tt.subTest, tt.expected, pgVersion) + t.Name(), tt.subTest, tt.expected, pgVersion) } } } @@ -287,7 +285,6 @@ func newMockCluster(opConfig config.Config) *Cluster { } func TestPodEnvironmentConfigMapVariables(t *testing.T) { - testName := "TestPodEnvironmentConfigMapVariables" tests := []struct { subTest string opConfig config.Config @@ -343,17 +340,17 @@ func TestPodEnvironmentConfigMapVariables(t *testing.T) { vars, err := c.getPodEnvironmentConfigMapVariables() if !reflect.DeepEqual(vars, tt.envVars) { t.Errorf("%s %s: expected `%v` but got `%v`", - testName, tt.subTest, tt.envVars, vars) + t.Name(), tt.subTest, tt.envVars, vars) } if tt.err != nil { if err.Error() != tt.err.Error() { t.Errorf("%s %s: expected error `%v` but got `%v`", - testName, tt.subTest, tt.err, err) + t.Name(), tt.subTest, tt.err, err) } } else { if err != nil { t.Errorf("%s %s: expected no error but got error: `%v`", - testName, tt.subTest, err) + t.Name(), tt.subTest, err) } } } @@ -362,7 +359,6 @@ func TestPodEnvironmentConfigMapVariables(t *testing.T) { // Test if the keys of an existing secret are properly referenced func TestPodEnvironmentSecretVariables(t *testing.T) { maxRetries := int(testResourceCheckTimeout / testResourceCheckInterval) - testName := "TestPodEnvironmentSecretVariables" tests := []struct { subTest string opConfig config.Config @@ -448,17 +444,17 @@ func TestPodEnvironmentSecretVariables(t *testing.T) { sort.Slice(vars, func(i, j int) bool { return vars[i].Name < vars[j].Name }) if !reflect.DeepEqual(vars, tt.envVars) { t.Errorf("%s %s: expected `%v` but got `%v`", - testName, tt.subTest, tt.envVars, vars) + t.Name(), tt.subTest, tt.envVars, vars) } if tt.err != nil { if err.Error() != tt.err.Error() { t.Errorf("%s %s: expected error `%v` but got `%v`", - testName, tt.subTest, tt.err, err) + t.Name(), tt.subTest, tt.err, err) } } else { if err != nil { t.Errorf("%s %s: expected no error but got error: `%v`", - testName, tt.subTest, err) + t.Name(), tt.subTest, err) } } } @@ -657,7 +653,6 @@ func TestGenerateSpiloPodEnvVars(t *testing.T) { }, } - testName := "TestGenerateSpiloPodEnvVars" tests := []struct { subTest string opConfig config.Config @@ -895,27 +890,26 @@ func TestGenerateSpiloPodEnvVars(t *testing.T) { if env.Name != ev.envVarConstant { t.Errorf("%s %s: expected env name %s, have %s instead", - testName, tt.subTest, ev.envVarConstant, env.Name) + t.Name(), tt.subTest, ev.envVarConstant, env.Name) } if ev.envVarValueRef != nil { if !reflect.DeepEqual(env.ValueFrom, ev.envVarValueRef) { t.Errorf("%s %s: expected env value reference %#v, have %#v instead", - testName, tt.subTest, ev.envVarValueRef, env.ValueFrom) + t.Name(), tt.subTest, ev.envVarValueRef, env.ValueFrom) } continue } if env.Value != ev.envVarValue { t.Errorf("%s %s: expected env value %s, have %s instead", - testName, tt.subTest, ev.envVarValue, env.Value) + t.Name(), tt.subTest, ev.envVarValue, env.Value) } } } } func TestGetNumberOfInstances(t *testing.T) { - testName := "TestGetNumberOfInstances" tests := []struct { subTest string config config.Config @@ -1039,13 +1033,12 @@ func TestGetNumberOfInstances(t *testing.T) { if numInstances != tt.provided { t.Errorf("%s %s: Expected to get %d instances, have %d instead", - testName, tt.subTest, tt.provided, numInstances) + t.Name(), tt.subTest, tt.provided, numInstances) } } } func TestCloneEnv(t *testing.T) { - testName := "TestCloneEnv" tests := []struct { subTest string cloneOpts *acidv1.CloneDescription @@ -1112,18 +1105,17 @@ func TestCloneEnv(t *testing.T) { if env.Name != tt.env.Name { t.Errorf("%s %s: Expected env name %s, have %s instead", - testName, tt.subTest, tt.env.Name, env.Name) + t.Name(), tt.subTest, tt.env.Name, env.Name) } if env.Value != tt.env.Value { t.Errorf("%s %s: Expected env value %s, have %s instead", - testName, tt.subTest, tt.env.Value, env.Value) + t.Name(), tt.subTest, tt.env.Value, env.Value) } } } func TestAppendEnvVar(t *testing.T) { - testName := "TestAppendEnvVar" tests := []struct { subTest string envs []v1.EnvVar @@ -1179,7 +1171,7 @@ func TestAppendEnvVar(t *testing.T) { if len(finalEnvs) != tt.expectedSize { t.Errorf("%s %s: expected %d env variables, got %d", - testName, tt.subTest, tt.expectedSize, len(finalEnvs)) + t.Name(), tt.subTest, tt.expectedSize, len(finalEnvs)) } for _, env := range tt.envs { @@ -1187,7 +1179,7 @@ func TestAppendEnvVar(t *testing.T) { if env.Name == finalEnv.Name { if env.Value != finalEnv.Value { t.Errorf("%s %s: expected env value %s of variable %s, got %s instead", - testName, tt.subTest, env.Value, env.Name, finalEnv.Value) + t.Name(), tt.subTest, env.Value, env.Name, finalEnv.Value) } } } @@ -1196,7 +1188,6 @@ func TestAppendEnvVar(t *testing.T) { } func TestStandbyEnv(t *testing.T) { - testName := "TestStandbyEnv" tests := []struct { subTest string standbyOpts *acidv1.StandbyDescription @@ -1279,17 +1270,17 @@ func TestStandbyEnv(t *testing.T) { if env.Name != tt.env.Name { t.Errorf("%s %s: Expected env name %s, have %s instead", - testName, tt.subTest, tt.env.Name, env.Name) + t.Name(), tt.subTest, tt.env.Name, env.Name) } if env.Value != tt.env.Value { t.Errorf("%s %s: Expected env value %s, have %s instead", - testName, tt.subTest, tt.env.Value, env.Value) + t.Name(), tt.subTest, tt.env.Value, env.Value) } if len(envs) != tt.envLen { t.Errorf("%s %s: Expected number of env variables %d, have %d instead", - testName, tt.subTest, tt.envLen, len(envs)) + t.Name(), tt.subTest, tt.envLen, len(envs)) } } } @@ -1471,8 +1462,59 @@ func testServiceOwnerReference(cluster *Cluster, service *v1.Service, role Postg return nil } -func TestTLS(t *testing.T) { +func TestSharePgSocketWithSidecars(t *testing.T) { + tests := []struct { + subTest string + podSpec *v1.PodSpec + runVolPos int + }{ + { + subTest: "empty PodSpec", + podSpec: &v1.PodSpec{ + Volumes: []v1.Volume{}, + Containers: []v1.Container{ + { + VolumeMounts: []v1.VolumeMount{}, + }, + }, + }, + runVolPos: 0, + }, + { + subTest: "non empty PodSpec", + podSpec: &v1.PodSpec{ + Volumes: []v1.Volume{{}}, + Containers: []v1.Container{ + { + Name: "postgres", + VolumeMounts: []v1.VolumeMount{ + {}, + }, + }, + }, + }, + runVolPos: 1, + }, + } + for _, tt := range tests { + addVarRunVolume(tt.podSpec) + postgresContainer := getPostgresContainer(tt.podSpec) + volumeName := tt.podSpec.Volumes[tt.runVolPos].Name + volumeMountName := postgresContainer.VolumeMounts[tt.runVolPos].Name + + if volumeName != constants.RunVolumeName { + t.Errorf("%s %s: Expected volume %s was not created, have %s instead", + t.Name(), tt.subTest, constants.RunVolumeName, volumeName) + } + if volumeMountName != constants.RunVolumeName { + t.Errorf("%s %s: Expected mount %s was not created, have %s instead", + t.Name(), tt.subTest, constants.RunVolumeName, volumeMountName) + } + } +} + +func TestTLS(t *testing.T) { client, _ := newFakeK8sTestClient() clusterName := "acid-test-cluster" namespace := "default" @@ -1561,7 +1603,6 @@ func TestTLS(t *testing.T) { } func TestShmVolume(t *testing.T) { - testName := "TestShmVolume" tests := []struct { subTest string podSpec *v1.PodSpec @@ -1604,17 +1645,16 @@ func TestShmVolume(t *testing.T) { if volumeName != constants.ShmVolumeName { t.Errorf("%s %s: Expected volume %s was not created, have %s instead", - testName, tt.subTest, constants.ShmVolumeName, volumeName) + t.Name(), tt.subTest, constants.ShmVolumeName, volumeName) } if volumeMountName != constants.ShmVolumeName { t.Errorf("%s %s: Expected mount %s was not created, have %s instead", - testName, tt.subTest, constants.ShmVolumeName, volumeMountName) + t.Name(), tt.subTest, constants.ShmVolumeName, volumeMountName) } } } func TestSecretVolume(t *testing.T) { - testName := "TestSecretVolume" tests := []struct { subTest string podSpec *v1.PodSpec @@ -1664,7 +1704,7 @@ func TestSecretVolume(t *testing.T) { if volumeName != additionalSecretMount { t.Errorf("%s %s: Expected volume %s was not created, have %s instead", - testName, tt.subTest, additionalSecretMount, volumeName) + t.Name(), tt.subTest, additionalSecretMount, volumeName) } for i := range tt.podSpec.Containers { @@ -1672,7 +1712,7 @@ func TestSecretVolume(t *testing.T) { if volumeMountName != additionalSecretMount { t.Errorf("%s %s: Expected mount %s was not created, have %s instead", - testName, tt.subTest, additionalSecretMount, volumeMountName) + t.Name(), tt.subTest, additionalSecretMount, volumeMountName) } } @@ -1687,8 +1727,6 @@ func TestSecretVolume(t *testing.T) { } func TestAdditionalVolume(t *testing.T) { - testName := "TestAdditionalVolume" - client, _ := newFakeK8sTestClient() clusterName := "acid-test-cluster" namespace := "default" @@ -1800,14 +1838,13 @@ func TestAdditionalVolume(t *testing.T) { if !util.IsEqualIgnoreOrder(mounts, tt.expectedMounts) { t.Errorf("%s %s: different volume mounts: got %v, epxected %v", - testName, tt.subTest, mounts, tt.expectedMounts) + t.Name(), tt.subTest, mounts, tt.expectedMounts) } } } } func TestVolumeSelector(t *testing.T) { - testName := "TestVolumeSelector" makeSpec := func(volume acidv1.Volume) acidv1.PostgresSpec { return acidv1.PostgresSpec{ TeamID: "myapp", @@ -1888,7 +1925,7 @@ func TestVolumeSelector(t *testing.T) { pgSpec := makeSpec(tt.volume) sts, err := cluster.generateStatefulSet(&pgSpec) if err != nil { - t.Fatalf("%s %s: no statefulset created %v", testName, tt.subTest, err) + t.Fatalf("%s %s: no statefulset created %v", t.Name(), tt.subTest, err) } volIdx := len(sts.Spec.VolumeClaimTemplates) @@ -1899,12 +1936,12 @@ func TestVolumeSelector(t *testing.T) { } } if volIdx == len(sts.Spec.VolumeClaimTemplates) { - t.Errorf("%s %s: no datavolume found in sts", testName, tt.subTest) + t.Errorf("%s %s: no datavolume found in sts", t.Name(), tt.subTest) } selector := sts.Spec.VolumeClaimTemplates[volIdx].Spec.Selector if !reflect.DeepEqual(selector, tt.wantSelector) { - t.Errorf("%s %s: expected: %#v but got: %#v", testName, tt.subTest, tt.wantSelector, selector) + t.Errorf("%s %s: expected: %#v but got: %#v", t.Name(), tt.subTest, tt.wantSelector, selector) } } } @@ -2320,7 +2357,6 @@ func TestCreateLoadBalancerLogic(t *testing.T) { }, }, k8sutil.KubernetesClient{}, acidv1.Postgresql{}, logger, eventRecorder) - testName := "TestCreateLoadBalancerLogic" tests := []struct { subtest string role PostgresRole @@ -2362,7 +2398,7 @@ func TestCreateLoadBalancerLogic(t *testing.T) { result := cluster.shouldCreateLoadBalancerForService(tt.role, tt.spec) if tt.result != result { t.Errorf("%s %s: Load balancer is %t, expect %t for role %#v and spec %#v", - testName, tt.subtest, result, tt.result, tt.role, tt.spec) + t.Name(), tt.subtest, result, tt.result, tt.role, tt.spec) } } } @@ -2410,7 +2446,6 @@ func getServices(serviceType v1.ServiceType, sourceRanges []string, extTrafficPo } func TestEnableLoadBalancers(t *testing.T) { - testName := "Test enabling LoadBalancers" client, _ := newLBFakeClient() clusterName := "acid-test-cluster" namespace := "default" @@ -2545,13 +2580,12 @@ func TestEnableLoadBalancers(t *testing.T) { generatedServices = append(generatedServices, cluster.ConnectionPooler[role].Service.Spec) } if !reflect.DeepEqual(tt.expectedServices, generatedServices) { - t.Errorf("%s %s: expected %#v but got %#v", testName, tt.subTest, tt.expectedServices, generatedServices) + t.Errorf("%s %s: expected %#v but got %#v", t.Name(), tt.subTest, tt.expectedServices, generatedServices) } } } func TestGenerateResourceRequirements(t *testing.T) { - testName := "TestGenerateResourceRequirements" client, _ := newFakeK8sTestClient() clusterName := "acid-test-cluster" namespace := "default" @@ -2921,14 +2955,12 @@ func TestGenerateResourceRequirements(t *testing.T) { } assert.NoError(t, err) if !reflect.DeepEqual(tt.expectedResources, clusterResources) { - t.Errorf("%s - %s: expected %#v but got %#v", testName, tt.subTest, tt.expectedResources, clusterResources) + t.Errorf("%s - %s: expected %#v but got %#v", t.Name(), tt.subTest, tt.expectedResources, clusterResources) } } } func TestGenerateCapabilities(t *testing.T) { - - testName := "TestGenerateCapabilities" tests := []struct { subTest string configured []string @@ -2968,7 +3000,7 @@ func TestGenerateCapabilities(t *testing.T) { caps := generateCapabilities(tt.configured) if !reflect.DeepEqual(caps, tt.capabilities) { t.Errorf("%s %s: expected `%v` but got `%v`", - testName, tt.subTest, tt.capabilities, caps) + t.Name(), tt.subTest, tt.capabilities, caps) } } } diff --git a/pkg/controller/operator_config.go b/pkg/controller/operator_config.go index 4e347b079..b1e46b6ae 100644 --- a/pkg/controller/operator_config.go +++ b/pkg/controller/operator_config.go @@ -86,7 +86,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur result.StorageResizeMode = util.Coalesce(fromCRD.Kubernetes.StorageResizeMode, "pvc") result.EnableInitContainers = util.CoalesceBool(fromCRD.Kubernetes.EnableInitContainers, util.True()) result.EnableSidecars = util.CoalesceBool(fromCRD.Kubernetes.EnableSidecars, util.True()) - result.SharePGSocketWithSidecars = util.CoalesceBool(fromCRD.Kubernetes.SharePGSocketWithSidecars, util.False()) + result.SharePgSocketWithSidecars = util.CoalesceBool(fromCRD.Kubernetes.SharePgSocketWithSidecars, util.False()) result.SecretNameTemplate = fromCRD.Kubernetes.SecretNameTemplate result.OAuthTokenSecretName = fromCRD.Kubernetes.OAuthTokenSecretName result.EnableCrossNamespaceSecret = fromCRD.Kubernetes.EnableCrossNamespaceSecret diff --git a/pkg/generated/clientset/versioned/fake/register.go b/pkg/generated/clientset/versioned/fake/register.go index a5d94de6b..313eeacc2 100644 --- a/pkg/generated/clientset/versioned/fake/register.go +++ b/pkg/generated/clientset/versioned/fake/register.go @@ -45,14 +45,14 @@ var localSchemeBuilder = runtime.SchemeBuilder{ // AddToScheme adds all types of this clientset into the given scheme. This allows composition // of clientsets, like in: // -// import ( -// "k8s.io/client-go/kubernetes" -// clientsetscheme "k8s.io/client-go/kubernetes/scheme" -// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" -// ) +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) // -// kclientset, _ := kubernetes.NewForConfig(c) -// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) // // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types // correctly. diff --git a/pkg/generated/clientset/versioned/scheme/register.go b/pkg/generated/clientset/versioned/scheme/register.go index 96ab81670..823909bcb 100644 --- a/pkg/generated/clientset/versioned/scheme/register.go +++ b/pkg/generated/clientset/versioned/scheme/register.go @@ -45,14 +45,14 @@ var localSchemeBuilder = runtime.SchemeBuilder{ // AddToScheme adds all types of this clientset into the given scheme. This allows composition // of clientsets, like in: // -// import ( -// "k8s.io/client-go/kubernetes" -// clientsetscheme "k8s.io/client-go/kubernetes/scheme" -// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" -// ) +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) // -// kclientset, _ := kubernetes.NewForConfig(c) -// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) // // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types // correctly. diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index 75c7043cb..7abb33056 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -213,7 +213,7 @@ type Config struct { EnablePodDisruptionBudget *bool `name:"enable_pod_disruption_budget" default:"true"` EnableInitContainers *bool `name:"enable_init_containers" default:"true"` EnableSidecars *bool `name:"enable_sidecars" default:"true"` - SharePGSocketWithSidecars *bool `name:"share_pg_socket_with_sidecars" default:"false"` + SharePgSocketWithSidecars *bool `name:"share_pgsocket_with_sidecars" default:"false"` Workers uint32 `name:"workers" default:"8"` APIPort int `name:"api_port" default:"8080"` RingLogLines int `name:"ring_log_lines" default:"100"` @@ -232,11 +232,11 @@ type Config struct { EnableTeamIdClusternamePrefix bool `name:"enable_team_id_clustername_prefix" default:"false"` MajorVersionUpgradeMode string `name:"major_version_upgrade_mode" default:"off"` MajorVersionUpgradeTeamAllowList []string `name:"major_version_upgrade_team_allow_list" default:""` - MinimalMajorVersion string `name:"minimal_major_version" default:"9.6"` + MinimalMajorVersion string `name:"minimal_major_version" default:"11"` TargetMajorVersion string `name:"target_major_version" default:"14"` PatroniAPICheckInterval time.Duration `name:"patroni_api_check_interval" default:"1s"` PatroniAPICheckTimeout time.Duration `name:"patroni_api_check_timeout" default:"5s"` - EnablePatroniFailsafeMode *bool `name:"enable_patroni_failsafe_mode" default:"false"` + EnablePatroniFailsafeMode *bool `name:"enable_patroni_failsafe_mode" default:"false"` } // MustMarshal marshals the config or panics diff --git a/pkg/util/constants/postgresql.go b/pkg/util/constants/postgresql.go index 41bfdd66e..8bd7508a7 100644 --- a/pkg/util/constants/postgresql.go +++ b/pkg/util/constants/postgresql.go @@ -15,4 +15,7 @@ const ( ShmVolumeName = "dshm" ShmVolumePath = "/dev/shm" + + RunVolumeName = "postgresql-run" + RunVolumePath = "/var/run/postgresql" ) From 819e4109597c879e0535ae6449814af85b17fe00 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Tue, 3 Jan 2023 11:34:02 +0100 Subject: [PATCH 54/77] refactor podAffinity generation (#2156) --- .../crds/operatorconfigurations.yaml | 6 +- charts/postgres-operator/values.yaml | 4 +- manifests/configmap.yaml | 1 + manifests/operatorconfiguration.crd.yaml | 3 + ...gresql-operator-default-configuration.yaml | 2 + pkg/apis/acid.zalan.do/v1/crds.go | 6 +- .../v1/operator_configuration_type.go | 4 +- pkg/cluster/connection_pooler.go | 3 +- pkg/cluster/k8sres.go | 81 +++++---- pkg/cluster/k8sres_test.go | 157 +++++++++--------- pkg/util/config/config.go | 2 +- 11 files changed, 151 insertions(+), 118 deletions(-) diff --git a/charts/postgres-operator/crds/operatorconfigurations.yaml b/charts/postgres-operator/crds/operatorconfigurations.yaml index 5c5da0474..f1589b92d 100644 --- a/charts/postgres-operator/crds/operatorconfigurations.yaml +++ b/charts/postgres-operator/crds/operatorconfigurations.yaml @@ -278,12 +278,12 @@ spec: pdb_name_format: type: string default: "postgres-{cluster}-pdb" - pod_antiaffinity_topology_key: - type: string - default: "kubernetes.io/hostname" 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: diff --git a/charts/postgres-operator/values.yaml b/charts/postgres-operator/values.yaml index 613ee50d5..20f961ca1 100644 --- a/charts/postgres-operator/values.yaml +++ b/charts/postgres-operator/values.yaml @@ -165,10 +165,10 @@ configKubernetes: # defines the template for PDB (Pod Disruption Budget) names pdb_name_format: "postgres-{cluster}-pdb" + # switches pod anti affinity type to `preferredDuringSchedulingIgnoredDuringExecution` + pod_antiaffinity_preferred_during_scheduling: false # override topology key for pod anti affinity pod_antiaffinity_topology_key: "kubernetes.io/hostname" - # switches pod anti affinity type to `preferredDuringSchedulingIgnoredDuringExecution` - # pod_antiaffinity_preferred_during_scheduling: true # namespaced name of the ConfigMap with environment variables to populate on every pod # pod_environment_configmap: "default/my-custom-config" # name of the Secret (in cluster namespace) with environment variables to populate on every pod diff --git a/manifests/configmap.yaml b/manifests/configmap.yaml index 5aebddb93..831a00341 100644 --- a/manifests/configmap.yaml +++ b/manifests/configmap.yaml @@ -109,6 +109,7 @@ data: # password_rotation_interval: "90" # password_rotation_user_retention: "180" pdb_name_format: "postgres-{cluster}-pdb" + # 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" diff --git a/manifests/operatorconfiguration.crd.yaml b/manifests/operatorconfiguration.crd.yaml index 58a27bc54..4e5ab803f 100644 --- a/manifests/operatorconfiguration.crd.yaml +++ b/manifests/operatorconfiguration.crd.yaml @@ -276,6 +276,9 @@ spec: pdb_name_format: type: string default: "postgres-{cluster}-pdb" + pod_antiaffinity_preferred_during_scheduling: + type: boolean + default: false pod_antiaffinity_topology_key: type: string default: "kubernetes.io/hostname" diff --git a/manifests/postgresql-operator-default-configuration.yaml b/manifests/postgresql-operator-default-configuration.yaml index aba77e848..4c7737d13 100644 --- a/manifests/postgresql-operator-default-configuration.yaml +++ b/manifests/postgresql-operator-default-configuration.yaml @@ -84,6 +84,7 @@ configuration: # node_readiness_label_merge: "OR" oauth_token_secret_name: postgresql-operator pdb_name_format: "postgres-{cluster}-pdb" + 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" @@ -95,6 +96,7 @@ configuration: # 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 diff --git a/pkg/apis/acid.zalan.do/v1/crds.go b/pkg/apis/acid.zalan.do/v1/crds.go index 5fdb87f21..40a8b380e 100644 --- a/pkg/apis/acid.zalan.do/v1/crds.go +++ b/pkg/apis/acid.zalan.do/v1/crds.go @@ -1372,12 +1372,12 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{ "pdb_name_format": { Type: "string", }, - "pod_antiaffinity_topology_key": { - Type: "string", - }, "pod_antiaffinity_preferred_during_scheduling": { Type: "boolean", }, + "pod_antiaffinity_topology_key": { + Type: "string", + }, "pod_environment_configmap": { Type: "string", }, diff --git a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go index 1279ba43b..29cddffa5 100644 --- a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go +++ b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go @@ -97,10 +97,10 @@ type KubernetesMetaConfiguration struct { PodPriorityClassName string `json:"pod_priority_class_name,omitempty"` MasterPodMoveTimeout Duration `json:"master_pod_move_timeout,omitempty"` EnablePodAntiAffinity bool `json:"enable_pod_antiaffinity,omitempty"` - PodAntiAffinityTopologyKey string `json:"pod_antiaffinity_topology_key,omitempty"` PodAntiAffinityPreferredDuringScheduling bool `json:"pod_antiaffinity_preferred_during_scheduling,omitempty"` + PodAntiAffinityTopologyKey string `json:"pod_antiaffinity_topology_key,omitempty"` PodManagementPolicy string `json:"pod_management_policy,omitempty"` - EnableReadinessProbe bool `json:"enable_readiness_probe,omitempty"` + EnableReadinessProbe bool `json:"enable_readiness_probe,omitempty"` EnableCrossNamespaceSecret bool `json:"enable_cross_namespace_secret,omitempty"` } diff --git a/pkg/cluster/connection_pooler.go b/pkg/cluster/connection_pooler.go index fd8b9251c..93bfb8854 100644 --- a/pkg/cluster/connection_pooler.go +++ b/pkg/cluster/connection_pooler.go @@ -354,11 +354,12 @@ func (c *Cluster) generateConnectionPoolerPodTemplate(role PostgresRole) ( nodeAffinity := c.nodeAffinity(c.OpConfig.NodeReadinessLabel, spec.NodeAffinity) if c.OpConfig.EnablePodAntiAffinity { labelsSet := labels.Set(c.connectionPoolerLabels(role, false).MatchLabels) - podTemplate.Spec.Affinity = generatePodAffinity( + podTemplate.Spec.Affinity = podAffinity( labelsSet, c.OpConfig.PodAntiAffinityTopologyKey, nodeAffinity, c.OpConfig.PodAntiAffinityPreferredDuringScheduling, + true, ) } else if nodeAffinity != nil { podTemplate.Spec.Affinity = nodeAffinity diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index 250d3156b..902e6f873 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -495,8 +495,14 @@ func (c *Cluster) nodeAffinity(nodeReadinessLabel map[string]string, nodeAffinit } } -func generatePodAffinity(labels labels.Set, topologyKey string, nodeAffinity *v1.Affinity, preferredDuringScheduling bool) *v1.Affinity { - // generate pod anti-affinity to avoid multiple pods of the same Postgres cluster in the same topology , e.g. node +func podAffinity( + labels labels.Set, + topologyKey string, + nodeAffinity *v1.Affinity, + preferredDuringScheduling bool, + anti bool) *v1.Affinity { + + var podAffinity v1.Affinity podAffinityTerm := v1.PodAffinityTerm{ LabelSelector: &metav1.LabelSelector{ @@ -505,17 +511,10 @@ func generatePodAffinity(labels labels.Set, topologyKey string, nodeAffinity *v1 TopologyKey: topologyKey, } - podAffinity := v1.Affinity{ - PodAntiAffinity: &v1.PodAntiAffinity{}, - } - - if preferredDuringScheduling { - podAffinity.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution = []v1.WeightedPodAffinityTerm{{ - Weight: 1, - PodAffinityTerm: podAffinityTerm, - }} + if anti { + podAffinity.PodAntiAffinity = generatePodAntiAffinity(podAffinityTerm, preferredDuringScheduling) } else { - podAffinity.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution = []v1.PodAffinityTerm{podAffinityTerm} + podAffinity.PodAffinity = generatePodAffinity(podAffinityTerm, preferredDuringScheduling) } if nodeAffinity != nil && nodeAffinity.NodeAffinity != nil { @@ -525,6 +524,36 @@ func generatePodAffinity(labels labels.Set, topologyKey string, nodeAffinity *v1 return &podAffinity } +func generatePodAffinity(podAffinityTerm v1.PodAffinityTerm, preferredDuringScheduling bool) *v1.PodAffinity { + podAffinity := &v1.PodAffinity{} + + if preferredDuringScheduling { + podAffinity.PreferredDuringSchedulingIgnoredDuringExecution = []v1.WeightedPodAffinityTerm{{ + Weight: 1, + PodAffinityTerm: podAffinityTerm, + }} + } else { + podAffinity.RequiredDuringSchedulingIgnoredDuringExecution = []v1.PodAffinityTerm{podAffinityTerm} + } + + return podAffinity +} + +func generatePodAntiAffinity(podAffinityTerm v1.PodAffinityTerm, preferredDuringScheduling bool) *v1.PodAntiAffinity { + podAntiAffinity := &v1.PodAntiAffinity{} + + if preferredDuringScheduling { + podAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution = []v1.WeightedPodAffinityTerm{{ + Weight: 1, + PodAffinityTerm: podAffinityTerm, + }} + } else { + podAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution = []v1.PodAffinityTerm{podAffinityTerm} + } + + return podAntiAffinity +} + func tolerations(tolerationsSpec *[]v1.Toleration, podToleration map[string]string) []v1.Toleration { // allow to override tolerations by postgresql manifest if len(*tolerationsSpec) > 0 { @@ -778,11 +807,12 @@ func (c *Cluster) generatePodTemplate( } if podAntiAffinity { - podSpec.Affinity = generatePodAffinity( + podSpec.Affinity = podAffinity( labels, podAntiAffinityTopologyKey, nodeAffinity, podAntiAffinityPreferredDuringScheduling, + true, ) } else if nodeAffinity != nil { podSpec.Affinity = nodeAffinity @@ -2100,20 +2130,15 @@ func (c *Cluster) generateLogicalBackupJob() (*batchv1.CronJob, error) { c.OpConfig.ClusterNameLabel: c.Name, "application": "spilo-logical-backup", } - podAffinityTerm := v1.PodAffinityTerm{ - LabelSelector: &metav1.LabelSelector{ - MatchLabels: labels, - }, - TopologyKey: "kubernetes.io/hostname", - } - podAffinity := v1.Affinity{ - PodAffinity: &v1.PodAffinity{ - PreferredDuringSchedulingIgnoredDuringExecution: []v1.WeightedPodAffinityTerm{{ - Weight: 1, - PodAffinityTerm: podAffinityTerm, - }, - }, - }} + + nodeAffinity := c.nodeAffinity(c.OpConfig.NodeReadinessLabel, nil) + podAffinity := podAffinity( + labels, + "kubernetes.io/hostname", + nodeAffinity, + true, + false, + ) annotations := c.generatePodAnnotations(&c.Spec) @@ -2147,7 +2172,7 @@ func (c *Cluster) generateLogicalBackupJob() (*batchv1.CronJob, error) { } // overwrite specific params of logical backups pods - podTemplate.Spec.Affinity = &podAffinity + podTemplate.Spec.Affinity = podAffinity podTemplate.Spec.RestartPolicy = "Never" // affects containers within a pod // configure a batch job diff --git a/pkg/cluster/k8sres_test.go b/pkg/cluster/k8sres_test.go index 4f62d59be..bfdca7e59 100644 --- a/pkg/cluster/k8sres_test.go +++ b/pkg/cluster/k8sres_test.go @@ -1351,93 +1351,94 @@ func TestNodeAffinity(t *testing.T) { assert.Equal(t, s.Spec.Template.Spec.Affinity.NodeAffinity, nodeAff, "cluster template has correct node affinity") } -func TestPodAntiAffinityrRequiredDuringScheduling(t *testing.T) { - var err error - var spiloRunAsUser = int64(101) - var spiloRunAsGroup = int64(103) - var spiloFSGroup = int64(103) +func TestPodAffinity(t *testing.T) { + clusterName := "acid-test-cluster" + namespace := "default" - spec := acidv1.PostgresSpec{ - TeamID: "myapp", NumberOfInstances: 1, - Resources: &acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, - ResourceLimits: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, + tests := []struct { + subTest string + preferred bool + anti bool + }{ + { + subTest: "generate affinity RequiredDuringSchedulingIgnoredDuringExecution", + preferred: false, + anti: false, }, - Volume: acidv1.Volume{ - Size: "1G", + { + subTest: "generate affinity PreferredDuringSchedulingIgnoredDuringExecution", + preferred: true, + anti: false, + }, + { + subTest: "generate anitAffinity RequiredDuringSchedulingIgnoredDuringExecution", + preferred: false, + anti: true, + }, + { + subTest: "generate anitAffinity PreferredDuringSchedulingIgnoredDuringExecution", + preferred: true, + anti: true, }, } - cluster := New( - Config{ - OpConfig: config.Config{ - PodManagementPolicy: "ordered_ready", - ProtectedRoles: []string{"admin"}, - Auth: config.Auth{ - SuperUsername: superUserName, - ReplicationUsername: replicationUserName, - }, - Resources: config.Resources{ - SpiloRunAsUser: &spiloRunAsUser, - SpiloRunAsGroup: &spiloRunAsGroup, - SpiloFSGroup: &spiloFSGroup, - }, - EnablePodAntiAffinity: true, + pg := acidv1.Postgresql{ + ObjectMeta: metav1.ObjectMeta{ + Name: clusterName, + Namespace: namespace, + }, + Spec: acidv1.PostgresSpec{ + NumberOfInstances: 1, + Resources: &acidv1.Resources{ + ResourceRequests: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, + ResourceLimits: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, }, - }, k8sutil.KubernetesClient{}, acidv1.Postgresql{}, logger, eventRecorder) - - s, err := cluster.generateStatefulSet(&spec) - if err != nil { - assert.NoError(t, err) - } - - assert.Nil(t, s.Spec.Template.Spec.Affinity.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution, "pod anti-affinity should not use preferredDuringScheduling") - assert.NotNil(t, s.Spec.Template.Spec.Affinity.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution, "pod anti-affinity should use requiredDuringScheduling") -} - -func TestPodAntiAffinityPreferredDuringScheduling(t *testing.T) { - var err error - var spiloRunAsUser = int64(101) - var spiloRunAsGroup = int64(103) - var spiloFSGroup = int64(103) - - spec := acidv1.PostgresSpec{ - TeamID: "myapp", NumberOfInstances: 1, - Resources: &acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, - ResourceLimits: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, - }, - Volume: acidv1.Volume{ - Size: "1G", - }, - } - - cluster := New( - Config{ - OpConfig: config.Config{ - PodManagementPolicy: "ordered_ready", - ProtectedRoles: []string{"admin"}, - Auth: config.Auth{ - SuperUsername: superUserName, - ReplicationUsername: replicationUserName, - }, - Resources: config.Resources{ - SpiloRunAsUser: &spiloRunAsUser, - SpiloRunAsGroup: &spiloRunAsGroup, - SpiloFSGroup: &spiloFSGroup, - }, - EnablePodAntiAffinity: true, - PodAntiAffinityPreferredDuringScheduling: true, + Volume: acidv1.Volume{ + Size: "1G", }, - }, k8sutil.KubernetesClient{}, acidv1.Postgresql{}, logger, eventRecorder) - - s, err := cluster.generateStatefulSet(&spec) - if err != nil { - assert.NoError(t, err) + }, } - assert.NotNil(t, s.Spec.Template.Spec.Affinity.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution, "pod anti-affinity should use preferredDuringScheduling") - assert.Nil(t, s.Spec.Template.Spec.Affinity.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution, "pod anti-affinity should not use requiredDuringScheduling") + for _, tt := range tests { + cluster := New( + Config{ + OpConfig: config.Config{ + EnablePodAntiAffinity: tt.anti, + PodManagementPolicy: "ordered_ready", + ProtectedRoles: []string{"admin"}, + PodAntiAffinityPreferredDuringScheduling: tt.preferred, + Resources: config.Resources{ + ClusterLabels: map[string]string{"application": "spilo"}, + ClusterNameLabel: "cluster-name", + DefaultCPURequest: "300m", + DefaultCPULimit: "300m", + DefaultMemoryRequest: "300Mi", + DefaultMemoryLimit: "300Mi", + PodRoleLabel: "spilo-role", + }, + }, + }, k8sutil.KubernetesClient{}, pg, logger, eventRecorder) + + cluster.Name = clusterName + cluster.Namespace = namespace + + s, err := cluster.generateStatefulSet(&pg.Spec) + if err != nil { + assert.NoError(t, err) + } + + if !tt.anti { + assert.Nil(t, s.Spec.Template.Spec.Affinity, "pod affinity should not be set") + } else { + if tt.preferred { + assert.NotNil(t, s.Spec.Template.Spec.Affinity.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution, "pod anti-affinity should use preferredDuringScheduling") + assert.Nil(t, s.Spec.Template.Spec.Affinity.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution, "pod anti-affinity should not use requiredDuringScheduling") + } else { + assert.Nil(t, s.Spec.Template.Spec.Affinity.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution, "pod anti-affinity should not use preferredDuringScheduling") + assert.NotNil(t, s.Spec.Template.Spec.Affinity.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution, "pod anti-affinity should use requiredDuringScheduling") + } + } + } } func testDeploymentOwnerReference(cluster *Cluster, deployment *appsv1.Deployment) error { diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index 7abb33056..80aa509e3 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -202,8 +202,8 @@ type Config struct { CustomServiceAnnotations map[string]string `name:"custom_service_annotations"` CustomPodAnnotations map[string]string `name:"custom_pod_annotations"` EnablePodAntiAffinity bool `name:"enable_pod_antiaffinity" default:"false"` - PodAntiAffinityTopologyKey string `name:"pod_antiaffinity_topology_key" default:"kubernetes.io/hostname"` PodAntiAffinityPreferredDuringScheduling bool `name:"pod_antiaffinity_preferred_during_scheduling" default:"false"` + PodAntiAffinityTopologyKey string `name:"pod_antiaffinity_topology_key" default:"kubernetes.io/hostname"` StorageResizeMode string `name:"storage_resize_mode" default:"pvc"` EnableLoadBalancer *bool `name:"enable_load_balancer"` // deprecated and kept for backward compatibility ExternalTrafficPolicy string `name:"external_traffic_policy" default:"Cluster"` From 486d5d66e0dd360a226872d8a865e74a661ef66a Mon Sep 17 00:00:00 2001 From: idanovinda Date: Tue, 3 Jan 2023 15:46:59 +0100 Subject: [PATCH 55/77] Allow drop slots when it gets deleted from the manifest (#2089) * Allow drop slots when it gets deleted from the manifest * use leader instead replica to query slots * fix and extend unit tests for config update checks Co-authored-by: Felix Kunde --- e2e/tests/test_e2e.py | 85 ++++++++++++++++++++++++- pkg/cluster/cluster.go | 6 ++ pkg/cluster/sync.go | 15 ++++- pkg/cluster/sync_test.go | 131 ++++++++++++++++++++++++++++++++++++--- 4 files changed, 223 insertions(+), 14 deletions(-) diff --git a/e2e/tests/test_e2e.py b/e2e/tests/test_e2e.py index 638cd05b2..39fd45323 100644 --- a/e2e/tests/test_e2e.py +++ b/e2e/tests/test_e2e.py @@ -395,12 +395,13 @@ class EndToEndTestCase(unittest.TestCase): "spec": { "postgresql": { "parameters": { - "max_connections": new_max_connections_value + "max_connections": new_max_connections_value, + "wal_level": "logical" } }, "patroni": { "slots": { - "test_slot": { + "first_slot": { "type": "physical" } }, @@ -437,6 +438,8 @@ class EndToEndTestCase(unittest.TestCase): "synchronous_mode not updated") self.assertEqual(desired_config["failsafe_mode"], effective_config["failsafe_mode"], "failsafe_mode not updated") + self.assertEqual(desired_config["slots"], effective_config["slots"], + "slots not updated") return True # check if Patroni config has been updated @@ -497,6 +500,84 @@ class EndToEndTestCase(unittest.TestCase): self.eventuallyEqual(lambda: self.query_database(replica.metadata.name, "postgres", setting_query)[0], lower_max_connections_value, "Previous max_connections setting not applied on replica", 10, 5) + # patch new slot via Patroni REST + patroni_slot = "test_patroni_slot" + patch_slot_command = """curl -s -XPATCH -d '{"slots": {"test_patroni_slot": {"type": "physical"}}}' localhost:8008/config""" + pg_patch_config["spec"]["patroni"]["slots"][patroni_slot] = {"type": "physical"} + + k8s.exec_with_kubectl(leader.metadata.name, patch_slot_command) + self.eventuallyEqual(lambda: k8s.get_operator_state(), {"0": "idle"}, "Operator does not get in sync") + self.eventuallyTrue(compare_config, "Postgres config not applied") + + # test adding new slots + pg_add_new_slots_patch = { + "spec": { + "patroni": { + "slots": { + "test_slot": { + "type": "logical", + "database": "foo", + "plugin": "pgoutput" + }, + "test_slot_2": { + "type": "physical" + } + } + } + } + } + + for slot_name, slot_details in pg_add_new_slots_patch["spec"]["patroni"]["slots"].items(): + pg_patch_config["spec"]["patroni"]["slots"][slot_name] = slot_details + + k8s.api.custom_objects_api.patch_namespaced_custom_object( + "acid.zalan.do", "v1", "default", "postgresqls", "acid-minimal-cluster", pg_add_new_slots_patch) + + self.eventuallyEqual(lambda: k8s.get_operator_state(), {"0": "idle"}, "Operator does not get in sync") + self.eventuallyTrue(compare_config, "Postgres config not applied") + + # delete test_slot_2 from config and change the database type for test_slot + slot_to_change = "test_slot" + slot_to_remove = "test_slot_2" + pg_delete_slot_patch = { + "spec": { + "patroni": { + "slots": { + "test_slot": { + "type": "logical", + "database": "bar", + "plugin": "pgoutput" + }, + "test_slot_2": None + } + } + } + } + + pg_patch_config["spec"]["patroni"]["slots"][slot_to_change]["database"] = "bar" + del pg_patch_config["spec"]["patroni"]["slots"][slot_to_remove] + + k8s.api.custom_objects_api.patch_namespaced_custom_object( + "acid.zalan.do", "v1", "default", "postgresqls", "acid-minimal-cluster", pg_delete_slot_patch) + + self.eventuallyEqual(lambda: k8s.get_operator_state(), {"0": "idle"}, "Operator does not get in sync") + self.eventuallyTrue(compare_config, "Postgres config not applied") + + get_slot_query = """ + SELECT %s + FROM pg_replication_slots + WHERE slot_name = '%s'; + """ + self.eventuallyEqual(lambda: len(self.query_database(leader.metadata.name, "postgres", get_slot_query%("slot_name", slot_to_remove))), 0, + "The replication slot cannot be deleted", 10, 5) + + self.eventuallyEqual(lambda: self.query_database(leader.metadata.name, "postgres", get_slot_query%("database", slot_to_change))[0], "bar", + "The replication slot cannot be updated", 10, 5) + + # make sure slot from Patroni didn't get deleted + self.eventuallyEqual(lambda: len(self.query_database(leader.metadata.name, "postgres", get_slot_query%("slot_name", patroni_slot))), 1, + "The replication slot from Patroni gets deleted", 10, 5) + except timeout_decorator.TimeoutError: print('Operator log: {}'.format(k8s.get_operator_log())) raise diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index f18cf8053..723fa4171 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -84,6 +84,7 @@ type Cluster struct { userSyncStrategy spec.UserSyncer deleteOptions metav1.DeleteOptions podEventsQueue *cache.FIFO + replicationSlots map[string]interface{} teamsAPIClient teams.Interface oauthTokenGetter OAuthTokenGetter @@ -140,6 +141,7 @@ func New(cfg Config, kubeClient k8sutil.KubernetesClient, pgSpec acidv1.Postgres podEventsQueue: podEventsQueue, KubeClient: kubeClient, currentMajorVersion: 0, + replicationSlots: make(map[string]interface{}), } cluster.logger = logger.WithField("pkg", "cluster").WithField("cluster-name", cluster.clusterName()) cluster.teamsAPIClient = teams.NewTeamsAPI(cfg.OpConfig.TeamsAPIUrl, logger) @@ -374,6 +376,10 @@ func (c *Cluster) Create() error { } } + for slotName, desiredSlot := range c.Spec.Patroni.Slots { + c.replicationSlots[slotName] = desiredSlot + } + return nil } diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index 76c9fd12a..af85eb076 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -579,8 +579,18 @@ func (c *Cluster) checkAndSetGlobalPostgreSQLConfiguration(pod *v1.Pod, effectiv } } + slotsToSet := make(map[string]interface{}) + // check if there is any slot deletion + for slotName, effectiveSlot := range c.replicationSlots { + if desiredSlot, exists := desiredPatroniConfig.Slots[slotName]; exists { + if reflect.DeepEqual(effectiveSlot, desiredSlot) { + continue + } + } + slotsToSet[slotName] = nil + delete(c.replicationSlots, slotName) + } // check if specified slots exist in config and if they differ - slotsToSet := make(map[string]map[string]string) for slotName, desiredSlot := range desiredPatroniConfig.Slots { if effectiveSlot, exists := effectivePatroniConfig.Slots[slotName]; exists { if reflect.DeepEqual(desiredSlot, effectiveSlot) { @@ -588,6 +598,7 @@ func (c *Cluster) checkAndSetGlobalPostgreSQLConfiguration(pod *v1.Pod, effectiv } } slotsToSet[slotName] = desiredSlot + c.replicationSlots[slotName] = desiredSlot } if len(slotsToSet) > 0 { configToSet["slots"] = slotsToSet @@ -614,7 +625,7 @@ func (c *Cluster) checkAndSetGlobalPostgreSQLConfiguration(pod *v1.Pod, effectiv } // check if there exist only config updates that require a restart of the primary - if !util.SliceContains(restartPrimary, false) && len(configToSet) == 0 { + if len(restartPrimary) > 0 && !util.SliceContains(restartPrimary, false) && len(configToSet) == 0 { requiresMasterRestart = true } diff --git a/pkg/cluster/sync_test.go b/pkg/cluster/sync_test.go index 4d50b791f..3cd3d3f28 100644 --- a/pkg/cluster/sync_test.go +++ b/pkg/cluster/sync_test.go @@ -144,6 +144,13 @@ func TestCheckAndSetGlobalPostgreSQLConfiguration(t *testing.T) { client, _ := newFakeK8sSyncClient() clusterName := "acid-test-cluster" namespace := "default" + testSlots := map[string]map[string]string{ + "slot1": { + "type": "logical", + "plugin": "wal2json", + "database": "foo", + }, + } ctrl := gomock.NewController(t) defer ctrl.Finish() @@ -208,11 +215,26 @@ func TestCheckAndSetGlobalPostgreSQLConfiguration(t *testing.T) { // simulate existing config that differs from cluster.Spec tests := []struct { - subtest string - patroni acidv1.Patroni - pgParams map[string]string - restartPrimary bool + subtest string + patroni acidv1.Patroni + desiredSlots map[string]map[string]string + removedSlots map[string]map[string]string + pgParams map[string]string + shouldBePatched bool + restartPrimary bool }{ + { + subtest: "Patroni and Postgresql.Parameters do not differ", + patroni: acidv1.Patroni{ + TTL: 20, + }, + pgParams: map[string]string{ + "log_min_duration_statement": "200", + "max_connections": "50", + }, + shouldBePatched: false, + restartPrimary: false, + }, { subtest: "Patroni and Postgresql.Parameters differ - restart replica first", patroni: acidv1.Patroni{ @@ -222,7 +244,8 @@ func TestCheckAndSetGlobalPostgreSQLConfiguration(t *testing.T) { "log_min_duration_statement": "500", // desired 200 "max_connections": "100", // desired 50 }, - restartPrimary: false, + shouldBePatched: true, + restartPrimary: false, }, { subtest: "multiple Postgresql.Parameters differ - restart replica first", @@ -231,7 +254,8 @@ func TestCheckAndSetGlobalPostgreSQLConfiguration(t *testing.T) { "log_min_duration_statement": "500", // desired 200 "max_connections": "100", // desired 50 }, - restartPrimary: false, + shouldBePatched: true, + restartPrimary: false, }, { subtest: "desired max_connections bigger - restart replica first", @@ -240,7 +264,8 @@ func TestCheckAndSetGlobalPostgreSQLConfiguration(t *testing.T) { "log_min_duration_statement": "200", "max_connections": "30", // desired 50 }, - restartPrimary: false, + shouldBePatched: true, + restartPrimary: false, }, { subtest: "desired max_connections smaller - restart master first", @@ -249,19 +274,105 @@ func TestCheckAndSetGlobalPostgreSQLConfiguration(t *testing.T) { "log_min_duration_statement": "200", "max_connections": "100", // desired 50 }, - restartPrimary: true, + shouldBePatched: true, + restartPrimary: true, + }, + { + subtest: "slot does not exist but is desired", + patroni: acidv1.Patroni{ + TTL: 20, + }, + desiredSlots: testSlots, + pgParams: map[string]string{ + "log_min_duration_statement": "200", + "max_connections": "50", + }, + shouldBePatched: true, + restartPrimary: false, + }, + { + subtest: "slot exist, nothing specified in manifest", + patroni: acidv1.Patroni{ + TTL: 20, + Slots: map[string]map[string]string{ + "slot1": { + "type": "logical", + "plugin": "pgoutput", + "database": "foo", + }, + }, + }, + pgParams: map[string]string{ + "log_min_duration_statement": "200", + "max_connections": "50", + }, + shouldBePatched: false, + restartPrimary: false, + }, + { + subtest: "slot is removed from manifest", + patroni: acidv1.Patroni{ + TTL: 20, + Slots: map[string]map[string]string{ + "slot1": { + "type": "logical", + "plugin": "pgoutput", + "database": "foo", + }, + }, + }, + removedSlots: testSlots, + pgParams: map[string]string{ + "log_min_duration_statement": "200", + "max_connections": "50", + }, + shouldBePatched: true, + restartPrimary: false, + }, + { + subtest: "slot plugin differs", + patroni: acidv1.Patroni{ + TTL: 20, + Slots: map[string]map[string]string{ + "slot1": { + "type": "logical", + "plugin": "pgoutput", + "database": "foo", + }, + }, + }, + desiredSlots: testSlots, + pgParams: map[string]string{ + "log_min_duration_statement": "200", + "max_connections": "50", + }, + shouldBePatched: true, + restartPrimary: false, }, } for _, tt := range tests { + if len(tt.desiredSlots) > 0 { + cluster.Spec.Patroni.Slots = tt.desiredSlots + } + if len(tt.removedSlots) > 0 { + for slotName, removedSlot := range tt.removedSlots { + cluster.replicationSlots[slotName] = removedSlot + } + } + configPatched, requirePrimaryRestart, err := cluster.checkAndSetGlobalPostgreSQLConfiguration(mockPod, tt.patroni, cluster.Spec.Patroni, tt.pgParams, cluster.Spec.Parameters) assert.NoError(t, err) - if configPatched != true { + if configPatched != tt.shouldBePatched { t.Errorf("%s - %s: expected config update did not happen", testName, tt.subtest) } if requirePrimaryRestart != tt.restartPrimary { t.Errorf("%s - %s: wrong master restart strategy, got restart %v, expected restart %v", testName, tt.subtest, requirePrimaryRestart, tt.restartPrimary) } + + // reset slots for next tests + cluster.Spec.Patroni.Slots = nil + cluster.replicationSlots = make(map[string]interface{}) } testsFailsafe := []struct { @@ -342,7 +453,7 @@ func TestCheckAndSetGlobalPostgreSQLConfiguration(t *testing.T) { effectiveVal: util.True(), desiredVal: true, shouldBePatched: false, // should not require patching - restartPrimary: true, + restartPrimary: false, }, } From 9c88fb9369e89dbee7892d3ba657511e27dbd6ee Mon Sep 17 00:00:00 2001 From: Dmitry Volodin Date: Wed, 4 Jan 2023 13:29:36 +0300 Subject: [PATCH 56/77] Update k8s API for kubectl-pg and regen generated code (#2158) --- .gitignore | 1 + kubectl-pg/go.mod | 32 +++--- kubectl-pg/go.sum | 99 +++++++++++++------ .../acid.zalan.do/v1/zz_generated.deepcopy.go | 2 +- .../clientset/versioned/clientset.go | 2 +- pkg/generated/clientset/versioned/doc.go | 2 +- .../versioned/fake/clientset_generated.go | 2 +- pkg/generated/clientset/versioned/fake/doc.go | 2 +- .../clientset/versioned/fake/register.go | 2 +- .../clientset/versioned/scheme/doc.go | 2 +- .../clientset/versioned/scheme/register.go | 2 +- .../acid.zalan.do/v1/acid.zalan.do_client.go | 2 +- .../versioned/typed/acid.zalan.do/v1/doc.go | 2 +- .../typed/acid.zalan.do/v1/fake/doc.go | 2 +- .../v1/fake/fake_acid.zalan.do_client.go | 2 +- .../v1/fake/fake_operatorconfiguration.go | 2 +- .../acid.zalan.do/v1/fake/fake_postgresql.go | 2 +- .../v1/fake/fake_postgresteam.go | 2 +- .../acid.zalan.do/v1/generated_expansion.go | 2 +- .../acid.zalan.do/v1/operatorconfiguration.go | 2 +- .../typed/acid.zalan.do/v1/postgresql.go | 2 +- .../typed/acid.zalan.do/v1/postgresteam.go | 2 +- .../versioned/typed/zalando.org/v1/doc.go | 2 +- .../typed/zalando.org/v1/fabriceventstream.go | 2 +- .../typed/zalando.org/v1/fake/doc.go | 2 +- .../v1/fake/fake_fabriceventstream.go | 2 +- .../v1/fake/fake_zalando.org_client.go | 2 +- .../zalando.org/v1/generated_expansion.go | 2 +- .../zalando.org/v1/zalando.org_client.go | 2 +- .../acid.zalan.do/interface.go | 2 +- .../acid.zalan.do/v1/interface.go | 2 +- .../acid.zalan.do/v1/postgresql.go | 2 +- .../acid.zalan.do/v1/postgresteam.go | 2 +- .../informers/externalversions/factory.go | 2 +- .../informers/externalversions/generic.go | 2 +- .../internalinterfaces/factory_interfaces.go | 2 +- .../externalversions/zalando.org/interface.go | 2 +- .../zalando.org/v1/fabriceventstream.go | 2 +- .../zalando.org/v1/interface.go | 2 +- .../acid.zalan.do/v1/expansion_generated.go | 2 +- .../listers/acid.zalan.do/v1/postgresql.go | 2 +- .../listers/acid.zalan.do/v1/postgresteam.go | 2 +- .../zalando.org/v1/expansion_generated.go | 2 +- .../zalando.org/v1/fabriceventstream.go | 2 +- 44 files changed, 130 insertions(+), 84 deletions(-) diff --git a/.gitignore b/.gitignore index e062f8479..1f2395f35 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ _testmain.go *.test *.prof /vendor/ +/kubectl-pg/vendor/ /build/ /docker/build/ /github.com/ diff --git a/kubectl-pg/go.mod b/kubectl-pg/go.mod index ae10b7eb6..8b580cb01 100644 --- a/kubectl-pg/go.mod +++ b/kubectl-pg/go.mod @@ -6,16 +6,16 @@ require ( github.com/spf13/cobra v1.2.1 github.com/spf13/viper v1.9.0 github.com/zalando/postgres-operator v1.8.1 - k8s.io/api v0.22.4 - k8s.io/apiextensions-apiserver v0.22.4 - k8s.io/apimachinery v0.22.4 - k8s.io/client-go v0.22.4 + k8s.io/api v0.23.5 + k8s.io/apiextensions-apiserver v0.23.5 + k8s.io/apimachinery v0.23.5 + k8s.io/client-go v0.23.5 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect - github.com/go-logr/logr v0.4.0 // indirect + github.com/go-logr/logr v1.2.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/google/go-cmp v0.5.6 // indirect @@ -24,13 +24,13 @@ require ( github.com/hashicorp/hcl v1.0.0 // indirect github.com/imdario/mergo v0.3.5 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect - github.com/json-iterator/go v1.1.11 // indirect + github.com/json-iterator/go v1.1.12 // indirect github.com/kr/text v0.2.0 // indirect github.com/magiconair/properties v1.8.5 // indirect github.com/mitchellh/mapstructure v1.4.2 // indirect github.com/moby/spdystream v0.2.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.1 // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect github.com/motomux/pretty v0.0.0-20161209205251-b2aad2c9a95d // indirect github.com/pelletier/go-toml v1.9.4 // indirect github.com/sirupsen/logrus v1.8.1 // indirect @@ -39,12 +39,12 @@ require ( github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.2.0 // indirect - golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect - golang.org/x/net v0.0.0-20210614182718-04defd469f4e // indirect + golang.org/x/crypto v0.0.0-20211202192323-5770296d904e // indirect + golang.org/x/net v0.0.0-20211209124913-491a49abca63 // indirect golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f // indirect - golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf // indirect - golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect - golang.org/x/text v0.3.6 // indirect + golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 // indirect + golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b // indirect + golang.org/x/text v0.3.7 // indirect golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.27.1 // indirect @@ -52,8 +52,10 @@ require ( gopkg.in/ini.v1 v1.63.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect - k8s.io/klog/v2 v2.9.0 // indirect - k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect + k8s.io/klog/v2 v2.30.0 // indirect + k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 // indirect + k8s.io/utils v0.0.0-20211116205334-6203023598ed // indirect + sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect sigs.k8s.io/yaml v1.2.0 // indirect ) diff --git a/kubectl-pg/go.sum b/kubectl-pg/go.sum index aaa4b2e36..51c09872b 100644 --- a/kubectl-pg/go.sum +++ b/kubectl-pg/go.sum @@ -65,13 +65,15 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210826220005-b48c857c3a0e/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/aws/aws-sdk-go v1.41.16/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= +github.com/aws/aws-sdk-go v1.42.18/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= @@ -125,6 +127,7 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= @@ -134,6 +137,7 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= +github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -147,8 +151,10 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc= github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v1.2.0 h1:QK40JKJyMdUDz+h+xvCsru/bJhvG0UxvePV0ufL/AcE= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/zapr v1.2.0/go.mod h1:Qa4Bsj2Vb+FAVeAKsLD8RLQ+YRJB8YDmOAKxaBQf7Ro= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= @@ -163,6 +169,7 @@ github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -199,6 +206,8 @@ github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/cel-go v0.9.0/go.mod h1:U7ayypeSkw23szu4GaQTPJGx66c20mx8JklMSxrmI1w= +github.com/google/cel-spec v0.6.0/go.mod h1:Nwjgxy5CbjlPrtCWjeDjUyKMl8w41YBYGjsyDdqk0xA= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -243,6 +252,7 @@ github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2c github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw= github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= @@ -294,8 +304,9 @@ github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFF github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= @@ -316,7 +327,7 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/lib/pq v1.10.3/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= @@ -354,8 +365,9 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/motomux/pretty v0.0.0-20161209205251-b2aad2c9a95d h1:LznySqW8MqVeFh+pW6rOkFdld9QQ7jRydBKKM6jyPVI= github.com/motomux/pretty v0.0.0-20161209205251-b2aad2c9a95d/go.mod h1:u3hJ0kqCQu/cPpsu3RbCOPZ0d7V3IjPjv1adNRleM9I= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= @@ -365,14 +377,17 @@ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRW github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= @@ -404,6 +419,7 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.28.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= @@ -472,8 +488,9 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/zalando/postgres-operator v1.7.1 h1:tDh7utqbrNoCNoQjy3seZiEoO+vT6SgP2+VlnSJ5mpg= -github.com/zalando/postgres-operator v1.7.1/go.mod h1:hZTzOQBITJvv5nDHZpGIwyKqOghomxpZQRyWOfkPzKs= +github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/zalando/postgres-operator v1.8.1 h1:MzBQeXL4fEwDux++R3Tanh7eoYxBst1cNNWW6d4uSQ0= +github.com/zalando/postgres-operator v1.8.1/go.mod h1:f7AXk8LO/tWFdW4myPJZCwMueGg6fI4RqTuOA0BefZE= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= @@ -509,6 +526,7 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/ go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -521,8 +539,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211202192323-5770296d904e h1:MUP6MR3rJ7Gk9LEia0LP2ytiH6MuCfs7qYz+47jGdD8= +golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -558,6 +576,7 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -604,8 +623,13 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211209124913-491a49abca63 h1:iocB37TsdFuN6IBRZ+ry36wrkoV51/tl5vOWqkcPGvY= +golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -704,12 +728,16 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf h1:2ucpDCmfkl8Bd/FsLtiD653Wf96cW37s+iGx93zsu4k= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 h1:TyHqChC80pFkXWraUUf6RuB5IqFdQieMLwwCJokV2pc= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE= +golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -717,8 +745,9 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -786,6 +815,8 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.6-0.20210820212750-d4cc65f0b2ff/go.mod h1:YD9qOF0M9xpSpdWTBbzEl5e/RnCefISl8E5Noe10jFM= +golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -860,6 +891,7 @@ google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201102152239-715cce707fb0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= @@ -881,6 +913,7 @@ google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKr google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -938,6 +971,7 @@ gopkg.in/ini.v1 v1.63.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -962,40 +996,49 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.22.3/go.mod h1:azgiXFiXqiWyLCfI62/eYBOu19rj2LKmIhFPP4+33fs= -k8s.io/api v0.22.4 h1:UvyHW0ezB2oIgHAxlYoo6UJQObYXU7awuNarwoHEOjw= k8s.io/api v0.22.4/go.mod h1:Rgs+9gIGYC5laXQSZZ9JqT5NevNgoGiOdVWi1BAB3qk= -k8s.io/apiextensions-apiserver v0.22.3/go.mod h1:f4plF+CXeqI89jAXL0Ml4LI/kSAZ54JS94+XOX1sae8= -k8s.io/apiextensions-apiserver v0.22.4 h1:2iGpcVyw4MnAyyXVJU2Xg6ZsbIxAOfRHo0LF5A5J0RA= +k8s.io/api v0.23.5 h1:zno3LUiMubxD/V1Zw3ijyKO3wxrhbUF1Ck+VjBvfaoA= +k8s.io/api v0.23.5/go.mod h1:Na4XuKng8PXJ2JsploYYrivXrINeTaycCGcYgF91Xm8= k8s.io/apiextensions-apiserver v0.22.4/go.mod h1:kH9lxD8dbJ+k0ZizGET55lFgdGjO8t45fgZnCVdZEpw= -k8s.io/apimachinery v0.22.3/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= -k8s.io/apimachinery v0.22.4 h1:9uwcvPpukBw/Ri0EUmWz+49cnFtaoiyEhQTK+xOe7Ck= +k8s.io/apiextensions-apiserver v0.23.5 h1:5SKzdXyvIJKu+zbfPc3kCbWpbxi+O+zdmAJBm26UJqI= +k8s.io/apiextensions-apiserver v0.23.5/go.mod h1:ntcPWNXS8ZPKN+zTXuzYMeg731CP0heCTl6gYBxLcuQ= k8s.io/apimachinery v0.22.4/go.mod h1:yU6oA6Gnax9RrxGzVvPFFJ+mpnW6PBSqp0sx0I0HHW0= -k8s.io/apiserver v0.22.3/go.mod h1:oam7lH/F1Kto/WTamyQYrD68fS0mGUBORAFf6x/9Mxs= +k8s.io/apimachinery v0.23.5 h1:Va7dwhp8wgkUPWsEXk6XglXWU4IKYLKNlv8VkX7SDM0= +k8s.io/apimachinery v0.23.5/go.mod h1:BEuFMMBaIbcOqVIJqNZJXGFTP4W6AycEpb5+m/97hrM= k8s.io/apiserver v0.22.4/go.mod h1:38WmcUZiiy41A7Aty8/VorWRa8vDGqoUzDf2XYlku0E= -k8s.io/client-go v0.22.3/go.mod h1:ElDjYf8gvZsKDYexmsmnMQ0DYO8W9RwBjfQ1PI53yow= -k8s.io/client-go v0.22.4 h1:aAQ1Wk+I3bjCNk35YWUqbaueqrIonkfDPJSPDDe8Kfg= +k8s.io/apiserver v0.23.5/go.mod h1:7wvMtGJ42VRxzgVI7jkbKvMbuCbVbgsWFT7RyXiRNTw= k8s.io/client-go v0.22.4/go.mod h1:Yzw4e5e7h1LNHA4uqnMVrpEpUs1hJOiuBsJKIlRCHDA= -k8s.io/code-generator v0.22.3/go.mod h1:eV77Y09IopzeXOJzndrDyCI88UBok2h6WxAlBwpxa+o= +k8s.io/client-go v0.23.5 h1:zUXHmEuqx0RY4+CsnkOn5l0GU+skkRXKGJrhmE2SLd8= +k8s.io/client-go v0.23.5/go.mod h1:flkeinTO1CirYgzMPRWxUCnV0G4Fbu2vLhYCObnt/r4= k8s.io/code-generator v0.22.4/go.mod h1:qjYl54pQ/emhkT0UxbufbREYJMWsHNNV/jSVwhYZQGw= -k8s.io/component-base v0.22.3/go.mod h1:kuybv1miLCMoOk3ebrqF93GbQHQx6W2287FC0YEQY6s= +k8s.io/code-generator v0.23.5/go.mod h1:S0Q1JVA+kSzTI1oUvbKAxZY/DYbA/ZUb4Uknog12ETk= k8s.io/component-base v0.22.4/go.mod h1:MrSaQy4a3tFVViff8TZL6JHYSewNCLshZCwHYM58v5A= +k8s.io/component-base v0.23.5/go.mod h1:c5Nq44KZyt1aLl0IpHX82fhsn84Sb0jjzwjpcA42bY0= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.9.0 h1:D7HV+n1V57XeZ0m6tdRkfknthUaM06VFbWldOFh8kzM= k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= -k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= +k8s.io/klog/v2 v2.30.0 h1:bUO6drIvCIsvZ/XFgfxoGFQU/a4Qkh0iAlvUR7vlHJw= +k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a h1:8dYfu/Fc9Gz2rNJKB9IQRGgQOh2clmRzNIPPY1xLY5g= +k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 h1:E3J9oCLlaobFUqsjG9DfKbP2BmgwBL2p7pn0A3dG9W4= +k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk= +k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20211116205334-6203023598ed h1:ck1fRPWPJWsMd8ZRFsWc6mh/zHp5fZ/shhbrgPUxDAE= +k8s.io/utils v0.0.0-20211116205334-6203023598ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.30/go.mod h1:fEO7lRTdivWO2qYVCVG7dEADOMo/MLDCVr8So2g88Uw= +sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 h1:fD1pz4yfdADVNfFmcP2aBEtudwUQ1AlLnRBALr33v3s= +sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno= sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= +sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y= +sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go b/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go index 3ad388318..9d9a185bd 100644 --- a/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go +++ b/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go @@ -2,7 +2,7 @@ // +build !ignore_autogenerated /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/clientset.go b/pkg/generated/clientset/versioned/clientset.go index ccda2baff..cf0079848 100644 --- a/pkg/generated/clientset/versioned/clientset.go +++ b/pkg/generated/clientset/versioned/clientset.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/doc.go b/pkg/generated/clientset/versioned/doc.go index ebb4ab535..bf8f860ed 100644 --- a/pkg/generated/clientset/versioned/doc.go +++ b/pkg/generated/clientset/versioned/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/fake/clientset_generated.go b/pkg/generated/clientset/versioned/fake/clientset_generated.go index 4c94d23a6..bb34757a9 100644 --- a/pkg/generated/clientset/versioned/fake/clientset_generated.go +++ b/pkg/generated/clientset/versioned/fake/clientset_generated.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/fake/doc.go b/pkg/generated/clientset/versioned/fake/doc.go index b9e99ecf2..b61991dbb 100644 --- a/pkg/generated/clientset/versioned/fake/doc.go +++ b/pkg/generated/clientset/versioned/fake/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/fake/register.go b/pkg/generated/clientset/versioned/fake/register.go index 313eeacc2..19d48b0d2 100644 --- a/pkg/generated/clientset/versioned/fake/register.go +++ b/pkg/generated/clientset/versioned/fake/register.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/scheme/doc.go b/pkg/generated/clientset/versioned/scheme/doc.go index 387784624..c246feb50 100644 --- a/pkg/generated/clientset/versioned/scheme/doc.go +++ b/pkg/generated/clientset/versioned/scheme/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/scheme/register.go b/pkg/generated/clientset/versioned/scheme/register.go index 823909bcb..b3ac16d07 100644 --- a/pkg/generated/clientset/versioned/scheme/register.go +++ b/pkg/generated/clientset/versioned/scheme/register.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/acid.zalan.do_client.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/acid.zalan.do_client.go index 0d7c2c7ed..44c350001 100644 --- a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/acid.zalan.do_client.go +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/acid.zalan.do_client.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/doc.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/doc.go index ba729b8ca..974871d0a 100644 --- a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/doc.go +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/doc.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/doc.go index 380725443..3fc0714cb 100644 --- a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/doc.go +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_acid.zalan.do_client.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_acid.zalan.do_client.go index c235b2761..e864a8ad0 100644 --- a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_acid.zalan.do_client.go +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_acid.zalan.do_client.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_operatorconfiguration.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_operatorconfiguration.go index fbea4930c..465bd1f7e 100644 --- a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_operatorconfiguration.go +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_operatorconfiguration.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_postgresql.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_postgresql.go index c9000a90c..6fc936eb1 100644 --- a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_postgresql.go +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_postgresql.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_postgresteam.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_postgresteam.go index c70b00e94..b7aef2c0d 100644 --- a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_postgresteam.go +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_postgresteam.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/generated_expansion.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/generated_expansion.go index a40f2bdb0..034ffdb84 100644 --- a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/generated_expansion.go +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/generated_expansion.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/operatorconfiguration.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/operatorconfiguration.go index a73fb0df6..70477053e 100644 --- a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/operatorconfiguration.go +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/operatorconfiguration.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/postgresql.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/postgresql.go index c58ed03ed..7e0a829cf 100644 --- a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/postgresql.go +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/postgresql.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/postgresteam.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/postgresteam.go index 002254ef1..2106a0f34 100644 --- a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/postgresteam.go +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/postgresteam.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/typed/zalando.org/v1/doc.go b/pkg/generated/clientset/versioned/typed/zalando.org/v1/doc.go index ba729b8ca..974871d0a 100644 --- a/pkg/generated/clientset/versioned/typed/zalando.org/v1/doc.go +++ b/pkg/generated/clientset/versioned/typed/zalando.org/v1/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/typed/zalando.org/v1/fabriceventstream.go b/pkg/generated/clientset/versioned/typed/zalando.org/v1/fabriceventstream.go index 26c3431f2..581716c55 100644 --- a/pkg/generated/clientset/versioned/typed/zalando.org/v1/fabriceventstream.go +++ b/pkg/generated/clientset/versioned/typed/zalando.org/v1/fabriceventstream.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/typed/zalando.org/v1/fake/doc.go b/pkg/generated/clientset/versioned/typed/zalando.org/v1/fake/doc.go index 380725443..3fc0714cb 100644 --- a/pkg/generated/clientset/versioned/typed/zalando.org/v1/fake/doc.go +++ b/pkg/generated/clientset/versioned/typed/zalando.org/v1/fake/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/typed/zalando.org/v1/fake/fake_fabriceventstream.go b/pkg/generated/clientset/versioned/typed/zalando.org/v1/fake/fake_fabriceventstream.go index b0c592b92..16cf81954 100644 --- a/pkg/generated/clientset/versioned/typed/zalando.org/v1/fake/fake_fabriceventstream.go +++ b/pkg/generated/clientset/versioned/typed/zalando.org/v1/fake/fake_fabriceventstream.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/typed/zalando.org/v1/fake/fake_zalando.org_client.go b/pkg/generated/clientset/versioned/typed/zalando.org/v1/fake/fake_zalando.org_client.go index 17786c9a7..2c151aff0 100644 --- a/pkg/generated/clientset/versioned/typed/zalando.org/v1/fake/fake_zalando.org_client.go +++ b/pkg/generated/clientset/versioned/typed/zalando.org/v1/fake/fake_zalando.org_client.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/typed/zalando.org/v1/generated_expansion.go b/pkg/generated/clientset/versioned/typed/zalando.org/v1/generated_expansion.go index 6a776af84..6f23bf51d 100644 --- a/pkg/generated/clientset/versioned/typed/zalando.org/v1/generated_expansion.go +++ b/pkg/generated/clientset/versioned/typed/zalando.org/v1/generated_expansion.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/clientset/versioned/typed/zalando.org/v1/zalando.org_client.go b/pkg/generated/clientset/versioned/typed/zalando.org/v1/zalando.org_client.go index 1937dd78f..984879e91 100644 --- a/pkg/generated/clientset/versioned/typed/zalando.org/v1/zalando.org_client.go +++ b/pkg/generated/clientset/versioned/typed/zalando.org/v1/zalando.org_client.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/informers/externalversions/acid.zalan.do/interface.go b/pkg/generated/informers/externalversions/acid.zalan.do/interface.go index b2b85d87d..8ea00f8dc 100644 --- a/pkg/generated/informers/externalversions/acid.zalan.do/interface.go +++ b/pkg/generated/informers/externalversions/acid.zalan.do/interface.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/informers/externalversions/acid.zalan.do/v1/interface.go b/pkg/generated/informers/externalversions/acid.zalan.do/v1/interface.go index 347c53f99..ee8873bee 100644 --- a/pkg/generated/informers/externalversions/acid.zalan.do/v1/interface.go +++ b/pkg/generated/informers/externalversions/acid.zalan.do/v1/interface.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/informers/externalversions/acid.zalan.do/v1/postgresql.go b/pkg/generated/informers/externalversions/acid.zalan.do/v1/postgresql.go index 61d88fb22..57f427331 100644 --- a/pkg/generated/informers/externalversions/acid.zalan.do/v1/postgresql.go +++ b/pkg/generated/informers/externalversions/acid.zalan.do/v1/postgresql.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/informers/externalversions/acid.zalan.do/v1/postgresteam.go b/pkg/generated/informers/externalversions/acid.zalan.do/v1/postgresteam.go index eb15e4fad..019e6de83 100644 --- a/pkg/generated/informers/externalversions/acid.zalan.do/v1/postgresteam.go +++ b/pkg/generated/informers/externalversions/acid.zalan.do/v1/postgresteam.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/informers/externalversions/factory.go b/pkg/generated/informers/externalversions/factory.go index 0842aa5ae..b76fec377 100644 --- a/pkg/generated/informers/externalversions/factory.go +++ b/pkg/generated/informers/externalversions/factory.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/informers/externalversions/generic.go b/pkg/generated/informers/externalversions/generic.go index afa4c6ac2..3bb58ec46 100644 --- a/pkg/generated/informers/externalversions/generic.go +++ b/pkg/generated/informers/externalversions/generic.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go b/pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go index 1046f8f48..be8c83c01 100644 --- a/pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go +++ b/pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/informers/externalversions/zalando.org/interface.go b/pkg/generated/informers/externalversions/zalando.org/interface.go index afece8ac3..5911e683e 100644 --- a/pkg/generated/informers/externalversions/zalando.org/interface.go +++ b/pkg/generated/informers/externalversions/zalando.org/interface.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/informers/externalversions/zalando.org/v1/fabriceventstream.go b/pkg/generated/informers/externalversions/zalando.org/v1/fabriceventstream.go index 13733e1d1..86b595193 100644 --- a/pkg/generated/informers/externalversions/zalando.org/v1/fabriceventstream.go +++ b/pkg/generated/informers/externalversions/zalando.org/v1/fabriceventstream.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/informers/externalversions/zalando.org/v1/interface.go b/pkg/generated/informers/externalversions/zalando.org/v1/interface.go index 329f15503..f7eedf04b 100644 --- a/pkg/generated/informers/externalversions/zalando.org/v1/interface.go +++ b/pkg/generated/informers/externalversions/zalando.org/v1/interface.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/listers/acid.zalan.do/v1/expansion_generated.go b/pkg/generated/listers/acid.zalan.do/v1/expansion_generated.go index 3189f5948..1d2a4fa3b 100644 --- a/pkg/generated/listers/acid.zalan.do/v1/expansion_generated.go +++ b/pkg/generated/listers/acid.zalan.do/v1/expansion_generated.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/listers/acid.zalan.do/v1/postgresql.go b/pkg/generated/listers/acid.zalan.do/v1/postgresql.go index 650f8d579..06c5aa2c0 100644 --- a/pkg/generated/listers/acid.zalan.do/v1/postgresql.go +++ b/pkg/generated/listers/acid.zalan.do/v1/postgresql.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/listers/acid.zalan.do/v1/postgresteam.go b/pkg/generated/listers/acid.zalan.do/v1/postgresteam.go index c63b8da58..b28fe8186 100644 --- a/pkg/generated/listers/acid.zalan.do/v1/postgresteam.go +++ b/pkg/generated/listers/acid.zalan.do/v1/postgresteam.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/listers/zalando.org/v1/expansion_generated.go b/pkg/generated/listers/zalando.org/v1/expansion_generated.go index 5a90e7828..2af7fefad 100644 --- a/pkg/generated/listers/zalando.org/v1/expansion_generated.go +++ b/pkg/generated/listers/zalando.org/v1/expansion_generated.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/generated/listers/zalando.org/v1/fabriceventstream.go b/pkg/generated/listers/zalando.org/v1/fabriceventstream.go index b4c895c74..eb549cfab 100644 --- a/pkg/generated/listers/zalando.org/v1/fabriceventstream.go +++ b/pkg/generated/listers/zalando.org/v1/fabriceventstream.go @@ -1,5 +1,5 @@ /* -Copyright 2022 Compose, Zalando SE +Copyright 2023 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From becf8a4715bc87c71346e32745c02c9f1ceb3eb8 Mon Sep 17 00:00:00 2001 From: yoshihikoueno <38683757+yoshihikoueno@users.noreply.github.com> Date: Wed, 4 Jan 2023 20:01:30 +0900 Subject: [PATCH 57/77] Bump spilo and target version for PostgreSQL 15 (#2139) * Bumped Spilo image tag to the one that supports PostgreSQL 15. Using CDP version temporarily until non-CDP one is released. * Added support for PostgreSQL 15 and made it default. 9.5 and 9.6 are now no longer supported * Bumped spilo image tag to 2.1-p9 * Bumped spilo image in test launcher Co-authored-by: yoshihiko Co-authored-by: Felix Kunde --- README.md | 4 ++-- charts/postgres-operator-ui/templates/deployment.yaml | 1 + charts/postgres-operator/crds/operatorconfigurations.yaml | 4 ++-- charts/postgres-operator/values.yaml | 4 ++-- docs/reference/operator_parameters.md | 2 +- docs/user.md | 4 ++-- e2e/run.sh | 2 +- e2e/tests/test_e2e.py | 4 ++-- manifests/complete-postgres-manifest.yaml | 4 ++-- manifests/configmap.yaml | 4 ++-- manifests/minimal-postgres-manifest.yaml | 2 +- manifests/operatorconfiguration.crd.yaml | 4 ++-- manifests/postgresql-operator-default-configuration.yaml | 4 ++-- manifests/standby-manifest.yaml | 2 +- pkg/apis/acid.zalan.do/v1/operator_configuration_type.go | 2 +- pkg/cluster/majorversionupgrade.go | 3 ++- pkg/controller/operator_config.go | 4 ++-- pkg/util/config/config.go | 4 ++-- ui/operator_ui/main.py | 2 +- 19 files changed, 31 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 99be3845a..660665ea3 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ pipelines with no access to Kubernetes API directly, promoting infrastructure as ### PostgreSQL features -* Supports PostgreSQL 14, starting from 10+ +* Supports PostgreSQL 15, starting from 10+ * Streaming replication cluster via Patroni * Point-In-Time-Recovery with [pg_basebackup](https://www.postgresql.org/docs/11/app-pgbasebackup.html) / @@ -61,7 +61,7 @@ We introduce the major version into the backup path to smoothen the [major versi The new operator configuration can set a compatibility flag *enable_spilo_wal_path_compat* to make Spilo look for wal segments in the current path but also old format paths. This comes at potential performance costs and should be disabled after a few days. -The newest Spilo image is: `registry.opensource.zalan.do/acid/spilo-14:2.1-p7` +The newest Spilo image is: `ghcr.io/zalando/spilo-15:2.1-p9` The last Spilo 12 image is: `registry.opensource.zalan.do/acid/spilo-12:1.6-p5` diff --git a/charts/postgres-operator-ui/templates/deployment.yaml b/charts/postgres-operator-ui/templates/deployment.yaml index c82d38c70..d9f0725a9 100644 --- a/charts/postgres-operator-ui/templates/deployment.yaml +++ b/charts/postgres-operator-ui/templates/deployment.yaml @@ -76,6 +76,7 @@ spec: "cost_core": 0.0575, "cost_memory": 0.014375, "postgresql_versions": [ + "15", "14", "13", "12", diff --git a/charts/postgres-operator/crds/operatorconfigurations.yaml b/charts/postgres-operator/crds/operatorconfigurations.yaml index f1589b92d..aaadce0c3 100644 --- a/charts/postgres-operator/crds/operatorconfigurations.yaml +++ b/charts/postgres-operator/crds/operatorconfigurations.yaml @@ -68,7 +68,7 @@ spec: type: string docker_image: type: string - default: "registry.opensource.zalan.do/acid/spilo-14:2.1-p7" + default: "ghcr.io/zalando/spilo-15:2.1-p9" enable_crd_registration: type: boolean default: true @@ -170,7 +170,7 @@ spec: default: "11" target_major_version: type: string - default: "14" + default: "15" kubernetes: type: object properties: diff --git a/charts/postgres-operator/values.yaml b/charts/postgres-operator/values.yaml index 20f961ca1..36c09c3ee 100644 --- a/charts/postgres-operator/values.yaml +++ b/charts/postgres-operator/values.yaml @@ -38,7 +38,7 @@ configGeneral: # etcd connection string for Patroni. Empty uses K8s-native DCS. etcd_host: "" # Spilo docker image - docker_image: registry.opensource.zalan.do/acid/spilo-14:2.1-p7 + docker_image: ghcr.io/zalando/spilo-15:2.1-p9 # key name for annotation to ignore globally configured instance limits # ignore_instance_limits_annotation_key: "" @@ -91,7 +91,7 @@ configMajorVersionUpgrade: # minimal Postgres major version that will not automatically be upgraded minimal_major_version: "11" # target Postgres major version when upgrading clusters automatically - target_major_version: "14" + target_major_version: "15" configKubernetes: # list of additional capabilities for postgres container diff --git a/docs/reference/operator_parameters.md b/docs/reference/operator_parameters.md index 16ec7320f..ca5d7b055 100644 --- a/docs/reference/operator_parameters.md +++ b/docs/reference/operator_parameters.md @@ -251,7 +251,7 @@ CRD-configuration, they are grouped under the `major_version_upgrade` key. * **target_major_version** The target Postgres major version when upgrading clusters automatically which violate the configured allowed `minimal_major_version` when - `major_version_upgrade_mode` is set to `"full"`. The default is `"14"`. + `major_version_upgrade_mode` is set to `"full"`. The default is `"15"`. ## Kubernetes resources diff --git a/docs/user.md b/docs/user.md index c5fcfda87..fa82e3344 100644 --- a/docs/user.md +++ b/docs/user.md @@ -30,7 +30,7 @@ spec: databases: foo: zalando postgresql: - version: "14" + version: "15" ``` Once you cloned the Postgres Operator [repository](https://github.com/zalando/postgres-operator) @@ -109,7 +109,7 @@ metadata: spec: [...] postgresql: - version: "14" + version: "15" parameters: password_encryption: scram-sha-256 ``` diff --git a/e2e/run.sh b/e2e/run.sh index f5ca56a05..12581a26a 100755 --- a/e2e/run.sh +++ b/e2e/run.sh @@ -8,7 +8,7 @@ IFS=$'\n\t' readonly cluster_name="postgres-operator-e2e-tests" readonly kubeconfig_path="/tmp/kind-config-${cluster_name}" -readonly spilo_image="registry.opensource.zalan.do/acid/spilo-14-e2e:0.1" +readonly spilo_image="registry.opensource.zalan.do/acid/spilo-15-e2e:0.1" readonly e2e_test_runner_image="registry.opensource.zalan.do/acid/postgres-operator-e2e-tests-runner:0.4" export GOPATH=${GOPATH-~/go} diff --git a/e2e/tests/test_e2e.py b/e2e/tests/test_e2e.py index 39fd45323..6b46dd7db 100644 --- a/e2e/tests/test_e2e.py +++ b/e2e/tests/test_e2e.py @@ -12,8 +12,8 @@ from kubernetes import client from tests.k8s_api import K8s from kubernetes.client.rest import ApiException -SPILO_CURRENT = "registry.opensource.zalan.do/acid/spilo-14-e2e:0.3" -SPILO_LAZY = "registry.opensource.zalan.do/acid/spilo-14-e2e:0.4" +SPILO_CURRENT = "registry.opensource.zalan.do/acid/spilo-15-e2e:0.1" +SPILO_LAZY = "registry.opensource.zalan.do/acid/spilo-15-e2e:0.2" def to_selector(labels): diff --git a/manifests/complete-postgres-manifest.yaml b/manifests/complete-postgres-manifest.yaml index bc8309684..8d4353bcb 100644 --- a/manifests/complete-postgres-manifest.yaml +++ b/manifests/complete-postgres-manifest.yaml @@ -10,7 +10,7 @@ metadata: # "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: registry.opensource.zalan.do/acid/spilo-14:2.1-p7 + dockerImage: ghcr.io/zalando/spilo-15:2.1-p9 teamId: "acid" numberOfInstances: 2 users: # Application/Robot users @@ -44,7 +44,7 @@ spec: defaultRoles: true defaultUsers: false postgresql: - version: "14" + version: "15" parameters: # Expert section shared_buffers: "32MB" max_connections: "10" diff --git a/manifests/configmap.yaml b/manifests/configmap.yaml index 831a00341..9485331de 100644 --- a/manifests/configmap.yaml +++ b/manifests/configmap.yaml @@ -34,7 +34,7 @@ data: # default_memory_request: 100Mi # delete_annotation_date_key: delete-date # delete_annotation_name_key: delete-clustername - docker_image: registry.opensource.zalan.do/acid/spilo-14:2.1-p7 + docker_image: ghcr.io/zalando/spilo-15:2.1-p9 # downscaler_annotations: "deployment-time,downscaler/*" # enable_admin_role_for_users: "true" # enable_crd_registration: "true" @@ -145,7 +145,7 @@ data: spilo_privileged: "false" storage_resize_mode: "pvc" super_username: postgres - # target_major_version: "14" + # 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 diff --git a/manifests/minimal-postgres-manifest.yaml b/manifests/minimal-postgres-manifest.yaml index b2a8fa2c8..00f11ebf7 100644 --- a/manifests/minimal-postgres-manifest.yaml +++ b/manifests/minimal-postgres-manifest.yaml @@ -17,4 +17,4 @@ spec: preparedDatabases: bar: {} postgresql: - version: "14" + version: "15" diff --git a/manifests/operatorconfiguration.crd.yaml b/manifests/operatorconfiguration.crd.yaml index 4e5ab803f..2f10f7be9 100644 --- a/manifests/operatorconfiguration.crd.yaml +++ b/manifests/operatorconfiguration.crd.yaml @@ -66,7 +66,7 @@ spec: type: string docker_image: type: string - default: "registry.opensource.zalan.do/acid/spilo-14:2.1-p7" + default: "ghcr.io/zalando/spilo-15:2.1-p9" enable_crd_registration: type: boolean default: true @@ -168,7 +168,7 @@ spec: default: "11" target_major_version: type: string - default: "14" + default: "15" kubernetes: type: object properties: diff --git a/manifests/postgresql-operator-default-configuration.yaml b/manifests/postgresql-operator-default-configuration.yaml index 4c7737d13..6939ec4d7 100644 --- a/manifests/postgresql-operator-default-configuration.yaml +++ b/manifests/postgresql-operator-default-configuration.yaml @@ -3,7 +3,7 @@ kind: OperatorConfiguration metadata: name: postgresql-operator-default-configuration configuration: - docker_image: registry.opensource.zalan.do/acid/spilo-14:2.1-p7 + docker_image: ghcr.io/zalando/spilo-15:2.1-p9 # enable_crd_registration: true # crd_categories: # - all @@ -40,7 +40,7 @@ configuration: # major_version_upgrade_team_allow_list: # - acid minimal_major_version: "11" - target_major_version: "14" + target_major_version: "15" kubernetes: # additional_pod_capabilities: # - "SYS_NICE" diff --git a/manifests/standby-manifest.yaml b/manifests/standby-manifest.yaml index ac94fe897..2db4d489b 100644 --- a/manifests/standby-manifest.yaml +++ b/manifests/standby-manifest.yaml @@ -8,7 +8,7 @@ spec: size: 1Gi numberOfInstances: 1 postgresql: - version: "14" + 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/" diff --git a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go index 29cddffa5..d21cbe48b 100644 --- a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go +++ b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go @@ -50,7 +50,7 @@ type MajorVersionUpgradeConfiguration struct { MajorVersionUpgradeMode string `json:"major_version_upgrade_mode" default:"off"` // off - no actions, manual - manifest triggers action, full - manifest and minimal version violation trigger upgrade MajorVersionUpgradeTeamAllowList []string `json:"major_version_upgrade_team_allow_list,omitempty"` MinimalMajorVersion string `json:"minimal_major_version" default:"11"` - TargetMajorVersion string `json:"target_major_version" default:"14"` + TargetMajorVersion string `json:"target_major_version" default:"15"` } // KubernetesMetaConfiguration defines k8s conf required for all Postgres clusters and the operator itself diff --git a/pkg/cluster/majorversionupgrade.go b/pkg/cluster/majorversionupgrade.go index 7a63da32f..f635dc604 100644 --- a/pkg/cluster/majorversionupgrade.go +++ b/pkg/cluster/majorversionupgrade.go @@ -17,6 +17,7 @@ var VersionMap = map[string]int{ "13": 130000, "14": 140000, "15": 150000, + } // IsBiggerPostgresVersion Compare two Postgres version numbers @@ -35,7 +36,7 @@ func (c *Cluster) GetDesiredMajorVersionAsInt() int { func (c *Cluster) GetDesiredMajorVersion() string { if c.Config.OpConfig.MajorVersionUpgradeMode == "full" { - // e.g. current is 10, minimal is 11 allowing 11 to 14 clusters, everything below is upgraded + // e.g. current is 10, minimal is 11 allowing 11 to 15 clusters, everything below is upgraded if IsBiggerPostgresVersion(c.Spec.PgVersion, c.Config.OpConfig.MinimalMajorVersion) { c.logger.Infof("overwriting configured major version %s to %s", c.Spec.PgVersion, c.Config.OpConfig.TargetMajorVersion) return c.Config.OpConfig.TargetMajorVersion diff --git a/pkg/controller/operator_config.go b/pkg/controller/operator_config.go index b1e46b6ae..40d639b20 100644 --- a/pkg/controller/operator_config.go +++ b/pkg/controller/operator_config.go @@ -39,7 +39,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur result.EnableTeamIdClusternamePrefix = fromCRD.EnableTeamIdClusternamePrefix result.EtcdHost = fromCRD.EtcdHost result.KubernetesUseConfigMaps = fromCRD.KubernetesUseConfigMaps - result.DockerImage = util.Coalesce(fromCRD.DockerImage, "registry.opensource.zalan.do/acid/spilo-14:2.1-p7") + result.DockerImage = util.Coalesce(fromCRD.DockerImage, "ghcr.io/zalando/spilo-15:2.1-p9") result.Workers = util.CoalesceUInt32(fromCRD.Workers, 8) result.MinInstances = fromCRD.MinInstances result.MaxInstances = fromCRD.MaxInstances @@ -63,7 +63,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur result.MajorVersionUpgradeMode = util.Coalesce(fromCRD.MajorVersionUpgrade.MajorVersionUpgradeMode, "off") result.MajorVersionUpgradeTeamAllowList = fromCRD.MajorVersionUpgrade.MajorVersionUpgradeTeamAllowList result.MinimalMajorVersion = util.Coalesce(fromCRD.MajorVersionUpgrade.MinimalMajorVersion, "11") - result.TargetMajorVersion = util.Coalesce(fromCRD.MajorVersionUpgrade.TargetMajorVersion, "14") + result.TargetMajorVersion = util.Coalesce(fromCRD.MajorVersionUpgrade.TargetMajorVersion, "15") // kubernetes config result.CustomPodAnnotations = fromCRD.Kubernetes.CustomPodAnnotations diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index 80aa509e3..4afc30e1a 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -165,7 +165,7 @@ type Config struct { WatchedNamespace string `name:"watched_namespace"` // special values: "*" means 'watch all namespaces', the empty string "" means 'watch a namespace where operator is deployed to' KubernetesUseConfigMaps bool `name:"kubernetes_use_configmaps" default:"false"` EtcdHost string `name:"etcd_host" default:""` // special values: the empty string "" means Patroni will use K8s as a DCS - DockerImage string `name:"docker_image" default:"registry.opensource.zalan.do/acid/spilo-14:2.1-p7"` + DockerImage string `name:"docker_image" default:"ghcr.io/zalando/spilo-15:2.1-p9"` SidecarImages map[string]string `name:"sidecar_docker_images"` // deprecated in favour of SidecarContainers SidecarContainers []v1.Container `name:"sidecars"` PodServiceAccountName string `name:"pod_service_account_name" default:"postgres-pod"` @@ -233,7 +233,7 @@ type Config struct { MajorVersionUpgradeMode string `name:"major_version_upgrade_mode" default:"off"` MajorVersionUpgradeTeamAllowList []string `name:"major_version_upgrade_team_allow_list" default:""` MinimalMajorVersion string `name:"minimal_major_version" default:"11"` - TargetMajorVersion string `name:"target_major_version" default:"14"` + TargetMajorVersion string `name:"target_major_version" default:"15"` PatroniAPICheckInterval time.Duration `name:"patroni_api_check_interval" default:"1s"` PatroniAPICheckTimeout time.Duration `name:"patroni_api_check_timeout" default:"5s"` EnablePatroniFailsafeMode *bool `name:"enable_patroni_failsafe_mode" default:"false"` diff --git a/ui/operator_ui/main.py b/ui/operator_ui/main.py index fdb61cd39..49920c8e0 100644 --- a/ui/operator_ui/main.py +++ b/ui/operator_ui/main.py @@ -321,7 +321,7 @@ DEFAULT_UI_CONFIG = { 'users_visible': True, 'databases_visible': True, 'resources_visible': RESOURCES_VISIBLE, - 'postgresql_versions': ['11', '12', '13', '14'], + 'postgresql_versions': ['11', '12', '13', '14', '15'], 'dns_format_string': '{0}.{1}', 'pgui_link': '', 'static_network_whitelist': {}, From c756cb2f8afff1c6f0556ce616d76e69f8f0cc35 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Thu, 5 Jan 2023 12:02:19 +0100 Subject: [PATCH 58/77] spec.env can override clone and standby variables (#2159) --- docs/administrator.md | 6 ++--- pkg/cluster/k8sres.go | 10 ++++---- pkg/cluster/k8sres_test.go | 47 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 8 deletions(-) diff --git a/docs/administrator.md b/docs/administrator.md index cd56289b4..8fde634c4 100644 --- a/docs/administrator.md +++ b/docs/administrator.md @@ -631,9 +631,9 @@ order (e.g. a variable defined in 4. overrides a variable with the same name in 5.): 1. Assigned by the operator -2. Clone section (with WAL settings from operator config when `s3_wal_path` is empty) -3. Standby section -4. `env` section in cluster manifest +2. `env` section in cluster manifest +3. Clone section (with WAL settings from operator config when `s3_wal_path` is empty) +4. Standby section 5. Pod environment secret via operator config 6. Pod environment config map via operator config 7. WAL and logical backup settings from operator config diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index 902e6f873..451a189fd 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -962,6 +962,11 @@ func (c *Cluster) generateSpiloPodEnvVars( envVars = append(envVars, v1.EnvVar{Name: "KUBERNETES_USE_CONFIGMAPS", Value: "true"}) } + // fetch cluster-specific variables that will override all subsequent global variables + if len(spec.Env) > 0 { + envVars = appendEnvVars(envVars, spec.Env...) + } + if spec.Clone != nil && spec.Clone.ClusterName != "" { envVars = append(envVars, c.generateCloneEnvironment(spec.Clone)...) } @@ -970,11 +975,6 @@ func (c *Cluster) generateSpiloPodEnvVars( envVars = append(envVars, c.generateStandbyEnvironment(spec.StandbyCluster)...) } - // fetch cluster-specific variables that will override all subsequent global variables - if len(spec.Env) > 0 { - envVars = appendEnvVars(envVars, spec.Env...) - } - // fetch variables from custom environment Secret // that will override all subsequent global variables secretEnvVarsList, err := c.getPodEnvironmentSecretVariables() diff --git a/pkg/cluster/k8sres_test.go b/pkg/cluster/k8sres_test.go index bfdca7e59..175f6fd7f 100644 --- a/pkg/cluster/k8sres_test.go +++ b/pkg/cluster/k8sres_test.go @@ -602,6 +602,23 @@ func TestGenerateSpiloPodEnvVars(t *testing.T) { envVarValue: "s3.eu-central-1.amazonaws.com", }, } + expectedCloneEnvSpecEnv := []ExpectedValue{ + { + envIndex: 15, + envVarConstant: "CLONE_WAL_BUCKET_SCOPE_PREFIX", + envVarValue: "test-cluster", + }, + { + envIndex: 17, + envVarConstant: "CLONE_WALE_S3_PREFIX", + envVarValue: "s3://another-bucket", + }, + { + envIndex: 21, + envVarConstant: "CLONE_AWS_ENDPOINT", + envVarValue: "s3.eu-central-1.amazonaws.com", + }, + } expectedCloneEnvConfigMap := []ExpectedValue{ { envIndex: 16, @@ -821,6 +838,36 @@ func TestGenerateSpiloPodEnvVars(t *testing.T) { standbyDescription: &acidv1.StandbyDescription{}, expectedValues: expectedCloneEnvSpec, }, + { + subTest: "will set CLONE_ parameters from manifest `env` section, followed by other options", + opConfig: config.Config{ + Resources: config.Resources{ + PodEnvironmentConfigMap: spec.NamespacedName{ + Name: testPodEnvironmentConfigMapName, + }, + }, + WALES3Bucket: "global-s3-bucket", + }, + cloneDescription: &acidv1.CloneDescription{ + ClusterName: "test-cluster", + EndTimestamp: "somewhen", + UID: dummyUUID, + S3WalPath: "s3://another-bucket", + S3Endpoint: "s3.eu-central-1.amazonaws.com", + }, + standbyDescription: &acidv1.StandbyDescription{}, + expectedValues: expectedCloneEnvSpecEnv, + pgsql: acidv1.Postgresql{ + Spec: acidv1.PostgresSpec{ + Env: []v1.EnvVar{ + { + Name: "CLONE_WAL_BUCKET_SCOPE_PREFIX", + Value: "test-cluster", + }, + }, + }, + }, + }, { subTest: "will set CLONE_AWS_ENDPOINT parameter from pod environment config map", opConfig: config.Config{ From bb2617a53f10fcc0fc38f621ed6853e148f39c3a Mon Sep 17 00:00:00 2001 From: Stef Graces Date: Thu, 5 Jan 2023 12:16:41 +0100 Subject: [PATCH 59/77] Add logical backup for azure (#2052) * Add logical backup for azure --- .../crds/operatorconfigurations.yaml | 6 ++++++ docker/logical-backup/Dockerfile | 1 + docker/logical-backup/dump.sh | 21 ++++++++++++++----- docs/reference/operator_parameters.md | 9 ++++++++ manifests/configmap.yaml | 3 +++ manifests/operatorconfiguration.crd.yaml | 6 ++++++ ...gresql-operator-default-configuration.yaml | 3 +++ pkg/apis/acid.zalan.do/v1/crds.go | 9 ++++++++ .../v1/operator_configuration_type.go | 3 +++ pkg/cluster/k8sres.go | 12 +++++++++++ pkg/controller/operator_config.go | 3 +++ pkg/util/config/config.go | 3 +++ 12 files changed, 74 insertions(+), 5 deletions(-) diff --git a/charts/postgres-operator/crds/operatorconfigurations.yaml b/charts/postgres-operator/crds/operatorconfigurations.yaml index aaadce0c3..b36c58b74 100644 --- a/charts/postgres-operator/crds/operatorconfigurations.yaml +++ b/charts/postgres-operator/crds/operatorconfigurations.yaml @@ -498,6 +498,12 @@ spec: type: string pattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$' default: "30 00 * * *" + logical_backup_azure_storage_account_name: + type: string + logical_backup_azure_storage_container: + type: string + logical_backup_azure_storage_account_key: + type: string debug: type: object properties: diff --git a/docker/logical-backup/Dockerfile b/docker/logical-backup/Dockerfile index 184e3be1c..08553fc5f 100644 --- a/docker/logical-backup/Dockerfile +++ b/docker/logical-backup/Dockerfile @@ -15,6 +15,7 @@ RUN apt-get update \ gnupg \ gcc \ libffi-dev \ + && curl -sL https://aka.ms/InstallAzureCLIDeb | bash \ && pip3 install --upgrade pip \ && pip3 install --no-cache-dir awscli --upgrade \ && pip3 install --no-cache-dir gsutil --upgrade \ diff --git a/docker/logical-backup/dump.sh b/docker/logical-backup/dump.sh index 2627ac8c9..0aa723ab5 100755 --- a/docker/logical-backup/dump.sh +++ b/docker/logical-backup/dump.sh @@ -40,6 +40,12 @@ function compress { pigz } +function az_upload { + PATH_TO_BACKUP=$LOGICAL_BACKUP_S3_BUCKET"/spilo/"$SCOPE$LOGICAL_BACKUP_S3_BUCKET_SCOPE_SUFFIX"/logical_backups/"$(date +%s).sql.gz + + az storage blob upload --file "$1" --account-name "$LOGICAL_BACKUP_AZURE_STORAGE_ACCOUNT_NAME" --account-key "$LOGICAL_BACKUP_AZURE_STORAGE_ACCOUNT_KEY" -c "$LOGICAL_BACKUP_AZURE_STORAGE_CONTAINER" -n "$PATH_TO_BACKUP" +} + function aws_delete_objects { args=( "--bucket=$LOGICAL_BACKUP_S3_BUCKET" @@ -120,7 +126,7 @@ function upload { "gcs") gcs_upload ;; - *) + "aws") aws_upload $(($(estimate_size) / DUMP_SIZE_COEFF)) aws_delete_outdated ;; @@ -174,8 +180,13 @@ for search in "${search_strategy[@]}"; do done set -x -dump | compress | upload -[[ ${PIPESTATUS[0]} != 0 || ${PIPESTATUS[1]} != 0 || ${PIPESTATUS[2]} != 0 ]] && (( ERRORCOUNT += 1 )) -set +x +if [ "$LOGICAL_BACKUP_PROVIDER" == "az" ]; then + dump | compress > /tmp/azure-backup.sql.gz + az_upload /tmp/azure-backup.sql.gz +else + dump | compress | upload + [[ ${PIPESTATUS[0]} != 0 || ${PIPESTATUS[1]} != 0 || ${PIPESTATUS[2]} != 0 ]] && (( ERRORCOUNT += 1 )) + set +x -exit $ERRORCOUNT + exit $ERRORCOUNT +fi diff --git a/docs/reference/operator_parameters.md b/docs/reference/operator_parameters.md index ca5d7b055..dd80ce569 100644 --- a/docs/reference/operator_parameters.md +++ b/docs/reference/operator_parameters.md @@ -765,6 +765,15 @@ grouped under the `logical_backup` key. [reference schedule format](https://kubernetes.io/docs/tasks/job/automated-tasks-with-cron-jobs/#schedule) into account. Default: "30 00 \* \* \*" +* **logical_backup_azure_storage_account_name** + Storage account name used to upload logical backups to when using Azure. Default: "" + +* **logical_backup_azure_storage_container** + Storage container used to upload logical backups to when using Azure. Default: "" + +* **logical_backup_azure_storage_account_key** + Storage account key used to authenticate with Azure when uploading logical backups. Default: "" + ## Debugging the operator Options to aid debugging of the operator itself. Grouped under the `debug` key. diff --git a/manifests/configmap.yaml b/manifests/configmap.yaml index 9485331de..15816c693 100644 --- a/manifests/configmap.yaml +++ b/manifests/configmap.yaml @@ -87,6 +87,9 @@ data: logical_backup_s3_sse: "AES256" # logical_backup_s3_retention_time: "" logical_backup_schedule: "30 00 * * *" + # logical_backup_azure_storage_account_name: "" + # logical_backup_azure_storage_container: "" + # logical_backup_azure_storage_account_key: "" major_version_upgrade_mode: "manual" # major_version_upgrade_team_allow_list: "" master_dns_name_format: "{cluster}.{namespace}.{hostedzone}" diff --git a/manifests/operatorconfiguration.crd.yaml b/manifests/operatorconfiguration.crd.yaml index 2f10f7be9..df6343852 100644 --- a/manifests/operatorconfiguration.crd.yaml +++ b/manifests/operatorconfiguration.crd.yaml @@ -496,6 +496,12 @@ spec: type: string pattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$' default: "30 00 * * *" + logical_backup_azure_storage_account_name: + type: string + logical_backup_azure_storage_container: + type: string + logical_backup_azure_storage_account_key: + type: string debug: type: object properties: diff --git a/manifests/postgresql-operator-default-configuration.yaml b/manifests/postgresql-operator-default-configuration.yaml index 6939ec4d7..36d009bbf 100644 --- a/manifests/postgresql-operator-default-configuration.yaml +++ b/manifests/postgresql-operator-default-configuration.yaml @@ -163,6 +163,9 @@ configuration: logical_backup_s3_sse: "AES256" # logical_backup_s3_retention_time: "" logical_backup_schedule: "30 00 * * *" + # logical_backup_azure_storage_account_name: "" + # logical_backup_azure_storage_container: "" + # logical_backup_azure_storage_account_key: "" debug: debug_logging: true enable_database_access: true diff --git a/pkg/apis/acid.zalan.do/v1/crds.go b/pkg/apis/acid.zalan.do/v1/crds.go index 40a8b380e..e0cb148eb 100644 --- a/pkg/apis/acid.zalan.do/v1/crds.go +++ b/pkg/apis/acid.zalan.do/v1/crds.go @@ -1655,6 +1655,15 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{ Type: "string", Pattern: "^(\\d+|\\*)(/\\d+)?(\\s+(\\d+|\\*)(/\\d+)?){4}$", }, + "logical_backup_azure_storage_account_name": { + Type: "string", + }, + "logical_backup_azure_storage_container": { + Type: "string", + }, + "logical_backup_azure_storage_account_key": { + Type: "string", + }, }, }, "debug": { diff --git a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go index d21cbe48b..c57dff0cb 100644 --- a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go +++ b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go @@ -227,6 +227,9 @@ type OperatorLogicalBackupConfiguration struct { RetentionTime string `json:"logical_backup_s3_retention_time,omitempty"` GoogleApplicationCredentials string `json:"logical_backup_google_application_credentials,omitempty"` JobPrefix string `json:"logical_backup_job_prefix,omitempty"` + AzureStorageAccountName string `json:"logical_backup_azure_storage_account_name,omitempty"` + AzureStorageContainer string `json:"logical_backup_azure_storage_container,omitempty"` + AzureStorageAccountKey string `json:"logical_backup_azure_storage_account_key,omitempty"` } // PatroniConfiguration defines configuration for Patroni diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index 451a189fd..963814941 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -2262,6 +2262,18 @@ func (c *Cluster) generateLogicalBackupPodEnvVars() []v1.EnvVar { Name: "LOGICAL_BACKUP_GOOGLE_APPLICATION_CREDENTIALS", Value: c.OpConfig.LogicalBackup.LogicalBackupGoogleApplicationCredentials, }, + { + Name: "LOGICAL_BACKUP_AZURE_STORAGE_ACCOUNT_NAME", + Value: c.OpConfig.LogicalBackup.LogicalBackupAzureStorageAccountName, + }, + { + Name: "LOGICAL_BACKUP_AZURE_STORAGE_CONTAINER", + Value: c.OpConfig.LogicalBackup.LogicalBackupAzureStorageContainer, + }, + { + Name: "LOGICAL_BACKUP_AZURE_STORAGE_ACCOUNT_KEY", + Value: c.OpConfig.LogicalBackup.LogicalBackupAzureStorageAccountKey, + }, // Postgres env vars { Name: "PG_VERSION", diff --git a/pkg/controller/operator_config.go b/pkg/controller/operator_config.go index 40d639b20..fc4aba73e 100644 --- a/pkg/controller/operator_config.go +++ b/pkg/controller/operator_config.go @@ -183,6 +183,9 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur result.LogicalBackupS3RetentionTime = fromCRD.LogicalBackup.RetentionTime result.LogicalBackupGoogleApplicationCredentials = fromCRD.LogicalBackup.GoogleApplicationCredentials result.LogicalBackupJobPrefix = util.Coalesce(fromCRD.LogicalBackup.JobPrefix, "logical-backup-") + result.LogicalBackupAzureStorageAccountName = fromCRD.LogicalBackup.AzureStorageAccountName + result.LogicalBackupAzureStorageAccountKey = fromCRD.LogicalBackup.AzureStorageAccountKey + result.LogicalBackupAzureStorageContainer = fromCRD.LogicalBackup.AzureStorageContainer // debug config result.DebugLogging = fromCRD.OperatorDebug.DebugLogging diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index 4afc30e1a..30e557447 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -137,6 +137,9 @@ type LogicalBackup struct { LogicalBackupS3RetentionTime string `name:"logical_backup_s3_retention_time" default:""` LogicalBackupGoogleApplicationCredentials string `name:"logical_backup_google_application_credentials" default:""` LogicalBackupJobPrefix string `name:"logical_backup_job_prefix" default:"logical-backup-"` + LogicalBackupAzureStorageAccountName string `name:"logical_backup_azure_storage_account_name" default:""` + LogicalBackupAzureStorageContainer string `name:"logical_backup_azure_storage_container" default:""` + LogicalBackupAzureStorageAccountKey string `name:"logical_backup_azure_storage_account_key" default:""` } // Operator options for connection pooler From bbc0de3ffdfc86c73e288625bd931e8a8c5d2c3d Mon Sep 17 00:00:00 2001 From: Pluggi <1277836+Pluggi@users.noreply.github.com> Date: Thu, 5 Jan 2023 15:05:11 +0100 Subject: [PATCH 60/77] Add custom annotations to postgres-operator-ui objects (#1748) * Add custom annotations to postgres-operator-ui's pod and service Signed-off-by: Antoine Bardoux --- charts/postgres-operator-ui/templates/deployment.yaml | 4 ++++ charts/postgres-operator-ui/templates/service.yaml | 4 ++++ charts/postgres-operator-ui/values.yaml | 6 ++++++ 3 files changed, 14 insertions(+) diff --git a/charts/postgres-operator-ui/templates/deployment.yaml b/charts/postgres-operator-ui/templates/deployment.yaml index d9f0725a9..c007d4ed4 100644 --- a/charts/postgres-operator-ui/templates/deployment.yaml +++ b/charts/postgres-operator-ui/templates/deployment.yaml @@ -19,6 +19,10 @@ spec: labels: app.kubernetes.io/name: {{ template "postgres-operator-ui.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} spec: serviceAccountName: {{ include "postgres-operator-ui.serviceAccountName" . }} {{- if .Values.imagePullSecrets }} diff --git a/charts/postgres-operator-ui/templates/service.yaml b/charts/postgres-operator-ui/templates/service.yaml index e14603720..c93e076ed 100644 --- a/charts/postgres-operator-ui/templates/service.yaml +++ b/charts/postgres-operator-ui/templates/service.yaml @@ -6,6 +6,10 @@ metadata: helm.sh/chart: {{ template "postgres-operator-ui.chart" . }} app.kubernetes.io/managed-by: {{ .Release.Service }} app.kubernetes.io/instance: {{ .Release.Name }} + {{- with .Values.service.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} name: {{ template "postgres-operator-ui.fullname" . }} namespace: {{ .Release.Namespace }} spec: diff --git a/charts/postgres-operator-ui/values.yaml b/charts/postgres-operator-ui/values.yaml index 1fe4d37a9..0c3f57796 100644 --- a/charts/postgres-operator-ui/values.yaml +++ b/charts/postgres-operator-ui/values.yaml @@ -48,6 +48,10 @@ envs: teams: - "acid" +# Extra pod annotations +podAnnotations: + {} + # configure extra UI ENVs # Extra ENVs are writen in kubenertes format and added "as is" to the pod's env variables # https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/ @@ -85,6 +89,8 @@ service: # If the type of the service is NodePort a port can be specified using the nodePort field # If the nodePort field is not specified, or if it has no value, then a random port is used # nodePort: 32521 + annotations: + {} # configure UI ingress. If needed: "enabled: true" ingress: From 29cec0ceda6dba6e761c217137f8fa9c1c90cb8c Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Thu, 5 Jan 2023 15:19:36 +0100 Subject: [PATCH 61/77] configurable resources for logical backup pod template (#710) * new config options to specify resources for logical backup jobs * bug in logical backup script for s3 dumps * define enum for logical_backup_provider * changed order of logical backup azure options * fix unit test for stream comparison --- .../crds/operatorconfigurations.yaml | 28 +++- charts/postgres-operator/values.yaml | 13 +- docker/logical-backup/dump.sh | 2 +- docs/administrator.md | 4 + docs/reference/operator_parameters.md | 29 ++-- manifests/configmap.yaml | 10 +- manifests/operatorconfiguration.crd.yaml | 28 +++- ...gresql-operator-default-configuration.yaml | 10 +- pkg/apis/acid.zalan.do/v1/crds.go | 45 ++++-- .../v1/operator_configuration_type.go | 10 +- pkg/cluster/k8sres.go | 24 +++- pkg/cluster/k8sres_test.go | 134 +++++++++++++++++- pkg/cluster/streams_test.go | 3 +- pkg/controller/operator_config.go | 10 +- pkg/util/config/config.go | 10 +- 15 files changed, 305 insertions(+), 55 deletions(-) diff --git a/charts/postgres-operator/crds/operatorconfigurations.yaml b/charts/postgres-operator/crds/operatorconfigurations.yaml index b36c58b74..e10296659 100644 --- a/charts/postgres-operator/crds/operatorconfigurations.yaml +++ b/charts/postgres-operator/crds/operatorconfigurations.yaml @@ -469,6 +469,18 @@ spec: 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.8.2" @@ -477,8 +489,18 @@ spec: 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 @@ -498,12 +520,6 @@ spec: type: string pattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$' default: "30 00 * * *" - logical_backup_azure_storage_account_name: - type: string - logical_backup_azure_storage_container: - type: string - logical_backup_azure_storage_account_key: - type: string debug: type: object properties: diff --git a/charts/postgres-operator/values.yaml b/charts/postgres-operator/values.yaml index 36c09c3ee..7766519ab 100644 --- a/charts/postgres-operator/values.yaml +++ b/charts/postgres-operator/values.yaml @@ -332,6 +332,17 @@ configAwsOrGcp: # configure K8s cron job managed by the operator configLogicalBackup: + # Azure Storage Account specs to store backup results + # logical_backup_azure_storage_account_name: "" + # logical_backup_azure_storage_container: "" + # logical_backup_azure_storage_account_key: "" + + # resources for logical backup pod, if empty configPostgresPodResources will be used + # logical_backup_cpu_limit: "" + # logical_backup_cpu_request: "" + # logical_backup_memory_limit: "" + # logical_backup_memory_request: "" + # image for pods of the logical backup job (example runs pg_dumpall) logical_backup_docker_image: "registry.opensource.zalan.do/acid/logical-backup:v1.8.0" # path of google cloud service account json file @@ -339,7 +350,7 @@ configLogicalBackup: # prefix for the backup job name logical_backup_job_prefix: "logical-backup-" - # storage provider - either "s3" or "gcs" + # storage provider - either "s3", "gcs" or "az" logical_backup_provider: "s3" # S3 Access Key ID logical_backup_s3_access_key_id: "" diff --git a/docker/logical-backup/dump.sh b/docker/logical-backup/dump.sh index 0aa723ab5..178577ced 100755 --- a/docker/logical-backup/dump.sh +++ b/docker/logical-backup/dump.sh @@ -126,7 +126,7 @@ function upload { "gcs") gcs_upload ;; - "aws") + "s3") aws_upload $(($(estimate_size) / DUMP_SIZE_COEFF)) aws_delete_outdated ;; diff --git a/docs/administrator.md b/docs/administrator.md index 8fde634c4..5e1ade60c 100644 --- a/docs/administrator.md +++ b/docs/administrator.md @@ -1201,6 +1201,10 @@ of the backup cron job. `cronjobs` resource from the `batch` API group for the operator service account. See [example RBAC](https://github.com/zalando/postgres-operator/blob/master/manifests/operator-service-account-rbac.yaml) +7. Resources of the pod template in the cron job can be configured. When left +empty [default values of spilo pods](reference/operator_parameters.md#kubernetes-resource-requests) +will be used. + ## Sidecars for Postgres clusters A list of sidecars is added to each cluster created by the operator. The default diff --git a/docs/reference/operator_parameters.md b/docs/reference/operator_parameters.md index dd80ce569..981ba1ab6 100644 --- a/docs/reference/operator_parameters.md +++ b/docs/reference/operator_parameters.md @@ -718,6 +718,13 @@ These parameters configure a K8s cron job managed by the operator to produce Postgres logical backups. In the CRD-based configuration those parameters are grouped under the `logical_backup` key. +* **logical_backup_cpu_limit** + **logical_backup_cpu_request** + **logical_backup_memory_limit** + **logical_backup_memory_request** + Resource configuration for pod template in logical backup cron job. If empty + default values from `postgres_pod_resources` will be used. + * **logical_backup_docker_image** An image for pods of the logical backup job. The [example image](https://github.com/zalando/postgres-operator/blob/master/docker/logical-backup/Dockerfile) runs `pg_dumpall` on a replica if possible and uploads compressed results to @@ -732,8 +739,17 @@ grouped under the `logical_backup` key. The prefix to be prepended to the name of a k8s CronJob running the backups. Beware the prefix counts towards the name length restrictions imposed by k8s. Empty string is a legitimate value. Operator does not do the actual renaming: It simply creates the job with the new prefix. You will have to delete the old cron job manually. Default: "logical-backup-". * **logical_backup_provider** - Specifies the storage provider to which the backup should be uploaded (`s3` or `gcs`). - Default: "s3" + Specifies the storage provider to which the backup should be uploaded + (`s3`, `gcs` or `az`). Default: "s3" + +* **logical_backup_azure_storage_account_name** + Storage account name used to upload logical backups to when using Azure. Default: "" + +* **logical_backup_azure_storage_container** + Storage container used to upload logical backups to when using Azure. Default: "" + +* **logical_backup_azure_storage_account_key** + Storage account key used to authenticate with Azure when uploading logical backups. Default: "" * **logical_backup_s3_access_key_id** When set, value will be in AWS_ACCESS_KEY_ID env variable. The Default is empty. @@ -765,15 +781,6 @@ grouped under the `logical_backup` key. [reference schedule format](https://kubernetes.io/docs/tasks/job/automated-tasks-with-cron-jobs/#schedule) into account. Default: "30 00 \* \* \*" -* **logical_backup_azure_storage_account_name** - Storage account name used to upload logical backups to when using Azure. Default: "" - -* **logical_backup_azure_storage_container** - Storage container used to upload logical backups to when using Azure. Default: "" - -* **logical_backup_azure_storage_account_key** - Storage account key used to authenticate with Azure when uploading logical backups. Default: "" - ## Debugging the operator Options to aid debugging of the operator itself. Grouped under the `debug` key. diff --git a/manifests/configmap.yaml b/manifests/configmap.yaml index 15816c693..1b8c6a42f 100644 --- a/manifests/configmap.yaml +++ b/manifests/configmap.yaml @@ -75,9 +75,16 @@ data: # 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.8.2" # 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" @@ -87,9 +94,6 @@ data: logical_backup_s3_sse: "AES256" # logical_backup_s3_retention_time: "" logical_backup_schedule: "30 00 * * *" - # logical_backup_azure_storage_account_name: "" - # logical_backup_azure_storage_container: "" - # logical_backup_azure_storage_account_key: "" major_version_upgrade_mode: "manual" # major_version_upgrade_team_allow_list: "" master_dns_name_format: "{cluster}.{namespace}.{hostedzone}" diff --git a/manifests/operatorconfiguration.crd.yaml b/manifests/operatorconfiguration.crd.yaml index df6343852..14c0106db 100644 --- a/manifests/operatorconfiguration.crd.yaml +++ b/manifests/operatorconfiguration.crd.yaml @@ -467,6 +467,18 @@ spec: 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.8.2" @@ -475,8 +487,18 @@ spec: 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 @@ -496,12 +518,6 @@ spec: type: string pattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$' default: "30 00 * * *" - logical_backup_azure_storage_account_name: - type: string - logical_backup_azure_storage_container: - type: string - logical_backup_azure_storage_account_key: - type: string debug: type: object properties: diff --git a/manifests/postgresql-operator-default-configuration.yaml b/manifests/postgresql-operator-default-configuration.yaml index 36d009bbf..b0e751d1b 100644 --- a/manifests/postgresql-operator-default-configuration.yaml +++ b/manifests/postgresql-operator-default-configuration.yaml @@ -151,6 +151,13 @@ configuration: # 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.8.2" # logical_backup_google_application_credentials: "" logical_backup_job_prefix: "logical-backup-" @@ -163,9 +170,6 @@ configuration: logical_backup_s3_sse: "AES256" # logical_backup_s3_retention_time: "" logical_backup_schedule: "30 00 * * *" - # logical_backup_azure_storage_account_name: "" - # logical_backup_azure_storage_container: "" - # logical_backup_azure_storage_account_key: "" debug: debug_logging: true enable_database_access: true diff --git a/pkg/apis/acid.zalan.do/v1/crds.go b/pkg/apis/acid.zalan.do/v1/crds.go index e0cb148eb..22d82a201 100644 --- a/pkg/apis/acid.zalan.do/v1/crds.go +++ b/pkg/apis/acid.zalan.do/v1/crds.go @@ -1618,6 +1618,23 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{ "logical_backup": { Type: "object", Properties: map[string]apiextv1.JSONSchemaProps{ + "logical_backup_azure_storage_account_name": { + Type: "string", + }, + "logical_backup_azure_storage_container": { + Type: "string", + }, + "logical_backup_azure_storage_account_key": { + Type: "string", + }, + "logical_backup_cpu_limit": { + Type: "string", + Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$", + }, + "logical_backup_cpu_request": { + Type: "string", + Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$", + }, "logical_backup_docker_image": { Type: "string", }, @@ -1627,8 +1644,27 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{ "logical_backup_job_prefix": { Type: "string", }, + "logical_backup_memory_limit": { + Type: "string", + Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$", + }, + "logical_backup_memory_request": { + Type: "string", + Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$", + }, "logical_backup_provider": { Type: "string", + Enum: []apiextv1.JSON{ + { + Raw: []byte(`"az"`), + }, + { + Raw: []byte(`"gcs"`), + }, + { + Raw: []byte(`"s3"`), + }, + }, }, "logical_backup_s3_access_key_id": { Type: "string", @@ -1655,15 +1691,6 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{ Type: "string", Pattern: "^(\\d+|\\*)(/\\d+)?(\\s+(\\d+|\\*)(/\\d+)?){4}$", }, - "logical_backup_azure_storage_account_name": { - Type: "string", - }, - "logical_backup_azure_storage_container": { - Type: "string", - }, - "logical_backup_azure_storage_account_key": { - Type: "string", - }, }, }, "debug": { diff --git a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go index c57dff0cb..c62f1ad31 100644 --- a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go +++ b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go @@ -218,6 +218,9 @@ type OperatorLogicalBackupConfiguration struct { Schedule string `json:"logical_backup_schedule,omitempty"` DockerImage string `json:"logical_backup_docker_image,omitempty"` BackupProvider string `json:"logical_backup_provider,omitempty"` + AzureStorageAccountName string `json:"logical_backup_azure_storage_account_name,omitempty"` + AzureStorageContainer string `json:"logical_backup_azure_storage_container,omitempty"` + AzureStorageAccountKey string `json:"logical_backup_azure_storage_account_key,omitempty"` S3Bucket string `json:"logical_backup_s3_bucket,omitempty"` S3Region string `json:"logical_backup_s3_region,omitempty"` S3Endpoint string `json:"logical_backup_s3_endpoint,omitempty"` @@ -227,9 +230,10 @@ type OperatorLogicalBackupConfiguration struct { RetentionTime string `json:"logical_backup_s3_retention_time,omitempty"` GoogleApplicationCredentials string `json:"logical_backup_google_application_credentials,omitempty"` JobPrefix string `json:"logical_backup_job_prefix,omitempty"` - AzureStorageAccountName string `json:"logical_backup_azure_storage_account_name,omitempty"` - AzureStorageContainer string `json:"logical_backup_azure_storage_container,omitempty"` - AzureStorageAccountKey string `json:"logical_backup_azure_storage_account_key,omitempty"` + CPURequest string `json:"logical_backup_cpu_request,omitempty"` + MemoryRequest string `json:"logical_backup_memory_request,omitempty"` + CPULimit string `json:"logical_backup_cpu_limit,omitempty"` + MemoryLimit string `json:"logical_backup_memory_limit,omitempty"` } // PatroniConfiguration defines configuration for Patroni diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index 963814941..4ef61f8b9 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -139,6 +139,23 @@ func makeDefaultResources(config *config.Config) acidv1.Resources { } } +func makeLogicalBackupResources(config *config.Config) acidv1.Resources { + + logicalBackupResourceRequests := acidv1.ResourceDescription{ + CPU: config.LogicalBackup.LogicalBackupCPURequest, + Memory: config.LogicalBackup.LogicalBackupMemoryRequest, + } + logicalBackupResourceLimits := acidv1.ResourceDescription{ + CPU: config.LogicalBackup.LogicalBackupCPULimit, + Memory: config.LogicalBackup.LogicalBackupMemoryLimit, + } + + return acidv1.Resources{ + ResourceRequests: logicalBackupResourceRequests, + ResourceLimits: logicalBackupResourceLimits, + } +} + func (c *Cluster) enforceMinResourceLimits(resources *v1.ResourceRequirements) error { var ( isSmaller bool @@ -2107,9 +2124,12 @@ func (c *Cluster) generateLogicalBackupJob() (*batchv1.CronJob, error) { c.logger.Debug("Generating logical backup pod template") - // allocate for the backup pod the same amount of resources as for normal DB pods + // allocate configured resources for logical backup pod + logicalBackupResources := makeLogicalBackupResources(&c.OpConfig) + // if not defined only default resources from spilo pods are used resourceRequirements, err = c.generateResourceRequirements( - c.Spec.Resources, makeDefaultResources(&c.OpConfig), logicalBackupContainerName) + &logicalBackupResources, makeDefaultResources(&c.OpConfig), logicalBackupContainerName) + if err != nil { return nil, fmt.Errorf("could not generate resource requirements for logical backup pods: %v", err) } diff --git a/pkg/cluster/k8sres_test.go b/pkg/cluster/k8sres_test.go index 175f6fd7f..226e5ced5 100644 --- a/pkg/cluster/k8sres_test.go +++ b/pkg/cluster/k8sres_test.go @@ -2638,7 +2638,6 @@ func TestGenerateResourceRequirements(t *testing.T) { clusterName := "acid-test-cluster" namespace := "default" clusterNameLabel := "cluster-name" - roleLabel := "spilo-role" sidecarName := "postgres-exporter" // enforceMinResourceLimits will be called 2 twice emitting 4 events (2x cpu, 2x memory raise) @@ -2657,7 +2656,7 @@ func TestGenerateResourceRequirements(t *testing.T) { DefaultMemoryLimit: "500Mi", MaxMemoryRequest: "1Gi", MinMemoryLimit: "250Mi", - PodRoleLabel: roleLabel, + PodRoleLabel: "spilo-role", } tests := []struct { @@ -3008,6 +3007,137 @@ func TestGenerateResourceRequirements(t *testing.T) { } } +func TestGenerateLogicalBackupJob(t *testing.T) { + clusterName := "acid-test-cluster" + configResources := config.Resources{ + DefaultCPURequest: "100m", + DefaultCPULimit: "1", + DefaultMemoryRequest: "100Mi", + DefaultMemoryLimit: "500Mi", + } + + tests := []struct { + subTest string + config config.Config + specSchedule string + expectedSchedule string + expectedJobName string + expectedResources acidv1.Resources + }{ + { + subTest: "test generation of logical backup pod resources when not configured", + config: config.Config{ + LogicalBackup: config.LogicalBackup{ + LogicalBackupJobPrefix: "logical-backup-", + LogicalBackupSchedule: "30 00 * * *", + }, + Resources: configResources, + PodManagementPolicy: "ordered_ready", + SetMemoryRequestToLimit: false, + }, + specSchedule: "", + expectedSchedule: "30 00 * * *", + expectedJobName: "logical-backup-acid-test-cluster", + expectedResources: acidv1.Resources{ + ResourceRequests: acidv1.ResourceDescription{CPU: "100m", Memory: "100Mi"}, + ResourceLimits: acidv1.ResourceDescription{CPU: "1", Memory: "500Mi"}, + }, + }, + { + subTest: "test generation of logical backup pod resources when configured", + config: config.Config{ + LogicalBackup: config.LogicalBackup{ + LogicalBackupCPURequest: "10m", + LogicalBackupCPULimit: "300m", + LogicalBackupMemoryRequest: "50Mi", + LogicalBackupMemoryLimit: "300Mi", + LogicalBackupJobPrefix: "lb-", + LogicalBackupSchedule: "30 00 * * *", + }, + Resources: configResources, + PodManagementPolicy: "ordered_ready", + SetMemoryRequestToLimit: false, + }, + specSchedule: "30 00 * * 7", + expectedSchedule: "30 00 * * 7", + expectedJobName: "lb-acid-test-cluster", + expectedResources: acidv1.Resources{ + ResourceRequests: acidv1.ResourceDescription{CPU: "10m", Memory: "50Mi"}, + ResourceLimits: acidv1.ResourceDescription{CPU: "300m", Memory: "300Mi"}, + }, + }, + { + subTest: "test generation of logical backup pod resources when partly configured", + config: config.Config{ + LogicalBackup: config.LogicalBackup{ + LogicalBackupCPURequest: "50m", + LogicalBackupCPULimit: "250m", + LogicalBackupJobPrefix: "", + LogicalBackupSchedule: "30 00 * * *", + }, + Resources: configResources, + PodManagementPolicy: "ordered_ready", + SetMemoryRequestToLimit: false, + }, + specSchedule: "", + expectedSchedule: "30 00 * * *", + expectedJobName: "acid-test-cluster", + expectedResources: acidv1.Resources{ + ResourceRequests: acidv1.ResourceDescription{CPU: "50m", Memory: "100Mi"}, + ResourceLimits: acidv1.ResourceDescription{CPU: "250m", Memory: "500Mi"}, + }, + }, + { + subTest: "test generation of logical backup pod resources with SetMemoryRequestToLimit enabled", + config: config.Config{ + LogicalBackup: config.LogicalBackup{ + LogicalBackupMemoryRequest: "80Mi", + LogicalBackupMemoryLimit: "200Mi", + LogicalBackupJobPrefix: "test-long-prefix-so-name-must-be-trimmed-", + LogicalBackupSchedule: "30 00 * * *", + }, + Resources: configResources, + PodManagementPolicy: "ordered_ready", + SetMemoryRequestToLimit: true, + }, + specSchedule: "", + expectedSchedule: "30 00 * * *", + expectedJobName: "test-long-prefix-so-name-must-be-trimmed-acid-test-c", + expectedResources: acidv1.Resources{ + ResourceRequests: acidv1.ResourceDescription{CPU: "100m", Memory: "200Mi"}, + ResourceLimits: acidv1.ResourceDescription{CPU: "1", Memory: "200Mi"}, + }, + }, + } + + for _, tt := range tests { + var cluster = New( + Config{ + OpConfig: tt.config, + }, k8sutil.NewMockKubernetesClient(), acidv1.Postgresql{}, logger, eventRecorder) + + cluster.ObjectMeta.Name = clusterName + cluster.Spec.LogicalBackupSchedule = tt.specSchedule + cronJob, err := cluster.generateLogicalBackupJob() + assert.NoError(t, err) + + if cronJob.Spec.Schedule != tt.expectedSchedule { + t.Errorf("%s - %s: expected schedule %s, got %s", t.Name(), tt.subTest, tt.expectedSchedule, cronJob.Spec.Schedule) + } + + if cronJob.Name != tt.expectedJobName { + t.Errorf("%s - %s: expected job name %s, got %s", t.Name(), tt.subTest, tt.expectedJobName, cronJob.Name) + } + + containers := cronJob.Spec.JobTemplate.Spec.Template.Spec.Containers + clusterResources, err := parseResourceRequirements(containers[0].Resources) + assert.NoError(t, err) + if !reflect.DeepEqual(tt.expectedResources, clusterResources) { + t.Errorf("%s - %s: expected resources %#v, got %#v", t.Name(), tt.subTest, tt.expectedResources, clusterResources) + } + } +} + func TestGenerateCapabilities(t *testing.T) { tests := []struct { subTest string diff --git a/pkg/cluster/streams_test.go b/pkg/cluster/streams_test.go index 1e59e0f86..00f18b7a2 100644 --- a/pkg/cluster/streams_test.go +++ b/pkg/cluster/streams_test.go @@ -2,7 +2,6 @@ package cluster import ( "fmt" - "reflect" "strings" "context" @@ -389,7 +388,7 @@ func TestUpdateFabricEventStream(t *testing.T) { assert.NoError(t, err) result := cluster.generateFabricEventStream(appId) - if !reflect.DeepEqual(result.Spec.EventStreams, streams.Items[0].Spec.EventStreams) { + if match, _ := sameStreams(streams.Items[0].Spec.EventStreams, result.Spec.EventStreams); !match { t.Errorf("Malformed FabricEventStream, expected %#v, got %#v", streams.Items[0], result) } } diff --git a/pkg/controller/operator_config.go b/pkg/controller/operator_config.go index fc4aba73e..00a7ea001 100644 --- a/pkg/controller/operator_config.go +++ b/pkg/controller/operator_config.go @@ -174,6 +174,9 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur result.LogicalBackupSchedule = util.Coalesce(fromCRD.LogicalBackup.Schedule, "30 00 * * *") result.LogicalBackupDockerImage = util.Coalesce(fromCRD.LogicalBackup.DockerImage, "registry.opensource.zalan.do/acid/logical-backup:v1.8.2") result.LogicalBackupProvider = util.Coalesce(fromCRD.LogicalBackup.BackupProvider, "s3") + result.LogicalBackupAzureStorageAccountName = fromCRD.LogicalBackup.AzureStorageAccountName + result.LogicalBackupAzureStorageAccountKey = fromCRD.LogicalBackup.AzureStorageAccountKey + result.LogicalBackupAzureStorageContainer = fromCRD.LogicalBackup.AzureStorageContainer result.LogicalBackupS3Bucket = fromCRD.LogicalBackup.S3Bucket result.LogicalBackupS3Region = fromCRD.LogicalBackup.S3Region result.LogicalBackupS3Endpoint = fromCRD.LogicalBackup.S3Endpoint @@ -183,9 +186,10 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur result.LogicalBackupS3RetentionTime = fromCRD.LogicalBackup.RetentionTime result.LogicalBackupGoogleApplicationCredentials = fromCRD.LogicalBackup.GoogleApplicationCredentials result.LogicalBackupJobPrefix = util.Coalesce(fromCRD.LogicalBackup.JobPrefix, "logical-backup-") - result.LogicalBackupAzureStorageAccountName = fromCRD.LogicalBackup.AzureStorageAccountName - result.LogicalBackupAzureStorageAccountKey = fromCRD.LogicalBackup.AzureStorageAccountKey - result.LogicalBackupAzureStorageContainer = fromCRD.LogicalBackup.AzureStorageContainer + result.LogicalBackupCPURequest = fromCRD.LogicalBackup.CPURequest + result.LogicalBackupMemoryRequest = fromCRD.LogicalBackup.MemoryRequest + result.LogicalBackupCPULimit = fromCRD.LogicalBackup.CPULimit + result.LogicalBackupMemoryLimit = fromCRD.LogicalBackup.MemoryLimit // debug config result.DebugLogging = fromCRD.OperatorDebug.DebugLogging diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index 30e557447..612b7a242 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -128,6 +128,9 @@ type LogicalBackup struct { LogicalBackupSchedule string `name:"logical_backup_schedule" default:"30 00 * * *"` LogicalBackupDockerImage string `name:"logical_backup_docker_image" default:"registry.opensource.zalan.do/acid/logical-backup:v1.8.2"` LogicalBackupProvider string `name:"logical_backup_provider" default:"s3"` + LogicalBackupAzureStorageAccountName string `name:"logical_backup_azure_storage_account_name" default:""` + LogicalBackupAzureStorageContainer string `name:"logical_backup_azure_storage_container" default:""` + LogicalBackupAzureStorageAccountKey string `name:"logical_backup_azure_storage_account_key" default:""` LogicalBackupS3Bucket string `name:"logical_backup_s3_bucket" default:""` LogicalBackupS3Region string `name:"logical_backup_s3_region" default:""` LogicalBackupS3Endpoint string `name:"logical_backup_s3_endpoint" default:""` @@ -137,9 +140,10 @@ type LogicalBackup struct { LogicalBackupS3RetentionTime string `name:"logical_backup_s3_retention_time" default:""` LogicalBackupGoogleApplicationCredentials string `name:"logical_backup_google_application_credentials" default:""` LogicalBackupJobPrefix string `name:"logical_backup_job_prefix" default:"logical-backup-"` - LogicalBackupAzureStorageAccountName string `name:"logical_backup_azure_storage_account_name" default:""` - LogicalBackupAzureStorageContainer string `name:"logical_backup_azure_storage_container" default:""` - LogicalBackupAzureStorageAccountKey string `name:"logical_backup_azure_storage_account_key" default:""` + LogicalBackupCPURequest string `name:"logical_backup_cpu_request"` + LogicalBackupMemoryRequest string `name:"logical_backup_memory_request"` + LogicalBackupCPULimit string `name:"logical_backup_cpu_limit"` + LogicalBackupMemoryLimit string `name:"logical_backup_memory_limit"` } // Operator options for connection pooler From 625e804dc45428871e191bcaa2c8d2841dff0015 Mon Sep 17 00:00:00 2001 From: Dmitry Volodin Date: Thu, 5 Jan 2023 20:29:47 +0300 Subject: [PATCH 62/77] Add operator deployment readiness probe (#1874) * Add operator deployment readiness probe --- charts/postgres-operator/templates/deployment.yaml | 8 ++++++++ charts/postgres-operator/values.yaml | 5 +++++ pkg/apiserver/apiserver.go | 6 +++++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/charts/postgres-operator/templates/deployment.yaml b/charts/postgres-operator/templates/deployment.yaml index b91062666..1752cb397 100644 --- a/charts/postgres-operator/templates/deployment.yaml +++ b/charts/postgres-operator/templates/deployment.yaml @@ -57,6 +57,14 @@ spec: {{ toYaml .Values.resources | indent 10 }} securityContext: {{ toYaml .Values.securityContext | indent 10 }} + {{- if .Values.readinessProbe }} + readinessProbe: + httpGet: + path: /readyz + port: {{ .Values.configLoggingRestApi.api_port }} + initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.readinessProbe.periodSeconds }} + {{- end }} {{- if .Values.imagePullSecrets }} imagePullSecrets: {{ toYaml .Values.imagePullSecrets | indent 8 }} diff --git a/charts/postgres-operator/values.yaml b/charts/postgres-operator/values.yaml index 7766519ab..d359029e5 100644 --- a/charts/postgres-operator/values.yaml +++ b/charts/postgres-operator/values.yaml @@ -470,6 +470,11 @@ securityContext: readOnlyRootFilesystem: true allowPrivilegeEscalation: false +# Allow to setup operator Deployment readiness probe +readinessProbe: + initialDelaySeconds: 5 + periodSeconds: 10 + # Affinity for pod assignment # Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity affinity: {} diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index df13141d4..97e389970 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -12,7 +12,6 @@ import ( "time" "github.com/sirupsen/logrus" - "github.com/zalando/postgres-operator/pkg/cluster" "github.com/zalando/postgres-operator/pkg/spec" "github.com/zalando/postgres-operator/pkg/util" @@ -87,6 +86,7 @@ func New(controller controllerInformer, port int, logger *logrus.Logger) *Server mux.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace)) mux.Handle("/status/", http.HandlerFunc(s.controllerStatus)) + mux.Handle("/readyz/", http.HandlerFunc(s.controllerReady)) mux.Handle("/config/", http.HandlerFunc(s.operatorConfig)) mux.HandleFunc("/clusters/", s.clusters) @@ -155,6 +155,10 @@ func (s *Server) controllerStatus(w http.ResponseWriter, req *http.Request) { s.respond(s.controller.GetStatus(), nil, w) } +func (s *Server) controllerReady(w http.ResponseWriter, req *http.Request) { + s.respond("OK", nil, w) +} + func (s *Server) operatorConfig(w http.ResponseWriter, req *http.Request) { s.respond(map[string]interface{}{ "controller": s.controller.GetConfig(), From 3139c1f3d0f548e30061efff873b5c5e4bb2d89b Mon Sep 17 00:00:00 2001 From: jeremie-seguin Date: Mon, 9 Jan 2023 17:16:00 +0100 Subject: [PATCH 63/77] Add Support for Custom TLS Certificates in Connection Pooler (#2146) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add volume with custom TLS config to pooler deployment * bump pg bouncer image tag which support new feature Co-authored-by: Jérémie Seguin --- .../crds/operatorconfigurations.yaml | 2 +- charts/postgres-operator/values.yaml | 2 +- manifests/configmap.yaml | 2 +- manifests/minimal-fake-pooler-deployment.yaml | 2 +- manifests/operatorconfiguration.crd.yaml | 2 +- ...gresql-operator-default-configuration.yaml | 2 +- pkg/cluster/connection_pooler.go | 48 +++++++++++++++++++ 7 files changed, 54 insertions(+), 6 deletions(-) diff --git a/charts/postgres-operator/crds/operatorconfigurations.yaml b/charts/postgres-operator/crds/operatorconfigurations.yaml index e10296659..5222832a4 100644 --- a/charts/postgres-operator/crds/operatorconfigurations.yaml +++ b/charts/postgres-operator/crds/operatorconfigurations.yaml @@ -631,7 +631,7 @@ spec: default: "pooler" connection_pooler_image: type: string - default: "registry.opensource.zalan.do/acid/pgbouncer:master-24" + default: "registry.opensource.zalan.do/acid/pgbouncer:master-26" connection_pooler_max_db_connections: type: integer default: 60 diff --git a/charts/postgres-operator/values.yaml b/charts/postgres-operator/values.yaml index d359029e5..b2136fef3 100644 --- a/charts/postgres-operator/values.yaml +++ b/charts/postgres-operator/values.yaml @@ -412,7 +412,7 @@ configConnectionPooler: # db user for pooler to use connection_pooler_user: "pooler" # docker image - connection_pooler_image: "registry.opensource.zalan.do/acid/pgbouncer:master-24" + connection_pooler_image: "registry.opensource.zalan.do/acid/pgbouncer:master-26" # max db connections the pooler should hold connection_pooler_max_db_connections: 60 # default pooling mode diff --git a/manifests/configmap.yaml b/manifests/configmap.yaml index 1b8c6a42f..5311d0a45 100644 --- a/manifests/configmap.yaml +++ b/manifests/configmap.yaml @@ -17,7 +17,7 @@ data: # 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-24" + connection_pooler_image: "registry.opensource.zalan.do/acid/pgbouncer:master-26" # connection_pooler_max_db_connections: 60 # connection_pooler_mode: "transaction" # connection_pooler_number_of_instances: 2 diff --git a/manifests/minimal-fake-pooler-deployment.yaml b/manifests/minimal-fake-pooler-deployment.yaml index 489f5b634..b05f4f4ca 100644 --- a/manifests/minimal-fake-pooler-deployment.yaml +++ b/manifests/minimal-fake-pooler-deployment.yaml @@ -23,7 +23,7 @@ spec: serviceAccountName: postgres-operator containers: - name: postgres-operator - image: registry.opensource.zalan.do/acid/pgbouncer:master-24 + image: registry.opensource.zalan.do/acid/pgbouncer:master-26 imagePullPolicy: IfNotPresent resources: requests: diff --git a/manifests/operatorconfiguration.crd.yaml b/manifests/operatorconfiguration.crd.yaml index 14c0106db..8fcfe739f 100644 --- a/manifests/operatorconfiguration.crd.yaml +++ b/manifests/operatorconfiguration.crd.yaml @@ -629,7 +629,7 @@ spec: default: "pooler" connection_pooler_image: type: string - default: "registry.opensource.zalan.do/acid/pgbouncer:master-24" + default: "registry.opensource.zalan.do/acid/pgbouncer:master-26" connection_pooler_max_db_connections: type: integer default: 60 diff --git a/manifests/postgresql-operator-default-configuration.yaml b/manifests/postgresql-operator-default-configuration.yaml index b0e751d1b..367529ef8 100644 --- a/manifests/postgresql-operator-default-configuration.yaml +++ b/manifests/postgresql-operator-default-configuration.yaml @@ -201,7 +201,7 @@ configuration: 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-24" + connection_pooler_image: "registry.opensource.zalan.do/acid/pgbouncer:master-26" # connection_pooler_max_db_connections: 60 connection_pooler_mode: "transaction" connection_pooler_number_of_instances: 2 diff --git a/pkg/cluster/connection_pooler.go b/pkg/cluster/connection_pooler.go index 93bfb8854..4a602a1e7 100644 --- a/pkg/cluster/connection_pooler.go +++ b/pkg/cluster/connection_pooler.go @@ -3,6 +3,7 @@ package cluster import ( "context" "fmt" + "path/filepath" "strings" "time" @@ -336,6 +337,52 @@ func (c *Cluster) generateConnectionPoolerPodTemplate(role PostgresRole) ( }, } + // If the cluster has custom TLS certificates configured, we do the following: + // 1. Add environment variables to tell pgBouncer where to find the TLS certificates + // 2. Reference the secret in a volume + // 3. Mount the volume to the container at /tls + poolerVolumes := []v1.Volume{} + if spec.TLS != nil && spec.TLS.SecretName != "" { + // Env vars + crtFile := spec.TLS.CertificateFile + keyFile := spec.TLS.PrivateKeyFile + if crtFile == "" { + crtFile = "tls.crt" + } + if keyFile == "" { + crtFile = "tls.key" + } + + envVars = append( + envVars, + v1.EnvVar{ + Name: "CONNECTION_POOLER_CLIENT_TLS_CRT", Value: filepath.Join("/tls", crtFile), + }, + v1.EnvVar{ + Name: "CONNECTION_POOLER_CLIENT_TLS_KEY", Value: filepath.Join("/tls", keyFile), + }, + ) + + // Volume + mode := int32(0640) + volume := v1.Volume{ + Name: "tls", + VolumeSource: v1.VolumeSource{ + Secret: &v1.SecretVolumeSource{ + SecretName: spec.TLS.SecretName, + DefaultMode: &mode, + }, + }, + } + poolerVolumes = append(poolerVolumes, volume) + + // Mount + poolerContainer.VolumeMounts = []v1.VolumeMount{{ + Name: "tls", + MountPath: "/tls", + }} + } + tolerationsSpec := tolerations(&spec.Tolerations, c.OpConfig.PodToleration) podTemplate := &v1.PodTemplateSpec{ @@ -348,6 +395,7 @@ func (c *Cluster) generateConnectionPoolerPodTemplate(role PostgresRole) ( TerminationGracePeriodSeconds: &gracePeriod, Containers: []v1.Container{poolerContainer}, Tolerations: tolerationsSpec, + Volumes: poolerVolumes, }, } From 77a185894d907f8f928337a9f53369aa0871ad31 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Tue, 10 Jan 2023 10:15:52 +0100 Subject: [PATCH 64/77] bump to go 1.18 (#2164) * bump to go 1.18 --- Makefile | 2 +- delivery.yaml | 2 +- go.mod | 2 +- go.sum | 1 - kubectl-pg/go.mod | 4 ++-- kubectl-pg/go.sum | 35 ++--------------------------------- 6 files changed, 7 insertions(+), 39 deletions(-) diff --git a/Makefile b/Makefile index 5478854a3..792c43075 100644 --- a/Makefile +++ b/Makefile @@ -73,7 +73,7 @@ docker: ${DOCKERDIR}/${DOCKERFILE} docker-context cd "${DOCKERDIR}" && docker build --rm -t "$(IMAGE):$(TAG)$(CDP_TAG)$(DEBUG_FRESH)$(DEBUG_POSTFIX)" -f "${DOCKERFILE}" . indocker-race: - docker run --rm -v "${GOPATH}":"${GOPATH}" -e GOPATH="${GOPATH}" -e RACE=1 -w ${PWD} golang:1.17.3 bash -c "make linux" + docker run --rm -v "${GOPATH}":"${GOPATH}" -e GOPATH="${GOPATH}" -e RACE=1 -w ${PWD} golang:1.18.9 bash -c "make linux" push: docker push "$(IMAGE):$(TAG)$(CDP_TAG)" diff --git a/delivery.yaml b/delivery.yaml index 15a30d42f..4e4ecc706 100644 --- a/delivery.yaml +++ b/delivery.yaml @@ -16,7 +16,7 @@ pipeline: - desc: 'Install go' cmd: | cd /tmp - wget -q https://storage.googleapis.com/golang/go1.17.4.linux-amd64.tar.gz -O go.tar.gz + wget -q https://storage.googleapis.com/golang/go1.18.9.linux-amd64.tar.gz -O go.tar.gz tar -xf go.tar.gz mv go /usr/local ln -s /usr/local/go/bin/go /usr/bin/go diff --git a/go.mod b/go.mod index 79c82a10c..2d20a7619 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/zalando/postgres-operator -go 1.17 +go 1.18 require ( github.com/aws/aws-sdk-go v1.42.18 diff --git a/go.sum b/go.sum index 1b68e78c2..7cb962923 100644 --- a/go.sum +++ b/go.sum @@ -586,7 +586,6 @@ golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211209124913-491a49abca63 h1:iocB37TsdFuN6IBRZ+ry36wrkoV51/tl5vOWqkcPGvY= golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= diff --git a/kubectl-pg/go.mod b/kubectl-pg/go.mod index 8b580cb01..711f1b90e 100644 --- a/kubectl-pg/go.mod +++ b/kubectl-pg/go.mod @@ -1,11 +1,11 @@ module github.com/zalando/postgres-operator/kubectl-pg -go 1.17 +go 1.18 require ( github.com/spf13/cobra v1.2.1 github.com/spf13/viper v1.9.0 - github.com/zalando/postgres-operator v1.8.1 + github.com/zalando/postgres-operator v1.8.2 k8s.io/api v0.23.5 k8s.io/apiextensions-apiserver v0.23.5 k8s.io/apimachinery v0.23.5 diff --git a/kubectl-pg/go.sum b/kubectl-pg/go.sum index 51c09872b..9853eabdf 100644 --- a/kubectl-pg/go.sum +++ b/kubectl-pg/go.sum @@ -71,7 +71,6 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/aws/aws-sdk-go v1.42.18/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -126,7 +125,6 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= @@ -151,7 +149,6 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v1.2.0 h1:QK40JKJyMdUDz+h+xvCsru/bJhvG0UxvePV0ufL/AcE= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/zapr v1.2.0/go.mod h1:Qa4Bsj2Vb+FAVeAKsLD8RLQ+YRJB8YDmOAKxaBQf7Ro= @@ -243,7 +240,6 @@ github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= @@ -296,8 +292,6 @@ github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= @@ -327,7 +321,6 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= @@ -426,7 +419,6 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/r3labs/diff v1.1.0/go.mod h1:7WjXasNzi0vJetRcB/RqNl5dlIsmXcTTLmF5IoH6Xig= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -460,7 +452,6 @@ github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t6 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -489,8 +480,8 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/zalando/postgres-operator v1.8.1 h1:MzBQeXL4fEwDux++R3Tanh7eoYxBst1cNNWW6d4uSQ0= -github.com/zalando/postgres-operator v1.8.1/go.mod h1:f7AXk8LO/tWFdW4myPJZCwMueGg6fI4RqTuOA0BefZE= +github.com/zalando/postgres-operator v1.8.2 h1:3FW3j2gXua1MSeE+NiSvB8cxM7k7fyoun46G1v++CCA= +github.com/zalando/postgres-operator v1.8.2/go.mod h1:f7AXk8LO/tWFdW4myPJZCwMueGg6fI4RqTuOA0BefZE= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= @@ -537,7 +528,6 @@ golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211202192323-5770296d904e h1:MUP6MR3rJ7Gk9LEia0LP2ytiH6MuCfs7qYz+47jGdD8= golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= @@ -576,7 +566,6 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -622,12 +611,9 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211209124913-491a49abca63 h1:iocB37TsdFuN6IBRZ+ry36wrkoV51/tl5vOWqkcPGvY= golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -733,9 +719,7 @@ golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 h1:TyHqChC80pFkXWraUUf6RuB5IqFdQieMLwwCJokV2pc= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -816,7 +800,6 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.6-0.20210820212750-d4cc65f0b2ff/go.mod h1:YD9qOF0M9xpSpdWTBbzEl5e/RnCefISl8E5Noe10jFM= -golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -996,48 +979,34 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.22.4/go.mod h1:Rgs+9gIGYC5laXQSZZ9JqT5NevNgoGiOdVWi1BAB3qk= k8s.io/api v0.23.5 h1:zno3LUiMubxD/V1Zw3ijyKO3wxrhbUF1Ck+VjBvfaoA= k8s.io/api v0.23.5/go.mod h1:Na4XuKng8PXJ2JsploYYrivXrINeTaycCGcYgF91Xm8= -k8s.io/apiextensions-apiserver v0.22.4/go.mod h1:kH9lxD8dbJ+k0ZizGET55lFgdGjO8t45fgZnCVdZEpw= k8s.io/apiextensions-apiserver v0.23.5 h1:5SKzdXyvIJKu+zbfPc3kCbWpbxi+O+zdmAJBm26UJqI= k8s.io/apiextensions-apiserver v0.23.5/go.mod h1:ntcPWNXS8ZPKN+zTXuzYMeg731CP0heCTl6gYBxLcuQ= -k8s.io/apimachinery v0.22.4/go.mod h1:yU6oA6Gnax9RrxGzVvPFFJ+mpnW6PBSqp0sx0I0HHW0= k8s.io/apimachinery v0.23.5 h1:Va7dwhp8wgkUPWsEXk6XglXWU4IKYLKNlv8VkX7SDM0= k8s.io/apimachinery v0.23.5/go.mod h1:BEuFMMBaIbcOqVIJqNZJXGFTP4W6AycEpb5+m/97hrM= -k8s.io/apiserver v0.22.4/go.mod h1:38WmcUZiiy41A7Aty8/VorWRa8vDGqoUzDf2XYlku0E= k8s.io/apiserver v0.23.5/go.mod h1:7wvMtGJ42VRxzgVI7jkbKvMbuCbVbgsWFT7RyXiRNTw= -k8s.io/client-go v0.22.4/go.mod h1:Yzw4e5e7h1LNHA4uqnMVrpEpUs1hJOiuBsJKIlRCHDA= k8s.io/client-go v0.23.5 h1:zUXHmEuqx0RY4+CsnkOn5l0GU+skkRXKGJrhmE2SLd8= k8s.io/client-go v0.23.5/go.mod h1:flkeinTO1CirYgzMPRWxUCnV0G4Fbu2vLhYCObnt/r4= -k8s.io/code-generator v0.22.4/go.mod h1:qjYl54pQ/emhkT0UxbufbREYJMWsHNNV/jSVwhYZQGw= k8s.io/code-generator v0.23.5/go.mod h1:S0Q1JVA+kSzTI1oUvbKAxZY/DYbA/ZUb4Uknog12ETk= -k8s.io/component-base v0.22.4/go.mod h1:MrSaQy4a3tFVViff8TZL6JHYSewNCLshZCwHYM58v5A= k8s.io/component-base v0.23.5/go.mod h1:c5Nq44KZyt1aLl0IpHX82fhsn84Sb0jjzwjpcA42bY0= -k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= k8s.io/klog/v2 v2.30.0 h1:bUO6drIvCIsvZ/XFgfxoGFQU/a4Qkh0iAlvUR7vlHJw= k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 h1:E3J9oCLlaobFUqsjG9DfKbP2BmgwBL2p7pn0A3dG9W4= k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk= k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20211116205334-6203023598ed h1:ck1fRPWPJWsMd8ZRFsWc6mh/zHp5fZ/shhbrgPUxDAE= k8s.io/utils v0.0.0-20211116205334-6203023598ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.30/go.mod h1:fEO7lRTdivWO2qYVCVG7dEADOMo/MLDCVr8So2g88Uw= sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 h1:fD1pz4yfdADVNfFmcP2aBEtudwUQ1AlLnRBALr33v3s= sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y= sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= From 974481328bdb0c64537350dbc09b03f5ad0cb5bf Mon Sep 17 00:00:00 2001 From: Alfred Schmid Date: Tue, 10 Jan 2023 16:37:51 +0100 Subject: [PATCH 65/77] allow templating in .Values.configGeneral also (#2166) This part was provided by alexey-gavrilov-flant in #1986 and is missing by accident in #2115 --- charts/postgres-operator/templates/operatorconfiguration.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/postgres-operator/templates/operatorconfiguration.yaml b/charts/postgres-operator/templates/operatorconfiguration.yaml index b548ce462..ef4674d94 100644 --- a/charts/postgres-operator/templates/operatorconfiguration.yaml +++ b/charts/postgres-operator/templates/operatorconfiguration.yaml @@ -10,7 +10,7 @@ metadata: app.kubernetes.io/managed-by: {{ .Release.Service }} app.kubernetes.io/instance: {{ .Release.Name }} configuration: -{{ toYaml .Values.configGeneral | indent 2 }} +{{ tpl (toYaml .Values.configGeneral) . | indent 2 }} users: {{ tpl (toYaml .Values.configUsers) . | indent 4 }} major_version_upgrade: From 021ab07a231c01dab08034aaf9850c31d3ad4d8a Mon Sep 17 00:00:00 2001 From: Owen Ou <169064+owenthereal@users.noreply.github.com> Date: Wed, 11 Jan 2023 04:29:16 -0800 Subject: [PATCH 66/77] Introduce `masterServiceAnnotations` & `replicaServiceAnnotations` (#2161) * Introduce `masterServiceAnnotations` & `replicaServiceAnnotations` Introduce `masterServiceAnnotations` & `replicaServiceAnnotations` to the `Postgresql` CRD. `masterServiceAnnotations` overrides `serviceAnnotations` for master role if not empty. `replicaServiceAnnotations` overrides `serviceAnnotations` for replica role if not empty. Existing definition of `serviceAnnotations` continue to work for backward compatibitlity when neither `masterServiceAnnotations` nor `replicaServiceAnnotations` is defined. This closes https://github.com/zalando/postgres-operator/issues/1927 * Accumulate service annotations First, global config, then ServiceAnnotations overriding, then MasterServiceAnnotations and ReplicaServiceAnnotations. This addresses https://github.com/zalando/postgres-operator/pull/2161#discussion_r1063558711. * Update admin doc with master & replica service annotations overrides Addressed https://github.com/zalando/postgres-operator/pull/2161#discussion_r1064744086 Co-authored-by: Felix Kunde --- .../postgres-operator/crds/postgresqls.yaml | 8 ++ docs/administrator.md | 12 ++- docs/reference/cluster_manifest.md | 16 ++++ go.mod | 18 ++-- go.sum | 30 +++--- manifests/postgresql.crd.yaml | 8 ++ pkg/apis/acid.zalan.do/v1/crds.go | 16 ++++ pkg/apis/acid.zalan.do/v1/postgresql_type.go | 12 ++- .../acid.zalan.do/v1/zz_generated.deepcopy.go | 14 +++ pkg/cluster/cluster_test.go | 94 ++++++++++++++----- pkg/cluster/k8sres.go | 16 ++-- 11 files changed, 184 insertions(+), 60 deletions(-) diff --git a/charts/postgres-operator/crds/postgresqls.yaml b/charts/postgres-operator/crds/postgresqls.yaml index 9d56a6ee3..6f938cf8f 100644 --- a/charts/postgres-operator/crds/postgresqls.yaml +++ b/charts/postgres-operator/crds/postgresqls.yaml @@ -223,6 +223,10 @@ spec: 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: @@ -402,6 +406,10 @@ spec: replicaLoadBalancer: type: boolean description: deprecated + replicaServiceAnnotations: + type: object + additionalProperties: + type: string resources: type: object properties: diff --git a/docs/administrator.md b/docs/administrator.md index 5e1ade60c..19cfe2109 100644 --- a/docs/administrator.md +++ b/docs/administrator.md @@ -784,9 +784,15 @@ services: This value can't be overwritten. If any changing in its value is needed, it MUST be done changing the DNS format operator config parameters; and - `service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout` with - a default value of "3600". This value can be overwritten with the operator - config parameter `custom_service_annotations` or the cluster parameter - `serviceAnnotations`. + a default value of "3600". + +There are multiple options to specify service annotations that will be merged +with each other and override in the following order (where latter take +precedence): +1. Default annotations if LoadBalancer is enabled +2. Globally configured `custom_service_annotations` +3. `serviceAnnotations` specified in the cluster manifest +4. `masterServiceAnnotations` and `replicaServiceAnnotations` specified in the cluster manifest To limit the range of IP addresses that can reach a load balancer, specify the desired ranges in the `allowedSourceRanges` field (applies to both master and diff --git a/docs/reference/cluster_manifest.md b/docs/reference/cluster_manifest.md index 9e5789d59..8cca890c8 100644 --- a/docs/reference/cluster_manifest.md +++ b/docs/reference/cluster_manifest.md @@ -173,6 +173,22 @@ These parameters are grouped directly under the `spec` key in the manifest. [administrator docs](https://github.com/zalando/postgres-operator/blob/master/docs/administrator.md#load-balancers-and-allowed-ip-ranges) for more information regarding default values and overwrite rules. +* **masterServiceAnnotations** + A map of key value pairs that gets attached as [annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) + to the master service created for the database cluster. Check the + [administrator docs](https://github.com/zalando/postgres-operator/blob/master/docs/administrator.md#load-balancers-and-allowed-ip-ranges) + for more information regarding default values and overwrite rules. + This field overrides `serviceAnnotations` with the same key for the master + service if not empty. + +* **replicaServiceAnnotations** + A map of key value pairs that gets attached as [annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) + to the replica service created for the database cluster. Check the + [administrator docs](https://github.com/zalando/postgres-operator/blob/master/docs/administrator.md#load-balancers-and-allowed-ip-ranges) + for more information regarding default values and overwrite rules. + This field overrides `serviceAnnotations` with the same key for the replica + service if not empty. + * **enableShmVolume** Start a database pod without limitations on shm memory. By default Docker limit `/dev/shm` to `64M` (see e.g. the [docker diff --git a/go.mod b/go.mod index 2d20a7619..39c76a9c5 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,8 @@ require ( github.com/r3labs/diff v1.1.0 github.com/sirupsen/logrus v1.8.1 github.com/stretchr/testify v1.7.0 - golang.org/x/crypto v0.0.0-20211202192323-5770296d904e + golang.org/x/crypto v0.1.0 + golang.org/x/exp v0.0.0-20230108222341-4b8118a2686a gopkg.in/yaml.v2 v2.4.0 k8s.io/api v0.23.5 k8s.io/apiextensions-apiserver v0.23.5 @@ -33,7 +34,7 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect - github.com/google/go-cmp v0.5.5 // indirect + github.com/google/go-cmp v0.5.8 // indirect github.com/google/gofuzz v1.1.0 // indirect github.com/googleapis/gnostic v0.5.5 // indirect github.com/imdario/mergo v0.3.5 // indirect @@ -47,15 +48,14 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/mod v0.5.1 // indirect - golang.org/x/net v0.0.0-20211209124913-491a49abca63 // indirect + golang.org/x/mod v0.6.0 // indirect + golang.org/x/net v0.1.0 // indirect golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f // indirect - golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 // indirect - golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b // indirect - golang.org/x/text v0.3.7 // indirect + golang.org/x/sys v0.1.0 // indirect + golang.org/x/term v0.1.0 // indirect + golang.org/x/text v0.4.0 // indirect golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect - golang.org/x/tools v0.1.7 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + golang.org/x/tools v0.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.27.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/go.sum b/go.sum index 7cb962923..385d31cdb 100644 --- a/go.sum +++ b/go.sum @@ -216,8 +216,9 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -500,8 +501,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211202192323-5770296d904e h1:MUP6MR3rJ7Gk9LEia0LP2ytiH6MuCfs7qYz+47jGdD8= -golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -512,6 +513,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20230108222341-4b8118a2686a h1:tlXy25amD5A7gOfbXdqCGN5k8ESEed/Ee1E5RcrYnqU= +golang.org/x/exp v0.0.0-20230108222341-4b8118a2686a/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -537,8 +540,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38= -golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I= +golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -586,8 +589,9 @@ golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211209124913-491a49abca63 h1:iocB37TsdFuN6IBRZ+ry36wrkoV51/tl5vOWqkcPGvY= golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -676,11 +680,12 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 h1:TyHqChC80pFkXWraUUf6RuB5IqFdQieMLwwCJokV2pc= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -689,8 +694,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -756,8 +762,8 @@ golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.6-0.20210820212750-d4cc65f0b2ff/go.mod h1:YD9qOF0M9xpSpdWTBbzEl5e/RnCefISl8E5Noe10jFM= -golang.org/x/tools v0.1.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ= -golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE= +golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/manifests/postgresql.crd.yaml b/manifests/postgresql.crd.yaml index b2178a246..6066abad1 100644 --- a/manifests/postgresql.crd.yaml +++ b/manifests/postgresql.crd.yaml @@ -221,6 +221,10 @@ spec: 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: @@ -400,6 +404,10 @@ spec: replicaLoadBalancer: type: boolean description: deprecated + replicaServiceAnnotations: + type: object + additionalProperties: + type: string resources: type: object properties: diff --git a/pkg/apis/acid.zalan.do/v1/crds.go b/pkg/apis/acid.zalan.do/v1/crds.go index 22d82a201..4a08ec2f4 100644 --- a/pkg/apis/acid.zalan.do/v1/crds.go +++ b/pkg/apis/acid.zalan.do/v1/crds.go @@ -355,6 +355,14 @@ var PostgresCRDResourceValidation = apiextv1.CustomResourceValidation{ }, }, }, + "masterServiceAnnotations": { + Type: "object", + AdditionalProperties: &apiextv1.JSONSchemaPropsOrBool{ + Schema: &apiextv1.JSONSchemaProps{ + Type: "string", + }, + }, + }, "nodeAffinity": { Type: "object", Properties: map[string]apiextv1.JSONSchemaProps{ @@ -654,6 +662,14 @@ var PostgresCRDResourceValidation = apiextv1.CustomResourceValidation{ Type: "boolean", Description: "deprecated", }, + "replicaServiceAnnotations": { + Type: "object", + AdditionalProperties: &apiextv1.JSONSchemaPropsOrBool{ + Schema: &apiextv1.JSONSchemaProps{ + Type: "string", + }, + }, + }, "resources": { Type: "object", Properties: map[string]apiextv1.JSONSchemaProps{ diff --git a/pkg/apis/acid.zalan.do/v1/postgresql_type.go b/pkg/apis/acid.zalan.do/v1/postgresql_type.go index 19e2f685f..67007b522 100644 --- a/pkg/apis/acid.zalan.do/v1/postgresql_type.go +++ b/pkg/apis/acid.zalan.do/v1/postgresql_type.go @@ -79,10 +79,14 @@ type PostgresSpec struct { StandbyCluster *StandbyDescription `json:"standby,omitempty"` PodAnnotations map[string]string `json:"podAnnotations,omitempty"` ServiceAnnotations map[string]string `json:"serviceAnnotations,omitempty"` - TLS *TLSDescription `json:"tls,omitempty"` - AdditionalVolumes []AdditionalVolume `json:"additionalVolumes,omitempty"` - Streams []Stream `json:"streams,omitempty"` - Env []v1.EnvVar `json:"env,omitempty"` + // MasterServiceAnnotations takes precedence over ServiceAnnotations for master role if not empty + MasterServiceAnnotations map[string]string `json:"masterServiceAnnotations,omitempty"` + // ReplicaServiceAnnotations takes precedence over ServiceAnnotations for replica role if not empty + ReplicaServiceAnnotations map[string]string `json:"replicaServiceAnnotations,omitempty"` + TLS *TLSDescription `json:"tls,omitempty"` + AdditionalVolumes []AdditionalVolume `json:"additionalVolumes,omitempty"` + Streams []Stream `json:"streams,omitempty"` + Env []v1.EnvVar `json:"env,omitempty"` // deprecated json tags InitContainersOld []v1.Container `json:"init_containers,omitempty"` diff --git a/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go b/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go index 9d9a185bd..a43c995c5 100644 --- a/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go +++ b/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go @@ -792,6 +792,20 @@ func (in *PostgresSpec) DeepCopyInto(out *PostgresSpec) { (*out)[key] = val } } + if in.MasterServiceAnnotations != nil { + in, out := &in.MasterServiceAnnotations, &out.MasterServiceAnnotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.ReplicaServiceAnnotations != nil { + in, out := &in.ReplicaServiceAnnotations, &out.ReplicaServiceAnnotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSDescription) diff --git a/pkg/cluster/cluster_test.go b/pkg/cluster/cluster_test.go index 24994cfc7..b9633d903 100644 --- a/pkg/cluster/cluster_test.go +++ b/pkg/cluster/cluster_test.go @@ -524,7 +524,9 @@ func TestServiceAnnotations(t *testing.T) { enableReplicaLoadBalancerOC bool enableTeamIdClusterPrefix bool operatorAnnotations map[string]string - clusterAnnotations map[string]string + serviceAnnotations map[string]string + masterServiceAnnotations map[string]string + replicaServiceAnnotations map[string]string expect map[string]string }{ //MASTER @@ -535,7 +537,7 @@ func TestServiceAnnotations(t *testing.T) { enableMasterLoadBalancerOC: false, enableTeamIdClusterPrefix: false, operatorAnnotations: make(map[string]string), - clusterAnnotations: make(map[string]string), + serviceAnnotations: make(map[string]string), expect: make(map[string]string), }, { @@ -545,7 +547,7 @@ func TestServiceAnnotations(t *testing.T) { enableMasterLoadBalancerOC: false, enableTeamIdClusterPrefix: false, operatorAnnotations: make(map[string]string), - clusterAnnotations: make(map[string]string), + serviceAnnotations: make(map[string]string), expect: map[string]string{ "external-dns.alpha.kubernetes.io/hostname": "acid-test.test.db.example.com,test.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", @@ -558,7 +560,7 @@ func TestServiceAnnotations(t *testing.T) { enableMasterLoadBalancerOC: true, enableTeamIdClusterPrefix: false, operatorAnnotations: make(map[string]string), - clusterAnnotations: make(map[string]string), + serviceAnnotations: make(map[string]string), expect: make(map[string]string), }, { @@ -567,7 +569,7 @@ func TestServiceAnnotations(t *testing.T) { enableMasterLoadBalancerOC: true, enableTeamIdClusterPrefix: false, operatorAnnotations: make(map[string]string), - clusterAnnotations: make(map[string]string), + serviceAnnotations: make(map[string]string), expect: map[string]string{ "external-dns.alpha.kubernetes.io/hostname": "acid-test.test.db.example.com,test.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", @@ -579,7 +581,7 @@ func TestServiceAnnotations(t *testing.T) { enableMasterLoadBalancerOC: true, enableTeamIdClusterPrefix: false, operatorAnnotations: make(map[string]string), - clusterAnnotations: map[string]string{"foo": "bar"}, + serviceAnnotations: map[string]string{"foo": "bar"}, expect: map[string]string{ "external-dns.alpha.kubernetes.io/hostname": "acid-test.test.db.example.com,test.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", @@ -593,7 +595,7 @@ func TestServiceAnnotations(t *testing.T) { enableMasterLoadBalancerOC: true, enableTeamIdClusterPrefix: false, operatorAnnotations: make(map[string]string), - clusterAnnotations: map[string]string{"foo": "bar"}, + serviceAnnotations: map[string]string{"foo": "bar"}, expect: map[string]string{"foo": "bar"}, }, { @@ -602,7 +604,7 @@ func TestServiceAnnotations(t *testing.T) { enableMasterLoadBalancerOC: true, enableTeamIdClusterPrefix: false, operatorAnnotations: map[string]string{"foo": "bar"}, - clusterAnnotations: make(map[string]string), + serviceAnnotations: make(map[string]string), expect: map[string]string{ "external-dns.alpha.kubernetes.io/hostname": "acid-test.test.db.example.com,test.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", @@ -617,7 +619,7 @@ func TestServiceAnnotations(t *testing.T) { operatorAnnotations: map[string]string{ "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "1800", }, - clusterAnnotations: make(map[string]string), + serviceAnnotations: make(map[string]string), expect: map[string]string{ "external-dns.alpha.kubernetes.io/hostname": "acid-test.test.db.example.com,test.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "1800", @@ -629,7 +631,7 @@ func TestServiceAnnotations(t *testing.T) { enableMasterLoadBalancerOC: true, enableTeamIdClusterPrefix: false, operatorAnnotations: make(map[string]string), - clusterAnnotations: map[string]string{ + serviceAnnotations: map[string]string{ "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "1800", }, expect: map[string]string{ @@ -643,7 +645,7 @@ func TestServiceAnnotations(t *testing.T) { enableMasterLoadBalancerOC: true, enableTeamIdClusterPrefix: false, operatorAnnotations: make(map[string]string), - clusterAnnotations: map[string]string{ + serviceAnnotations: map[string]string{ "external-dns.alpha.kubernetes.io/hostname": "wrong.external-dns-name.example.com", }, expect: map[string]string{ @@ -656,13 +658,32 @@ func TestServiceAnnotations(t *testing.T) { role: "master", enableMasterLoadBalancerOC: true, enableTeamIdClusterPrefix: true, - clusterAnnotations: make(map[string]string), + serviceAnnotations: make(map[string]string), operatorAnnotations: make(map[string]string), expect: map[string]string{ "external-dns.alpha.kubernetes.io/hostname": "acid-test.test.db.example.com,test.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", }, }, + { + about: "Master with master service annotations override service annotations", + role: "master", + enableMasterLoadBalancerOC: true, + enableTeamIdClusterPrefix: false, + operatorAnnotations: make(map[string]string), + serviceAnnotations: map[string]string{ + "service.beta.kubernetes.io/aws-load-balancer-nlb-target-type": "ip", + "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "1800", + }, + masterServiceAnnotations: map[string]string{ + "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "2000", + }, + expect: map[string]string{ + "external-dns.alpha.kubernetes.io/hostname": "acid-test.test.db.example.com,test.acid.db.example.com", + "service.beta.kubernetes.io/aws-load-balancer-nlb-target-type": "ip", + "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "2000", + }, + }, // REPLICA { about: "Replica with no annotations and EnableReplicaLoadBalancer disabled on spec and OperatorConfig", @@ -671,7 +692,7 @@ func TestServiceAnnotations(t *testing.T) { enableReplicaLoadBalancerOC: false, enableTeamIdClusterPrefix: false, operatorAnnotations: make(map[string]string), - clusterAnnotations: make(map[string]string), + serviceAnnotations: make(map[string]string), expect: make(map[string]string), }, { @@ -681,7 +702,7 @@ func TestServiceAnnotations(t *testing.T) { enableReplicaLoadBalancerOC: false, enableTeamIdClusterPrefix: false, operatorAnnotations: make(map[string]string), - clusterAnnotations: make(map[string]string), + serviceAnnotations: make(map[string]string), expect: map[string]string{ "external-dns.alpha.kubernetes.io/hostname": "acid-test-repl.test.db.example.com,test-repl.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", @@ -694,7 +715,7 @@ func TestServiceAnnotations(t *testing.T) { enableReplicaLoadBalancerOC: true, enableTeamIdClusterPrefix: false, operatorAnnotations: make(map[string]string), - clusterAnnotations: make(map[string]string), + serviceAnnotations: make(map[string]string), expect: make(map[string]string), }, { @@ -703,7 +724,7 @@ func TestServiceAnnotations(t *testing.T) { enableReplicaLoadBalancerOC: true, enableTeamIdClusterPrefix: false, operatorAnnotations: make(map[string]string), - clusterAnnotations: make(map[string]string), + serviceAnnotations: make(map[string]string), expect: map[string]string{ "external-dns.alpha.kubernetes.io/hostname": "acid-test-repl.test.db.example.com,test-repl.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", @@ -715,7 +736,7 @@ func TestServiceAnnotations(t *testing.T) { enableReplicaLoadBalancerOC: true, enableTeamIdClusterPrefix: false, operatorAnnotations: make(map[string]string), - clusterAnnotations: map[string]string{"foo": "bar"}, + serviceAnnotations: map[string]string{"foo": "bar"}, expect: map[string]string{ "external-dns.alpha.kubernetes.io/hostname": "acid-test-repl.test.db.example.com,test-repl.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", @@ -729,7 +750,7 @@ func TestServiceAnnotations(t *testing.T) { enableReplicaLoadBalancerOC: true, enableTeamIdClusterPrefix: false, operatorAnnotations: make(map[string]string), - clusterAnnotations: map[string]string{"foo": "bar"}, + serviceAnnotations: map[string]string{"foo": "bar"}, expect: map[string]string{"foo": "bar"}, }, { @@ -738,7 +759,7 @@ func TestServiceAnnotations(t *testing.T) { enableReplicaLoadBalancerOC: true, enableTeamIdClusterPrefix: false, operatorAnnotations: map[string]string{"foo": "bar"}, - clusterAnnotations: make(map[string]string), + serviceAnnotations: make(map[string]string), expect: map[string]string{ "external-dns.alpha.kubernetes.io/hostname": "acid-test-repl.test.db.example.com,test-repl.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", @@ -753,7 +774,7 @@ func TestServiceAnnotations(t *testing.T) { operatorAnnotations: map[string]string{ "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "1800", }, - clusterAnnotations: make(map[string]string), + serviceAnnotations: make(map[string]string), expect: map[string]string{ "external-dns.alpha.kubernetes.io/hostname": "acid-test-repl.test.db.example.com,test-repl.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "1800", @@ -765,7 +786,7 @@ func TestServiceAnnotations(t *testing.T) { enableReplicaLoadBalancerOC: true, enableTeamIdClusterPrefix: false, operatorAnnotations: make(map[string]string), - clusterAnnotations: map[string]string{ + serviceAnnotations: map[string]string{ "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "1800", }, expect: map[string]string{ @@ -779,7 +800,7 @@ func TestServiceAnnotations(t *testing.T) { enableReplicaLoadBalancerOC: true, enableTeamIdClusterPrefix: false, operatorAnnotations: make(map[string]string), - clusterAnnotations: map[string]string{ + serviceAnnotations: map[string]string{ "external-dns.alpha.kubernetes.io/hostname": "wrong.external-dns-name.example.com", }, expect: map[string]string{ @@ -792,13 +813,32 @@ func TestServiceAnnotations(t *testing.T) { role: "replica", enableReplicaLoadBalancerOC: true, enableTeamIdClusterPrefix: true, - clusterAnnotations: make(map[string]string), + serviceAnnotations: make(map[string]string), operatorAnnotations: make(map[string]string), expect: map[string]string{ "external-dns.alpha.kubernetes.io/hostname": "acid-test-repl.test.db.example.com,test-repl.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", }, }, + { + about: "Replica with replica service annotations override service annotations", + role: "replica", + enableReplicaLoadBalancerOC: true, + enableTeamIdClusterPrefix: false, + operatorAnnotations: make(map[string]string), + serviceAnnotations: map[string]string{ + "service.beta.kubernetes.io/aws-load-balancer-nlb-target-type": "ip", + "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "1800", + }, + replicaServiceAnnotations: map[string]string{ + "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "2000", + }, + expect: map[string]string{ + "external-dns.alpha.kubernetes.io/hostname": "acid-test-repl.test.db.example.com,test-repl.acid.db.example.com", + "service.beta.kubernetes.io/aws-load-balancer-nlb-target-type": "ip", + "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "2000", + }, + }, // COMMON { about: "cluster annotations append to operator annotations", @@ -806,7 +846,7 @@ func TestServiceAnnotations(t *testing.T) { enableReplicaLoadBalancerOC: false, enableTeamIdClusterPrefix: false, operatorAnnotations: map[string]string{"foo": "bar"}, - clusterAnnotations: map[string]string{"post": "gres"}, + serviceAnnotations: map[string]string{"post": "gres"}, expect: map[string]string{"foo": "bar", "post": "gres"}, }, { @@ -815,7 +855,7 @@ func TestServiceAnnotations(t *testing.T) { enableReplicaLoadBalancerOC: false, enableTeamIdClusterPrefix: false, operatorAnnotations: map[string]string{"foo": "bar", "post": "gres"}, - clusterAnnotations: map[string]string{"post": "greSQL"}, + serviceAnnotations: map[string]string{"post": "greSQL"}, expect: map[string]string{"foo": "bar", "post": "greSQL"}, }, } @@ -833,7 +873,9 @@ func TestServiceAnnotations(t *testing.T) { cl.Postgresql.Spec.ClusterName = "" cl.Postgresql.Spec.TeamID = "acid" - cl.Postgresql.Spec.ServiceAnnotations = tt.clusterAnnotations + cl.Postgresql.Spec.ServiceAnnotations = tt.serviceAnnotations + cl.Postgresql.Spec.MasterServiceAnnotations = tt.masterServiceAnnotations + cl.Postgresql.Spec.ReplicaServiceAnnotations = tt.replicaServiceAnnotations cl.Postgresql.Spec.EnableMasterLoadBalancer = tt.enableMasterLoadBalancerSpec cl.Postgresql.Spec.EnableReplicaLoadBalancer = tt.enableReplicaLoadBalancerSpec diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index 4ef61f8b9..2e317c1a2 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -28,6 +28,7 @@ import ( "github.com/zalando/postgres-operator/pkg/util/k8sutil" "github.com/zalando/postgres-operator/pkg/util/patroni" "github.com/zalando/postgres-operator/pkg/util/retryutil" + "golang.org/x/exp/maps" batchv1 "k8s.io/api/batch/v1" "k8s.io/apimachinery/pkg/labels" ) @@ -1901,13 +1902,16 @@ func (c *Cluster) configureLoadBalanceService(serviceSpec *v1.ServiceSpec, sourc func (c *Cluster) generateServiceAnnotations(role PostgresRole, spec *acidv1.PostgresSpec) map[string]string { annotations := make(map[string]string) + maps.Copy(annotations, c.OpConfig.CustomServiceAnnotations) - for k, v := range c.OpConfig.CustomServiceAnnotations { - annotations[k] = v - } - if spec != nil || spec.ServiceAnnotations != nil { - for k, v := range spec.ServiceAnnotations { - annotations[k] = v + if spec != nil { + maps.Copy(annotations, spec.ServiceAnnotations) + + switch role { + case Master: + maps.Copy(annotations, spec.MasterServiceAnnotations) + case Replica: + maps.Copy(annotations, spec.ReplicaServiceAnnotations) } } From ce1fee858634f4058ea82bff354e3c64b78d952d Mon Sep 17 00:00:00 2001 From: Dmitry Volodin Date: Thu, 12 Jan 2023 13:38:54 +0300 Subject: [PATCH 67/77] Ineffectual assignment of the envVars for connection pooler (#2165) * Ineffectual assignment of the envVars for connection pooler * Fixed codegen in case of the GOPATH is specified explicitly --- hack/update-codegen.sh | 3 ++- pkg/cluster/connection_pooler.go | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/hack/update-codegen.sh b/hack/update-codegen.sh index d34db9c45..ff78d68c3 100755 --- a/hack/update-codegen.sh +++ b/hack/update-codegen.sh @@ -18,7 +18,8 @@ trap "cleanup" EXIT SIGINT bash "${CODEGEN_PKG}/generate-groups.sh" all \ "${OPERATOR_PACKAGE_ROOT}/pkg/generated" "${OPERATOR_PACKAGE_ROOT}/pkg/apis" \ "acid.zalan.do:v1 zalando.org:v1" \ - --go-header-file "${SCRIPT_ROOT}"/hack/custom-boilerplate.go.txt + --go-header-file "${SCRIPT_ROOT}"/hack/custom-boilerplate.go.txt \ + -o ./ cp -r "${OPERATOR_PACKAGE_ROOT}"/pkg/* "${TARGET_CODE_DIR}" diff --git a/pkg/cluster/connection_pooler.go b/pkg/cluster/connection_pooler.go index 4a602a1e7..d7fcbbb38 100644 --- a/pkg/cluster/connection_pooler.go +++ b/pkg/cluster/connection_pooler.go @@ -324,7 +324,6 @@ func (c *Cluster) generateConnectionPoolerPodTemplate(role PostgresRole) ( Protocol: v1.ProtocolTCP, }, }, - Env: envVars, ReadinessProbe: &v1.Probe{ ProbeHandler: v1.ProbeHandler{ TCPSocket: &v1.TCPSocketAction{ @@ -341,7 +340,7 @@ func (c *Cluster) generateConnectionPoolerPodTemplate(role PostgresRole) ( // 1. Add environment variables to tell pgBouncer where to find the TLS certificates // 2. Reference the secret in a volume // 3. Mount the volume to the container at /tls - poolerVolumes := []v1.Volume{} + var poolerVolumes []v1.Volume if spec.TLS != nil && spec.TLS.SecretName != "" { // Env vars crtFile := spec.TLS.CertificateFile @@ -383,6 +382,7 @@ func (c *Cluster) generateConnectionPoolerPodTemplate(role PostgresRole) ( }} } + poolerContainer.Env = envVars tolerationsSpec := tolerations(&spec.Tolerations, c.OpConfig.PodToleration) podTemplate := &v1.PodTemplateSpec{ From 28cd2f188aee6cad7b0dac22ce49ea49a1696a69 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Tue, 17 Jan 2023 10:06:11 +0100 Subject: [PATCH 68/77] better backwards compatibility with old DNS name format for LBs (#2171) * better backwards compatibility with legacy DNS name format for LBs * improve docs on DNS string --- .../crds/operatorconfigurations.yaml | 6 +++ charts/postgres-operator/values.yaml | 4 ++ docs/reference/operator_parameters.md | 51 ++++++++++++++----- manifests/configmap.yaml | 2 + manifests/operatorconfiguration.crd.yaml | 6 +++ ...gresql-operator-default-configuration.yaml | 2 + pkg/apis/acid.zalan.do/v1/crds.go | 6 +++ .../v1/operator_configuration_type.go | 2 + pkg/cluster/cluster_test.go | 42 +++++++-------- pkg/cluster/util.go | 27 ++++++---- pkg/controller/operator_config.go | 2 + pkg/util/config/config.go | 2 + 12 files changed, 110 insertions(+), 42 deletions(-) diff --git a/charts/postgres-operator/crds/operatorconfigurations.yaml b/charts/postgres-operator/crds/operatorconfigurations.yaml index 5222832a4..0e2353d35 100644 --- a/charts/postgres-operator/crds/operatorconfigurations.yaml +++ b/charts/postgres-operator/crds/operatorconfigurations.yaml @@ -434,9 +434,15 @@ spec: 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: diff --git a/charts/postgres-operator/values.yaml b/charts/postgres-operator/values.yaml index b2136fef3..e351015f4 100644 --- a/charts/postgres-operator/values.yaml +++ b/charts/postgres-operator/values.yaml @@ -277,8 +277,12 @@ configLoadBalancer: external_traffic_policy: "Cluster" # defines the DNS name string template for the master load balancer cluster master_dns_name_format: "{cluster}.{namespace}.{hostedzone}" + # deprecated DNS template for master load balancer using team name + master_legacy_dns_name_format: "{cluster}.{team}.{hostedzone}" # defines the DNS name string template for the replica load balancer cluster replica_dns_name_format: "{cluster}-repl.{namespace}.{hostedzone}" + # deprecated DNS template for replica load balancer using team name + replica_legacy_dns_name_format: "{cluster}-repl.{team}.{hostedzone}" # options to aid debugging of the operator itself configDebug: diff --git a/docs/reference/operator_parameters.md b/docs/reference/operator_parameters.md index 981ba1ab6..e61cff24b 100644 --- a/docs/reference/operator_parameters.md +++ b/docs/reference/operator_parameters.md @@ -627,22 +627,47 @@ In the CRD-based configuration they are grouped under the `load_balancer` key. the cluster. Can be overridden by individual cluster settings. The default is `false`. -* **external_traffic_policy** defines external traffic policy for load +* **external_traffic_policy** + defines external traffic policy for load balancers. Allowed values are `Cluster` (default) and `Local`. -* **master_dns_name_format** defines the DNS name string template for the - master load balancer cluster. The default is - `{cluster}.{namespace}.{hostedzone}`, where `{cluster}` is replaced by the cluster - name, `{namespace}` is replaced with the namespace and `{hostedzone}` is replaced - with the hosted zone (the value of the `db_hosted_zone` parameter). No other - placeholders are allowed. +* **master_dns_name_format** + defines the DNS name string template for the master load balancer cluster. + The default is `{cluster}.{namespace}.{hostedzone}`, where `{cluster}` is + replaced by the cluster name, `{namespace}` is replaced with the namespace + and `{hostedzone}` is replaced with the hosted zone (the value of the + `db_hosted_zone` parameter). The `{team}` placeholder can still be used, + although it is not recommened because the team of a cluster can change. + If the cluster name starts with the `teamId` it will also be part of the + DNS, aynway. No other placeholders are allowed! -* **replica_dns_name_format** defines the DNS name string template for the - replica load balancer cluster. The default is - `{cluster}-repl.{namespace}.{hostedzone}`, where `{cluster}` is replaced by the - cluster name, `{namespace}` is replaced with the namespace and `{hostedzone}` is - replaced with the hosted zone (the value of the `db_hosted_zone` parameter). - No other placeholders are allowed. +* **master_legacy_dns_name_format** + *deprecated* default master DNS template `{cluster}.{team}.{hostedzone}` as + of pre `v1.9.0`. If cluster name starts with `teamId` then a second DNS + entry will be created using the template defined here to provide backwards + compatibility. The `teamId` prefix will be extracted from the clustername + because it follows later in the DNS string. When using a customized + `master_dns_name_format` make sure to define the legacy DNS format when + switching to v1.9.0. + +* **replica_dns_name_format** + defines the DNS name string template for the replica load balancer cluster. + The default is `{cluster}-repl.{namespace}.{hostedzone}`, where `{cluster}` + is replaced by the cluster name, `{namespace}` is replaced with the + namespace and `{hostedzone}` is replaced with the hosted zone (the value of + the `db_hosted_zone` parameter). The `{team}` placeholder can still be used, + although it is not recommened because the team of a cluster can change. + If the cluster name starts with the `teamId` it will also be part of the + DNS, aynway. No other placeholders are allowed! + +* **replica_legacy_dns_name_format** + *deprecated* default master DNS template `{cluster}-repl.{team}.{hostedzone}` + as of pre `v1.9.0`. If cluster name starts with `teamId` then a second DNS + entry will be created using the template defined here to provide backwards + compatibility. The `teamId` prefix will be extracted from the clustername + because it follows later in the DNS string. When using a customized + `master_dns_name_format` make sure to define the legacy DNS format when + switching to v1.9.0. ## AWS or GCP interaction diff --git a/manifests/configmap.yaml b/manifests/configmap.yaml index 5311d0a45..1a64c90f8 100644 --- a/manifests/configmap.yaml +++ b/manifests/configmap.yaml @@ -97,6 +97,7 @@ data: 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" @@ -135,6 +136,7 @@ data: 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 diff --git a/manifests/operatorconfiguration.crd.yaml b/manifests/operatorconfiguration.crd.yaml index 8fcfe739f..7df1a1ae3 100644 --- a/manifests/operatorconfiguration.crd.yaml +++ b/manifests/operatorconfiguration.crd.yaml @@ -432,9 +432,15 @@ spec: 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: diff --git a/manifests/postgresql-operator-default-configuration.yaml b/manifests/postgresql-operator-default-configuration.yaml index 367529ef8..6ea85171e 100644 --- a/manifests/postgresql-operator-default-configuration.yaml +++ b/manifests/postgresql-operator-default-configuration.yaml @@ -137,7 +137,9 @@ configuration: 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" diff --git a/pkg/apis/acid.zalan.do/v1/crds.go b/pkg/apis/acid.zalan.do/v1/crds.go index 4a08ec2f4..b82aa30b6 100644 --- a/pkg/apis/acid.zalan.do/v1/crds.go +++ b/pkg/apis/acid.zalan.do/v1/crds.go @@ -1594,9 +1594,15 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{ "master_dns_name_format": { Type: "string", }, + "master_legacy_dns_name_format": { + Type: "string", + }, "replica_dns_name_format": { Type: "string", }, + "replica_legacy_dns_name_format": { + Type: "string", + }, }, }, "aws_or_gcp": { diff --git a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go index c62f1ad31..4ff5ee81e 100644 --- a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go +++ b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go @@ -137,7 +137,9 @@ type LoadBalancerConfiguration struct { EnableReplicaPoolerLoadBalancer bool `json:"enable_replica_pooler_load_balancer,omitempty"` CustomServiceAnnotations map[string]string `json:"custom_service_annotations,omitempty"` MasterDNSNameFormat config.StringTemplate `json:"master_dns_name_format,omitempty"` + MasterLegacyDNSNameFormat config.StringTemplate `json:"master_legacy_dns_name_format,omitempty"` ReplicaDNSNameFormat config.StringTemplate `json:"replica_dns_name_format,omitempty"` + ReplicaLegacyDNSNameFormat config.StringTemplate `json:"replica_legacy_dns_name_format,omitempty"` ExternalTrafficPolicy string `json:"external_traffic_policy" default:"Cluster"` } diff --git a/pkg/cluster/cluster_test.go b/pkg/cluster/cluster_test.go index b9633d903..29272eac9 100644 --- a/pkg/cluster/cluster_test.go +++ b/pkg/cluster/cluster_test.go @@ -549,7 +549,7 @@ func TestServiceAnnotations(t *testing.T) { operatorAnnotations: make(map[string]string), serviceAnnotations: make(map[string]string), expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "acid-test.test.db.example.com,test.acid.db.example.com", + "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg.test.db.example.com,test-stg.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", }, }, @@ -571,7 +571,7 @@ func TestServiceAnnotations(t *testing.T) { operatorAnnotations: make(map[string]string), serviceAnnotations: make(map[string]string), expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "acid-test.test.db.example.com,test.acid.db.example.com", + "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg.test.db.example.com,test-stg.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", }, }, @@ -583,7 +583,7 @@ func TestServiceAnnotations(t *testing.T) { operatorAnnotations: make(map[string]string), serviceAnnotations: map[string]string{"foo": "bar"}, expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "acid-test.test.db.example.com,test.acid.db.example.com", + "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg.test.db.example.com,test-stg.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", "foo": "bar", }, @@ -606,7 +606,7 @@ func TestServiceAnnotations(t *testing.T) { operatorAnnotations: map[string]string{"foo": "bar"}, serviceAnnotations: make(map[string]string), expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "acid-test.test.db.example.com,test.acid.db.example.com", + "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg.test.db.example.com,test-stg.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", "foo": "bar", }, @@ -621,7 +621,7 @@ func TestServiceAnnotations(t *testing.T) { }, serviceAnnotations: make(map[string]string), expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "acid-test.test.db.example.com,test.acid.db.example.com", + "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg.test.db.example.com,test-stg.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "1800", }, }, @@ -635,7 +635,7 @@ func TestServiceAnnotations(t *testing.T) { "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "1800", }, expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "acid-test.test.db.example.com,test.acid.db.example.com", + "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg.test.db.example.com,test-stg.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "1800", }, }, @@ -649,7 +649,7 @@ func TestServiceAnnotations(t *testing.T) { "external-dns.alpha.kubernetes.io/hostname": "wrong.external-dns-name.example.com", }, expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "acid-test.test.db.example.com,test.acid.db.example.com", + "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg.test.db.example.com,test-stg.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", }, }, @@ -661,7 +661,7 @@ func TestServiceAnnotations(t *testing.T) { serviceAnnotations: make(map[string]string), operatorAnnotations: make(map[string]string), expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "acid-test.test.db.example.com,test.acid.db.example.com", + "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg.test.db.example.com,test-stg.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", }, }, @@ -679,7 +679,7 @@ func TestServiceAnnotations(t *testing.T) { "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "2000", }, expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "acid-test.test.db.example.com,test.acid.db.example.com", + "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg.test.db.example.com,test-stg.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-nlb-target-type": "ip", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "2000", }, @@ -704,7 +704,7 @@ func TestServiceAnnotations(t *testing.T) { operatorAnnotations: make(map[string]string), serviceAnnotations: make(map[string]string), expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "acid-test-repl.test.db.example.com,test-repl.acid.db.example.com", + "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg-repl.test.db.example.com,test-stg-repl.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", }, }, @@ -726,7 +726,7 @@ func TestServiceAnnotations(t *testing.T) { operatorAnnotations: make(map[string]string), serviceAnnotations: make(map[string]string), expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "acid-test-repl.test.db.example.com,test-repl.acid.db.example.com", + "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg-repl.test.db.example.com,test-stg-repl.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", }, }, @@ -738,7 +738,7 @@ func TestServiceAnnotations(t *testing.T) { operatorAnnotations: make(map[string]string), serviceAnnotations: map[string]string{"foo": "bar"}, expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "acid-test-repl.test.db.example.com,test-repl.acid.db.example.com", + "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg-repl.test.db.example.com,test-stg-repl.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", "foo": "bar", }, @@ -761,7 +761,7 @@ func TestServiceAnnotations(t *testing.T) { operatorAnnotations: map[string]string{"foo": "bar"}, serviceAnnotations: make(map[string]string), expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "acid-test-repl.test.db.example.com,test-repl.acid.db.example.com", + "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg-repl.test.db.example.com,test-stg-repl.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", "foo": "bar", }, @@ -776,7 +776,7 @@ func TestServiceAnnotations(t *testing.T) { }, serviceAnnotations: make(map[string]string), expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "acid-test-repl.test.db.example.com,test-repl.acid.db.example.com", + "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg-repl.test.db.example.com,test-stg-repl.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "1800", }, }, @@ -790,7 +790,7 @@ func TestServiceAnnotations(t *testing.T) { "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "1800", }, expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "acid-test-repl.test.db.example.com,test-repl.acid.db.example.com", + "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg-repl.test.db.example.com,test-stg-repl.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "1800", }, }, @@ -804,7 +804,7 @@ func TestServiceAnnotations(t *testing.T) { "external-dns.alpha.kubernetes.io/hostname": "wrong.external-dns-name.example.com", }, expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "acid-test-repl.test.db.example.com,test-repl.acid.db.example.com", + "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg-repl.test.db.example.com,test-stg-repl.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", }, }, @@ -816,7 +816,7 @@ func TestServiceAnnotations(t *testing.T) { serviceAnnotations: make(map[string]string), operatorAnnotations: make(map[string]string), expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "acid-test-repl.test.db.example.com,test-repl.acid.db.example.com", + "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg-repl.test.db.example.com,test-stg-repl.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", }, }, @@ -834,7 +834,7 @@ func TestServiceAnnotations(t *testing.T) { "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "2000", }, expect: map[string]string{ - "external-dns.alpha.kubernetes.io/hostname": "acid-test-repl.test.db.example.com,test-repl.acid.db.example.com", + "external-dns.alpha.kubernetes.io/hostname": "acid-test-stg-repl.test.db.example.com,test-stg-repl.acid.db.example.com", "service.beta.kubernetes.io/aws-load-balancer-nlb-target-type": "ip", "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "2000", }, @@ -867,8 +867,10 @@ func TestServiceAnnotations(t *testing.T) { cl.OpConfig.CustomServiceAnnotations = tt.operatorAnnotations cl.OpConfig.EnableMasterLoadBalancer = tt.enableMasterLoadBalancerOC cl.OpConfig.EnableReplicaLoadBalancer = tt.enableReplicaLoadBalancerOC - cl.OpConfig.MasterDNSNameFormat = "{cluster}.{namespace}.{hostedzone}" - cl.OpConfig.ReplicaDNSNameFormat = "{cluster}-repl.{namespace}.{hostedzone}" + cl.OpConfig.MasterDNSNameFormat = "{cluster}-stg.{namespace}.{hostedzone}" + cl.OpConfig.MasterLegacyDNSNameFormat = "{cluster}-stg.{team}.{hostedzone}" + cl.OpConfig.ReplicaDNSNameFormat = "{cluster}-stg-repl.{namespace}.{hostedzone}" + cl.OpConfig.ReplicaLegacyDNSNameFormat = "{cluster}-stg-repl.{team}.{hostedzone}" cl.OpConfig.DbHostedZone = "db.example.com" cl.Postgresql.Spec.ClusterName = "" diff --git a/pkg/cluster/util.go b/pkg/cluster/util.go index fcaf8b9e6..079567421 100644 --- a/pkg/cluster/util.go +++ b/pkg/cluster/util.go @@ -506,7 +506,7 @@ func (c *Cluster) roleLabelsSet(shouldAddExtraLabels bool, role PostgresRole) la } func (c *Cluster) dnsName(role PostgresRole) string { - var dnsString string + var dnsString, oldDnsString string if role == Master { dnsString = c.masterDNSName() @@ -517,10 +517,12 @@ func (c *Cluster) dnsName(role PostgresRole) string { // if cluster name starts with teamID we might need to provide backwards compatibility clusterNameWithoutTeamPrefix, _ := acidv1.ExtractClusterName(c.Name, c.Spec.TeamID) if clusterNameWithoutTeamPrefix != "" { - if role == Replica { - clusterNameWithoutTeamPrefix = fmt.Sprintf("%s-repl", clusterNameWithoutTeamPrefix) + if role == Master { + oldDnsString = c.oldMasterDNSName(clusterNameWithoutTeamPrefix) + } else { + oldDnsString = c.oldReplicaDNSName(clusterNameWithoutTeamPrefix) } - dnsString = fmt.Sprintf("%s,%s", dnsString, c.oldDNSFormat(clusterNameWithoutTeamPrefix)) + dnsString = fmt.Sprintf("%s,%s", dnsString, oldDnsString) } return dnsString @@ -542,11 +544,18 @@ func (c *Cluster) replicaDNSName() string { "hostedzone", c.OpConfig.DbHostedZone)) } -func (c *Cluster) oldDNSFormat(clusterName string) string { - return fmt.Sprintf("%s.%s.%s", - clusterName, - c.teamName(), - c.OpConfig.DbHostedZone) +func (c *Cluster) oldMasterDNSName(clusterName string) string { + return strings.ToLower(c.OpConfig.MasterLegacyDNSNameFormat.Format( + "cluster", clusterName, + "team", c.teamName(), + "hostedzone", c.OpConfig.DbHostedZone)) +} + +func (c *Cluster) oldReplicaDNSName(clusterName string) string { + return strings.ToLower(c.OpConfig.ReplicaLegacyDNSNameFormat.Format( + "cluster", clusterName, + "team", c.teamName(), + "hostedzone", c.OpConfig.DbHostedZone)) } func (c *Cluster) credentialSecretName(username string) string { diff --git a/pkg/controller/operator_config.go b/pkg/controller/operator_config.go index 00a7ea001..a1a1050ca 100644 --- a/pkg/controller/operator_config.go +++ b/pkg/controller/operator_config.go @@ -154,7 +154,9 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur result.EnableReplicaPoolerLoadBalancer = fromCRD.LoadBalancer.EnableReplicaPoolerLoadBalancer result.CustomServiceAnnotations = fromCRD.LoadBalancer.CustomServiceAnnotations result.MasterDNSNameFormat = fromCRD.LoadBalancer.MasterDNSNameFormat + result.MasterLegacyDNSNameFormat = fromCRD.LoadBalancer.MasterLegacyDNSNameFormat result.ReplicaDNSNameFormat = fromCRD.LoadBalancer.ReplicaDNSNameFormat + result.ReplicaLegacyDNSNameFormat = fromCRD.LoadBalancer.ReplicaLegacyDNSNameFormat result.ExternalTrafficPolicy = util.Coalesce(fromCRD.LoadBalancer.ExternalTrafficPolicy, "Cluster") // AWS or GCP config diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index 612b7a242..179d5b96c 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -215,7 +215,9 @@ type Config struct { EnableLoadBalancer *bool `name:"enable_load_balancer"` // deprecated and kept for backward compatibility ExternalTrafficPolicy string `name:"external_traffic_policy" default:"Cluster"` MasterDNSNameFormat StringTemplate `name:"master_dns_name_format" default:"{cluster}.{namespace}.{hostedzone}"` + MasterLegacyDNSNameFormat StringTemplate `name:"master_legacy_dns_name_format" default:"{cluster}.{team}.{hostedzone}"` ReplicaDNSNameFormat StringTemplate `name:"replica_dns_name_format" default:"{cluster}-repl.{namespace}.{hostedzone}"` + ReplicaLegacyDNSNameFormat StringTemplate `name:"replica_legacy_dns_name_format" default:"{cluster}-repl.{team}.{hostedzone}"` PDBNameFormat StringTemplate `name:"pdb_name_format" default:"postgres-{cluster}-pdb"` EnablePodDisruptionBudget *bool `name:"enable_pod_disruption_budget" default:"true"` EnableInitContainers *bool `name:"enable_init_containers" default:"true"` From a4f95e97e040d8ffe4fb68f379da8965db20df27 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Tue, 17 Jan 2023 12:58:14 +0100 Subject: [PATCH 69/77] do not rotate secrets for standby clusters (#2175) --- pkg/cluster/sync.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index af85eb076..75d4e8a65 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -759,7 +759,7 @@ func (c *Cluster) updateSecret( // globally enabled rotation is only allowed for manifest and bootstrapped roles allowedRoleTypes := []spec.RoleOrigin{spec.RoleOriginManifest, spec.RoleOriginBootstrap} - rotationAllowed := !pwdUser.IsDbOwner && util.SliceContains(allowedRoleTypes, pwdUser.Origin) + rotationAllowed := !pwdUser.IsDbOwner && util.SliceContains(allowedRoleTypes, pwdUser.Origin) && c.Spec.StandbyCluster == nil if (c.OpConfig.EnablePasswordRotation && rotationAllowed) || rotationEnabledInManifest { updateSecretMsg, err = c.rotatePasswordInSecret(secret, secretUsername, pwdUser.Origin, currentTime, retentionUsers) From 63c9f916a68d833fe3ab03fdfcb45d59582e7a7e Mon Sep 17 00:00:00 2001 From: idanovinda Date: Tue, 17 Jan 2023 17:18:08 +0100 Subject: [PATCH 70/77] fix default cpu memory (#2178) --- ui/operator_ui/main.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ui/operator_ui/main.py b/ui/operator_ui/main.py index 49920c8e0..0399f14f8 100644 --- a/ui/operator_ui/main.py +++ b/ui/operator_ui/main.py @@ -79,7 +79,7 @@ TOKENINFO_URL = getenv('OAUTH2_TOKEN_INFO_URL') OPERATOR_API_URL = getenv('OPERATOR_API_URL', 'http://postgres-operator') OPERATOR_CLUSTER_NAME_LABEL = getenv('OPERATOR_CLUSTER_NAME_LABEL', 'cluster-name') -OPERATOR_UI_CONFIG = getenv('OPERATOR_UI_CONFIG', '{}') +OPERATOR_UI_CONFIG = loads(getenv('OPERATOR_UI_CONFIG', '{}')) OPERATOR_UI_MAINTENANCE_CHECK = getenv('OPERATOR_UI_MAINTENANCE_CHECK', '{}') READ_ONLY_MODE = getenv('READ_ONLY_MODE', False) in [True, 'true'] SPILO_S3_BACKUP_PREFIX = getenv('SPILO_S3_BACKUP_PREFIX', 'spilo/') @@ -348,7 +348,7 @@ DEFAULT_UI_CONFIG = { @authorize def get_config(): config = DEFAULT_UI_CONFIG.copy() - config.update(loads(OPERATOR_UI_CONFIG)) + config.update(OPERATOR_UI_CONFIG) config['namespaces'] = ( [TARGET_NAMESPACE] @@ -508,10 +508,10 @@ def get_postgresqls(): postgresqls = [ { 'nodes': spec.get('numberOfInstances', ''), - 'memory': spec.get('resources', {}).get('requests', {}).get('memory', 0), - 'memory_limit': spec.get('resources', {}).get('limits', {}).get('memory', 0), - 'cpu': spec.get('resources', {}).get('requests', {}).get('cpu', 0), - 'cpu_limit': spec.get('resources', {}).get('limits', {}).get('cpu', 0), + 'memory': spec.get('resources', {}).get('requests', {}).get('memory', OPERATOR_UI_CONFIG.get("default_memory", DEFAULT_MEMORY)), + 'memory_limit': spec.get('resources', {}).get('limits', {}).get('memory', OPERATOR_UI_CONFIG.get("default_memory_limit", DEFAULT_MEMORY_LIMIT)), + 'cpu': spec.get('resources', {}).get('requests', {}).get('cpu', OPERATOR_UI_CONFIG.get("default_cpu", DEFAULT_CPU)), + 'cpu_limit': spec.get('resources', {}).get('limits', {}).get('cpu', OPERATOR_UI_CONFIG.get("default_cpu_limit", DEFAULT_CPU_LIMIT)), 'volume_size': spec.get('volume', {}).get('size', 0), 'iops': spec.get('volume', {}).get('iops', 3000), 'throughput': spec.get('volume', {}).get('throughput', 125), From 4741b3f7340f5007935697695d88468f95282f3e Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Wed, 25 Jan 2023 10:48:23 +0100 Subject: [PATCH 71/77] copy rolconfig during password rotation (#2183) * copy rolconfig during password rotation Co-authored-by: idanovinda --- e2e/tests/test_e2e.py | 29 +++++++++++++++---- pkg/cluster/database.go | 2 +- pkg/cluster/sync.go | 26 ++++++++++++++--- pkg/cluster/sync_test.go | 3 +- .../clientset/versioned/fake/register.go | 14 ++++----- .../clientset/versioned/scheme/register.go | 14 ++++----- pkg/spec/types.go | 1 + pkg/util/constants/roles.go | 1 + 8 files changed, 65 insertions(+), 25 deletions(-) diff --git a/e2e/tests/test_e2e.py b/e2e/tests/test_e2e.py index 6b46dd7db..8b087b909 100644 --- a/e2e/tests/test_e2e.py +++ b/e2e/tests/test_e2e.py @@ -932,7 +932,8 @@ class EndToEndTestCase(unittest.TestCase): "AdminRole": "", "Origin": 2, "IsDbOwner": False, - "Deleted": False + "Deleted": False, + "Rotated": False }) return True except: @@ -1472,9 +1473,9 @@ class EndToEndTestCase(unittest.TestCase): # create fake rotation users that should be removed by operator # but have one that would still fit into the retention period create_fake_rotation_user = """ - CREATE ROLE foo_user201031 IN ROLE foo_user; - CREATE ROLE foo_user211031 IN ROLE foo_user; - CREATE ROLE foo_user"""+(today-timedelta(days=40)).strftime("%y%m%d")+""" IN ROLE foo_user; + CREATE USER foo_user201031 IN ROLE foo_user; + CREATE USER foo_user211031 IN ROLE foo_user; + CREATE USER foo_user"""+(today-timedelta(days=40)).strftime("%y%m%d")+""" IN ROLE foo_user; """ self.query_database(leader.metadata.name, "postgres", create_fake_rotation_user) @@ -1491,6 +1492,12 @@ class EndToEndTestCase(unittest.TestCase): namespace="default", body=secret_fake_rotation) + # update rolconfig for foo_user that will be copied for new rotation user + alter_foo_user_search_path = """ + ALTER ROLE foo_user SET search_path TO data; + """ + self.query_database(leader.metadata.name, "postgres", alter_foo_user_search_path) + # enable password rotation for all other users (foo_user) # this will force a sync of secrets for further assertions enable_password_rotation = { @@ -1526,6 +1533,18 @@ class EndToEndTestCase(unittest.TestCase): self.eventuallyEqual(lambda: len(self.query_database(leader.metadata.name, "postgres", user_query)), 3, "Found incorrect number of rotation users", 10, 5) + # check if rolconfig was passed from foo_user to foo_user+today + # and that no foo_user has been deprecated (can still login) + user_query = """ + SELECT rolname + FROM pg_catalog.pg_roles + WHERE rolname LIKE 'foo_user%' + AND rolconfig = ARRAY['search_path=data']::text[] + AND rolcanlogin; + """ + self.eventuallyEqual(lambda: len(self.query_database(leader.metadata.name, "postgres", user_query)), 2, + "Rolconfig not applied to new rotation user", 10, 5) + # test that rotation_user can connect to the database self.eventuallyEqual(lambda: len(self.query_database_with_user(leader.metadata.name, "postgres", "SELECT 1", "foo_user")), 1, "Could not connect to the database with rotation user {}".format(rotation_user), 10, 5) @@ -1559,7 +1578,7 @@ class EndToEndTestCase(unittest.TestCase): WHERE rolname LIKE 'foo_user%'; """ self.eventuallyEqual(lambda: len(self.query_database(leader.metadata.name, "postgres", user_query)), 2, - "Found incorrect number of rotation users", 10, 5) + "Found incorrect number of rotation users after disabling password rotation", 10, 5) @timeout_decorator.timeout(TEST_TIMEOUT_SEC) def test_rolling_update_flag(self): diff --git a/pkg/cluster/database.go b/pkg/cluster/database.go index f1ea736ce..3ec36bb67 100644 --- a/pkg/cluster/database.go +++ b/pkg/cluster/database.go @@ -284,7 +284,7 @@ func (c *Cluster) cleanupRotatedUsers(rotatedUsers []string, db *sql.DB) error { retentionDate := time.Now().AddDate(0, 0, int(retenionDays)*-1) for rotatedUser, dateSuffix := range extraUsers { - userCreationDate, err := time.Parse("060102", dateSuffix) + userCreationDate, err := time.Parse(constants.RotationUserDateFormat, dateSuffix) if err != nil { c.logger.Errorf("could not parse creation date suffix of user %q: %v", rotatedUser, err) continue diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index 75d4e8a65..797a07256 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -656,7 +656,6 @@ func (c *Cluster) checkAndSetGlobalPostgreSQLConfiguration(pod *v1.Pod, effectiv } func (c *Cluster) syncSecrets() error { - c.logger.Info("syncing secrets") c.setProcessName("syncing secrets") generatedSecrets := c.generateUserSecrets() @@ -792,6 +791,7 @@ func (c *Cluster) updateSecret( pwdUser.Password = string(secret.Data["password"]) // update membership if we deal with a rotation user if secretUsername != pwdUser.Name { + pwdUser.Rotated = true pwdUser.MemberOf = []string{secretUsername} } userMap[userKey] = pwdUser @@ -842,7 +842,7 @@ func (c *Cluster) rotatePasswordInSecret( if currentTime.After(nextRotationDate) { // create rotation user if role is not listed for in-place password update if !util.SliceContains(c.Spec.UsersWithInPlaceSecretRotation, secretUsername) { - rotationUsername := fmt.Sprintf("%s%s", secretUsername, currentTime.Format("060102")) + rotationUsername := fmt.Sprintf("%s%s", secretUsername, currentTime.Format(constants.RotationUserDateFormat)) secret.Data["username"] = []byte(rotationUsername) c.logger.Infof("updating username in secret %s and creating rotation user %s in the database", secretName, rotationUsername) // whenever there is a rotation, check if old rotation users can be deleted @@ -924,6 +924,12 @@ func (c *Cluster) syncRoles() (err error) { for _, u := range c.pgUsers { pgRole := u.Name userNames = append(userNames, pgRole) + + // when a rotation happened add group role to query its rolconfig + if u.Rotated { + userNames = append(userNames, u.MemberOf[0]) + } + // add team member role name with rename suffix in case we need to rename it back if u.Origin == spec.RoleOriginTeamsAPI && c.OpConfig.EnableTeamMemberDeprecation { deletedUsers[pgRole+c.OpConfig.RoleDeletionSuffix] = pgRole @@ -950,9 +956,21 @@ func (c *Cluster) syncRoles() (err error) { return fmt.Errorf("error getting users from the database: %v", err) } - // update pgUsers where a deleted role was found - // so that they are skipped in ProduceSyncRequests +DBUSERS: for _, dbUser := range dbUsers { + // copy rolconfig to rotation users + for pgUserName, pgUser := range c.pgUsers { + if pgUser.Rotated && pgUser.MemberOf[0] == dbUser.Name { + pgUser.Parameters = dbUser.Parameters + c.pgUsers[pgUserName] = pgUser + // remove group role from dbUsers to not count as deleted role + delete(dbUsers, dbUser.Name) + continue DBUSERS + } + } + + // update pgUsers where a deleted role was found + // so that they are skipped in ProduceSyncRequests originalUsername, foundDeletedUser := deletedUsers[dbUser.Name] // check if original user does not exist in dbUsers _, originalUserAlreadyExists := dbUsers[originalUsername] diff --git a/pkg/cluster/sync_test.go b/pkg/cluster/sync_test.go index 3cd3d3f28..f7f8ad9c7 100644 --- a/pkg/cluster/sync_test.go +++ b/pkg/cluster/sync_test.go @@ -22,6 +22,7 @@ import ( "github.com/zalando/postgres-operator/pkg/spec" "github.com/zalando/postgres-operator/pkg/util" "github.com/zalando/postgres-operator/pkg/util/config" + "github.com/zalando/postgres-operator/pkg/util/constants" "github.com/zalando/postgres-operator/pkg/util/k8sutil" "github.com/zalando/postgres-operator/pkg/util/patroni" "k8s.io/client-go/kubernetes/fake" @@ -593,7 +594,7 @@ func TestUpdateSecret(t *testing.T) { t.Errorf("%s: username differs in updated secret: expected %s, got %s", testName, username, secretUsername) } } else { - rotatedUsername := username + dayAfterTomorrow.Format("060102") + rotatedUsername := username + dayAfterTomorrow.Format(constants.RotationUserDateFormat) if secretUsername != rotatedUsername { t.Errorf("%s: updated secret does not contain correct username: expected %s, got %s", testName, rotatedUsername, secretUsername) } diff --git a/pkg/generated/clientset/versioned/fake/register.go b/pkg/generated/clientset/versioned/fake/register.go index 19d48b0d2..a156f8f52 100644 --- a/pkg/generated/clientset/versioned/fake/register.go +++ b/pkg/generated/clientset/versioned/fake/register.go @@ -45,14 +45,14 @@ var localSchemeBuilder = runtime.SchemeBuilder{ // AddToScheme adds all types of this clientset into the given scheme. This allows composition // of clientsets, like in: // -// import ( -// "k8s.io/client-go/kubernetes" -// clientsetscheme "k8s.io/client-go/kubernetes/scheme" -// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" -// ) +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) // -// kclientset, _ := kubernetes.NewForConfig(c) -// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) // // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types // correctly. diff --git a/pkg/generated/clientset/versioned/scheme/register.go b/pkg/generated/clientset/versioned/scheme/register.go index b3ac16d07..b1509deb9 100644 --- a/pkg/generated/clientset/versioned/scheme/register.go +++ b/pkg/generated/clientset/versioned/scheme/register.go @@ -45,14 +45,14 @@ var localSchemeBuilder = runtime.SchemeBuilder{ // AddToScheme adds all types of this clientset into the given scheme. This allows composition // of clientsets, like in: // -// import ( -// "k8s.io/client-go/kubernetes" -// clientsetscheme "k8s.io/client-go/kubernetes/scheme" -// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" -// ) +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) // -// kclientset, _ := kubernetes.NewForConfig(c) -// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) // // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types // correctly. diff --git a/pkg/spec/types.go b/pkg/spec/types.go index 66f26a312..023f9660f 100644 --- a/pkg/spec/types.go +++ b/pkg/spec/types.go @@ -58,6 +58,7 @@ type PgUser struct { AdminRole string `yaml:"admin_role"` IsDbOwner bool `yaml:"is_db_owner"` Deleted bool `yaml:"deleted"` + Rotated bool `yaml:"rotated"` } func (user *PgUser) Valid() bool { diff --git a/pkg/util/constants/roles.go b/pkg/util/constants/roles.go index 8c81e2f04..34f1d0737 100644 --- a/pkg/util/constants/roles.go +++ b/pkg/util/constants/roles.go @@ -20,4 +20,5 @@ const ( WriterRoleNameSuffix = "_writer" UserRoleNameSuffix = "_user" DefaultSearchPath = "\"$user\"" + RotationUserDateFormat = "060102" ) From b9165190e13272bc69c4e971995e740c7da3a1bf Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Wed, 25 Jan 2023 17:06:31 +0100 Subject: [PATCH 72/77] set wal_level for streams in statefulSet sync (#2187) * set wal_level for streams in statefulSet sync --- pkg/cluster/streams.go | 59 +++------------- pkg/cluster/sync.go | 154 ++++++++++++++++++++++++++--------------- 2 files changed, 111 insertions(+), 102 deletions(-) diff --git a/pkg/cluster/streams.go b/pkg/cluster/streams.go index bb03c2e36..683740af3 100644 --- a/pkg/cluster/streams.go +++ b/pkg/cluster/streams.go @@ -79,38 +79,6 @@ func gatherApplicationIds(streams []acidv1.Stream) []string { return appIds } -func (c *Cluster) syncPostgresConfig(requiredPatroniConfig acidv1.Patroni) (bool, error) { - errorMsg := "no pods found to update config" - - // if streams are defined wal_level must be switched to logical - requiredPgParameters := map[string]string{"wal_level": "logical"} - - // apply config changes in pods - pods, err := c.listPods() - if err != nil { - errorMsg = fmt.Sprintf("could not list pods of the statefulset: %v", err) - } - for i, pod := range pods { - podName := util.NameFromMeta(pods[i].ObjectMeta) - effectivePatroniConfig, effectivePgParameters, err := c.patroni.GetConfig(&pod) - if err != nil { - errorMsg = fmt.Sprintf("could not get Postgres config from pod %s: %v", podName, err) - continue - } - - configPatched, _, err := c.checkAndSetGlobalPostgreSQLConfiguration(&pod, effectivePatroniConfig, requiredPatroniConfig, effectivePgParameters, requiredPgParameters) - if err != nil { - errorMsg = fmt.Sprintf("could not set PostgreSQL configuration options for pod %s: %v", podName, err) - continue - } - - // Patroni's config endpoint is just a "proxy" to DCS. It is enough to patch it only once and it doesn't matter which pod is used - return configPatched, nil - } - - return false, fmt.Errorf(errorMsg) -} - func (c *Cluster) syncPublication(publication, dbName string, tables map[string]acidv1.StreamTable) error { createPublications := make(map[string]string) alterPublications := make(map[string]string) @@ -273,7 +241,6 @@ func (c *Cluster) getStreamConnection(database, user, appId string) zalandov1.Co } func (c *Cluster) syncStreams() error { - c.setProcessName("syncing streams") _, err := c.KubeClient.CustomResourceDefinitions().Get(context.TODO(), constants.EventStreamCRDName, metav1.GetOptions{}) @@ -282,20 +249,10 @@ func (c *Cluster) syncStreams() error { return nil } - // update config to set wal_level: logical - requiredPatroniConfig := c.Spec.Patroni - requiresRestart, err := c.syncPostgresConfig(requiredPatroniConfig) - if err != nil { - return fmt.Errorf("failed to snyc Postgres config for event streaming: %v", err) - } - if requiresRestart { - c.logger.Debugf("updated Postgres config. Server will be restarted and streams will get created during next sync") - return nil - } - slots := make(map[string]map[string]string) slotsToSync := make(map[string]map[string]string) publications := make(map[string]map[string]acidv1.StreamTable) + requiredPatroniConfig := c.Spec.Patroni if len(requiredPatroniConfig.Slots) > 0 { slots = requiredPatroniConfig.Slots @@ -343,13 +300,19 @@ func (c *Cluster) syncStreams() error { return nil } - // add extra logical slots to Patroni config - _, err = c.syncPostgresConfig(requiredPatroniConfig) + c.logger.Debug("syncing logical replication slots") + pods, err := c.listPods() if err != nil { - return fmt.Errorf("failed to snyc Postgres config for event streaming: %v", err) + return fmt.Errorf("could not get list of pods to sync logical replication slots via Patroni API: %v", err) } - // after Postgres was restarted we can create stream CRDs + // sync logical replication slots in Patroni config + configPatched, _, _, err := c.syncPatroniConfig(pods, requiredPatroniConfig, nil) + if err != nil { + c.logger.Warningf("Patroni config updated? %v - errors during config sync: %v", configPatched, err) + } + + // finally sync stream CRDs err = c.createOrUpdateStreams() if err != nil { return err diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index 797a07256..7fc97d9ea 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -395,69 +395,30 @@ func (c *Cluster) syncStatefulSet() error { } } - // Apply special PostgreSQL parameters that can only be set via the Patroni API. + // apply PostgreSQL parameters that can only be set via the Patroni API. // it is important to do it after the statefulset pods are there, but before the rolling update // since those parameters require PostgreSQL restart. pods, err = c.listPods() if err != nil { - c.logger.Warnf("could not get list of pods to apply special PostgreSQL parameters only to be set via Patroni API: %v", err) + c.logger.Warnf("could not get list of pods to apply PostgreSQL parameters only to be set via Patroni API: %v", err) } - // get Postgres config, compare with manifest and update via Patroni PATCH endpoint if it differs - // Patroni's config endpoint is just a "proxy" to DCS. It is enough to patch it only once and it doesn't matter which pod is used - for i, pod := range pods { - patroniConfig, pgParameters, err := c.getPatroniConfig(&pod) - if err != nil { - c.logger.Warningf("%v", err) - isSafeToRecreatePods = false - continue - } - restartWait = patroniConfig.LoopWait - - // empty config probably means cluster is not fully initialized yet, e.g. restoring from backup - // do not attempt a restart - if !reflect.DeepEqual(patroniConfig, acidv1.Patroni{}) || len(pgParameters) > 0 { - // compare config returned from Patroni with what is specified in the manifest - configPatched, restartPrimaryFirst, err = c.checkAndSetGlobalPostgreSQLConfiguration(&pod, patroniConfig, c.Spec.Patroni, pgParameters, c.Spec.Parameters) - if err != nil { - c.logger.Warningf("could not set PostgreSQL configuration options for pod %s: %v", pods[i].Name, err) - continue - } - - // it could take up to LoopWait to apply the config - if configPatched { - time.Sleep(time.Duration(restartWait)*time.Second + time.Second*2) - break - } - } + requiredPgParameters := c.Spec.Parameters + // if streams are defined wal_level must be switched to logical + if len(c.Spec.Streams) > 0 { + requiredPgParameters["wal_level"] = "logical" } - // restart instances if it is still pending - remainingPods := make([]*v1.Pod, 0) - skipRole := Master - if restartPrimaryFirst { - skipRole = Replica - } - for i, pod := range pods { - role := PostgresRole(pod.Labels[c.OpConfig.PodRoleLabel]) - if role == skipRole { - remainingPods = append(remainingPods, &pods[i]) - continue - } - if err = c.restartInstance(&pod, restartWait); err != nil { - c.logger.Errorf("%v", err) - isSafeToRecreatePods = false - } + // sync Patroni config + if configPatched, restartPrimaryFirst, restartWait, err = c.syncPatroniConfig(pods, c.Spec.Patroni, requiredPgParameters); err != nil { + c.logger.Warningf("Patroni config updated? %v - errors during config sync: %v", configPatched, err) + isSafeToRecreatePods = false } - // in most cases only the master should be left to restart - if len(remainingPods) > 0 { - for _, remainingPod := range remainingPods { - if err = c.restartInstance(remainingPod, restartWait); err != nil { - c.logger.Errorf("%v", err) - isSafeToRecreatePods = false - } - } + // restart Postgres where it is still pending + if err = c.restartInstances(pods, restartWait, restartPrimaryFirst); err != nil { + c.logger.Errorf("errors while restarting Postgres in pods via Patroni API: %v", err) + isSafeToRecreatePods = false } // if we get here we also need to re-create the pods (either leftovers from the old @@ -471,13 +432,98 @@ func (c *Cluster) syncStatefulSet() error { } c.eventRecorder.Event(c.GetReference(), v1.EventTypeNormal, "Update", "Rolling update done - pods have been recreated") } else { - c.logger.Warningf("postpone pod recreation until next sync") + c.logger.Warningf("postpone pod recreation until next sync because of errors during config sync") } } return nil } +func (c *Cluster) syncPatroniConfig(pods []v1.Pod, requiredPatroniConfig acidv1.Patroni, requiredPgParameters map[string]string) (bool, bool, uint32, error) { + var ( + effectivePatroniConfig acidv1.Patroni + effectivePgParameters map[string]string + loopWait uint32 + configPatched bool + restartPrimaryFirst bool + err error + ) + + errors := make([]string, 0) + + // get Postgres config, compare with manifest and update via Patroni PATCH endpoint if it differs + for i, pod := range pods { + podName := util.NameFromMeta(pods[i].ObjectMeta) + effectivePatroniConfig, effectivePgParameters, err = c.patroni.GetConfig(&pod) + if err != nil { + errors = append(errors, fmt.Sprintf("could not get Postgres config from pod %s: %v", podName, err)) + continue + } + loopWait = effectivePatroniConfig.LoopWait + + // empty config probably means cluster is not fully initialized yet, e.g. restoring from backup + if reflect.DeepEqual(effectivePatroniConfig, acidv1.Patroni{}) || len(effectivePgParameters) == 0 { + errors = append(errors, fmt.Sprintf("empty Patroni config on pod %s - skipping config patch", podName)) + } else { + configPatched, restartPrimaryFirst, err = c.checkAndSetGlobalPostgreSQLConfiguration(&pod, effectivePatroniConfig, requiredPatroniConfig, effectivePgParameters, requiredPgParameters) + if err != nil { + errors = append(errors, fmt.Sprintf("could not set PostgreSQL configuration options for pod %s: %v", podName, err)) + continue + } + + // it could take up to LoopWait to apply the config + if configPatched { + time.Sleep(time.Duration(loopWait)*time.Second + time.Second*2) + // Patroni's config endpoint is just a "proxy" to DCS. + // It is enough to patch it only once and it doesn't matter which pod is used + break + } + } + } + + if len(errors) > 0 { + err = fmt.Errorf("%v", strings.Join(errors, `', '`)) + } + + return configPatched, restartPrimaryFirst, loopWait, err +} + +func (c *Cluster) restartInstances(pods []v1.Pod, restartWait uint32, restartPrimaryFirst bool) (err error) { + errors := make([]string, 0) + remainingPods := make([]*v1.Pod, 0) + + skipRole := Master + if restartPrimaryFirst { + skipRole = Replica + } + + for i, pod := range pods { + role := PostgresRole(pod.Labels[c.OpConfig.PodRoleLabel]) + if role == skipRole { + remainingPods = append(remainingPods, &pods[i]) + continue + } + if err = c.restartInstance(&pod, restartWait); err != nil { + errors = append(errors, fmt.Sprintf("%v", err)) + } + } + + // in most cases only the master should be left to restart + if len(remainingPods) > 0 { + for _, remainingPod := range remainingPods { + if err = c.restartInstance(remainingPod, restartWait); err != nil { + errors = append(errors, fmt.Sprintf("%v", err)) + } + } + } + + if len(errors) > 0 { + return fmt.Errorf("%v", strings.Join(errors, `', '`)) + } + + return nil +} + func (c *Cluster) restartInstance(pod *v1.Pod, restartWait uint32) error { // if the config update requires a restart, call Patroni restart podName := util.NameFromMeta(pod.ObjectMeta) From 7887ebbbcee853a7d6e1bb5360c63d818179c397 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Thu, 26 Jan 2023 09:43:03 +0100 Subject: [PATCH 73/77] set wal_level config not on empty parameters map (#2189) * set wal_level config not on empty parameters map * UPDATE event must trigger statefulSet sync when streams are added --- pkg/cluster/cluster.go | 9 ++++++++- pkg/cluster/sync.go | 5 ++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index 723fa4171..590fe6564 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -836,6 +836,11 @@ func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error { c.logger.Infof("Storage resize is disabled (storage_resize_mode is off). Skipping volume sync.") } + // streams configuration + if len(oldSpec.Spec.Streams) == 0 && len(newSpec.Spec.Streams) > 0 { + syncStatefulSet = true + } + // Statefulset func() { oldSs, err := c.generateStatefulSet(&oldSpec.Spec) @@ -851,6 +856,7 @@ func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error { updateFailed = true return } + if syncStatefulSet || !reflect.DeepEqual(oldSs, newSs) { c.logger.Debugf("syncing statefulsets") syncStatefulSet = false @@ -942,6 +948,7 @@ func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error { updateFailed = true } + // streams if len(newSpec.Spec.Streams) > 0 { if err := c.syncStreams(); err != nil { c.logger.Errorf("could not sync streams: %v", err) @@ -1034,7 +1041,7 @@ func (c *Cluster) Delete() { } -//NeedsRepair returns true if the cluster should be included in the repair scan (based on its in-memory status). +// NeedsRepair returns true if the cluster should be included in the repair scan (based on its in-memory status). func (c *Cluster) NeedsRepair() (bool, acidv1.PostgresStatus) { c.specMu.RLock() defer c.specMu.RUnlock() diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index 7fc97d9ea..477cbdc75 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -403,7 +403,10 @@ func (c *Cluster) syncStatefulSet() error { c.logger.Warnf("could not get list of pods to apply PostgreSQL parameters only to be set via Patroni API: %v", err) } - requiredPgParameters := c.Spec.Parameters + requiredPgParameters := make(map[string]string) + for k, v := range c.Spec.Parameters { + requiredPgParameters[k] = v + } // if streams are defined wal_level must be switched to logical if len(c.Spec.Streams) > 0 { requiredPgParameters["wal_level"] = "logical" From c9cada66c7daa1fea5f564834fbdbb5be4a3ceb1 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Fri, 27 Jan 2023 12:07:48 +0100 Subject: [PATCH 74/77] add pooler suffix to DNS annotation of pooler LoadBalancer service (#2188) * add pooler suffix to DNS annotation of pooler LoadBalancer service * need generatePoolerServiceAnnotations function --- docs/administrator.md | 3 ++ e2e/tests/test_e2e.py | 14 ++++++ manifests/complete-postgres-manifest.yaml | 2 + pkg/cluster/connection_pooler.go | 61 +++++++++++++++++------ pkg/cluster/k8sres.go | 38 ++++++++------ pkg/cluster/util.go | 12 ++--- pkg/util/constants/pooler.go | 1 + 7 files changed, 93 insertions(+), 38 deletions(-) diff --git a/docs/administrator.md b/docs/administrator.md index 19cfe2109..57c2c4574 100644 --- a/docs/administrator.md +++ b/docs/administrator.md @@ -810,6 +810,9 @@ Load balancer services can also be enabled for the [connection pooler](user.md#c pods with manifest flags `enableMasterPoolerLoadBalancer` and/or `enableReplicaPoolerLoadBalancer` or in the operator configuration with `enable_master_pooler_load_balancer` and/or `enable_replica_pooler_load_balancer`. +For the `external-dns.alpha.kubernetes.io/hostname` annotation the `-pooler` +suffix will be appended to the cluster name used in the template which is +defined in `master|replica_dns_name_format`. ## Running periodic 'autorepair' scans of K8s objects diff --git a/e2e/tests/test_e2e.py b/e2e/tests/test_e2e.py index 8b087b909..d28cd6241 100644 --- a/e2e/tests/test_e2e.py +++ b/e2e/tests/test_e2e.py @@ -670,6 +670,20 @@ class EndToEndTestCase(unittest.TestCase): 'LoadBalancer', "Expected LoadBalancer service type for replica pooler pod, found {}") + master_annotations = { + "external-dns.alpha.kubernetes.io/hostname": "acid-minimal-cluster-pooler.default.db.example.com", + "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", + } + self.eventuallyTrue(lambda: k8s.check_service_annotations( + master_pooler_label+","+pooler_label, master_annotations), "Wrong annotations") + + replica_annotations = { + "external-dns.alpha.kubernetes.io/hostname": "acid-minimal-cluster-pooler-repl.default.db.example.com", + "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", + } + self.eventuallyTrue(lambda: k8s.check_service_annotations( + replica_pooler_label+","+pooler_label, replica_annotations), "Wrong annotations") + # Turn off only master connection pooler k8s.api.custom_objects_api.patch_namespaced_custom_object( 'acid.zalan.do', 'v1', 'default', diff --git a/manifests/complete-postgres-manifest.yaml b/manifests/complete-postgres-manifest.yaml index 8d4353bcb..8d197a75d 100644 --- a/manifests/complete-postgres-manifest.yaml +++ b/manifests/complete-postgres-manifest.yaml @@ -28,6 +28,8 @@ spec: 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: diff --git a/pkg/cluster/connection_pooler.go b/pkg/cluster/connection_pooler.go index d7fcbbb38..761644e13 100644 --- a/pkg/cluster/connection_pooler.go +++ b/pkg/cluster/connection_pooler.go @@ -45,7 +45,7 @@ type ConnectionPoolerObjects struct { } func (c *Cluster) connectionPoolerName(role PostgresRole) string { - name := c.Name + "-pooler" + name := fmt.Sprintf("%s-%s", c.Name, constants.ConnectionPoolerResourceSuffix) if role == Replica { name = fmt.Sprintf("%s-%s", name, "repl") } @@ -163,24 +163,27 @@ func (c *Cluster) createConnectionPooler(LookupFunction InstallFunction) (SyncRe return reason, nil } -// // Generate pool size related environment variables. // // MAX_DB_CONN would specify the global maximum for connections to a target -// database. +// +// database. // // MAX_CLIENT_CONN is not configurable at the moment, just set it high enough. // // DEFAULT_SIZE is a pool size per db/user (having in mind the use case when -// most of the queries coming through a connection pooler are from the same -// user to the same db). In case if we want to spin up more connection pooler -// instances, take this into account and maintain the same number of -// connections. +// +// most of the queries coming through a connection pooler are from the same +// user to the same db). In case if we want to spin up more connection pooler +// instances, take this into account and maintain the same number of +// connections. // // MIN_SIZE is a pool's minimal size, to prevent situation when sudden workload -// have to wait for spinning up a new connections. +// +// have to wait for spinning up a new connections. // // RESERVE_SIZE is how many additional connections to allow for a pooler. + func (c *Cluster) getConnectionPoolerEnvVars() []v1.EnvVar { spec := &c.Spec connectionPoolerSpec := spec.ConnectionPooler @@ -475,23 +478,23 @@ func (c *Cluster) generateConnectionPoolerDeployment(connectionPooler *Connectio } func (c *Cluster) generateConnectionPoolerService(connectionPooler *ConnectionPoolerObjects) *v1.Service { - spec := &c.Spec + poolerRole := connectionPooler.Role serviceSpec := v1.ServiceSpec{ Ports: []v1.ServicePort{ { Name: connectionPooler.Name, Port: pgPort, - TargetPort: intstr.IntOrString{IntVal: c.servicePort(connectionPooler.Role)}, + TargetPort: intstr.IntOrString{IntVal: c.servicePort(poolerRole)}, }, }, Type: v1.ServiceTypeClusterIP, Selector: map[string]string{ - "connection-pooler": c.connectionPoolerName(connectionPooler.Role), + "connection-pooler": c.connectionPoolerName(poolerRole), }, } - if c.shouldCreateLoadBalancerForPoolerService(connectionPooler.Role, spec) { + if c.shouldCreateLoadBalancerForPoolerService(poolerRole, spec) { c.configureLoadBalanceService(&serviceSpec, spec.AllowedSourceRanges) } @@ -500,7 +503,7 @@ func (c *Cluster) generateConnectionPoolerService(connectionPooler *ConnectionPo Name: connectionPooler.Name, Namespace: connectionPooler.Namespace, Labels: c.connectionPoolerLabels(connectionPooler.Role, false).MatchLabels, - Annotations: c.annotationsSet(c.generateServiceAnnotations(connectionPooler.Role, spec)), + Annotations: c.annotationsSet(c.generatePoolerServiceAnnotations(poolerRole, spec)), // make StatefulSet object its owner to represent the dependency. // By itself StatefulSet is being deleted with "Orphaned" // propagation policy, which means that it's deletion will not @@ -515,6 +518,32 @@ func (c *Cluster) generateConnectionPoolerService(connectionPooler *ConnectionPo return service } +func (c *Cluster) generatePoolerServiceAnnotations(role PostgresRole, spec *acidv1.PostgresSpec) map[string]string { + var dnsString string + annotations := c.getCustomServiceAnnotations(role, spec) + + if c.shouldCreateLoadBalancerForPoolerService(role, spec) { + // set ELB Timeout annotation with default value + if _, ok := annotations[constants.ElbTimeoutAnnotationName]; !ok { + annotations[constants.ElbTimeoutAnnotationName] = constants.ElbTimeoutAnnotationValue + } + // -repl suffix will be added by replicaDNSName + clusterNameWithPoolerSuffix := c.connectionPoolerName(Master) + if role == Master { + dnsString = c.masterDNSName(clusterNameWithPoolerSuffix) + } else { + dnsString = c.replicaDNSName(clusterNameWithPoolerSuffix) + } + annotations[constants.ZalandoDNSNameAnnotation] = dnsString + } + + if len(annotations) == 0 { + return nil + } + + return annotations +} + func (c *Cluster) shouldCreateLoadBalancerForPoolerService(role PostgresRole, spec *acidv1.PostgresSpec) bool { switch role { @@ -546,7 +575,7 @@ func (c *Cluster) listPoolerPods(listOptions metav1.ListOptions) ([]v1.Pod, erro return pods.Items, nil } -//delete connection pooler +// delete connection pooler func (c *Cluster) deleteConnectionPooler(role PostgresRole) (err error) { c.logger.Infof("deleting connection pooler spilo-role=%s", role) @@ -605,7 +634,7 @@ func (c *Cluster) deleteConnectionPooler(role PostgresRole) (err error) { return nil } -//delete connection pooler +// delete connection pooler func (c *Cluster) deleteConnectionPoolerSecret() (err error) { // Repeat the same for the secret object secretName := c.credentialSecretName(c.OpConfig.ConnectionPooler.User) @@ -654,7 +683,7 @@ func updateConnectionPoolerDeployment(KubeClient k8sutil.KubernetesClient, newDe return deployment, nil } -//updateConnectionPoolerAnnotations updates the annotations of connection pooler deployment +// updateConnectionPoolerAnnotations updates the annotations of connection pooler deployment func updateConnectionPoolerAnnotations(KubeClient k8sutil.KubernetesClient, deployment *appsv1.Deployment, annotations map[string]string) (*appsv1.Deployment, error) { patchData, err := metaAnnotationsPatch(annotations) if err != nil { diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index 2e317c1a2..c652608a4 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -1901,6 +1901,28 @@ func (c *Cluster) configureLoadBalanceService(serviceSpec *v1.ServiceSpec, sourc } func (c *Cluster) generateServiceAnnotations(role PostgresRole, spec *acidv1.PostgresSpec) map[string]string { + annotations := c.getCustomServiceAnnotations(role, spec) + + if c.shouldCreateLoadBalancerForService(role, spec) { + dnsName := c.dnsName(role) + + // Just set ELB Timeout annotation with default value, if it does not + // have a custom value + if _, ok := annotations[constants.ElbTimeoutAnnotationName]; !ok { + annotations[constants.ElbTimeoutAnnotationName] = constants.ElbTimeoutAnnotationValue + } + // External DNS name annotation is not customizable + annotations[constants.ZalandoDNSNameAnnotation] = dnsName + } + + if len(annotations) == 0 { + return nil + } + + return annotations +} + +func (c *Cluster) getCustomServiceAnnotations(role PostgresRole, spec *acidv1.PostgresSpec) map[string]string { annotations := make(map[string]string) maps.Copy(annotations, c.OpConfig.CustomServiceAnnotations) @@ -1915,22 +1937,6 @@ func (c *Cluster) generateServiceAnnotations(role PostgresRole, spec *acidv1.Pos } } - if c.shouldCreateLoadBalancerForService(role, spec) { - dnsName := c.dnsName(role) - - // Just set ELB Timeout annotation with default value, if it does not - // have a cutom value - if _, ok := annotations[constants.ElbTimeoutAnnotationName]; !ok { - annotations[constants.ElbTimeoutAnnotationName] = constants.ElbTimeoutAnnotationValue - } - // External DNS name annotation is not customizable - annotations[constants.ZalandoDNSNameAnnotation] = dnsName - } - - if len(annotations) == 0 { - return nil - } - return annotations } diff --git a/pkg/cluster/util.go b/pkg/cluster/util.go index 079567421..401e43155 100644 --- a/pkg/cluster/util.go +++ b/pkg/cluster/util.go @@ -509,9 +509,9 @@ func (c *Cluster) dnsName(role PostgresRole) string { var dnsString, oldDnsString string if role == Master { - dnsString = c.masterDNSName() + dnsString = c.masterDNSName(c.Name) } else { - dnsString = c.replicaDNSName() + dnsString = c.replicaDNSName(c.Name) } // if cluster name starts with teamID we might need to provide backwards compatibility @@ -528,17 +528,17 @@ func (c *Cluster) dnsName(role PostgresRole) string { return dnsString } -func (c *Cluster) masterDNSName() string { +func (c *Cluster) masterDNSName(clusterName string) string { return strings.ToLower(c.OpConfig.MasterDNSNameFormat.Format( - "cluster", c.Name, + "cluster", clusterName, "namespace", c.Namespace, "team", c.teamName(), "hostedzone", c.OpConfig.DbHostedZone)) } -func (c *Cluster) replicaDNSName() string { +func (c *Cluster) replicaDNSName(clusterName string) string { return strings.ToLower(c.OpConfig.ReplicaDNSNameFormat.Format( - "cluster", c.Name, + "cluster", clusterName, "namespace", c.Namespace, "team", c.teamName(), "hostedzone", c.OpConfig.DbHostedZone)) diff --git a/pkg/util/constants/pooler.go b/pkg/util/constants/pooler.go index ded795bbe..14c137e74 100644 --- a/pkg/util/constants/pooler.go +++ b/pkg/util/constants/pooler.go @@ -2,6 +2,7 @@ package constants // Connection pooler specific constants const ( + ConnectionPoolerResourceSuffix = "pooler" ConnectionPoolerUserName = "pooler" ConnectionPoolerSchemaName = "pooler" ConnectionPoolerDefaultType = "pgbouncer" From d5251c5fc848e425258fe298655fc27cbd4035d0 Mon Sep 17 00:00:00 2001 From: idanovinda Date: Fri, 27 Jan 2023 12:47:30 +0100 Subject: [PATCH 75/77] Update UI library dependencies (#2191) * Update UI library dependencies * change to node v14 * update js-yaml load and dump function --- ui/Makefile | 4 ++-- ui/app/package.json | 37 ++++++++++++++--------------- ui/app/src/edit.tag.pug | 10 ++++---- ui/app/src/new.tag.pug | 2 +- ui/app/src/postgresql.tag.pug | 2 +- ui/operator_ui/cluster_discovery.py | 2 +- ui/requirements.txt | 16 ++++++------- 7 files changed, 36 insertions(+), 37 deletions(-) diff --git a/ui/Makefile b/ui/Makefile index 29c8d9409..7d86b2df6 100644 --- a/ui/Makefile +++ b/ui/Makefile @@ -21,8 +21,8 @@ test: tox appjs: - docker run $(TTYFLAGS) -u $$(id -u) -v $$(pwd):/workdir -w /workdir/app node:10.1.0-alpine npm install - docker run $(TTYFLAGS) -u $$(id -u) -v $$(pwd):/workdir -w /workdir/app node:10.1.0-alpine npm run build + docker run $(TTYFLAGS) -u $$(id -u) -v $$(pwd):/workdir -w /workdir/app node:14.21.2-alpine npm install + docker run $(TTYFLAGS) -u $$(id -u) -v $$(pwd):/workdir -w /workdir/app node:14.21.2-alpine npm run build docker: appjs echo `(env)` diff --git a/ui/app/package.json b/ui/app/package.json index c0e497f0b..d4542ff68 100644 --- a/ui/app/package.json +++ b/ui/app/package.json @@ -26,29 +26,28 @@ }, "homepage": "https://github.com/zalando/postgres-operator.git#readme", "dependencies": { - "@babel/core": "^7.0.0-beta.46", - "@babel/polyfill": "^7.0.0-beta.46", - "@babel/runtime": "^7.0.0-beta.46", - "pixi.js": "^4.7.3" + "@babel/core": "^7.20.12", + "@babel/polyfill": "^7.12.1", + "@babel/runtime": "^7.20.13", + "pixi.js": "^7.1.1" }, "devDependencies": { - "@babel/plugin-transform-runtime": "^7.0.0-beta.46", - "@babel/preset-env": "^7.0.0-beta.46", - "babel-loader": "^8.0.0-beta.2", - "brfs": "^1.6.1", + "@babel/plugin-transform-runtime": "^7.19.6", + "@babel/preset-env": "^7.20.2", + "babel-loader": "^8.2.5", + "brfs": "^2.0.2", "dedent-js": "1.0.1", - "eslint": "^4.19.1", - "eslint-loader": "^1.6.1", - "js-yaml": "3.13.1", - "pug": "^2.0.3", - "rimraf": "^2.5.4", - "riot": "^3.9.5", + "eslint": "^8.32.0", + "js-yaml": "4.1.0", + "pug": "^3.0.2", + "rimraf": "^4.1.2", + "riot": "^3.13.2", "riot-hot-reload": "1.0.0", - "riot-route": "^3.1.3", - "riot-tag-loader": "2.0.2", + "riot-route": "^3.1.4", + "riot-tag-loader": "2.1.0", "sort-by": "^1.2.0", - "transform-loader": "^0.2.3", - "webpack": "^4.28.2", - "webpack-cli": "^3.1.2" + "transform-loader": "^0.2.4", + "webpack": "^4.46.0", + "webpack-cli": "^4.10.0" } } diff --git a/ui/app/src/edit.tag.pug b/ui/app/src/edit.tag.pug index 5b03c06da..d3064ab9f 100644 --- a/ui/app/src/edit.tag.pug +++ b/ui/app/src/edit.tag.pug @@ -71,8 +71,8 @@ edit this.updateEditable = e => { if (this.refs.changedProperties.value) { - this.editablePropertiesPreview = yamlParser.safeDump( - yamlParser.safeLoad( + this.editablePropertiesPreview = yamlParser.dump( + yamlParser.load( this.refs.changedProperties.value, ), ) @@ -85,7 +85,7 @@ edit this.saveMessage = '' jsonPayload = JSON.stringify( - yamlParser.safeLoad( + yamlParser.load( this.refs.changedProperties.value, ), ) @@ -129,7 +129,7 @@ edit if (i.metadata.managedFields) { delete i.metadata.managedFields } this.update() - this.refs.yamlNice.innerHTML = yamlParser.safeDump(i.postgresql, {sortKeys: true}) + this.refs.yamlNice.innerHTML = yamlParser.dump(i.postgresql, {sortKeys: true}) // Output data: const o = this.editableProperties = { spec: {} } @@ -192,7 +192,7 @@ edit this.editablePropertiesText = ( yamlParser - .safeDump(this.editableProperties) + .dump(this.editableProperties) .slice(0, -1) ) this.editablePropertiesPreview = this.editablePropertiesText diff --git a/ui/app/src/new.tag.pug b/ui/app/src/new.tag.pug index 5c21f93c7..d4ff3d311 100644 --- a/ui/app/src/new.tag.pug +++ b/ui/app/src/new.tag.pug @@ -793,7 +793,7 @@ new } this.requestCreate = e => { - jsonPayload = JSON.stringify(yamlParser.safeLoad(this.getYAML())) + jsonPayload = JSON.stringify(yamlParser.load(this.getYAML())) this.creating = true this.update() diff --git a/ui/app/src/postgresql.tag.pug b/ui/app/src/postgresql.tag.pug index e40315bb5..1091d32fa 100644 --- a/ui/app/src/postgresql.tag.pug +++ b/ui/app/src/postgresql.tag.pug @@ -220,7 +220,7 @@ postgresql delete manifest.status if (this.refs.yamlNice) { - this.refs.yamlNice.innerHTML = yamlParser.safeDump( + this.refs.yamlNice.innerHTML = yamlParser.dump( this.progress.postgresqlManifest, { sortKeys: true, diff --git a/ui/operator_ui/cluster_discovery.py b/ui/operator_ui/cluster_discovery.py index 9c89735ac..6bb211646 100644 --- a/ui/operator_ui/cluster_discovery.py +++ b/ui/operator_ui/cluster_discovery.py @@ -73,7 +73,7 @@ class StaticClusterDiscoverer: cluster = Cluster(generate_cluster_id(DEFAULT_CLUSTERS), DEFAULT_CLUSTERS) else: logger.info("in cluster configuration failed") - config = kubernetes.client.configuration + config = kubernetes.client.Configuration() cluster = Cluster( generate_cluster_id(config.host), config.host, diff --git a/ui/requirements.txt b/ui/requirements.txt index 7e33ea5ba..790bc6cdd 100644 --- a/ui/requirements.txt +++ b/ui/requirements.txt @@ -1,15 +1,15 @@ Flask-OAuthlib==0.9.6 -Flask==2.2.1 -backoff==2.1.2 -boto3==1.24.46 +Flask==2.2.2 +backoff==2.2.1 +boto3==1.26.51 boto==2.49.0 click==8.1.3 furl==2.1.3 -gevent==21.12.0 -jq==1.2.2 +gevent==22.10.2 +jq==1.4.0 json_delta>=2.0.2 -kubernetes==3.0.0 -requests==2.28.1 +kubernetes==11.0.0 +requests==2.28.2 stups-tokens>=1.1.19 wal_e==1.1.1 -werkzeug==2.2.1 +werkzeug==2.2.2 From 7a90fbcb0028391275260e0fbc088a703181d3c0 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Fri, 27 Jan 2023 18:03:37 +0100 Subject: [PATCH 76/77] fix sync of stream slots (#2194) --- pkg/cluster/sync.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index 477cbdc75..ecdda6998 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -647,7 +647,12 @@ func (c *Cluster) checkAndSetGlobalPostgreSQLConfiguration(pod *v1.Pod, effectiv } } slotsToSet[slotName] = desiredSlot - c.replicationSlots[slotName] = desiredSlot + // only add slots specified in manifest to c.replicationSlots + for manifestSlotName, _ := range c.Spec.Patroni.Slots { + if manifestSlotName == slotName { + c.replicationSlots[slotName] = desiredSlot + } + } } if len(slotsToSet) > 0 { configToSet["slots"] = slotsToSet From 30b612489a2a20d968262791857d1db1a85e0b36 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Mon, 30 Jan 2023 10:15:16 +0100 Subject: [PATCH 77/77] bump to v1.9.0 (#2177) * bump to v1.9.0 * some minor UI config updates * bump UI package.json to 1.9.0, too --- charts/postgres-operator-ui/Chart.yaml | 4 +- charts/postgres-operator-ui/index.yaml | 127 ++++-------------- .../postgres-operator-ui-1.6.0.tgz | Bin 3759 -> 0 bytes .../postgres-operator-ui-1.6.1.tgz | Bin 3877 -> 0 bytes .../postgres-operator-ui-1.6.2.tgz | Bin 4047 -> 0 bytes .../postgres-operator-ui-1.6.3.tgz | Bin 4050 -> 0 bytes .../postgres-operator-ui-1.9.0.tgz | Bin 0 -> 4926 bytes .../templates/deployment.yaml | 4 + charts/postgres-operator-ui/values.yaml | 2 +- charts/postgres-operator/Chart.yaml | 4 +- .../crds/operatorconfigurations.yaml | 2 +- charts/postgres-operator/index.yaml | 122 ++++------------- .../postgres-operator-1.6.0.tgz | Bin 18962 -> 0 bytes .../postgres-operator-1.6.1.tgz | Bin 19296 -> 0 bytes .../postgres-operator-1.6.2.tgz | Bin 19963 -> 0 bytes .../postgres-operator-1.6.3.tgz | Bin 20124 -> 0 bytes .../postgres-operator-1.9.0.tgz | Bin 0 -> 17544 bytes charts/postgres-operator/values.yaml | 2 +- docs/reference/operator_parameters.md | 2 +- manifests/configmap.yaml | 2 +- manifests/operatorconfiguration.crd.yaml | 2 +- manifests/postgres-operator.yaml | 2 +- ...gresql-operator-default-configuration.yaml | 2 +- pkg/controller/operator_config.go | 2 +- pkg/util/config/config.go | 2 +- ui/app/package.json | 2 +- ui/manifests/deployment.yaml | 2 +- ui/run_local.sh | 6 + 28 files changed, 83 insertions(+), 208 deletions(-) delete mode 100644 charts/postgres-operator-ui/postgres-operator-ui-1.6.0.tgz delete mode 100644 charts/postgres-operator-ui/postgres-operator-ui-1.6.1.tgz delete mode 100644 charts/postgres-operator-ui/postgres-operator-ui-1.6.2.tgz delete mode 100644 charts/postgres-operator-ui/postgres-operator-ui-1.6.3.tgz create mode 100644 charts/postgres-operator-ui/postgres-operator-ui-1.9.0.tgz delete mode 100644 charts/postgres-operator/postgres-operator-1.6.0.tgz delete mode 100644 charts/postgres-operator/postgres-operator-1.6.1.tgz delete mode 100644 charts/postgres-operator/postgres-operator-1.6.2.tgz delete mode 100644 charts/postgres-operator/postgres-operator-1.6.3.tgz create mode 100644 charts/postgres-operator/postgres-operator-1.9.0.tgz diff --git a/charts/postgres-operator-ui/Chart.yaml b/charts/postgres-operator-ui/Chart.yaml index 23ecad0c3..2c86208dc 100644 --- a/charts/postgres-operator-ui/Chart.yaml +++ b/charts/postgres-operator-ui/Chart.yaml @@ -1,7 +1,7 @@ apiVersion: v2 name: postgres-operator-ui -version: 1.8.2 -appVersion: 1.8.2 +version: 1.9.0 +appVersion: 1.9.0 home: https://github.com/zalando/postgres-operator description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience keywords: diff --git a/charts/postgres-operator-ui/index.yaml b/charts/postgres-operator-ui/index.yaml index df6018f9c..2da263625 100644 --- a/charts/postgres-operator-ui/index.yaml +++ b/charts/postgres-operator-ui/index.yaml @@ -1,9 +1,32 @@ apiVersion: v1 entries: postgres-operator-ui: + - apiVersion: v2 + appVersion: 1.9.0 + created: "2023-01-17T15:45:57.564334046+01:00" + description: Postgres Operator UI provides a graphical interface for a convenient + database-as-a-service user experience + digest: df434af6c8b697fe0631017ecc25e3c79e125361ae6622347cea41a545153bdc + home: https://github.com/zalando/postgres-operator + keywords: + - postgres + - operator + - ui + - cloud-native + - patroni + - spilo + maintainers: + - email: opensource@zalando.de + name: Zalando + name: postgres-operator-ui + sources: + - https://github.com/zalando/postgres-operator + urls: + - postgres-operator-ui-1.9.0.tgz + version: 1.9.0 - apiVersion: v2 appVersion: 1.8.2 - created: "2022-06-20T11:58:48.148537324+02:00" + created: "2023-01-17T15:45:57.562574292+01:00" description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience digest: fbfc90fa8fd007a08a7c02e0ec9108bb8282cbb42b8c976d88f2193d6edff30c @@ -26,7 +49,7 @@ entries: version: 1.8.2 - apiVersion: v2 appVersion: 1.8.1 - created: "2022-06-20T11:58:48.147974157+02:00" + created: "2023-01-17T15:45:57.561981294+01:00" description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience digest: d26342e385ea51a0fbfbe23477999863e9489664ae803ea5c56da8897db84d24 @@ -49,7 +72,7 @@ entries: version: 1.8.1 - apiVersion: v1 appVersion: 1.8.0 - created: "2022-06-20T11:58:48.147454782+02:00" + created: "2023-01-17T15:45:57.561383172+01:00" description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience digest: d4a7b40c23fd167841cc28342afdbd5ecc809181913a5c31061c83139187f148 @@ -72,7 +95,7 @@ entries: version: 1.8.0 - apiVersion: v1 appVersion: 1.7.1 - created: "2022-06-20T11:58:48.14693682+02:00" + created: "2023-01-17T15:45:57.560738084+01:00" description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience digest: 97aed1a1d37cd5f8441eea9522f38e56cc829786ad2134c437a5e6a15c995869 @@ -95,7 +118,7 @@ entries: version: 1.7.1 - apiVersion: v1 appVersion: 1.7.0 - created: "2022-06-20T11:58:48.146431264+02:00" + created: "2023-01-17T15:45:57.560150807+01:00" description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience digest: 37fba1968347daad393dbd1c6ee6e5b6a24d1095f972c0102197531c62dcada8 @@ -116,96 +139,4 @@ entries: urls: - postgres-operator-ui-1.7.0.tgz version: 1.7.0 - - apiVersion: v1 - appVersion: 1.6.3 - created: "2022-06-20T11:58:48.14552248+02:00" - description: Postgres Operator UI provides a graphical interface for a convenient - database-as-a-service user experience - digest: 08b810aa632dcc719e4785ef184e391267f7c460caa99677f2d00719075aac78 - home: https://github.com/zalando/postgres-operator - keywords: - - postgres - - operator - - ui - - cloud-native - - patroni - - spilo - maintainers: - - email: opensource@zalando.de - name: Zalando - name: postgres-operator-ui - sources: - - https://github.com/zalando/postgres-operator - urls: - - postgres-operator-ui-1.6.3.tgz - version: 1.6.3 - - apiVersion: v1 - appVersion: 1.6.2 - created: "2022-06-20T11:58:48.145033254+02:00" - description: Postgres Operator UI provides a graphical interface for a convenient - database-as-a-service user experience - digest: 14d1559bb0bd1e1e828f2daaaa6f6ac9ffc268d79824592c3589b55dd39241f6 - home: https://github.com/zalando/postgres-operator - keywords: - - postgres - - operator - - ui - - cloud-native - - patroni - - spilo - maintainers: - - email: opensource@zalando.de - name: Zalando - name: postgres-operator-ui - sources: - - https://github.com/zalando/postgres-operator - urls: - - postgres-operator-ui-1.6.2.tgz - version: 1.6.2 - - apiVersion: v1 - appVersion: 1.6.1 - created: "2022-06-20T11:58:48.144518247+02:00" - description: Postgres Operator UI provides a graphical interface for a convenient - database-as-a-service user experience - digest: 3d321352f2f1e7bb7450aa8876e3d818aa9f9da9bd4250507386f0490f2c1969 - home: https://github.com/zalando/postgres-operator - keywords: - - postgres - - operator - - ui - - cloud-native - - patroni - - spilo - maintainers: - - email: opensource@zalando.de - name: Zalando - name: postgres-operator-ui - sources: - - https://github.com/zalando/postgres-operator - urls: - - postgres-operator-ui-1.6.1.tgz - version: 1.6.1 - - apiVersion: v1 - appVersion: 1.6.0 - created: "2022-06-20T11:58:48.143943237+02:00" - description: Postgres Operator UI provides a graphical interface for a convenient - database-as-a-service user experience - digest: 1e0aa1e7db3c1daa96927ffbf6fdbcdb434562f961833cb5241ddbe132220ee4 - home: https://github.com/zalando/postgres-operator - keywords: - - postgres - - operator - - ui - - cloud-native - - patroni - - spilo - maintainers: - - email: opensource@zalando.de - name: Zalando - name: postgres-operator-ui - sources: - - https://github.com/zalando/postgres-operator - urls: - - postgres-operator-ui-1.6.0.tgz - version: 1.6.0 -generated: "2022-06-20T11:58:48.143164875+02:00" +generated: "2023-01-17T15:45:57.558968127+01:00" diff --git a/charts/postgres-operator-ui/postgres-operator-ui-1.6.0.tgz b/charts/postgres-operator-ui/postgres-operator-ui-1.6.0.tgz deleted file mode 100644 index 7d3b2b738d4795a6848a59cd76fb93cf69c95fe0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3759 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 Z?0s!L+?H+GmTh_L@;|^d=G_2P003JjF_-`V diff --git a/charts/postgres-operator-ui/postgres-operator-ui-1.6.1.tgz b/charts/postgres-operator-ui/postgres-operator-ui-1.6.1.tgz deleted file mode 100644 index c59d20b2f1fe34400f2b4b338a9096373a1d70ea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3877 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 diff --git a/charts/postgres-operator-ui/postgres-operator-ui-1.6.2.tgz b/charts/postgres-operator-ui/postgres-operator-ui-1.6.2.tgz deleted file mode 100644 index 2e5298164ecfbc25a5e9f16b227163df122cf264..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4047 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 B{38GW diff --git a/charts/postgres-operator-ui/postgres-operator-ui-1.6.3.tgz b/charts/postgres-operator-ui/postgres-operator-ui-1.6.3.tgz deleted file mode 100644 index 45e3d00b2cc022508d5560eec44d630897b98d4b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4050 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 E0Gd|!Gynhq diff --git a/charts/postgres-operator-ui/postgres-operator-ui-1.9.0.tgz b/charts/postgres-operator-ui/postgres-operator-ui-1.9.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..7c04e368896a3e0c77a3822d7a2a286d35e9320e GIT binary patch literal 4926 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 literal 0 HcmV?d00001 diff --git a/charts/postgres-operator-ui/templates/deployment.yaml b/charts/postgres-operator-ui/templates/deployment.yaml index c007d4ed4..23eb750a7 100644 --- a/charts/postgres-operator-ui/templates/deployment.yaml +++ b/charts/postgres-operator-ui/templates/deployment.yaml @@ -79,6 +79,10 @@ spec: "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", diff --git a/charts/postgres-operator-ui/values.yaml b/charts/postgres-operator-ui/values.yaml index 0c3f57796..31b925c73 100644 --- a/charts/postgres-operator-ui/values.yaml +++ b/charts/postgres-operator-ui/values.yaml @@ -8,7 +8,7 @@ replicaCount: 1 image: registry: registry.opensource.zalan.do repository: acid/postgres-operator-ui - tag: v1.8.2 + tag: v1.9.0 pullPolicy: "IfNotPresent" # Optionally specify an array of imagePullSecrets. diff --git a/charts/postgres-operator/Chart.yaml b/charts/postgres-operator/Chart.yaml index 96dd679fb..7ab3e394d 100644 --- a/charts/postgres-operator/Chart.yaml +++ b/charts/postgres-operator/Chart.yaml @@ -1,7 +1,7 @@ apiVersion: v2 name: postgres-operator -version: 1.8.2 -appVersion: 1.8.2 +version: 1.9.0 +appVersion: 1.9.0 home: https://github.com/zalando/postgres-operator description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes keywords: diff --git a/charts/postgres-operator/crds/operatorconfigurations.yaml b/charts/postgres-operator/crds/operatorconfigurations.yaml index 0e2353d35..e01a5f997 100644 --- a/charts/postgres-operator/crds/operatorconfigurations.yaml +++ b/charts/postgres-operator/crds/operatorconfigurations.yaml @@ -489,7 +489,7 @@ spec: pattern: '^(\d+m|\d+(\.\d{1,3})?)$' logical_backup_docker_image: type: string - default: "registry.opensource.zalan.do/acid/logical-backup:v1.8.2" + default: "registry.opensource.zalan.do/acid/logical-backup:v1.9.0" logical_backup_google_application_credentials: type: string logical_backup_job_prefix: diff --git a/charts/postgres-operator/index.yaml b/charts/postgres-operator/index.yaml index ff025baa8..c42fc35d5 100644 --- a/charts/postgres-operator/index.yaml +++ b/charts/postgres-operator/index.yaml @@ -1,9 +1,31 @@ apiVersion: v1 entries: postgres-operator: + - apiVersion: v2 + appVersion: 1.9.0 + created: "2023-01-17T15:33:03.869287885+01:00" + description: Postgres Operator creates and manages PostgreSQL clusters running + in Kubernetes + digest: 64df90c898ca591eb3a330328173ffaadfbf9ddd474d8c42ed143edc9e3f4276 + home: https://github.com/zalando/postgres-operator + keywords: + - postgres + - operator + - cloud-native + - patroni + - spilo + maintainers: + - email: opensource@zalando.de + name: Zalando + name: postgres-operator + sources: + - https://github.com/zalando/postgres-operator + urls: + - postgres-operator-1.9.0.tgz + version: 1.9.0 - apiVersion: v2 appVersion: 1.8.2 - created: "2022-06-20T11:57:53.031245647+02:00" + created: "2023-01-17T15:33:03.86746187+01:00" description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes digest: f77ffad2e98b72a621e5527015cf607935d3ed688f10ba4b626435acb9631b5b @@ -25,7 +47,7 @@ entries: version: 1.8.2 - apiVersion: v2 appVersion: 1.8.1 - created: "2022-06-20T11:57:53.029722276+02:00" + created: "2023-01-17T15:33:03.865880826+01:00" description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes digest: ee0c3bb6ba72fa4289ba3b1c6060e5b312dd023faba2a61b4cb7d9e5e2cc57a5 @@ -47,7 +69,7 @@ entries: version: 1.8.1 - apiVersion: v1 appVersion: 1.8.0 - created: "2022-06-20T11:57:53.028188865+02:00" + created: "2023-01-17T15:33:03.8643608+01:00" description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes digest: 3ae232cf009e09aa2ad11c171484cd2f1b72e63c59735e58fbe2b6eb842f4c86 @@ -69,7 +91,7 @@ entries: version: 1.8.0 - apiVersion: v1 appVersion: 1.7.1 - created: "2022-06-20T11:57:53.026647776+02:00" + created: "2023-01-17T15:33:03.862914146+01:00" description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes digest: 7262563bec0b058e669ae6bcff0226e33fa9ece9c41ac46a53274046afe7700c @@ -91,7 +113,7 @@ entries: version: 1.7.1 - apiVersion: v1 appVersion: 1.7.0 - created: "2022-06-20T11:57:53.02514275+02:00" + created: "2023-01-17T15:33:03.861539439+01:00" description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes digest: c3e99fb94305f81484b8b1af18eefb78681f3b5d057d5ad10565e4afb7c65ffe @@ -111,92 +133,4 @@ entries: urls: - postgres-operator-1.7.0.tgz version: 1.7.0 - - apiVersion: v1 - appVersion: 1.6.3 - created: "2022-06-20T11:57:53.022692764+02:00" - description: Postgres Operator creates and manages PostgreSQL clusters running - in Kubernetes - digest: ea08f991bf23c9ad114bca98ebcbe3e2fa15beab163061399394905eaee89b35 - home: https://github.com/zalando/postgres-operator - keywords: - - postgres - - operator - - cloud-native - - patroni - - spilo - maintainers: - - email: opensource@zalando.de - name: Zalando - name: postgres-operator - sources: - - https://github.com/zalando/postgres-operator - urls: - - postgres-operator-1.6.3.tgz - version: 1.6.3 - - apiVersion: v1 - appVersion: 1.6.2 - created: "2022-06-20T11:57:53.021045272+02:00" - description: Postgres Operator creates and manages PostgreSQL clusters running - in Kubernetes - digest: d886f8a0879ca07d1e5246ee7bc55710e1c872f3977280fe495db6fc2057a7f4 - home: https://github.com/zalando/postgres-operator - keywords: - - postgres - - operator - - cloud-native - - patroni - - spilo - maintainers: - - email: opensource@zalando.de - name: Zalando - name: postgres-operator - sources: - - https://github.com/zalando/postgres-operator - urls: - - postgres-operator-1.6.2.tgz - version: 1.6.2 - - apiVersion: v1 - appVersion: 1.6.1 - created: "2022-06-20T11:57:53.019428631+02:00" - description: Postgres Operator creates and manages PostgreSQL clusters running - in Kubernetes - digest: 4ba5972cd486dcaa2d11c5613a6f97f6b7b831822e610fe9e10a57ea1db23556 - home: https://github.com/zalando/postgres-operator - keywords: - - postgres - - operator - - cloud-native - - patroni - - spilo - maintainers: - - email: opensource@zalando.de - name: Zalando - name: postgres-operator - sources: - - https://github.com/zalando/postgres-operator - urls: - - postgres-operator-1.6.1.tgz - version: 1.6.1 - - apiVersion: v1 - appVersion: 1.6.0 - created: "2022-06-20T11:57:53.017863057+02:00" - description: Postgres Operator creates and manages PostgreSQL clusters running - in Kubernetes - digest: f52149718ea364f46b4b9eec9a65f6253ad182bb78df541d14cd5277b9c8a8c3 - home: https://github.com/zalando/postgres-operator - keywords: - - postgres - - operator - - cloud-native - - patroni - - spilo - maintainers: - - email: opensource@zalando.de - name: Zalando - name: postgres-operator - sources: - - https://github.com/zalando/postgres-operator - urls: - - postgres-operator-1.6.0.tgz - version: 1.6.0 -generated: "2022-06-20T11:57:53.016179465+02:00" +generated: "2023-01-17T15:33:03.859917247+01:00" diff --git a/charts/postgres-operator/postgres-operator-1.6.0.tgz b/charts/postgres-operator/postgres-operator-1.6.0.tgz deleted file mode 100644 index d52c405eb2701f8824e5e59d74d9a7907969ea2a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18962 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�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

r^N@<{! zfmh2NX*ii+11Q5d^VAoPJhCdndEVF@@s)Z?Od^6yeO27tixtePlUFoY9*x->e|~i9 z__zQ>7bSpR9-8YX@q$zed$@2(UM@*TiuaGoLlK1Y34T_iz}P29UA$J5V5H9UXVA5_ z;W9O<1l1(ztG3uMHI5w+Ar_-_5tB#@IPpG&RDiL8WFp$(>?USmn+mJTU5eLjTKU2j z6|N&uTsDbZBMvfs6-=in)?|2!Y9>T$wB_%HQo;uqWi2W!|@i7BfD(OV&s;kPY8} zwC#h`No$Cyi1UvUZ%7t&8xH0!n)wr{51~*&*|CW<8Ii?YW}Lvert?iwbRZ6lWHti{ zMCUdY`yje#MMHI0cGb}aNpc~LNEAYl7!QD{m|*_!UJbQT;(eJQy?S^{isSO$ip6dG z6D5d5a(}trmFu2M-;WaU?d8!To$f>H?_kSELBwHP@Ed#fK*1xhp#vptDp*T_dc#sX zww8HP4@4tW`$#u{HNAHQ%;H2ZSMiFL>odk4%?9p}xvx5O*V3kj50XkgT2P3!29yY6 zT)3e3Vu0n6wgqgf#FKV7aj406Y*kCL`n-Jr)e~m-4WrA-IC>3fYcU8B6-}Qx4~}mb zEdo}5PT_mJ$7X~D zin64GcZI=(21{6M*h@+RC)uPNVO`t?4vaU8<@LuLo6vG9sT0!TCbTP{LP|FHlC3}i z@*k9z@v*IW2j0c%ddLKVr2Qd{Vhk+$NWUm->|;x_bZQ<6hdyvv0Np)-GJ=*O#GZSD>Ji(5)Vr+qTy#ev&b4@U73^7>TnJUd(s6FM+TsSP=1vsxPm;#jzgf~o{i|BUr94{@~(SdC?*XK0a_?>qHr2^i`OVfBG zkff`WEh*i}M1zV+qOwi3TwA|`;s2_o1RCBaogm2SB&d`tJFA!Fg4Ei)BAL7#%r(?I z*blGGz`R|`E6M3XHGqtfnD-aU?ZF;J3uW?XNlN0}l!S&mfBr3fSW)b=Va_#AR zt&guy@@6G6P=Of0jVagz(N9V`0A~#(E1f}P2vJ?lod>2d?{||t=70f~Xzl?NqWq_< zf8d;=y-LU5?qq3;Z7NrfeC6~~0=M~ygg-}HOU)+4p4XnV#Z3y_6Qu5EpLQKt5o;!Dpu+h;L~!)e4VqP7A79CFJi1FRl@@4{KYCIj2b$TU69sRY@y<;HCE71M!LCRr*BXL0EYwM;Di)-|by$YD+n%K9}B<0L6TY({StzygXUGa!Fy41@Lea45}kWkZVQp!sVjWr&lK z{}Z*)LT-?sbSF~0KSjhSo*B@}88SmH> z;{sSs!VvZ-j-M@A?*5)Y_mN$wDK)X-nzrFusHRnAzu>ST;Kr8le6p0Zl70&rVrw#@ zb0LKWRD}T*RFi$g8|C-jQkIGFfsf4A8W45&T zx3!1^4N)>Z7)SnC2}7~G3=w5b#W-J|UPe;2tALXr)g7JQ>>nI*eK~+C>$u+Man-QURsl%ebSYZ>uIP5fSNK0# zdy@5gTEoPFoi1SQsdGBGfVHnhk*iYn@x2ycu$1k%XiOmHM=Te#G$x7KrIO2!vb8`(=XiJ2Ao?vz`8j2()bo8OHGLWa*cZn)Xl&(0R;&epZHtZ%G zxjpD~#?Cv>qE8llYD;@?JGmXT^8sCwdsf}P4Knjk9&yK>$(#XrXDf1jrOcw%gn?UG za4Rql1Yyh5@#v4nzgr3rH0+=7@!E8Qh&3QoShvr5v}<7SCSP{JXz$$XD9VMCo6@)7a7Z83!JMR0fwN? z8P=YYe5}{44gsd~YbcFtX`OFDM1o=Kb{5M_EMN@mKpJcOB;QUepLZt1wKWj`mi=X& xozNwAQ%{HL{ky>{Gc}^flJcE@1YY^Mk48Q{PtViy^!!sl{~yG%rf2}10RVyt!WRGl diff --git a/charts/postgres-operator/postgres-operator-1.6.1.tgz b/charts/postgres-operator/postgres-operator-1.6.1.tgz deleted file mode 100644 index 48ffb90147ea471fa347a68f6060fd3077991073..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19296 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

>E-$F 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 diff --git a/charts/postgres-operator/postgres-operator-1.6.2.tgz b/charts/postgres-operator/postgres-operator-1.6.2.tgz deleted file mode 100644 index 4daf847e1b49988574d4eaedabfc46703779d060..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19963 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 A;{X5v diff --git a/charts/postgres-operator/postgres-operator-1.6.3.tgz b/charts/postgres-operator/postgres-operator-1.6.3.tgz deleted file mode 100644 index af84bf57bc65b7418564d29b4a7169f72c545f85..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20124 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 diff --git a/charts/postgres-operator/postgres-operator-1.9.0.tgz b/charts/postgres-operator/postgres-operator-1.9.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..8106bcf157948108ea832fa94018d1b77ad12ab9 GIT binary patch literal 17544 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 literal 0 HcmV?d00001 diff --git a/charts/postgres-operator/values.yaml b/charts/postgres-operator/values.yaml index e351015f4..bca269b0a 100644 --- a/charts/postgres-operator/values.yaml +++ b/charts/postgres-operator/values.yaml @@ -1,7 +1,7 @@ image: registry: registry.opensource.zalan.do repository: acid/postgres-operator - tag: v1.8.2 + tag: v1.9.0 pullPolicy: "IfNotPresent" # Optionally specify an array of imagePullSecrets. diff --git a/docs/reference/operator_parameters.md b/docs/reference/operator_parameters.md index e61cff24b..198870d77 100644 --- a/docs/reference/operator_parameters.md +++ b/docs/reference/operator_parameters.md @@ -755,7 +755,7 @@ grouped under the `logical_backup` key. runs `pg_dumpall` on a replica if possible and uploads compressed results to an S3 bucket under the key `/spilo/pg_cluster_name/cluster_k8s_uuid/logical_backups`. The default image is the same image built with the Zalando-internal CI - pipeline. Default: "registry.opensource.zalan.do/acid/logical-backup:v1.8.2" + pipeline. Default: "registry.opensource.zalan.do/acid/logical-backup:v1.9.0" * **logical_backup_google_application_credentials** Specifies the path of the google cloud service account json file. Default is empty. diff --git a/manifests/configmap.yaml b/manifests/configmap.yaml index 1a64c90f8..e2fb21504 100644 --- a/manifests/configmap.yaml +++ b/manifests/configmap.yaml @@ -80,7 +80,7 @@ data: # 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.8.2" + logical_backup_docker_image: "registry.opensource.zalan.do/acid/logical-backup:v1.9.0" # logical_backup_google_application_credentials: "" logical_backup_job_prefix: "logical-backup-" # logical_backup_memory_limit: "" diff --git a/manifests/operatorconfiguration.crd.yaml b/manifests/operatorconfiguration.crd.yaml index 7df1a1ae3..8582c866a 100644 --- a/manifests/operatorconfiguration.crd.yaml +++ b/manifests/operatorconfiguration.crd.yaml @@ -487,7 +487,7 @@ spec: pattern: '^(\d+m|\d+(\.\d{1,3})?)$' logical_backup_docker_image: type: string - default: "registry.opensource.zalan.do/acid/logical-backup:v1.8.2" + default: "registry.opensource.zalan.do/acid/logical-backup:v1.9.0" logical_backup_google_application_credentials: type: string logical_backup_job_prefix: diff --git a/manifests/postgres-operator.yaml b/manifests/postgres-operator.yaml index cb3dd48a6..a8f565c8e 100644 --- a/manifests/postgres-operator.yaml +++ b/manifests/postgres-operator.yaml @@ -19,7 +19,7 @@ spec: serviceAccountName: postgres-operator containers: - name: postgres-operator - image: registry.opensource.zalan.do/acid/postgres-operator:v1.8.2 + image: registry.opensource.zalan.do/acid/postgres-operator:v1.9.0 imagePullPolicy: IfNotPresent resources: requests: diff --git a/manifests/postgresql-operator-default-configuration.yaml b/manifests/postgresql-operator-default-configuration.yaml index 6ea85171e..2e475910c 100644 --- a/manifests/postgresql-operator-default-configuration.yaml +++ b/manifests/postgresql-operator-default-configuration.yaml @@ -160,7 +160,7 @@ configuration: # logical_backup_cpu_request: "" # logical_backup_memory_limit: "" # logical_backup_memory_request: "" - logical_backup_docker_image: "registry.opensource.zalan.do/acid/logical-backup:v1.8.2" + logical_backup_docker_image: "registry.opensource.zalan.do/acid/logical-backup:v1.9.0" # logical_backup_google_application_credentials: "" logical_backup_job_prefix: "logical-backup-" logical_backup_provider: "s3" diff --git a/pkg/controller/operator_config.go b/pkg/controller/operator_config.go index a1a1050ca..a66ece2fa 100644 --- a/pkg/controller/operator_config.go +++ b/pkg/controller/operator_config.go @@ -174,7 +174,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur // logical backup config result.LogicalBackupSchedule = util.Coalesce(fromCRD.LogicalBackup.Schedule, "30 00 * * *") - result.LogicalBackupDockerImage = util.Coalesce(fromCRD.LogicalBackup.DockerImage, "registry.opensource.zalan.do/acid/logical-backup:v1.8.2") + result.LogicalBackupDockerImage = util.Coalesce(fromCRD.LogicalBackup.DockerImage, "registry.opensource.zalan.do/acid/logical-backup:v1.9.0") result.LogicalBackupProvider = util.Coalesce(fromCRD.LogicalBackup.BackupProvider, "s3") result.LogicalBackupAzureStorageAccountName = fromCRD.LogicalBackup.AzureStorageAccountName result.LogicalBackupAzureStorageAccountKey = fromCRD.LogicalBackup.AzureStorageAccountKey diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index 179d5b96c..df1cf6bb8 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -126,7 +126,7 @@ type Scalyr struct { // LogicalBackup defines configuration for logical backup type LogicalBackup struct { LogicalBackupSchedule string `name:"logical_backup_schedule" default:"30 00 * * *"` - LogicalBackupDockerImage string `name:"logical_backup_docker_image" default:"registry.opensource.zalan.do/acid/logical-backup:v1.8.2"` + LogicalBackupDockerImage string `name:"logical_backup_docker_image" default:"registry.opensource.zalan.do/acid/logical-backup:v1.9.0"` LogicalBackupProvider string `name:"logical_backup_provider" default:"s3"` LogicalBackupAzureStorageAccountName string `name:"logical_backup_azure_storage_account_name" default:""` LogicalBackupAzureStorageContainer string `name:"logical_backup_azure_storage_container" default:""` diff --git a/ui/app/package.json b/ui/app/package.json index d4542ff68..9f66d760b 100644 --- a/ui/app/package.json +++ b/ui/app/package.json @@ -1,6 +1,6 @@ { "name": "postgres-operator-ui", - "version": "1.8.1", + "version": "1.9.0", "description": "PostgreSQL Operator UI", "main": "src/app.js", "config": { diff --git a/ui/manifests/deployment.yaml b/ui/manifests/deployment.yaml index bff1a3bc9..df23e1aad 100644 --- a/ui/manifests/deployment.yaml +++ b/ui/manifests/deployment.yaml @@ -18,7 +18,7 @@ spec: serviceAccountName: postgres-operator-ui containers: - name: "service" - image: registry.opensource.zalan.do/acid/postgres-operator-ui:v1.8.1 + image: registry.opensource.zalan.do/acid/postgres-operator-ui:v1.9.0 ports: - containerPort: 8081 protocol: "TCP" diff --git a/ui/run_local.sh b/ui/run_local.sh index bee5c3e55..c2918505a 100755 --- a/ui/run_local.sh +++ b/ui/run_local.sh @@ -16,7 +16,9 @@ default_operator_ui_config='{ "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, @@ -24,6 +26,10 @@ default_operator_ui_config='{ "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",