Skip to content

Commit e6beb45

Browse files
committed
Add validation rule that ensures that all instances have an additional volume when attempting to log pgbackrest sidecar to /volumes. Add test cases.
1 parent 06c0bde commit e6beb45

File tree

3 files changed

+109
-14
lines changed

3 files changed

+109
-14
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18453,6 +18453,11 @@ spec:
1845318453
message: all instances need an additional volume to log in "/volumes"
1845418454
rule: self.?config.parameters.log_directory.optMap(v, type(v) != string
1845518455
|| !v.startsWith("/volumes") || self.instances.all(i, i.?volumes.additional.hasValue())).orValue(true)
18456+
- fieldPath: .backups.pgbackrest.log.path
18457+
message: all instances need an additional volume for pgbackrest sidecar
18458+
to log in "/volumes"
18459+
rule: self.?backups.pgbackrest.log.path.optMap(v, !v.startsWith("/volumes")
18460+
|| self.instances.all(i, i.?volumes.additional.hasValue())).orValue(true)
1845618461
status:
1845718462
description: PostgresClusterStatus defines the observed state of PostgresCluster
1845818463
properties:

internal/testing/validation/pgbackrest_test.go

Lines changed: 97 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -63,23 +63,106 @@ func TestV1PGBackRestLogging(t *testing.T) {
6363
t.Run("Cannot set pgbackrest sidecar's log.path without correct subdir", func(t *testing.T) {
6464
tmp := base.DeepCopy()
6565

66-
require.UnmarshalInto(t, &tmp.Spec.Backups.PGBackRest, `{
67-
log: {
68-
path: "/something/wrong"
69-
}
70-
}`)
66+
t.Run("Wrong subdir", func(t *testing.T) {
67+
require.UnmarshalInto(t, &tmp.Spec.Backups.PGBackRest, `{
68+
log: {
69+
path: "/something/wrong"
70+
}
71+
}`)
7172

72-
err := cc.Create(ctx, tmp.DeepCopy(), client.DryRunAll)
73-
assert.Assert(t, apierrors.IsInvalid(err))
74-
assert.ErrorContains(t, err, "pgbackrest sidecar log path is restricted to an existing additional volume")
73+
err := cc.Create(ctx, tmp.DeepCopy(), client.DryRunAll)
74+
assert.Assert(t, apierrors.IsInvalid(err))
75+
assert.ErrorContains(t, err, "pgbackrest sidecar log path is restricted to an existing additional volume")
76+
})
7577

76-
require.UnmarshalInto(t, &tmp.Spec.Backups.PGBackRest, `{
77-
log: {
78-
path: "/volumes/this/should/pass"
79-
}
80-
}`)
78+
t.Run("Single instance - missing additional volume", func(t *testing.T) {
79+
require.UnmarshalInto(t, &tmp.Spec.Backups.PGBackRest, `{
80+
log: {
81+
path: "/volumes/anything"
82+
}
83+
}`)
8184

82-
assert.NilError(t, cc.Create(ctx, tmp.DeepCopy(), client.DryRunAll), "expected log.path to be valid")
85+
err := cc.Create(ctx, tmp.DeepCopy(), client.DryRunAll)
86+
assert.Assert(t, apierrors.IsInvalid(err))
87+
assert.ErrorContains(t, err, `all instances need an additional volume for pgbackrest sidecar to log in "/volumes"`)
88+
})
89+
90+
t.Run("Multiple instances - one missing additional volume", func(t *testing.T) {
91+
require.UnmarshalInto(t, &tmp.Spec.Backups.PGBackRest, `{
92+
log: {
93+
path: "/volumes/anything"
94+
}
95+
}`)
96+
97+
require.UnmarshalInto(t, &tmp.Spec.InstanceSets, `[{
98+
dataVolumeClaimSpec: {
99+
accessModes: [ReadWriteOnce],
100+
resources: { requests: { storage: 1Mi } },
101+
},
102+
volumes: {
103+
additional: [{
104+
name: "test",
105+
claimName: "pvc-claim"
106+
}]
107+
}
108+
},{
109+
name: "instance2",
110+
dataVolumeClaimSpec: {
111+
accessModes: [ReadWriteOnce],
112+
resources: { requests: { storage: 1Mi } },
113+
},
114+
}]`)
115+
116+
err := cc.Create(ctx, tmp.DeepCopy(), client.DryRunAll)
117+
assert.Assert(t, apierrors.IsInvalid(err))
118+
assert.ErrorContains(t, err, `all instances need an additional volume for pgbackrest sidecar to log in "/volumes"`)
119+
})
120+
121+
t.Run("Single instance - additional volume present", func(t *testing.T) {
122+
require.UnmarshalInto(t, &tmp.Spec.InstanceSets, `[{
123+
dataVolumeClaimSpec: {
124+
accessModes: [ReadWriteOnce],
125+
resources: { requests: { storage: 1Mi } },
126+
},
127+
volumes: {
128+
additional: [{
129+
name: "test",
130+
claimName: "pvc-claim"
131+
}]
132+
}
133+
}]`)
134+
135+
assert.NilError(t, cc.Create(ctx, tmp.DeepCopy(), client.DryRunAll), "expected log.path to be valid")
136+
})
137+
138+
t.Run("Multiple instances - additional volume present", func(t *testing.T) {
139+
require.UnmarshalInto(t, &tmp.Spec.InstanceSets, `[{
140+
dataVolumeClaimSpec: {
141+
accessModes: [ReadWriteOnce],
142+
resources: { requests: { storage: 1Mi } },
143+
},
144+
volumes: {
145+
additional: [{
146+
name: "test",
147+
claimName: "pvc-claim"
148+
}]
149+
}
150+
},{
151+
name: "instance2",
152+
dataVolumeClaimSpec: {
153+
accessModes: [ReadWriteOnce],
154+
resources: { requests: { storage: 1Mi } },
155+
},
156+
volumes: {
157+
additional: [{
158+
name: "another",
159+
claimName: "another-pvc-claim"
160+
}]
161+
}
162+
}]`)
163+
164+
assert.NilError(t, cc.Create(ctx, tmp.DeepCopy(), client.DryRunAll), "expected log.path to be valid")
165+
})
83166
})
84167

85168
t.Run("Cannot set logging on volumes that don't exist", func(t *testing.T) {

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@ import (
2626
//
2727
// TODO: Also check the above path against volume names: `i.?volumes.additional.hasValue() && i.volumes.additional.exists(directory.startsWith("/volumes/" + volume.name))`
2828
// https://github.com/kubernetes-sigs/controller-tools/pull/1270#issuecomment-3272211184
29+
//
30+
// # pgBackRest Logging
31+
//
32+
// +kubebuilder:validation:XValidation:fieldPath=`.backups.pgbackrest.log.path`,message=`all instances need an additional volume for pgbackrest sidecar to log in "/volumes"`,rule=`self.?backups.pgbackrest.log.path.optMap(v, !v.startsWith("/volumes") || self.instances.all(i, i.?volumes.additional.hasValue())).orValue(true)`
33+
//
34+
// TODO: Also check the above path against volume names: `i.?volumes.additional.hasValue() && i.volumes.additional.exists(a, v.startsWith("/volumes/" + a.name))`
35+
// https://github.com/kubernetes-sigs/controller-tools/pull/1270#issuecomment-3272211184
2936
type PostgresClusterSpec struct {
3037
// +optional
3138
Metadata *v1beta1.Metadata `json:"metadata,omitempty"`

0 commit comments

Comments
 (0)