Merge branch 'master' into replace-spilo-application-by-owner
This commit is contained in:
commit
bf8954d149
File diff suppressed because it is too large
Load Diff
|
|
@ -50,7 +50,7 @@ configGeneral:
|
||||||
# ignore_resources_limits_annotation_key: ""
|
# ignore_resources_limits_annotation_key: ""
|
||||||
|
|
||||||
# Select if setup uses endpoints (default), or configmaps to manage leader (DCS=k8s)
|
# Select if setup uses endpoints (default), or configmaps to manage leader (DCS=k8s)
|
||||||
# kubernetes_use_configmaps: false
|
kubernetes_use_configmaps: true
|
||||||
|
|
||||||
# maintenance windows applied to all Postgres clusters unless overridden in the manifest
|
# maintenance windows applied to all Postgres clusters unless overridden in the manifest
|
||||||
# maintenance_windows:
|
# maintenance_windows:
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
<h1>Migrate from v1 to v2</h1>
|
||||||
|
|
||||||
|
Version 2.0 changes some default settings and removes deprecated fields. Please read the following sections before upgrading the Postgres Operator deployment.
|
||||||
|
|
||||||
|
## K8s Endpoints are deprecated
|
||||||
|
|
||||||
|
If your current operator v1.x deployment is relying on K8s endpoints (the default setup) for Patroni to manage the HA state you have to start planning to switch to configmaps, because endpoints are deprecated from K8s 1.33 onwards. The default of the corresponding parameter `kubernetes_use_configmaps` is changing to `true` with v2.0 of the operator. This means you have to explicity set it to `false` in your configuration before you start the upgrade.
|
||||||
|
|
||||||
|
We explicitly warn you to go straight to configmap-based HA management with database clusters that use replicas, because there's is a danger to run into split-brain scenarios during the rolling update of pods when there exists a leader endpoint and leader config map at the same time. To play it safe, here is what you should do - before or after the Postgres Operator upgrade:
|
||||||
|
|
||||||
|
1. Scale-in all your database clusters to only one primary instance. This can be done by changing the global config options `max_instances` and `min_instances` to `1`. If you have allowed users to ignore globally defined instance limits by configuring an `ignore_instance_limits_annotation_key`, remove it for now.
|
||||||
|
|
||||||
|
2. Wait for all clusters to be healthy and change the `kubernetes_use_configmaps` setting to `true`. This will trigger the replacement of the primary pod of all clusters and cause downtime for as long as the pods are rescheduled and start up.
|
||||||
|
|
||||||
|
3. Check again that all clusters are healthy with configmaps created. There should be three for each cluster called like cluster name with suffixes `-config`, `-failover` and `-leader`. Now, revert the changes from step 1 and scale-out the to number of instances set in the manifests.
|
||||||
|
|
||||||
|
4. The orphaned endpoints, which use the same names like the new configmaps, have to be deleted by you or your K8s garbage collection.
|
||||||
|
|
||||||
|
|
||||||
|
## Dropped manifest fields
|
||||||
|
|
||||||
|
We removed some deprecated fields from the Postgresql CRD. Please, make sure that you do not specify them in any of your cluster manifests. If you do, switch to the listed alternative:
|
||||||
|
|
||||||
|
| Removed field in v2 | Alternative |
|
||||||
|
| --- | --- |
|
||||||
|
| init_containers | initContainers |
|
||||||
|
| pod_priority_class_name | podPriorityClassName |
|
||||||
|
| replicaLoadBalancer | enableReplicaLoadBalancer|
|
||||||
|
| useLoadBalancer | enableMasterLoadBalancer |
|
||||||
|
|
@ -100,15 +100,13 @@ Those are top-level keys, containing both leaf keys and groups.
|
||||||
Kubernetes-native DCS).
|
Kubernetes-native DCS).
|
||||||
|
|
||||||
* **kubernetes_use_configmaps**
|
* **kubernetes_use_configmaps**
|
||||||
Select if setup uses endpoints (default), or configmaps to manage leader when
|
Select if setup uses endpoints or configmaps (default) to manage leader when
|
||||||
DCS is kubernetes (not etcd or similar). In OpenShift it is not possible to
|
DCS is kubernetes (not etcd or similar). In OpenShift it is not possible to
|
||||||
use endpoints option, and configmaps is required. Starting with K8s 1.33,
|
use endpoints option, and configmaps is required. Starting with K8s 1.33,
|
||||||
endpoints are marked as deprecated. It's recommended to switch to config maps
|
endpoints are marked as deprecated. It's recommended to switch to config maps
|
||||||
instead. But, to do so make sure you scale the Postgres cluster down to just
|
instead. But, to do so make sure you scale the Postgres cluster down to just
|
||||||
one primary pod (e.g. using `max_instances` option). Otherwise, you risk
|
one primary pod (e.g. using `max_instances` option). Otherwise, you risk
|
||||||
running into a split-brain scenario.
|
running into a split-brain scenario. Default is `true`.
|
||||||
By default, `kubernetes_use_configmaps: false`, meaning endpoints will be used.
|
|
||||||
Starting from v1.16.0 the default will be changed to `true`.
|
|
||||||
|
|
||||||
* **docker_image**
|
* **docker_image**
|
||||||
Spilo Docker image for Postgres instances. For production, don't rely on the
|
Spilo Docker image for Postgres instances. For production, don't rely on the
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ configuration:
|
||||||
etcd_host: ""
|
etcd_host: ""
|
||||||
# ignore_instance_limits_annotation_key: ""
|
# ignore_instance_limits_annotation_key: ""
|
||||||
# ignore_resources_limits_annotation_key: ""
|
# ignore_resources_limits_annotation_key: ""
|
||||||
# kubernetes_use_configmaps: false
|
kubernetes_use_configmaps: true
|
||||||
# maintenance_windows:
|
# maintenance_windows:
|
||||||
# - "Sat:22:00-23:59"
|
# - "Sat:22:00-23:59"
|
||||||
# - "Sun:00:00-01:00"
|
# - "Sun:00:00-01:00"
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -410,7 +410,7 @@ type OperatorConfigurationData struct {
|
||||||
// +kubebuilder:default=""
|
// +kubebuilder:default=""
|
||||||
EtcdHost string `json:"etcd_host,omitempty"`
|
EtcdHost string `json:"etcd_host,omitempty"`
|
||||||
// +kubebuilder:default=true
|
// +kubebuilder:default=true
|
||||||
KubernetesUseConfigMaps bool `json:"kubernetes_use_configmaps,omitempty"`
|
KubernetesUseConfigMaps *bool `json:"kubernetes_use_configmaps,omitempty"`
|
||||||
// +kubebuilder:default="ghcr.io/zalando/spilo-18:4.1-p1"
|
// +kubebuilder:default="ghcr.io/zalando/spilo-18:4.1-p1"
|
||||||
DockerImage string `json:"docker_image,omitempty"`
|
DockerImage string `json:"docker_image,omitempty"`
|
||||||
// +kubebuilder:validation:Minimum=1
|
// +kubebuilder:validation:Minimum=1
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -76,12 +76,6 @@ type PostgresSpec struct {
|
||||||
EnableReplicaPoolerNodePort *bool `json:"enableReplicaPoolerNodePort,omitempty"`
|
EnableReplicaPoolerNodePort *bool `json:"enableReplicaPoolerNodePort,omitempty"`
|
||||||
ReplicaPoolerNodePort *int32 `json:"replicaPoolerNodePort,omitempty"`
|
ReplicaPoolerNodePort *int32 `json:"replicaPoolerNodePort,omitempty"`
|
||||||
|
|
||||||
// deprecated load balancer settings maintained for backward compatibility
|
|
||||||
// see "Load balancers" operator docs
|
|
||||||
UseLoadBalancer *bool `json:"useLoadBalancer,omitempty"`
|
|
||||||
// deprecated
|
|
||||||
ReplicaLoadBalancer *bool `json:"replicaLoadBalancer,omitempty"`
|
|
||||||
|
|
||||||
// load balancers' source ranges are the same for master and replica services
|
// load balancers' source ranges are the same for master and replica services
|
||||||
// +nullable
|
// +nullable
|
||||||
// +kubebuilder:validation:items:Pattern=`^((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\/(\d|[1-2]\d|3[0-2])|(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))\/(12[0-8]|1[01][0-9]|[1-9]?[0-9]))$`
|
// +kubebuilder:validation:items:Pattern=`^((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\/(\d|[1-2]\d|3[0-2])|(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))\/(12[0-8]|1[01][0-9]|[1-9]?[0-9]))$`
|
||||||
|
|
@ -130,11 +124,6 @@ type PostgresSpec struct {
|
||||||
AdditionalVolumes []AdditionalVolume `json:"additionalVolumes,omitempty"`
|
AdditionalVolumes []AdditionalVolume `json:"additionalVolumes,omitempty"`
|
||||||
Streams []Stream `json:"streams,omitempty"`
|
Streams []Stream `json:"streams,omitempty"`
|
||||||
Env []v1.EnvVar `json:"env,omitempty"`
|
Env []v1.EnvVar `json:"env,omitempty"`
|
||||||
|
|
||||||
// deprecated
|
|
||||||
InitContainersOld []v1.Container `json:"init_containers,omitempty"`
|
|
||||||
// deprecated
|
|
||||||
PodPriorityClassNameOld string `json:"pod_priority_class_name,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/zalando/postgres-operator/pkg/util"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -178,7 +177,8 @@ var unmarshalCluster = []struct {
|
||||||
"metadata": {"name": "acid-testcluster1"}, "spec": {"teamId": 100}}`), &tmp).Error(),
|
"metadata": {"name": "acid-testcluster1"}, "spec": {"teamId": 100}}`), &tmp).Error(),
|
||||||
},
|
},
|
||||||
marshal: []byte(`{"kind":"Postgresql","apiVersion":"acid.zalan.do/v1","metadata":{"name":"acid-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":"","allowedSourceRanges":null,"numberOfInstances":0,"users":null,"clone":null},"status":"Invalid"}`),
|
marshal: []byte(`{"kind":"Postgresql","apiVersion":"acid.zalan.do/v1","metadata":{"name":"acid-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":"","allowedSourceRanges":null,"numberOfInstances":0,"users":null,"clone":null},"status":"Invalid"}`),
|
||||||
err: nil},
|
err: nil,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
about: "example with /status subresource",
|
about: "example with /status subresource",
|
||||||
in: []byte(`{
|
in: []byte(`{
|
||||||
|
|
@ -199,156 +199,8 @@ var unmarshalCluster = []struct {
|
||||||
"metadata": {"name": "acid-testcluster1"}, "spec": {"teamId": 100}}`), &tmp).Error(),
|
"metadata": {"name": "acid-testcluster1"}, "spec": {"teamId": 100}}`), &tmp).Error(),
|
||||||
},
|
},
|
||||||
marshal: []byte(`{"kind":"Postgresql","apiVersion":"acid.zalan.do/v1","metadata":{"name":"acid-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":"","allowedSourceRanges":null,"numberOfInstances":0,"users":null,"clone":null},"status":{"PostgresClusterStatus":"Invalid"}}`),
|
marshal: []byte(`{"kind":"Postgresql","apiVersion":"acid.zalan.do/v1","metadata":{"name":"acid-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":"","allowedSourceRanges":null,"numberOfInstances":0,"users":null,"clone":null},"status":{"PostgresClusterStatus":"Invalid"}}`),
|
||||||
err: nil},
|
err: nil,
|
||||||
{
|
},
|
||||||
about: "example with detailed input manifest and deprecated pod_priority_class_name -> podPriorityClassName",
|
|
||||||
in: []byte(`{
|
|
||||||
"kind": "Postgresql",
|
|
||||||
"apiVersion": "acid.zalan.do/v1",
|
|
||||||
"metadata": {
|
|
||||||
"name": "acid-testcluster1"
|
|
||||||
},
|
|
||||||
"spec": {
|
|
||||||
"teamId": "acid",
|
|
||||||
"pod_priority_class_name": "spilo-pod-priority",
|
|
||||||
"volume": {
|
|
||||||
"size": "5Gi",
|
|
||||||
"storageClass": "SSD",
|
|
||||||
"subPath": "subdir"
|
|
||||||
},
|
|
||||||
"numberOfInstances": 2,
|
|
||||||
"users": {
|
|
||||||
"zalando": [
|
|
||||||
"superuser",
|
|
||||||
"createdb"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"allowedSourceRanges": [
|
|
||||||
"127.0.0.1/32"
|
|
||||||
],
|
|
||||||
"postgresql": {
|
|
||||||
"version": "18",
|
|
||||||
"parameters": {
|
|
||||||
"shared_buffers": "32MB",
|
|
||||||
"max_connections": "10",
|
|
||||||
"log_statement": "all"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"resources": {
|
|
||||||
"requests": {
|
|
||||||
"cpu": "10m",
|
|
||||||
"memory": "50Mi"
|
|
||||||
},
|
|
||||||
"limits": {
|
|
||||||
"cpu": "300m",
|
|
||||||
"memory": "3000Mi"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"clone" : {
|
|
||||||
"cluster": "acid-batman"
|
|
||||||
},
|
|
||||||
"enableShmVolume": false,
|
|
||||||
"patroni": {
|
|
||||||
"initdb": {
|
|
||||||
"encoding": "UTF8",
|
|
||||||
"locale": "en_US.UTF-8",
|
|
||||||
"data-checksums": "true"
|
|
||||||
},
|
|
||||||
"pg_hba": [
|
|
||||||
"hostssl all all 0.0.0.0/0 md5",
|
|
||||||
"host all all 0.0.0.0/0 md5"
|
|
||||||
],
|
|
||||||
"ttl": 30,
|
|
||||||
"loop_wait": 10,
|
|
||||||
"retry_timeout": 10,
|
|
||||||
"maximum_lag_on_failover": 33554432,
|
|
||||||
"slots" : {
|
|
||||||
"permanent_logical_1" : {
|
|
||||||
"type" : "logical",
|
|
||||||
"database" : "foo",
|
|
||||||
"plugin" : "pgoutput"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"maintenanceWindows": [
|
|
||||||
"Mon:01:00-06:00",
|
|
||||||
"Sat:00:00-04:00",
|
|
||||||
"05:00-05:15"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}`),
|
|
||||||
out: Postgresql{
|
|
||||||
TypeMeta: metav1.TypeMeta{
|
|
||||||
Kind: "Postgresql",
|
|
||||||
APIVersion: "acid.zalan.do/v1",
|
|
||||||
},
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: "acid-testcluster1",
|
|
||||||
},
|
|
||||||
Spec: PostgresSpec{
|
|
||||||
PostgresqlParam: PostgresqlParam{
|
|
||||||
PgVersion: "18",
|
|
||||||
Parameters: map[string]string{
|
|
||||||
"shared_buffers": "32MB",
|
|
||||||
"max_connections": "10",
|
|
||||||
"log_statement": "all",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
PodPriorityClassNameOld: "spilo-pod-priority",
|
|
||||||
Volume: Volume{
|
|
||||||
Size: "5Gi",
|
|
||||||
StorageClass: "SSD",
|
|
||||||
SubPath: "subdir",
|
|
||||||
},
|
|
||||||
ShmVolume: util.False(),
|
|
||||||
Patroni: Patroni{
|
|
||||||
InitDB: map[string]string{
|
|
||||||
"encoding": "UTF8",
|
|
||||||
"locale": "en_US.UTF-8",
|
|
||||||
"data-checksums": "true",
|
|
||||||
},
|
|
||||||
PgHba: []string{"hostssl all all 0.0.0.0/0 md5", "host all all 0.0.0.0/0 md5"},
|
|
||||||
TTL: 30,
|
|
||||||
LoopWait: 10,
|
|
||||||
RetryTimeout: 10,
|
|
||||||
MaximumLagOnFailover: 33554432,
|
|
||||||
Slots: map[string]map[string]string{"permanent_logical_1": {"type": "logical", "database": "foo", "plugin": "pgoutput"}},
|
|
||||||
},
|
|
||||||
Resources: &Resources{
|
|
||||||
ResourceRequests: ResourceDescription{CPU: stringToPointer("10m"), Memory: stringToPointer("50Mi")},
|
|
||||||
ResourceLimits: ResourceDescription{CPU: stringToPointer("300m"), Memory: stringToPointer("3000Mi")},
|
|
||||||
},
|
|
||||||
|
|
||||||
TeamID: "acid",
|
|
||||||
AllowedSourceRanges: []string{"127.0.0.1/32"},
|
|
||||||
NumberOfInstances: 2,
|
|
||||||
Users: map[string]UserFlags{"zalando": {"superuser", "createdb"}},
|
|
||||||
MaintenanceWindows: []MaintenanceWindow{{
|
|
||||||
Everyday: false,
|
|
||||||
Weekday: time.Monday,
|
|
||||||
StartTime: mustParseTime("01:00"),
|
|
||||||
EndTime: mustParseTime("06:00"),
|
|
||||||
}, {
|
|
||||||
Everyday: false,
|
|
||||||
Weekday: time.Saturday,
|
|
||||||
StartTime: mustParseTime("00:00"),
|
|
||||||
EndTime: mustParseTime("04:00"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Everyday: true,
|
|
||||||
Weekday: time.Sunday,
|
|
||||||
StartTime: mustParseTime("05:00"),
|
|
||||||
EndTime: mustParseTime("05:15"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Clone: &CloneDescription{
|
|
||||||
ClusterName: "acid-batman",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Error: "",
|
|
||||||
},
|
|
||||||
marshal: []byte(`{"kind":"Postgresql","apiVersion":"acid.zalan.do/v1","metadata":{"name":"acid-testcluster1","creationTimestamp":null},"spec":{"postgresql":{"version":"18","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",
|
about: "example with clone",
|
||||||
in: []byte(`{"kind": "Postgresql","apiVersion": "acid.zalan.do/v1","metadata": {"name": "acid-testcluster1"}, "spec": {"teamId": "acid", "clone": {"cluster": "team-batman"}}}`),
|
in: []byte(`{"kind": "Postgresql","apiVersion": "acid.zalan.do/v1","metadata": {"name": "acid-testcluster1"}, "spec": {"teamId": "acid", "clone": {"cluster": "team-batman"}}}`),
|
||||||
|
|
@ -369,7 +221,8 @@ var unmarshalCluster = []struct {
|
||||||
Error: "",
|
Error: "",
|
||||||
},
|
},
|
||||||
marshal: []byte(`{"kind":"Postgresql","apiVersion":"acid.zalan.do/v1","metadata":{"name":"acid-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":{"cluster":"team-batman"}},"status":{"PostgresClusterStatus":""}}`),
|
marshal: []byte(`{"kind":"Postgresql","apiVersion":"acid.zalan.do/v1","metadata":{"name":"acid-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":{"cluster":"team-batman"}},"status":{"PostgresClusterStatus":""}}`),
|
||||||
err: nil},
|
err: nil,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
about: "standby example",
|
about: "standby example",
|
||||||
in: []byte(`{"kind": "Postgresql","apiVersion": "acid.zalan.do/v1","metadata": {"name": "acid-testcluster1"}, "spec": {"teamId": "acid", "standby": {"s3_wal_path": "s3://custom/path/to/bucket/"}}}`),
|
in: []byte(`{"kind": "Postgresql","apiVersion": "acid.zalan.do/v1","metadata": {"name": "acid-testcluster1"}, "spec": {"teamId": "acid", "standby": {"s3_wal_path": "s3://custom/path/to/bucket/"}}}`),
|
||||||
|
|
@ -390,7 +243,8 @@ var unmarshalCluster = []struct {
|
||||||
Error: "",
|
Error: "",
|
||||||
},
|
},
|
||||||
marshal: []byte(`{"kind":"Postgresql","apiVersion":"acid.zalan.do/v1","metadata":{"name":"acid-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,"standby":{"s3_wal_path":"s3://custom/path/to/bucket/"}},"status":{"PostgresClusterStatus":""}}`),
|
marshal: []byte(`{"kind":"Postgresql","apiVersion":"acid.zalan.do/v1","metadata":{"name":"acid-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,"standby":{"s3_wal_path":"s3://custom/path/to/bucket/"}},"status":{"PostgresClusterStatus":""}}`),
|
||||||
err: nil},
|
err: nil,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
about: "expect error on malformatted JSON",
|
about: "expect error on malformatted JSON",
|
||||||
in: []byte(`{"kind": "Postgresql","apiVersion": "acid.zalan.do/v1"`),
|
in: []byte(`{"kind": "Postgresql","apiVersion": "acid.zalan.do/v1"`),
|
||||||
|
|
|
||||||
|
|
@ -443,6 +443,11 @@ func (in *OperatorConfigurationData) DeepCopyInto(out *OperatorConfigurationData
|
||||||
*out = make([]string, len(*in))
|
*out = make([]string, len(*in))
|
||||||
copy(*out, *in)
|
copy(*out, *in)
|
||||||
}
|
}
|
||||||
|
if in.KubernetesUseConfigMaps != nil {
|
||||||
|
in, out := &in.KubernetesUseConfigMaps, &out.KubernetesUseConfigMaps
|
||||||
|
*out = new(bool)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
if in.ResyncPeriod != nil {
|
if in.ResyncPeriod != nil {
|
||||||
in, out := &in.ResyncPeriod, &out.ResyncPeriod
|
in, out := &in.ResyncPeriod, &out.ResyncPeriod
|
||||||
*out = new(metav1.Duration)
|
*out = new(metav1.Duration)
|
||||||
|
|
@ -843,16 +848,6 @@ func (in *PostgresSpec) DeepCopyInto(out *PostgresSpec) {
|
||||||
*out = new(int32)
|
*out = new(int32)
|
||||||
**out = **in
|
**out = **in
|
||||||
}
|
}
|
||||||
if in.UseLoadBalancer != nil {
|
|
||||||
in, out := &in.UseLoadBalancer, &out.UseLoadBalancer
|
|
||||||
*out = new(bool)
|
|
||||||
**out = **in
|
|
||||||
}
|
|
||||||
if in.ReplicaLoadBalancer != nil {
|
|
||||||
in, out := &in.ReplicaLoadBalancer, &out.ReplicaLoadBalancer
|
|
||||||
*out = new(bool)
|
|
||||||
**out = **in
|
|
||||||
}
|
|
||||||
if in.AllowedSourceRanges != nil {
|
if in.AllowedSourceRanges != nil {
|
||||||
in, out := &in.AllowedSourceRanges, &out.AllowedSourceRanges
|
in, out := &in.AllowedSourceRanges, &out.AllowedSourceRanges
|
||||||
*out = make([]string, len(*in))
|
*out = make([]string, len(*in))
|
||||||
|
|
@ -1021,13 +1016,6 @@ func (in *PostgresSpec) DeepCopyInto(out *PostgresSpec) {
|
||||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if in.InitContainersOld != nil {
|
|
||||||
in, out := &in.InitContainersOld, &out.InitContainersOld
|
|
||||||
*out = make([]corev1.Container, len(*in))
|
|
||||||
for i := range *in {
|
|
||||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1083,7 +1083,7 @@ func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error {
|
||||||
|
|
||||||
// Patroni service and endpoints / config maps
|
// Patroni service and endpoints / config maps
|
||||||
if err := c.syncPatroniResources(); err != nil {
|
if err := c.syncPatroniResources(); err != nil {
|
||||||
c.logger.Errorf("could not sync services: %v", err)
|
c.logger.Errorf("could not sync Patroni resources: %v", err)
|
||||||
updateFailed = true
|
updateFailed = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,7 @@ func TestCreate(t *testing.T) {
|
||||||
|
|
||||||
client := k8sutil.KubernetesClient{
|
client := k8sutil.KubernetesClient{
|
||||||
DeploymentsGetter: clientSet.AppsV1(),
|
DeploymentsGetter: clientSet.AppsV1(),
|
||||||
|
ConfigMapsGetter: clientSet.CoreV1(),
|
||||||
CronJobsGetter: clientSet.BatchV1(),
|
CronJobsGetter: clientSet.BatchV1(),
|
||||||
EndpointsGetter: clientSet.CoreV1(),
|
EndpointsGetter: clientSet.CoreV1(),
|
||||||
PersistentVolumeClaimsGetter: clientSet.CoreV1(),
|
PersistentVolumeClaimsGetter: clientSet.CoreV1(),
|
||||||
|
|
|
||||||
|
|
@ -1340,28 +1340,6 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// backward compatible check for InitContainers
|
|
||||||
if spec.InitContainersOld != nil {
|
|
||||||
msg := "manifest parameter init_containers is deprecated."
|
|
||||||
if spec.InitContainers == nil {
|
|
||||||
c.logger.Warningf("%s Consider using initContainers instead.", msg)
|
|
||||||
spec.InitContainers = spec.InitContainersOld
|
|
||||||
} else {
|
|
||||||
c.logger.Warningf("%s Only value from initContainers is used", msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// backward compatible check for PodPriorityClassName
|
|
||||||
if spec.PodPriorityClassNameOld != "" {
|
|
||||||
msg := "manifest parameter pod_priority_class_name is deprecated."
|
|
||||||
if spec.PodPriorityClassName == "" {
|
|
||||||
c.logger.Warningf("%s Consider using podPriorityClassName instead.", msg)
|
|
||||||
spec.PodPriorityClassName = spec.PodPriorityClassNameOld
|
|
||||||
} else {
|
|
||||||
c.logger.Warningf("%s Only value from podPriorityClassName is used", msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
spiloConfiguration, err := generateSpiloJSONConfiguration(&spec.PostgresqlParam, &spec.Patroni, &c.OpConfig, c.logger)
|
spiloConfiguration, err := generateSpiloJSONConfiguration(&spec.PostgresqlParam, &spec.Patroni, &c.OpConfig, c.logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not generate Spilo JSON configuration: %v", err)
|
return nil, fmt.Errorf("could not generate Spilo JSON configuration: %v", err)
|
||||||
|
|
|
||||||
|
|
@ -582,60 +582,60 @@ func TestGenerateSpiloPodEnvVars(t *testing.T) {
|
||||||
}
|
}
|
||||||
expectedValuesS3Bucket := []ExpectedValue{
|
expectedValuesS3Bucket := []ExpectedValue{
|
||||||
{
|
{
|
||||||
envIndex: 15,
|
envIndex: 16,
|
||||||
envVarConstant: "WAL_S3_BUCKET",
|
envVarConstant: "WAL_S3_BUCKET",
|
||||||
envVarValue: "global-s3-bucket",
|
envVarValue: "global-s3-bucket",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
envIndex: 16,
|
envIndex: 17,
|
||||||
envVarConstant: "WAL_BUCKET_SCOPE_SUFFIX",
|
envVarConstant: "WAL_BUCKET_SCOPE_SUFFIX",
|
||||||
envVarValue: fmt.Sprintf("/%s", dummyUUID),
|
envVarValue: fmt.Sprintf("/%s", dummyUUID),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
envIndex: 17,
|
envIndex: 18,
|
||||||
envVarConstant: "WAL_BUCKET_SCOPE_PREFIX",
|
envVarConstant: "WAL_BUCKET_SCOPE_PREFIX",
|
||||||
envVarValue: "",
|
envVarValue: "",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
expectedValuesGCPCreds := []ExpectedValue{
|
expectedValuesGCPCreds := []ExpectedValue{
|
||||||
{
|
{
|
||||||
envIndex: 15,
|
envIndex: 16,
|
||||||
envVarConstant: "WAL_GS_BUCKET",
|
envVarConstant: "WAL_GS_BUCKET",
|
||||||
envVarValue: "global-gs-bucket",
|
envVarValue: "global-gs-bucket",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
envIndex: 16,
|
envIndex: 17,
|
||||||
envVarConstant: "WAL_BUCKET_SCOPE_SUFFIX",
|
envVarConstant: "WAL_BUCKET_SCOPE_SUFFIX",
|
||||||
envVarValue: fmt.Sprintf("/%s", dummyUUID),
|
envVarValue: fmt.Sprintf("/%s", dummyUUID),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
envIndex: 17,
|
envIndex: 18,
|
||||||
envVarConstant: "WAL_BUCKET_SCOPE_PREFIX",
|
envVarConstant: "WAL_BUCKET_SCOPE_PREFIX",
|
||||||
envVarValue: "",
|
envVarValue: "",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
envIndex: 18,
|
envIndex: 19,
|
||||||
envVarConstant: "GOOGLE_APPLICATION_CREDENTIALS",
|
envVarConstant: "GOOGLE_APPLICATION_CREDENTIALS",
|
||||||
envVarValue: "some-path-to-credentials",
|
envVarValue: "some-path-to-credentials",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
expectedS3BucketConfigMap := []ExpectedValue{
|
expectedS3BucketConfigMap := []ExpectedValue{
|
||||||
{
|
{
|
||||||
envIndex: 17,
|
envIndex: 18,
|
||||||
envVarConstant: "wal_s3_bucket",
|
envVarConstant: "wal_s3_bucket",
|
||||||
envVarValue: "global-s3-bucket-configmap",
|
envVarValue: "global-s3-bucket-configmap",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
expectedCustomS3BucketSpec := []ExpectedValue{
|
expectedCustomS3BucketSpec := []ExpectedValue{
|
||||||
{
|
{
|
||||||
envIndex: 15,
|
envIndex: 16,
|
||||||
envVarConstant: "WAL_S3_BUCKET",
|
envVarConstant: "WAL_S3_BUCKET",
|
||||||
envVarValue: "custom-s3-bucket",
|
envVarValue: "custom-s3-bucket",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
expectedCustomVariableSecret := []ExpectedValue{
|
expectedCustomVariableSecret := []ExpectedValue{
|
||||||
{
|
{
|
||||||
envIndex: 16,
|
envIndex: 17,
|
||||||
envVarConstant: "custom_variable",
|
envVarConstant: "custom_variable",
|
||||||
envVarValueRef: &v1.EnvVarSource{
|
envVarValueRef: &v1.EnvVarSource{
|
||||||
SecretKeyRef: &v1.SecretKeySelector{
|
SecretKeyRef: &v1.SecretKeySelector{
|
||||||
|
|
@ -649,72 +649,72 @@ func TestGenerateSpiloPodEnvVars(t *testing.T) {
|
||||||
}
|
}
|
||||||
expectedCustomVariableConfigMap := []ExpectedValue{
|
expectedCustomVariableConfigMap := []ExpectedValue{
|
||||||
{
|
{
|
||||||
envIndex: 16,
|
envIndex: 17,
|
||||||
envVarConstant: "custom_variable",
|
envVarConstant: "custom_variable",
|
||||||
envVarValue: "configmap-test",
|
envVarValue: "configmap-test",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
expectedCustomVariableSpec := []ExpectedValue{
|
expectedCustomVariableSpec := []ExpectedValue{
|
||||||
{
|
{
|
||||||
envIndex: 15,
|
envIndex: 16,
|
||||||
envVarConstant: "CUSTOM_VARIABLE",
|
envVarConstant: "CUSTOM_VARIABLE",
|
||||||
envVarValue: "spec-env-test",
|
envVarValue: "spec-env-test",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
expectedCloneEnvSpec := []ExpectedValue{
|
expectedCloneEnvSpec := []ExpectedValue{
|
||||||
{
|
{
|
||||||
envIndex: 16,
|
envIndex: 17,
|
||||||
envVarConstant: "CLONE_WALE_S3_PREFIX",
|
envVarConstant: "CLONE_WALE_S3_PREFIX",
|
||||||
envVarValue: "s3://another-bucket",
|
envVarValue: "s3://another-bucket",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
envIndex: 19,
|
envIndex: 20,
|
||||||
envVarConstant: "CLONE_WAL_BUCKET_SCOPE_PREFIX",
|
envVarConstant: "CLONE_WAL_BUCKET_SCOPE_PREFIX",
|
||||||
envVarValue: "",
|
envVarValue: "",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
envIndex: 20,
|
envIndex: 21,
|
||||||
envVarConstant: "CLONE_AWS_ENDPOINT",
|
envVarConstant: "CLONE_AWS_ENDPOINT",
|
||||||
envVarValue: "s3.eu-central-1.amazonaws.com",
|
envVarValue: "s3.eu-central-1.amazonaws.com",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
expectedCloneEnvSpecEnv := []ExpectedValue{
|
expectedCloneEnvSpecEnv := []ExpectedValue{
|
||||||
{
|
{
|
||||||
envIndex: 15,
|
envIndex: 16,
|
||||||
envVarConstant: "CLONE_WAL_BUCKET_SCOPE_PREFIX",
|
envVarConstant: "CLONE_WAL_BUCKET_SCOPE_PREFIX",
|
||||||
envVarValue: "test-cluster",
|
envVarValue: "test-cluster",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
envIndex: 17,
|
envIndex: 18,
|
||||||
envVarConstant: "CLONE_WALE_S3_PREFIX",
|
envVarConstant: "CLONE_WALE_S3_PREFIX",
|
||||||
envVarValue: "s3://another-bucket",
|
envVarValue: "s3://another-bucket",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
envIndex: 21,
|
envIndex: 22,
|
||||||
envVarConstant: "CLONE_AWS_ENDPOINT",
|
envVarConstant: "CLONE_AWS_ENDPOINT",
|
||||||
envVarValue: "s3.eu-central-1.amazonaws.com",
|
envVarValue: "s3.eu-central-1.amazonaws.com",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
expectedCloneEnvConfigMap := []ExpectedValue{
|
expectedCloneEnvConfigMap := []ExpectedValue{
|
||||||
{
|
{
|
||||||
envIndex: 16,
|
envIndex: 17,
|
||||||
envVarConstant: "CLONE_WAL_S3_BUCKET",
|
envVarConstant: "CLONE_WAL_S3_BUCKET",
|
||||||
envVarValue: "global-s3-bucket",
|
envVarValue: "global-s3-bucket",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
envIndex: 17,
|
envIndex: 18,
|
||||||
envVarConstant: "CLONE_WAL_BUCKET_SCOPE_SUFFIX",
|
envVarConstant: "CLONE_WAL_BUCKET_SCOPE_SUFFIX",
|
||||||
envVarValue: fmt.Sprintf("/%s", dummyUUID),
|
envVarValue: fmt.Sprintf("/%s", dummyUUID),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
envIndex: 21,
|
envIndex: 22,
|
||||||
envVarConstant: "clone_aws_endpoint",
|
envVarConstant: "clone_aws_endpoint",
|
||||||
envVarValue: "s3.eu-west-1.amazonaws.com",
|
envVarValue: "s3.eu-west-1.amazonaws.com",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
expectedCloneEnvSecret := []ExpectedValue{
|
expectedCloneEnvSecret := []ExpectedValue{
|
||||||
{
|
{
|
||||||
envIndex: 21,
|
envIndex: 22,
|
||||||
envVarConstant: "clone_aws_access_key_id",
|
envVarConstant: "clone_aws_access_key_id",
|
||||||
envVarValueRef: &v1.EnvVarSource{
|
envVarValueRef: &v1.EnvVarSource{
|
||||||
SecretKeyRef: &v1.SecretKeySelector{
|
SecretKeyRef: &v1.SecretKeySelector{
|
||||||
|
|
@ -728,12 +728,12 @@ func TestGenerateSpiloPodEnvVars(t *testing.T) {
|
||||||
}
|
}
|
||||||
expectedStandbyEnvSecret := []ExpectedValue{
|
expectedStandbyEnvSecret := []ExpectedValue{
|
||||||
{
|
{
|
||||||
envIndex: 15,
|
envIndex: 16,
|
||||||
envVarConstant: "STANDBY_WALE_GS_PREFIX",
|
envVarConstant: "STANDBY_WALE_GS_PREFIX",
|
||||||
envVarValue: "gs://some/path/",
|
envVarValue: "gs://some/path/",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
envIndex: 20,
|
envIndex: 21,
|
||||||
envVarConstant: "standby_google_application_credentials",
|
envVarConstant: "standby_google_application_credentials",
|
||||||
envVarValueRef: &v1.EnvVarSource{
|
envVarValueRef: &v1.EnvVarSource{
|
||||||
SecretKeyRef: &v1.SecretKeySelector{
|
SecretKeyRef: &v1.SecretKeySelector{
|
||||||
|
|
@ -2976,6 +2976,7 @@ func getServices(serviceType v1.ServiceType, sourceRanges []string, extTrafficPo
|
||||||
ExternalTrafficPolicy: v1.ServiceExternalTrafficPolicyType(extTrafficPolicy),
|
ExternalTrafficPolicy: v1.ServiceExternalTrafficPolicyType(extTrafficPolicy),
|
||||||
LoadBalancerSourceRanges: sourceRanges,
|
LoadBalancerSourceRanges: sourceRanges,
|
||||||
Ports: []v1.ServicePort{{Name: "postgresql", Port: 5432, TargetPort: intstr.IntOrString{IntVal: 5432}, NodePort: nodePort}},
|
Ports: []v1.ServicePort{{Name: "postgresql", Port: 5432, TargetPort: intstr.IntOrString{IntVal: 5432}, NodePort: nodePort}},
|
||||||
|
Selector: map[string]string{"spilo-role": "master", "application": "spilo", "cluster-name": clusterName},
|
||||||
Type: serviceType,
|
Type: serviceType,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,10 @@ func (c *Cluster) Sync(newSpec *acidv1.Postgresql) error {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
if !c.patroniKubernetesUseConfigMaps() {
|
||||||
|
c.logger.Warning("K8s endpoints are deprecated. Please, enable kubernetes_use_configmaps. Requires scale-in to a single primary, see v1 -> v2 migration docs!")
|
||||||
|
}
|
||||||
|
|
||||||
if err = c.syncFinalizer(); err != nil {
|
if err = c.syncFinalizer(); err != nil {
|
||||||
c.logger.Debugf("could not sync finalizers: %v", err)
|
c.logger.Debugf("could not sync finalizers: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -627,9 +627,12 @@ func (c *Cluster) patroniKubernetesUseConfigMaps() bool {
|
||||||
if !c.patroniUsesKubernetes() {
|
if !c.patroniUsesKubernetes() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if c.OpConfig.KubernetesUseConfigMaps == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// otherwise, follow the operator configuration
|
// otherwise, follow the operator configuration
|
||||||
return c.OpConfig.KubernetesUseConfigMaps
|
return *c.OpConfig.KubernetesUseConfigMaps
|
||||||
}
|
}
|
||||||
|
|
||||||
// Earlier arguments take priority
|
// Earlier arguments take priority
|
||||||
|
|
|
||||||
|
|
@ -299,7 +299,7 @@ func newInheritedAnnotationsCluster(client k8sutil.KubernetesClient) (*Cluster,
|
||||||
OpConfig: config.Config{
|
OpConfig: config.Config{
|
||||||
PatroniAPICheckInterval: &metav1.Duration{Duration: 1 * time.Second},
|
PatroniAPICheckInterval: &metav1.Duration{Duration: 1 * time.Second},
|
||||||
PatroniAPICheckTimeout: &metav1.Duration{Duration: 5 * time.Second},
|
PatroniAPICheckTimeout: &metav1.Duration{Duration: 5 * time.Second},
|
||||||
KubernetesUseConfigMaps: true,
|
KubernetesUseConfigMaps: util.True(),
|
||||||
ConnectionPooler: config.ConnectionPooler{
|
ConnectionPooler: config.ConnectionPooler{
|
||||||
ConnectionPoolerDefaultCPURequest: "100m",
|
ConnectionPoolerDefaultCPURequest: "100m",
|
||||||
ConnectionPoolerDefaultCPULimit: "100m",
|
ConnectionPoolerDefaultCPULimit: "100m",
|
||||||
|
|
@ -388,7 +388,7 @@ func createPatroniResources(cluster *Cluster) error {
|
||||||
Labels: cluster.labelsSet(false),
|
Labels: cluster.labelsSet(false),
|
||||||
}
|
}
|
||||||
|
|
||||||
if cluster.OpConfig.KubernetesUseConfigMaps {
|
if cluster.OpConfig.KubernetesUseConfigMaps != nil && *cluster.OpConfig.KubernetesUseConfigMaps {
|
||||||
configMap := v1.ConfigMap{
|
configMap := v1.ConfigMap{
|
||||||
ObjectMeta: metadata,
|
ObjectMeta: metadata,
|
||||||
}
|
}
|
||||||
|
|
@ -598,7 +598,7 @@ func TestInheritedAnnotations(t *testing.T) {
|
||||||
// 3. Change from ConfigMaps to Endpoints
|
// 3. Change from ConfigMaps to Endpoints
|
||||||
err = cluster.deletePatroniResources()
|
err = cluster.deletePatroniResources()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
cluster.OpConfig.KubernetesUseConfigMaps = false
|
cluster.OpConfig.KubernetesUseConfigMaps = util.False()
|
||||||
err = createPatroniResources(cluster)
|
err = createPatroniResources(cluster)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
err = cluster.Sync(newSpec.DeepCopy())
|
err = cluster.Sync(newSpec.DeepCopy())
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
|
||||||
result.EnableSpiloWalPathCompat = fromCRD.EnableSpiloWalPathCompat
|
result.EnableSpiloWalPathCompat = fromCRD.EnableSpiloWalPathCompat
|
||||||
result.EnableTeamIdClusternamePrefix = fromCRD.EnableTeamIdClusternamePrefix
|
result.EnableTeamIdClusternamePrefix = fromCRD.EnableTeamIdClusternamePrefix
|
||||||
result.EtcdHost = fromCRD.EtcdHost
|
result.EtcdHost = fromCRD.EtcdHost
|
||||||
result.KubernetesUseConfigMaps = fromCRD.KubernetesUseConfigMaps
|
result.KubernetesUseConfigMaps = util.CoalesceBool(fromCRD.KubernetesUseConfigMaps, util.True())
|
||||||
result.DockerImage = util.Coalesce(fromCRD.DockerImage, "ghcr.io/zalando/spilo-18:4.1-p1")
|
result.DockerImage = util.Coalesce(fromCRD.DockerImage, "ghcr.io/zalando/spilo-18:4.1-p1")
|
||||||
result.Workers = util.CoalesceUInt32(fromCRD.Workers, 8)
|
result.Workers = util.CoalesceUInt32(fromCRD.Workers, 8)
|
||||||
result.MinInstances = fromCRD.MinInstances
|
result.MinInstances = fromCRD.MinInstances
|
||||||
|
|
|
||||||
|
|
@ -213,22 +213,10 @@ func (c *Controller) processEvent(event ClusterEvent, isInInitialList bool) {
|
||||||
}
|
}
|
||||||
lg.Debugf("observed cluster status %s, running sync scan to repair the cluster", lastOperationStatus)
|
lg.Debugf("observed cluster status %s, running sync scan to repair the cluster", lastOperationStatus)
|
||||||
event.EventType = EventSync
|
event.EventType = EventSync
|
||||||
}
|
} else if event.EventType != EventDelete {
|
||||||
|
|
||||||
if event.EventType == EventAdd || event.EventType == EventUpdate || event.EventType == EventSync {
|
|
||||||
// handle deprecated parameters by possibly assigning their values to the new ones.
|
|
||||||
if event.OldSpec != nil {
|
|
||||||
c.mergeDeprecatedPostgreSQLSpecParameters(&event.OldSpec.Spec)
|
|
||||||
}
|
|
||||||
if event.NewSpec != nil {
|
|
||||||
c.warnOnDeprecatedPostgreSQLSpecParameters(&event.NewSpec.Spec)
|
|
||||||
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)
|
c.logger.Warnf("pods and/or Patroni may misfunction due to the lack of permissions: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch event.EventType {
|
switch event.EventType {
|
||||||
|
|
@ -397,45 +385,6 @@ func (c *Controller) processClusterEventsQueue(idx int, stopCh <-chan struct{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) warnOnDeprecatedPostgreSQLSpecParameters(spec *acidv1.PostgresSpec) {
|
|
||||||
deprecate := func(deprecated, replacement string) {
|
|
||||||
c.logger.Warningf("parameter %q is deprecated. Consider setting %q instead", deprecated, replacement)
|
|
||||||
}
|
|
||||||
|
|
||||||
if spec.UseLoadBalancer != nil {
|
|
||||||
deprecate("useLoadBalancer", "enableMasterLoadBalancer")
|
|
||||||
}
|
|
||||||
if spec.ReplicaLoadBalancer != nil {
|
|
||||||
deprecate("replicaLoadBalancer", "enableReplicaLoadBalancer")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spec.UseLoadBalancer != nil || spec.ReplicaLoadBalancer != nil) &&
|
|
||||||
(spec.EnableReplicaLoadBalancer != nil || spec.EnableMasterLoadBalancer != nil) {
|
|
||||||
c.logger.Warnf("both old and new load balancer parameters are present in the manifest, ignoring old ones")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// mergeDeprecatedPostgreSQLSpecParameters modifies the spec passed to the cluster by setting current parameter
|
|
||||||
// values from the obsolete ones. Note: while the spec that is modified is a copy made in queueClusterEvent, it is
|
|
||||||
// still a shallow copy, so be extra careful not to modify values pointer fields point to, but copy them instead.
|
|
||||||
func (c *Controller) mergeDeprecatedPostgreSQLSpecParameters(spec *acidv1.PostgresSpec) *acidv1.PostgresSpec {
|
|
||||||
if (spec.UseLoadBalancer != nil || spec.ReplicaLoadBalancer != nil) &&
|
|
||||||
(spec.EnableReplicaLoadBalancer == nil && spec.EnableMasterLoadBalancer == nil) {
|
|
||||||
if spec.UseLoadBalancer != nil {
|
|
||||||
spec.EnableMasterLoadBalancer = new(bool)
|
|
||||||
*spec.EnableMasterLoadBalancer = *spec.UseLoadBalancer
|
|
||||||
}
|
|
||||||
if spec.ReplicaLoadBalancer != nil {
|
|
||||||
spec.EnableReplicaLoadBalancer = new(bool)
|
|
||||||
*spec.EnableReplicaLoadBalancer = *spec.ReplicaLoadBalancer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
spec.ReplicaLoadBalancer = nil
|
|
||||||
spec.UseLoadBalancer = nil
|
|
||||||
|
|
||||||
return spec
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Controller) queueClusterEvent(informerOldSpec, informerNewSpec *acidv1.Postgresql, eventType EventType) {
|
func (c *Controller) queueClusterEvent(informerOldSpec, informerNewSpec *acidv1.Postgresql, eventType EventType) {
|
||||||
var (
|
var (
|
||||||
uid types.UID
|
uid types.UID
|
||||||
|
|
|
||||||
|
|
@ -64,35 +64,6 @@ func TestControllerOwnershipOnPostgresql(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMergeDeprecatedPostgreSQLSpecParameters(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
in *acidv1.PostgresSpec
|
|
||||||
out *acidv1.PostgresSpec
|
|
||||||
error string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
"Check that old parameters propagate values to the new ones",
|
|
||||||
&acidv1.PostgresSpec{UseLoadBalancer: &True, ReplicaLoadBalancer: &True},
|
|
||||||
&acidv1.PostgresSpec{UseLoadBalancer: nil, ReplicaLoadBalancer: nil,
|
|
||||||
EnableMasterLoadBalancer: &True, EnableReplicaLoadBalancer: &True},
|
|
||||||
"New parameters should be set from the values of old ones",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Check that new parameters are not set when both old and new ones are present",
|
|
||||||
&acidv1.PostgresSpec{UseLoadBalancer: &True, EnableMasterLoadBalancer: &False},
|
|
||||||
&acidv1.PostgresSpec{UseLoadBalancer: nil, EnableMasterLoadBalancer: &False},
|
|
||||||
"New parameters should remain unchanged when both old and new are present",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
result := postgresqlTestController.mergeDeprecatedPostgreSQLSpecParameters(tt.in)
|
|
||||||
if !reflect.DeepEqual(result, tt.out) {
|
|
||||||
t.Errorf("%s: %v", tt.name, tt.error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMeetsClusterDeleteAnnotations(t *testing.T) {
|
func TestMeetsClusterDeleteAnnotations(t *testing.T) {
|
||||||
// set delete annotations in configuration
|
// set delete annotations in configuration
|
||||||
postgresqlTestController.opConfig.DeleteAnnotationDateKey = "delete-date"
|
postgresqlTestController.opConfig.DeleteAnnotationDateKey = "delete-date"
|
||||||
|
|
|
||||||
|
|
@ -177,7 +177,7 @@ type Config struct {
|
||||||
ConnectionPooler
|
ConnectionPooler
|
||||||
|
|
||||||
WatchedNamespace string `name:"watched_namespace"` // special values: "*" means 'watch all namespaces', the empty string "" means 'watch a namespace where operator is deployed to'
|
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"`
|
KubernetesUseConfigMaps *bool `name:"kubernetes_use_configmaps" default:"true"`
|
||||||
EtcdHost string `name:"etcd_host" default:""` // special values: the empty string "" means Patroni will use K8s as a DCS
|
EtcdHost string `name:"etcd_host" default:""` // special values: the empty string "" means Patroni will use K8s as a DCS
|
||||||
EnableMaintenanceWindows *bool `name:"enable_maintenance_windows" default:"true"`
|
EnableMaintenanceWindows *bool `name:"enable_maintenance_windows" default:"true"`
|
||||||
MaintenanceWindows []string `name:"maintenance_windows"`
|
MaintenanceWindows []string `name:"maintenance_windows"`
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue