This commit is contained in:
Karolin Kostial 2026-05-08 09:57:59 +02:00 committed by GitHub
commit b93ccdd193
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 50 additions and 13 deletions

View File

@ -324,6 +324,12 @@ explanation of `ttl` and `loop_wait` parameters.
custom `pg_hba` should include the pam line to avoid breaking pam custom `pg_hba` should include the pam line to avoid breaking pam
authentication. Optional. authentication. Optional.
* **pg_ident**
list of custom `pg_ident` lines defining user name maps for external
authentication methods (e.g. cert, gss, peer). Each line is of the form
`mapname system-username pg-username`. Patroni manages `pg_ident.conf`
and reloads PostgreSQL when this list changes. Optional.
* **ttl** * **ttl**
Patroni `ttl` parameter value, optional. The default is set by the Spilo Patroni `ttl` parameter value, optional. The default is set by the Spilo
Docker image. Optional. Docker image. Optional.

View File

@ -137,6 +137,9 @@ spec:
# pg_hba: # pg_hba:
# - hostssl all all 0.0.0.0/0 md5 # - hostssl all all 0.0.0.0/0 md5
# - host all all 0.0.0.0/0 md5 # - host all all 0.0.0.0/0 md5
# pg_ident:
# - mymap /^(.*)@example\.com$ \1
# - mymap admin@example.com postgres
# slots: # slots:
# permanent_physical_1: # permanent_physical_1:
# type: physical # type: physical

View File

@ -3484,6 +3484,10 @@ spec:
items: items:
type: string type: string
type: array type: array
pg_ident:
items:
type: string
type: array
retry_timeout: retry_timeout:
format: int32 format: int32
type: integer type: integer

View File

@ -3484,6 +3484,10 @@ spec:
items: items:
type: string type: string
type: array type: array
pg_ident:
items:
type: string
type: array
retry_timeout: retry_timeout:
format: int32 format: int32
type: integer type: integer

View File

@ -235,6 +235,7 @@ type Resources struct {
type Patroni struct { type Patroni struct {
InitDB map[string]string `json:"initdb,omitempty"` InitDB map[string]string `json:"initdb,omitempty"`
PgHba []string `json:"pg_hba,omitempty"` PgHba []string `json:"pg_hba,omitempty"`
PgIdent []string `json:"pg_ident,omitempty"`
TTL uint32 `json:"ttl,omitempty"` TTL uint32 `json:"ttl,omitempty"`
LoopWait uint32 `json:"loop_wait,omitempty"` LoopWait uint32 `json:"loop_wait,omitempty"`
RetryTimeout uint32 `json:"retry_timeout,omitempty"` RetryTimeout uint32 `json:"retry_timeout,omitempty"`

View File

@ -257,6 +257,10 @@ var unmarshalCluster = []struct {
"hostssl all all 0.0.0.0/0 md5", "hostssl all all 0.0.0.0/0 md5",
"host all all 0.0.0.0/0 md5" "host all all 0.0.0.0/0 md5"
], ],
"pg_ident": [
"mymap user1 dbuser1",
"mymap user2 dbuser2"
],
"ttl": 30, "ttl": 30,
"loop_wait": 10, "loop_wait": 10,
"retry_timeout": 10, "retry_timeout": 10,
@ -307,6 +311,7 @@ var unmarshalCluster = []struct {
"data-checksums": "true", "data-checksums": "true",
}, },
PgHba: []string{"hostssl all all 0.0.0.0/0 md5", "host all all 0.0.0.0/0 md5"}, PgHba: []string{"hostssl all all 0.0.0.0/0 md5", "host all all 0.0.0.0/0 md5"},
PgIdent: []string{"mymap user1 dbuser1", "mymap user2 dbuser2"},
TTL: 30, TTL: 30,
LoopWait: 10, LoopWait: 10,
RetryTimeout: 10, RetryTimeout: 10,
@ -346,7 +351,7 @@ var unmarshalCluster = []struct {
}, },
Error: "", 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":""}}`), 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"],"pg_ident":["mymap user1 dbuser1","mymap user2 dbuser2"],"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}, err: nil},
{ {
about: "example with clone", about: "example with clone",

View File

@ -587,6 +587,11 @@ func (in *Patroni) DeepCopyInto(out *Patroni) {
*out = make([]string, len(*in)) *out = make([]string, len(*in))
copy(*out, *in) copy(*out, *in)
} }
if in.PgIdent != nil {
in, out := &in.PgIdent, &out.PgIdent
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Slots != nil { if in.Slots != nil {
in, out := &in.Slots, &out.Slots in, out := &in.Slots, &out.Slots
*out = make(map[string]map[string]string, len(*in)) *out = make(map[string]map[string]string, len(*in))

View File

@ -35,15 +35,16 @@ import (
) )
const ( const (
pgBinariesLocationTemplate = "/usr/lib/postgresql/%v/bin" pgBinariesLocationTemplate = "/usr/lib/postgresql/%v/bin"
patroniPGBinariesParameterName = "bin_dir" patroniPGBinariesParameterName = "bin_dir"
patroniPGHBAConfParameterName = "pg_hba" patroniPGHBAConfParameterName = "pg_hba"
localHost = "127.0.0.1/32" patroniPGIdentConfParameterName = "pg_ident"
scalyrSidecarName = "scalyr-sidecar" localHost = "127.0.0.1/32"
logicalBackupContainerName = "logical-backup" scalyrSidecarName = "scalyr-sidecar"
connectionPoolerContainer = "connection-pooler" logicalBackupContainerName = "logical-backup"
pgPort = 5432 connectionPoolerContainer = "connection-pooler"
operatorPort = 8080 pgPort = 5432
operatorPort = 8080
) )
type patroniDCS struct { type patroniDCS struct {
@ -469,12 +470,16 @@ PatroniInitDBParams:
config.Bootstrap.DCS.PGBootstrapConfiguration[constants.PatroniPGParametersParameterName] = bootstrap config.Bootstrap.DCS.PGBootstrapConfiguration[constants.PatroniPGParametersParameterName] = bootstrap
} }
} }
// Patroni gives us a choice of writing pg_hba.conf to either the bootstrap section or to the local postgresql one.
// We choose the local one, because we need Patroni to change pg_hba.conf in PostgreSQL after the user changes the // Patroni gives us a choice of writing pg_hba.conf and pg_ident.conf to either the bootstrap section or to the local postgresql one.
// We choose the local one, because we need Patroni to change them in PostgreSQL after the user changes the
// relevant section in the manifest. // relevant section in the manifest.
if len(patroni.PgHba) > 0 { if len(patroni.PgHba) > 0 {
config.PgLocalConfiguration[patroniPGHBAConfParameterName] = patroni.PgHba config.PgLocalConfiguration[patroniPGHBAConfParameterName] = patroni.PgHba
} }
if len(patroni.PgIdent) > 0 {
config.PgLocalConfiguration[patroniPGIdentConfParameterName] = patroni.PgIdent
}
res, err := json.Marshal(config) res, err := json.Marshal(config)
return string(res), err return string(res), err

View File

@ -91,6 +91,7 @@ func TestGenerateSpiloJSONConfiguration(t *testing.T) {
"data-checksums": "true", "data-checksums": "true",
}, },
PgHba: []string{"hostssl all all 0.0.0.0/0 md5", "host all all 0.0.0.0/0 md5"}, PgHba: []string{"hostssl all all 0.0.0.0/0 md5", "host all all 0.0.0.0/0 md5"},
PgIdent: []string{"mymap user1 dbuser1", "mymap user2 dbuser2"},
TTL: 30, TTL: 30,
LoopWait: 10, LoopWait: 10,
RetryTimeout: 10, RetryTimeout: 10,
@ -102,7 +103,7 @@ func TestGenerateSpiloJSONConfiguration(t *testing.T) {
FailsafeMode: util.True(), FailsafeMode: util.True(),
}, },
opConfig: &config.Config{}, opConfig: &config.Config{},
result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/18/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"}],"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/18/bin","pg_hba":["hostssl all all 0.0.0.0/0 md5","host all all 0.0.0.0/0 md5"],"pg_ident":["mymap user1 dbuser1","mymap user2 dbuser2"]},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"},"data-checksums",{"encoding":"UTF8"},{"locale":"en_US.UTF-8"}],"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", subtest: "Patroni failsafe_mode configured globally",

View File

@ -917,6 +917,9 @@ func (c *Cluster) checkAndSetGlobalPostgreSQLConfiguration(pod *v1.Pod, effectiv
if desiredPatroniConfig.PgHba != nil && !reflect.DeepEqual(desiredPatroniConfig.PgHba, effectivePatroniConfig.PgHba) { if desiredPatroniConfig.PgHba != nil && !reflect.DeepEqual(desiredPatroniConfig.PgHba, effectivePatroniConfig.PgHba) {
configToSet["pg_hba"] = desiredPatroniConfig.PgHba configToSet["pg_hba"] = desiredPatroniConfig.PgHba
} }
if desiredPatroniConfig.PgIdent != nil && !reflect.DeepEqual(desiredPatroniConfig.PgIdent, effectivePatroniConfig.PgIdent) {
configToSet["pg_ident"] = desiredPatroniConfig.PgIdent
}
if desiredPatroniConfig.RetryTimeout > 0 && desiredPatroniConfig.RetryTimeout != effectivePatroniConfig.RetryTimeout { if desiredPatroniConfig.RetryTimeout > 0 && desiredPatroniConfig.RetryTimeout != effectivePatroniConfig.RetryTimeout {
configToSet["retry_timeout"] = desiredPatroniConfig.RetryTimeout configToSet["retry_timeout"] = desiredPatroniConfig.RetryTimeout
} }