Skip to content

Commit 5b40675

Browse files
committed
Add an API struct representing a single Secret value
This adds validation to the recurring pattern of selecting a single value from a Secret. Note that the "name" field is now required. Secrets are best mounted as files, and the logic for translating these references into volume projections is now consolidated in two exported methods.
1 parent 8b87822 commit 5b40675

File tree

11 files changed

+229
-72
lines changed

11 files changed

+229
-72
lines changed

config/crd/bases/postgres-operator.crunchydata.com_pgadmins.yaml

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -973,24 +973,24 @@ spec:
973973
More info: https://www.pgadmin.org/docs/pgadmin4/latest/external_database.html
974974
properties:
975975
key:
976-
description: The key of the secret to select from. Must be
977-
a valid secret key.
976+
description: Name of the data field within the Secret.
977+
maxLength: 253
978+
minLength: 1
979+
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?([.][a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
978980
type: string
979981
name:
980-
default: ""
981-
description: |-
982-
Name of the referent.
983-
This field is effectively required, but due to backwards compatibility is
984-
allowed to be empty. Instances of this type with an empty value here are
985-
almost certainly wrong.
986-
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
982+
description: Name of the Secret.
983+
maxLength: 253
984+
minLength: 1
985+
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?([.][a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
987986
type: string
988987
optional:
989-
description: Specify whether the Secret or its key must be
990-
defined
988+
description: Whether or not the Secret or its data must be
989+
defined. Defaults to false.
991990
type: boolean
992991
required:
993992
- key
993+
- name
994994
type: object
995995
x-kubernetes-map-type: atomic
996996
files:
@@ -1327,24 +1327,24 @@ spec:
13271327
More info: https://www.pgadmin.org/docs/pgadmin4/latest/ldap.html
13281328
properties:
13291329
key:
1330-
description: The key of the secret to select from. Must be
1331-
a valid secret key.
1330+
description: Name of the data field within the Secret.
1331+
maxLength: 253
1332+
minLength: 1
1333+
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?([.][a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
13321334
type: string
13331335
name:
1334-
default: ""
1335-
description: |-
1336-
Name of the referent.
1337-
This field is effectively required, but due to backwards compatibility is
1338-
allowed to be empty. Instances of this type with an empty value here are
1339-
almost certainly wrong.
1340-
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
1336+
description: Name of the Secret.
1337+
maxLength: 253
1338+
minLength: 1
1339+
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?([.][a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
13411340
type: string
13421341
optional:
1343-
description: Specify whether the Secret or its key must be
1344-
defined
1342+
description: Whether or not the Secret or its data must be
1343+
defined. Defaults to false.
13451344
type: boolean
13461345
required:
13471346
- key
1347+
- name
13481348
type: object
13491349
x-kubernetes-map-type: atomic
13501350
settings:

config/crd/bases/postgres-operator.crunchydata.com_postgresclusters.yaml

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16895,24 +16895,24 @@ spec:
1689516895
More info: https://www.pgadmin.org/docs/pgadmin4/latest/ldap.html
1689616896
properties:
1689716897
key:
16898-
description: The key of the secret to select from. Must
16899-
be a valid secret key.
16898+
description: Name of the data field within the Secret.
16899+
maxLength: 253
16900+
minLength: 1
16901+
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?([.][a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
1690016902
type: string
1690116903
name:
16902-
default: ""
16903-
description: |-
16904-
Name of the referent.
16905-
This field is effectively required, but due to backwards compatibility is
16906-
allowed to be empty. Instances of this type with an empty value here are
16907-
almost certainly wrong.
16908-
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
16904+
description: Name of the Secret.
16905+
maxLength: 253
16906+
minLength: 1
16907+
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?([.][a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
1690916908
type: string
1691016909
optional:
16911-
description: Specify whether the Secret or its key
16912-
must be defined
16910+
description: Whether or not the Secret or its data
16911+
must be defined. Defaults to false.
1691316912
type: boolean
1691416913
required:
1691516914
- key
16915+
- name
1691616916
type: object
1691716917
x-kubernetes-map-type: atomic
1691816918
settings:

internal/controller/standalone_pgadmin/pod.go

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -229,16 +229,9 @@ func podConfigFiles(configmap *corev1.ConfigMap, pgadmin v1beta1.PGAdmin) []core
229229

230230
if pgadmin.Spec.Config.ConfigDatabaseURI != nil {
231231
config = append(config, corev1.VolumeProjection{
232-
Secret: &corev1.SecretProjection{
233-
LocalObjectReference: pgadmin.Spec.Config.ConfigDatabaseURI.LocalObjectReference,
234-
Optional: pgadmin.Spec.Config.ConfigDatabaseURI.Optional,
235-
Items: []corev1.KeyToPath{
236-
{
237-
Key: pgadmin.Spec.Config.ConfigDatabaseURI.Key,
238-
Path: configDatabaseURIPath,
239-
},
240-
},
241-
},
232+
Secret: initialize.Pointer(
233+
pgadmin.Spec.Config.ConfigDatabaseURI.AsProjection(configDatabaseURIPath),
234+
),
242235
})
243236
}
244237

@@ -252,16 +245,9 @@ func podConfigFiles(configmap *corev1.ConfigMap, pgadmin v1beta1.PGAdmin) []core
252245
// - https://www.pgadmin.org/docs/pgadmin4/development/enabling_ldap_authentication.html
253246
if pgadmin.Spec.Config.LDAPBindPassword != nil {
254247
config = append(config, corev1.VolumeProjection{
255-
Secret: &corev1.SecretProjection{
256-
LocalObjectReference: pgadmin.Spec.Config.LDAPBindPassword.LocalObjectReference,
257-
Optional: pgadmin.Spec.Config.LDAPBindPassword.Optional,
258-
Items: []corev1.KeyToPath{
259-
{
260-
Key: pgadmin.Spec.Config.LDAPBindPassword.Key,
261-
Path: ldapFilePath,
262-
},
263-
},
264-
},
248+
Secret: initialize.Pointer(
249+
pgadmin.Spec.Config.LDAPBindPassword.AsProjection(ldapFilePath),
250+
),
265251
})
266252
}
267253

internal/pgadmin/config.go

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
corev1 "k8s.io/api/core/v1"
1111

12+
"github.com/crunchydata/postgres-operator/internal/initialize"
1213
"github.com/crunchydata/postgres-operator/pkg/apis/postgres-operator.crunchydata.com/v1beta1"
1314
)
1415

@@ -94,16 +95,9 @@ func podConfigFiles(configmap *corev1.ConfigMap, spec v1beta1.PGAdminPodSpec) []
9495
// - https://www.pgadmin.org/docs/pgadmin4/development/enabling_ldap_authentication.html
9596
if spec.Config.LDAPBindPassword != nil {
9697
config = append(config, corev1.VolumeProjection{
97-
Secret: &corev1.SecretProjection{
98-
LocalObjectReference: spec.Config.LDAPBindPassword.LocalObjectReference,
99-
Optional: spec.Config.LDAPBindPassword.Optional,
100-
Items: []corev1.KeyToPath{
101-
{
102-
Key: spec.Config.LDAPBindPassword.Key,
103-
Path: ldapPasswordPath,
104-
},
105-
},
106-
},
98+
Secret: initialize.Pointer(
99+
spec.Config.LDAPBindPassword.AsProjection(ldapPasswordPath),
100+
),
107101
})
108102
}
109103

internal/pgadmin/reconcile_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -316,11 +316,11 @@ volumes:
316316
Name: "test",
317317
}},
318318
}}
319-
cluster.Spec.UserInterface.PGAdmin.Config.LDAPBindPassword = &corev1.SecretKeySelector{
320-
LocalObjectReference: corev1.LocalObjectReference{
319+
cluster.Spec.UserInterface.PGAdmin.Config.LDAPBindPassword = &v1beta1.OptionalSecretKeyRef{
320+
SecretKeyRef: v1beta1.SecretKeyRef{
321321
Name: "podtest",
322+
Key: "podtestpw",
322323
},
323-
Key: "podtestpw",
324324
}
325325

326326
call()
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright 2021 - 2025 Crunchy Data Solutions, Inc.
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
package v1beta1
6+
7+
import (
8+
corev1 "k8s.io/api/core/v1"
9+
)
10+
11+
// +structType=atomic
12+
type OptionalSecretKeyRef struct {
13+
SecretKeyRef `json:",inline"`
14+
15+
// Whether or not the Secret or its data must be defined. Defaults to false.
16+
// +optional
17+
Optional *bool `json:"optional,omitempty"`
18+
}
19+
20+
// AsProjection returns a copy of this as a [corev1.SecretProjection].
21+
func (in *OptionalSecretKeyRef) AsProjection(path string) corev1.SecretProjection {
22+
out := in.SecretKeyRef.AsProjection(path)
23+
if in.Optional != nil {
24+
v := *in.Optional
25+
out.Optional = &v
26+
}
27+
return out
28+
}
29+
30+
// +structType=atomic
31+
type SecretKeyRef struct {
32+
// Name of the Secret.
33+
// ---
34+
// https://pkg.go.dev/k8s.io/kubernetes/pkg/apis/core/validation#ValidateSecretName
35+
// +required
36+
Name DNS1123Subdomain `json:"name"`
37+
38+
// Name of the data field within the Secret.
39+
// ---
40+
// https://pkg.go.dev/k8s.io/apimachinery/pkg/util/validation#IsConfigMapKey
41+
// +required
42+
Key DNS1123Subdomain `json:"key"`
43+
}
44+
45+
// AsProjection returns a copy of this as a [corev1.SecretProjection].
46+
func (in *SecretKeyRef) AsProjection(path string) corev1.SecretProjection {
47+
var out corev1.SecretProjection
48+
out.Name = in.Name
49+
out.Items = []corev1.KeyToPath{{Key: in.Key, Path: path}}
50+
return out
51+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// Copyright 2021 - 2025 Crunchy Data Solutions, Inc.
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
package v1beta1_test
6+
7+
import (
8+
"strings"
9+
"testing"
10+
11+
"gotest.tools/v3/assert"
12+
"sigs.k8s.io/yaml"
13+
14+
"github.com/crunchydata/postgres-operator/pkg/apis/postgres-operator.crunchydata.com/v1beta1"
15+
)
16+
17+
func TestOptionalSecretKeyRefAsProjection(t *testing.T) {
18+
t.Run("Null", func(t *testing.T) {
19+
in := v1beta1.OptionalSecretKeyRef{}
20+
in.Name, in.Key = "one", "two"
21+
22+
out := in.AsProjection("three")
23+
b, err := yaml.Marshal(out)
24+
assert.NilError(t, err)
25+
assert.DeepEqual(t, string(b), strings.TrimSpace(`
26+
items:
27+
- key: two
28+
path: three
29+
name: one
30+
`)+"\n")
31+
})
32+
33+
t.Run("True", func(t *testing.T) {
34+
True := true
35+
in := v1beta1.OptionalSecretKeyRef{Optional: &True}
36+
in.Name, in.Key = "one", "two"
37+
38+
out := in.AsProjection("three")
39+
b, err := yaml.Marshal(out)
40+
assert.NilError(t, err)
41+
assert.DeepEqual(t, string(b), strings.TrimSpace(`
42+
items:
43+
- key: two
44+
path: three
45+
name: one
46+
optional: true
47+
`)+"\n")
48+
})
49+
50+
t.Run("False", func(t *testing.T) {
51+
False := false
52+
in := v1beta1.OptionalSecretKeyRef{Optional: &False}
53+
in.Name, in.Key = "one", "two"
54+
55+
out := in.AsProjection("three")
56+
b, err := yaml.Marshal(out)
57+
assert.NilError(t, err)
58+
assert.DeepEqual(t, string(b), strings.TrimSpace(`
59+
items:
60+
- key: two
61+
path: three
62+
name: one
63+
optional: false
64+
`)+"\n")
65+
})
66+
}
67+
68+
func TestSecretKeyRefAsProjection(t *testing.T) {
69+
in := v1beta1.SecretKeyRef{Name: "asdf", Key: "foobar"}
70+
out := in.AsProjection("some-path")
71+
72+
b, err := yaml.Marshal(out)
73+
assert.NilError(t, err)
74+
assert.DeepEqual(t, string(b), strings.TrimSpace(`
75+
items:
76+
- key: foobar
77+
path: some-path
78+
name: asdf
79+
`)+"\n")
80+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ type PGAdminConfiguration struct {
1717
// A Secret containing the value for the LDAP_BIND_PASSWORD setting.
1818
// More info: https://www.pgadmin.org/docs/pgadmin4/latest/ldap.html
1919
// +optional
20-
LDAPBindPassword *corev1.SecretKeySelector `json:"ldapBindPassword,omitempty"`
20+
LDAPBindPassword *OptionalSecretKeyRef `json:"ldapBindPassword,omitempty"`
2121

2222
// Settings for the pgAdmin server process. Keys should be uppercase and
2323
// values must be constants.

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,16 @@ import (
1313
"k8s.io/kube-openapi/pkg/validation/strfmt"
1414
)
1515

16+
// ---
17+
// https://docs.k8s.io/concepts/overview/working-with-objects/names/#dns-subdomain-names
18+
// https://pkg.go.dev/k8s.io/apimachinery/pkg/util/validation#IsDNS1123Subdomain
19+
// https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Format
20+
//
21+
// +kubebuilder:validation:MinLength=1
22+
// +kubebuilder:validation:MaxLength=253
23+
// +kubebuilder:validation:Pattern=`^[a-z0-9]([-a-z0-9]*[a-z0-9])?([.][a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`
24+
type DNS1123Subdomain = string
25+
1626
// ---
1727
// Duration represents a string accepted by the Kubernetes API in the "duration"
1828
// [format]. This format extends the "duration" [defined by OpenAPI] by allowing

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ type StandalonePGAdminConfiguration struct {
1919
// A Secret containing the value for the CONFIG_DATABASE_URI setting.
2020
// More info: https://www.pgadmin.org/docs/pgadmin4/latest/external_database.html
2121
// +optional
22-
ConfigDatabaseURI *corev1.SecretKeySelector `json:"configDatabaseURI,omitempty"`
22+
ConfigDatabaseURI *OptionalSecretKeyRef `json:"configDatabaseURI,omitempty"`
2323

2424
// Settings for the gunicorn server.
2525
// More info: https://docs.gunicorn.org/en/latest/settings.html
@@ -32,7 +32,7 @@ type StandalonePGAdminConfiguration struct {
3232
// A Secret containing the value for the LDAP_BIND_PASSWORD setting.
3333
// More info: https://www.pgadmin.org/docs/pgadmin4/latest/ldap.html
3434
// +optional
35-
LDAPBindPassword *corev1.SecretKeySelector `json:"ldapBindPassword,omitempty"`
35+
LDAPBindPassword *OptionalSecretKeyRef `json:"ldapBindPassword,omitempty"`
3636

3737
// Settings for the pgAdmin server process. Keys should be uppercase and
3838
// values must be constants.

0 commit comments

Comments
 (0)