Skip to content

Commit 9b7ac1b

Browse files
committed
Make ImageVolumeSource.Reference required via OpenAPI
OpenAPI validation does not count toward the CRD XValidation CEL budget.
1 parent da69a57 commit 9b7ac1b

12 files changed

+233
-115
lines changed

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

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2620,7 +2620,9 @@ spec:
26202620
type: array
26212621
x-kubernetes-list-type: set
26222622
image:
2623-
description: Details for adding an image volume
2623+
description: |-
2624+
Reference to an image or OCI artifact.
2625+
More info: https://kubernetes.io/docs/concepts/storage/volumes#image
26242626
properties:
26252627
pullPolicy:
26262628
description: |-
@@ -2629,6 +2631,11 @@ spec:
26292631
Never: the kubelet never pulls the reference and only uses a local image or artifact. Container creation will fail if the reference isn't present.
26302632
IfNotPresent: the kubelet pulls if the reference isn't already present on disk. Container creation will fail if the reference isn't present and the pull fails.
26312633
Defaults to Always if :latest tag is specified, or IfNotPresent otherwise.
2634+
enum:
2635+
- Always
2636+
- Never
2637+
- IfNotPresent
2638+
maxLength: 12
26322639
type: string
26332640
reference:
26342641
description: |-
@@ -2638,7 +2645,10 @@ spec:
26382645
More info: https://kubernetes.io/docs/concepts/containers/images
26392646
This field is optional to allow higher level config management to default or override
26402647
container images in workload controllers like Deployments and StatefulSets.
2648+
minLength: 1
26412649
type: string
2650+
required:
2651+
- reference
26422652
type: object
26432653
name:
26442654
description: |-
@@ -2659,11 +2669,8 @@ spec:
26592669
x-kubernetes-validations:
26602670
- message: you must set only one of image or claimName
26612671
rule: has(self.claimName) != has(self.image)
2662-
- message: readOnly cannot be set false when using an ImageVolumeSource
2672+
- message: image volumes must be readOnly
26632673
rule: '!has(self.image) || !has(self.readOnly) || self.readOnly'
2664-
- message: if using an ImageVolumeSource, you must set a reference
2665-
rule: '!has(self.image) || (self.?image.reference.hasValue()
2666-
&& self.image.reference.size() > 0)'
26672674
maxItems: 10
26682675
type: array
26692676
x-kubernetes-list-map-keys:

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

Lines changed: 168 additions & 98 deletions
Large diffs are not rendered by default.

internal/crd/post-process.jq

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,26 @@
33
# SPDX-License-Identifier: Apache-2.0
44
#
55
# This [jq] filter modifies a Kubernetes CustomResourceDefinition.
6+
# Use the controller-gen "+kubebuilder:title" marker to identify schemas that need special manipulation.
67
#
78
# [jq]: https://jqlang.org
89

910
# merge recursively combines a stream of objects.
1011
# https://jqlang.org/manual#multiplication-division-modulo
1112
def merge(stream): reduce stream as $i ({}; . * $i);
1213

14+
# https://pkg.go.dev/k8s.io/api/core/v1#ImageVolumeSource
15+
reduce paths(try .title == "$corev1.ImageVolumeSource") as $path (.;
16+
getpath($path) as $schema |
17+
setpath($path; $schema * {
18+
required: (["reference"] + ($schema.required // []) | sort),
19+
properties: {
20+
pullPolicy: { enum: ["Always", "Never", "IfNotPresent"] },
21+
reference: { minLength: 1 }
22+
}
23+
} | del(.title))
24+
) |
25+
1326
# Kubernetes assumes the evaluation cost of an enum value is very large: https://issue.k8s.io/119511
1427
# Look at every schema that has a populated "enum" property.
1528
reduce paths(try .enum | length > 0) as $path (.;
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

internal/testing/validation/postgrescluster_test.go renamed to internal/crd/validation/postgrescluster_test.go

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"sigs.k8s.io/controller-runtime/pkg/client"
1515
"sigs.k8s.io/yaml"
1616

17+
"github.com/crunchydata/postgres-operator/internal/testing/cmp"
1718
"github.com/crunchydata/postgres-operator/internal/testing/require"
1819
v1 "github.com/crunchydata/postgres-operator/pkg/apis/postgres-operator.crunchydata.com/v1"
1920
"github.com/crunchydata/postgres-operator/pkg/apis/postgres-operator.crunchydata.com/v1beta1"
@@ -110,6 +111,7 @@ func TestPostgresUserInterfaceAcrossVersions(t *testing.T) {
110111
func TestAdditionalVolumes(t *testing.T) {
111112
ctx := context.Background()
112113
cc := require.KubernetesAtLeast(t, "1.30")
114+
dryrun := client.NewDryRunClient(cc)
113115
t.Parallel()
114116

115117
namespace := require.Namespace(t, cc)
@@ -154,8 +156,13 @@ func TestAdditionalVolumes(t *testing.T) {
154156
}]
155157
}
156158
}]`, "spec", "instances")
157-
err := cc.Create(ctx, tmp.DeepCopy(), client.DryRunAll)
159+
160+
err := dryrun.Create(ctx, tmp.DeepCopy())
158161
assert.Assert(t, apierrors.IsInvalid(err))
162+
163+
details := require.StatusErrorDetails(t, err)
164+
assert.Assert(t, cmp.Len(details.Causes, 1))
165+
assert.Equal(t, details.Causes[0].Field, "spec.instances[0].volumes.additional[0]")
159166
assert.ErrorContains(t, err, "you must set only one of image or claimName")
160167
})
161168

@@ -178,9 +185,14 @@ func TestAdditionalVolumes(t *testing.T) {
178185
}]
179186
}
180187
}]`, "spec", "instances")
181-
err := cc.Create(ctx, tmp.DeepCopy(), client.DryRunAll)
188+
189+
err := dryrun.Create(ctx, tmp.DeepCopy())
182190
assert.Assert(t, apierrors.IsInvalid(err))
183-
assert.ErrorContains(t, err, "readOnly cannot be set false when using an ImageVolumeSource")
191+
192+
details := require.StatusErrorDetails(t, err)
193+
assert.Assert(t, cmp.Len(details.Causes, 1))
194+
assert.Equal(t, details.Causes[0].Field, "spec.instances[0].volumes.additional[0]")
195+
assert.ErrorContains(t, err, "image volumes must be readOnly")
184196
})
185197

186198
t.Run("Reference must be set when using image volume", func(t *testing.T) {
@@ -201,9 +213,15 @@ func TestAdditionalVolumes(t *testing.T) {
201213
}]
202214
}
203215
}]`, "spec", "instances")
204-
err := cc.Create(ctx, tmp.DeepCopy(), client.DryRunAll)
216+
217+
err := dryrun.Create(ctx, tmp.DeepCopy())
205218
assert.Assert(t, apierrors.IsInvalid(err))
206-
assert.ErrorContains(t, err, "if using an ImageVolumeSource, you must set a reference")
219+
220+
details := require.StatusErrorDetails(t, err)
221+
assert.Assert(t, cmp.Len(details.Causes, 2))
222+
assert.Assert(t, cmp.Equal(details.Causes[0].Field, "spec.instances[0].volumes.additional[0].image.reference"))
223+
assert.Assert(t, cmp.Equal(details.Causes[0].Type, "FieldValueRequired"))
224+
assert.ErrorContains(t, err, "Required")
207225
})
208226

209227
t.Run("Reference cannot be an empty string when using image volume", func(t *testing.T) {
@@ -225,9 +243,15 @@ func TestAdditionalVolumes(t *testing.T) {
225243
}]
226244
}
227245
}]`, "spec", "instances")
228-
err := cc.Create(ctx, tmp.DeepCopy(), client.DryRunAll)
246+
247+
err := dryrun.Create(ctx, tmp.DeepCopy())
229248
assert.Assert(t, apierrors.IsInvalid(err))
230-
assert.ErrorContains(t, err, "if using an ImageVolumeSource, you must set a reference")
249+
250+
details := require.StatusErrorDetails(t, err)
251+
assert.Assert(t, cmp.Len(details.Causes, 1))
252+
assert.Assert(t, cmp.Equal(details.Causes[0].Field, "spec.instances[0].volumes.additional[0].image.reference"))
253+
assert.Assert(t, cmp.Equal(details.Causes[0].Type, "FieldValueInvalid"))
254+
assert.ErrorContains(t, err, "at least 1 chars long")
231255
})
232256

233257
t.Run("ReadOnly can be omitted or set true when using image volume", func(t *testing.T) {
@@ -265,6 +289,6 @@ func TestAdditionalVolumes(t *testing.T) {
265289
}]
266290
}
267291
}]`, "spec", "instances")
268-
assert.NilError(t, cc.Create(ctx, tmp.DeepCopy(), client.DryRunAll))
292+
assert.NilError(t, dryrun.Create(ctx, tmp.DeepCopy()))
269293
})
270294
}

0 commit comments

Comments
 (0)