Skip to content

Commit 6ba9057

Browse files
committed
Change PostgresCluster.spec.config to a pointer
This field is optional, but the struct always includes it in YAML output.
1 parent bfd4160 commit 6ba9057

File tree

12 files changed

+102
-72
lines changed

12 files changed

+102
-72
lines changed

internal/config/config.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@ func defaultFromEnv(value, key string) string {
2222
// FetchKeyCommand returns the fetch_key_cmd value stored in the encryption_key_command
2323
// variable used to enable TDE.
2424
func FetchKeyCommand(spec *v1beta1.PostgresClusterSpec) string {
25-
if parameters := spec.Config.Parameters; parameters != nil {
26-
if v, ok := parameters["encryption_key_command"]; ok {
27-
return v.String()
25+
if config := spec.Config; config != nil {
26+
if parameters := config.Parameters; parameters != nil {
27+
if v, ok := parameters["encryption_key_command"]; ok {
28+
return v.String()
29+
}
2830
}
2931
}
3032

internal/patroni/config.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,8 +255,10 @@ func DynamicConfiguration(
255255
}
256256
}
257257
// Copy spec.config.parameters over spec.patroni...parameters.
258-
for k, v := range spec.Config.Parameters {
259-
parameters[k] = v
258+
if spec.Config != nil {
259+
for k, v := range spec.Config.Parameters {
260+
parameters[k] = v
261+
}
260262
}
261263
// Override all of the above with mandatory parameters.
262264
if pgParameters.Mandatory != nil {

internal/pgbackrest/reconcile.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ func AddConfigToRestorePod(
213213
}
214214

215215
// mount any provided configuration files to the restore Job Pod
216-
if len(cluster.Spec.Config.Files) != 0 {
216+
if cluster.Spec.Config != nil && len(cluster.Spec.Config.Files) != 0 {
217217
additionalConfigVolumeMount := postgres.ConfigVolumeMount()
218218
additionalConfigVolume := corev1.Volume{Name: additionalConfigVolumeMount.Name}
219219
additionalConfigVolume.Projected = &corev1.ProjectedVolumeSource{

internal/pgbackrest/reconcile_test.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -522,8 +522,10 @@ func TestAddConfigToRestorePod(t *testing.T) {
522522
custom.Name = "custom-configmap-files"
523523

524524
cluster := cluster.DeepCopy()
525-
cluster.Spec.Config.Files = []corev1.VolumeProjection{
526-
{ConfigMap: &custom},
525+
cluster.Spec.Config = &v1beta1.PostgresConfig{
526+
Files: []corev1.VolumeProjection{
527+
{ConfigMap: &custom},
528+
},
527529
}
528530

529531
sourceCluster := cluster.DeepCopy()

internal/postgres/reconcile.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ func InstancePod(ctx context.Context,
232232
startup.VolumeMounts = append(startup.VolumeMounts, tablespaceVolumeMount)
233233
}
234234

235-
if len(inCluster.Spec.Config.Files) != 0 {
235+
if inCluster.Spec.Config != nil && len(inCluster.Spec.Config.Files) != 0 {
236236
additionalConfigVolumeMount := ConfigVolumeMount()
237237
additionalConfigVolume := corev1.Volume{Name: additionalConfigVolumeMount.Name}
238238
additionalConfigVolume.Projected = &corev1.ProjectedVolumeSource{

internal/postgres/reconcile_test.go

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"github.com/crunchydata/postgres-operator/internal/initialize"
1717
"github.com/crunchydata/postgres-operator/internal/naming"
1818
"github.com/crunchydata/postgres-operator/internal/testing/cmp"
19+
"github.com/crunchydata/postgres-operator/internal/testing/require"
1920
"github.com/crunchydata/postgres-operator/pkg/apis/postgres-operator.crunchydata.com/v1beta1"
2021
)
2122

@@ -480,15 +481,9 @@ volumes:
480481

481482
t.Run("WithAdditionalConfigFiles", func(t *testing.T) {
482483
clusterWithConfig := cluster.DeepCopy()
483-
clusterWithConfig.Spec.Config.Files = []corev1.VolumeProjection{
484-
{
485-
Secret: &corev1.SecretProjection{
486-
LocalObjectReference: corev1.LocalObjectReference{
487-
Name: "keytab",
488-
},
489-
},
490-
},
491-
}
484+
require.UnmarshalInto(t, &clusterWithConfig.Spec.Config, `{
485+
files: [{ secret: { name: keytab } }],
486+
}`)
492487

493488
pod := new(corev1.PodSpec)
494489
InstancePod(ctx, clusterWithConfig, instance,

internal/testing/require/errors.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright 2021 - 2025 Crunchy Data Solutions, Inc.
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
package require
6+
7+
import (
8+
"errors"
9+
"testing"
10+
11+
"gotest.tools/v3/assert"
12+
apierrors "k8s.io/apimachinery/pkg/api/errors"
13+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
14+
)
15+
16+
// StatusError returns the [metav1.Status] within err's tree.
17+
// It calls t.Fatal when err is nil or there is no status.
18+
func StatusError(t testing.TB, err error) metav1.Status {
19+
status, ok := err.(apierrors.APIStatus)
20+
21+
assert.Assert(t, ok || errors.As(err, &status),
22+
"%T does not implement %T", err, status)
23+
24+
return status.Status()
25+
}
26+
27+
// Value returns v or panics when err is not nil.
28+
func Value[T any](v T, err error) T {
29+
if err != nil {
30+
panic(err)
31+
}
32+
return v
33+
}

internal/testing/validation/pgadmin_test.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,7 @@ func TestPGAdminInstrumentation(t *testing.T) {
4646
assert.ErrorContains(t, err, "hour|day|week")
4747
assert.ErrorContains(t, err, "one hour")
4848

49-
//nolint:errorlint // This is a test, and a panic is unlikely.
50-
status := err.(apierrors.APIStatus).Status()
49+
status := require.StatusError(t, err)
5150
assert.Assert(t, status.Details != nil)
5251
assert.Assert(t, cmp.Len(status.Details.Causes, 2))
5352

internal/testing/validation/postgrescluster_test.go

Lines changed: 41 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,7 @@ func TestPostgresConfigParameters(t *testing.T) {
6060
{"archive_timeout", "20s"},
6161
} {
6262
t.Run(tt.key, func(t *testing.T) {
63-
cluster, err := runtime.ToUnstructuredObject(base)
64-
assert.NilError(t, err)
63+
cluster := require.Value(runtime.ToUnstructuredObject(base))
6564
assert.NilError(t, unstructured.SetNestedField(cluster.Object,
6665
tt.value, "spec", "config", "parameters", tt.key))
6766

@@ -89,16 +88,14 @@ func TestPostgresConfigParameters(t *testing.T) {
8988
{key: "wal_log_hints", value: "off"},
9089
} {
9190
t.Run(tt.key, func(t *testing.T) {
92-
cluster, err := runtime.ToUnstructuredObject(base)
93-
assert.NilError(t, err)
91+
cluster := require.Value(runtime.ToUnstructuredObject(base))
9492
assert.NilError(t, unstructured.SetNestedField(cluster.Object,
9593
tt.value, "spec", "config", "parameters", tt.key))
9694

97-
err = cc.Create(ctx, cluster, client.DryRunAll)
95+
err := cc.Create(ctx, cluster, client.DryRunAll)
9896
assert.Assert(t, apierrors.IsInvalid(err))
9997

100-
//nolint:errorlint // This is a test, and a panic is unlikely.
101-
status := err.(apierrors.APIStatus).Status()
98+
status := require.StatusError(t, err)
10299
assert.Assert(t, status.Details != nil)
103100
assert.Assert(t, cmp.Len(status.Details.Causes, 1))
104101

@@ -112,18 +109,17 @@ func TestPostgresConfigParameters(t *testing.T) {
112109
t.Run("NoConnections", func(t *testing.T) {
113110
for _, tt := range []struct {
114111
key string
115-
value intstr.IntOrString
112+
value any
116113
}{
117-
{key: "ssl", value: intstr.FromString("off")},
118-
{key: "ssl_ca_file", value: intstr.FromString("")},
119-
{key: "unix_socket_directories", value: intstr.FromString("one")},
120-
{key: "unix_socket_group", value: intstr.FromString("two")},
114+
{key: "ssl", value: "off"},
115+
{key: "ssl_ca_file", value: ""},
116+
{key: "unix_socket_directories", value: "one"},
117+
{key: "unix_socket_group", value: "two"},
121118
} {
122119
t.Run(tt.key, func(t *testing.T) {
123-
cluster := base.DeepCopy()
124-
cluster.Spec.Config.Parameters = map[string]intstr.IntOrString{
125-
tt.key: tt.value,
126-
}
120+
cluster := require.Value(runtime.ToUnstructuredObject(base))
121+
assert.NilError(t, unstructured.SetNestedField(cluster.Object,
122+
tt.value, "spec", "config", "parameters", tt.key))
127123

128124
err := cc.Create(ctx, cluster, client.DryRunAll)
129125
assert.Assert(t, apierrors.IsInvalid(err))
@@ -134,19 +130,18 @@ func TestPostgresConfigParameters(t *testing.T) {
134130
t.Run("NoWriteAheadLog", func(t *testing.T) {
135131
for _, tt := range []struct {
136132
key string
137-
value intstr.IntOrString
133+
value any
138134
}{
139-
{key: "archive_mode", value: intstr.FromString("off")},
140-
{key: "archive_command", value: intstr.FromString("true")},
141-
{key: "restore_command", value: intstr.FromString("true")},
142-
{key: "recovery_target", value: intstr.FromString("immediate")},
143-
{key: "recovery_target_name", value: intstr.FromString("doot")},
135+
{key: "archive_mode", value: "off"},
136+
{key: "archive_command", value: "true"},
137+
{key: "restore_command", value: "true"},
138+
{key: "recovery_target", value: "immediate"},
139+
{key: "recovery_target_name", value: "doot"},
144140
} {
145141
t.Run(tt.key, func(t *testing.T) {
146-
cluster := base.DeepCopy()
147-
cluster.Spec.Config.Parameters = map[string]intstr.IntOrString{
148-
tt.key: tt.value,
149-
}
142+
cluster := require.Value(runtime.ToUnstructuredObject(base))
143+
assert.NilError(t, unstructured.SetNestedField(cluster.Object,
144+
tt.value, "spec", "config", "parameters", tt.key))
150145

151146
err := cc.Create(ctx, cluster, client.DryRunAll)
152147
assert.Assert(t, apierrors.IsInvalid(err))
@@ -158,25 +153,28 @@ func TestPostgresConfigParameters(t *testing.T) {
158153
t.Run("Valid", func(t *testing.T) {
159154
cluster := base.DeepCopy()
160155

161-
cluster.Spec.Config.Parameters = map[string]intstr.IntOrString{
162-
"wal_level": intstr.FromString("logical"),
156+
cluster.Spec.Config = &v1beta1.PostgresConfig{
157+
Parameters: map[string]intstr.IntOrString{
158+
"wal_level": intstr.FromString("logical"),
159+
},
163160
}
164161
assert.NilError(t, cc.Create(ctx, cluster, client.DryRunAll))
165162
})
166163

167164
t.Run("Invalid", func(t *testing.T) {
168165
cluster := base.DeepCopy()
169166

170-
cluster.Spec.Config.Parameters = map[string]intstr.IntOrString{
171-
"wal_level": intstr.FromString("minimal"),
167+
cluster.Spec.Config = &v1beta1.PostgresConfig{
168+
Parameters: map[string]intstr.IntOrString{
169+
"wal_level": intstr.FromString("minimal"),
170+
},
172171
}
173172

174173
err := cc.Create(ctx, cluster, client.DryRunAll)
175174
assert.Assert(t, apierrors.IsInvalid(err))
176175
assert.ErrorContains(t, err, `"replica" or higher`)
177176

178-
//nolint:errorlint // This is a test, and a panic is unlikely.
179-
status := err.(apierrors.APIStatus).Status()
177+
status := require.StatusError(t, err)
180178
assert.Assert(t, status.Details != nil)
181179
assert.Assert(t, cmp.Len(status.Details.Causes, 1))
182180
assert.Equal(t, status.Details.Causes[0].Field, "spec.config.parameters")
@@ -187,18 +185,17 @@ func TestPostgresConfigParameters(t *testing.T) {
187185
t.Run("NoReplication", func(t *testing.T) {
188186
for _, tt := range []struct {
189187
key string
190-
value intstr.IntOrString
188+
value any
191189
}{
192-
{key: "synchronous_standby_names", value: intstr.FromString("")},
193-
{key: "primary_conninfo", value: intstr.FromString("")},
194-
{key: "primary_slot_name", value: intstr.FromString("")},
195-
{key: "recovery_min_apply_delay", value: intstr.FromString("")},
190+
{key: "synchronous_standby_names", value: ""},
191+
{key: "primary_conninfo", value: ""},
192+
{key: "primary_slot_name", value: ""},
193+
{key: "recovery_min_apply_delay", value: ""},
196194
} {
197195
t.Run(tt.key, func(t *testing.T) {
198-
cluster := base.DeepCopy()
199-
cluster.Spec.Config.Parameters = map[string]intstr.IntOrString{
200-
tt.key: tt.value,
201-
}
196+
cluster := require.Value(runtime.ToUnstructuredObject(base))
197+
assert.NilError(t, unstructured.SetNestedField(cluster.Object,
198+
tt.value, "spec", "config", "parameters", tt.key))
202199

203200
err := cc.Create(ctx, cluster, client.DryRunAll)
204201
assert.Assert(t, apierrors.IsInvalid(err))
@@ -251,8 +248,7 @@ func TestPostgresUserOptions(t *testing.T) {
251248
assert.Assert(t, apierrors.IsInvalid(err))
252249
assert.ErrorContains(t, err, "cannot contain comments")
253250

254-
//nolint:errorlint // This is a test, and a panic is unlikely.
255-
status := err.(apierrors.APIStatus).Status()
251+
status := require.StatusError(t, err)
256252
assert.Assert(t, status.Details != nil)
257253
assert.Assert(t, cmp.Len(status.Details.Causes, 3))
258254

@@ -273,8 +269,7 @@ func TestPostgresUserOptions(t *testing.T) {
273269
assert.Assert(t, apierrors.IsInvalid(err))
274270
assert.ErrorContains(t, err, "cannot assign password")
275271

276-
//nolint:errorlint // This is a test, and a panic is unlikely.
277-
status := err.(apierrors.APIStatus).Status()
272+
status := require.StatusError(t, err)
278273
assert.Assert(t, status.Details != nil)
279274
assert.Assert(t, cmp.Len(status.Details.Causes, 2))
280275

@@ -294,8 +289,7 @@ func TestPostgresUserOptions(t *testing.T) {
294289
assert.Assert(t, apierrors.IsInvalid(err))
295290
assert.ErrorContains(t, err, "should match")
296291

297-
//nolint:errorlint // This is a test, and a panic is unlikely.
298-
status := err.(apierrors.APIStatus).Status()
292+
status := require.StatusError(t, err)
299293
assert.Assert(t, status.Details != nil)
300294
assert.Assert(t, cmp.Len(status.Details.Causes, 1))
301295
assert.Equal(t, status.Details.Causes[0].Field, "spec.users[0].options")

pkg/apis/postgres-operator.crunchydata.com/v1beta1/postgrescluster_types.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ type PostgresClusterSpec struct {
2525
// +optional
2626
Backups Backups `json:"backups,omitempty"`
2727

28+
// +optional
29+
Config *PostgresConfig `json:"config,omitempty"`
30+
2831
// The secret containing the Certificates and Keys to encrypt PostgreSQL
2932
// traffic will need to contain the server TLS certificate, TLS key and the
3033
// Certificate Authority certificate with the data keys set to tls.crt,
@@ -188,8 +191,6 @@ type PostgresClusterSpec struct {
188191
// +kubebuilder:validation:MaxItems=64
189192
// +optional
190193
Users []PostgresUserSpec `json:"users,omitempty"`
191-
192-
Config PostgresConfig `json:"config,omitempty"`
193194
}
194195

195196
// DataSource defines data sources for a new PostgresCluster.

0 commit comments

Comments
 (0)