mirror of
https://github.com/zalando/postgres-operator.git
synced 2026-09-30 09:41:39 +02:00
Standby section improvements (#3033)
- Allow standby_host to be specified together with wal_path - Add standby_primary_slot_name
This commit is contained in:
@@ -246,13 +246,15 @@ type Patroni struct {
|
||||
FailsafeMode *bool `json:"failsafe_mode,omitempty"`
|
||||
}
|
||||
|
||||
// StandbyDescription contains remote primary config or s3/gs wal path
|
||||
// +kubebuilder:validation:ExactlyOneOf=s3_wal_path;gs_wal_path;standby_host
|
||||
// StandbyDescription contains remote primary config and/or s3/gs wal path.
|
||||
// standby_host can be specified alone or together with either s3_wal_path OR gs_wal_path (mutually exclusive).
|
||||
// At least one field must be specified. s3_wal_path and gs_wal_path are mutually exclusive.
|
||||
type StandbyDescription struct {
|
||||
S3WalPath string `json:"s3_wal_path,omitempty"`
|
||||
GSWalPath string `json:"gs_wal_path,omitempty"`
|
||||
StandbyHost string `json:"standby_host,omitempty"`
|
||||
StandbyPort string `json:"standby_port,omitempty"`
|
||||
S3WalPath string `json:"s3_wal_path,omitempty"`
|
||||
GSWalPath string `json:"gs_wal_path,omitempty"`
|
||||
StandbyHost string `json:"standby_host,omitempty"`
|
||||
StandbyPort string `json:"standby_port,omitempty"`
|
||||
StandbyPrimarySlotName string `json:"standby_primary_slot_name,omitempty"`
|
||||
}
|
||||
|
||||
// TLSDescription specs TLS properties
|
||||
|
||||
+19
-13
@@ -2207,23 +2207,29 @@ func (c *Cluster) generateStandbyEnvironment(description *acidv1.StandbyDescript
|
||||
Value: description.StandbyPort,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
c.logger.Info("standby cluster streaming from WAL location")
|
||||
if description.S3WalPath != "" {
|
||||
if description.StandbyPrimarySlotName != "" {
|
||||
result = append(result, v1.EnvVar{
|
||||
Name: "STANDBY_WALE_S3_PREFIX",
|
||||
Value: description.S3WalPath,
|
||||
Name: "STANDBY_PRIMARY_SLOT_NAME",
|
||||
Value: description.StandbyPrimarySlotName,
|
||||
})
|
||||
} else if description.GSWalPath != "" {
|
||||
result = append(result, v1.EnvVar{
|
||||
Name: "STANDBY_WALE_GS_PREFIX",
|
||||
Value: description.GSWalPath,
|
||||
})
|
||||
} else {
|
||||
c.logger.Error("no WAL path specified in standby section")
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
// WAL archive can be specified with or without standby_host
|
||||
if description.S3WalPath != "" {
|
||||
c.logger.Info("standby cluster using S3 WAL archive")
|
||||
result = append(result, v1.EnvVar{
|
||||
Name: "STANDBY_WALE_S3_PREFIX",
|
||||
Value: description.S3WalPath,
|
||||
})
|
||||
result = append(result, v1.EnvVar{Name: "STANDBY_METHOD", Value: "STANDBY_WITH_WALE"})
|
||||
result = append(result, v1.EnvVar{Name: "STANDBY_WAL_BUCKET_SCOPE_PREFIX", Value: ""})
|
||||
} else if description.GSWalPath != "" {
|
||||
c.logger.Info("standby cluster using GCS WAL archive")
|
||||
result = append(result, v1.EnvVar{
|
||||
Name: "STANDBY_WALE_GS_PREFIX",
|
||||
Value: description.GSWalPath,
|
||||
})
|
||||
result = append(result, v1.EnvVar{Name: "STANDBY_METHOD", Value: "STANDBY_WITH_WALE"})
|
||||
result = append(result, v1.EnvVar{Name: "STANDBY_WAL_BUCKET_SCOPE_PREFIX", Value: ""})
|
||||
}
|
||||
|
||||
@@ -1370,7 +1370,33 @@ func TestStandbyEnv(t *testing.T) {
|
||||
envLen: 2,
|
||||
},
|
||||
{
|
||||
subTest: "from remote primary - ignore WAL path",
|
||||
subTest: "from remote primary with S3 WAL path",
|
||||
standbyOpts: &acidv1.StandbyDescription{
|
||||
S3WalPath: "s3://some/path/",
|
||||
StandbyHost: "remote-primary",
|
||||
},
|
||||
env: v1.EnvVar{
|
||||
Name: "STANDBY_HOST",
|
||||
Value: "remote-primary",
|
||||
},
|
||||
envPos: 0,
|
||||
envLen: 4,
|
||||
},
|
||||
{
|
||||
subTest: "verify S3 WAL env with standby host",
|
||||
standbyOpts: &acidv1.StandbyDescription{
|
||||
S3WalPath: "s3://some/path/",
|
||||
StandbyHost: "remote-primary",
|
||||
},
|
||||
env: v1.EnvVar{
|
||||
Name: "STANDBY_WALE_S3_PREFIX",
|
||||
Value: "s3://some/path/",
|
||||
},
|
||||
envPos: 1,
|
||||
envLen: 4,
|
||||
},
|
||||
{
|
||||
subTest: "from remote primary with GCS WAL path",
|
||||
standbyOpts: &acidv1.StandbyDescription{
|
||||
GSWalPath: "gs://some/path/",
|
||||
StandbyHost: "remote-primary",
|
||||
@@ -1380,7 +1406,20 @@ func TestStandbyEnv(t *testing.T) {
|
||||
Value: "remote-primary",
|
||||
},
|
||||
envPos: 0,
|
||||
envLen: 1,
|
||||
envLen: 4,
|
||||
},
|
||||
{
|
||||
subTest: "from remote primary with slot name",
|
||||
standbyOpts: &acidv1.StandbyDescription{
|
||||
StandbyHost: "remote-primary",
|
||||
StandbyPrimarySlotName: "my_slot",
|
||||
},
|
||||
env: v1.EnvVar{
|
||||
Name: "STANDBY_PRIMARY_SLOT_NAME",
|
||||
Value: "my_slot",
|
||||
},
|
||||
envPos: 1,
|
||||
envLen: 2,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -1031,6 +1031,23 @@ func (c *Cluster) syncStandbyClusterConfiguration() error {
|
||||
standbyOptionsToSet["create_replica_methods"] = []string{"bootstrap_standby_with_wale", "basebackup_fast_xlog"}
|
||||
standbyOptionsToSet["restore_command"] = "envdir \"/run/etc/wal-e.d/env-standby\" /scripts/restore_command.sh \"%f\" \"%p\""
|
||||
|
||||
if c.Spec.StandbyCluster.StandbyHost != "" {
|
||||
standbyOptionsToSet["host"] = c.Spec.StandbyCluster.StandbyHost
|
||||
} else {
|
||||
standbyOptionsToSet["host"] = nil
|
||||
}
|
||||
|
||||
if c.Spec.StandbyCluster.StandbyPort != "" {
|
||||
standbyOptionsToSet["port"] = c.Spec.StandbyCluster.StandbyPort
|
||||
} else {
|
||||
standbyOptionsToSet["port"] = nil
|
||||
}
|
||||
|
||||
if c.Spec.StandbyCluster.StandbyPrimarySlotName != "" {
|
||||
standbyOptionsToSet["primary_slot_name"] = c.Spec.StandbyCluster.StandbyPrimarySlotName
|
||||
} else {
|
||||
standbyOptionsToSet["primary_slot_name"] = nil
|
||||
}
|
||||
} else {
|
||||
c.logger.Infof("promoting standby cluster and detach from source")
|
||||
standbyOptionsToSet = nil
|
||||
|
||||
@@ -801,6 +801,41 @@ func TestSyncStandbyClusterConfiguration(t *testing.T) {
|
||||
// this should update the Patroni config again
|
||||
err = cluster.syncStandbyClusterConfiguration()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// test with standby_host, standby_port and standby_primary_slot_name
|
||||
cluster.Spec.StandbyCluster = &acidv1.StandbyDescription{
|
||||
StandbyHost: "remote-primary.example.com",
|
||||
StandbyPort: "5433",
|
||||
StandbyPrimarySlotName: "standby_slot",
|
||||
}
|
||||
cluster.syncStatefulSet()
|
||||
updatedSts4 := cluster.Statefulset
|
||||
|
||||
// check that pods have all three STANDBY_* environment variables
|
||||
assert.Contains(t, updatedSts4.Spec.Template.Spec.Containers[0].Env, v1.EnvVar{Name: "STANDBY_HOST", Value: "remote-primary.example.com"})
|
||||
assert.Contains(t, updatedSts4.Spec.Template.Spec.Containers[0].Env, v1.EnvVar{Name: "STANDBY_PORT", Value: "5433"})
|
||||
assert.Contains(t, updatedSts4.Spec.Template.Spec.Containers[0].Env, v1.EnvVar{Name: "STANDBY_PRIMARY_SLOT_NAME", Value: "standby_slot"})
|
||||
|
||||
// this should update the Patroni config with host, port and primary_slot_name
|
||||
err = cluster.syncStandbyClusterConfiguration()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// test property deletion: remove standby_primary_slot_name
|
||||
cluster.Spec.StandbyCluster = &acidv1.StandbyDescription{
|
||||
StandbyHost: "remote-primary.example.com",
|
||||
StandbyPort: "5433",
|
||||
}
|
||||
cluster.syncStatefulSet()
|
||||
updatedSts5 := cluster.Statefulset
|
||||
|
||||
// check that STANDBY_PRIMARY_SLOT_NAME is not present
|
||||
assert.Contains(t, updatedSts5.Spec.Template.Spec.Containers[0].Env, v1.EnvVar{Name: "STANDBY_HOST", Value: "remote-primary.example.com"})
|
||||
assert.Contains(t, updatedSts5.Spec.Template.Spec.Containers[0].Env, v1.EnvVar{Name: "STANDBY_PORT", Value: "5433"})
|
||||
assert.NotContains(t, updatedSts5.Spec.Template.Spec.Containers[0].Env, v1.EnvVar{Name: "STANDBY_PRIMARY_SLOT_NAME", Value: "standby_slot"})
|
||||
|
||||
// this should update the Patroni config and set primary_slot_name to nil
|
||||
err = cluster.syncStandbyClusterConfiguration()
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestUpdateSecret(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user