Skip to content

Commit 9758f5c

Browse files
aerfioalvaroaleman
andauthored
✨ Add FieldValidation setting to client.Options (#3393)
* add FieldValidation field to client.Options struct * improve new field documentation * Update pkg/client/client.go Co-authored-by: Alvaro Aleman <[email protected]> * remove duplicate information in client.Options.FieldValidation documentation * Update pkg/client/client.go Co-authored-by: Alvaro Aleman <[email protected]> --------- Co-authored-by: Alvaro Aleman <[email protected]>
1 parent 3893e04 commit 9758f5c

File tree

2 files changed

+60
-0
lines changed

2 files changed

+60
-0
lines changed

pkg/client/client.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,16 @@ type Options struct {
6161
// This default can be overridden for a specific call by passing a [FieldOwner] option
6262
// to the method.
6363
FieldOwner string
64+
65+
// FieldValidation sets the field validation strategy for all mutating operations performed by this client
66+
// and subresource clients created from it.
67+
// The exception are apply requests which are always strict, regardless of the FieldValidation setting.
68+
// Available values for this option can be found in "k8s.io/apimachinery/pkg/apis/meta/v1" package and are:
69+
// - FieldValidationIgnore
70+
// - FieldValidationWarn
71+
// - FieldValidationStrict
72+
// For more details, see: https://kubernetes.io/docs/reference/using-api/api-concepts/#field-validation
73+
FieldValidation string
6474
}
6575

6676
// CacheOptions are options for creating a cache-backed client.
@@ -111,6 +121,9 @@ func New(config *rest.Config, options Options) (c Client, err error) {
111121
if fo := options.FieldOwner; fo != "" {
112122
c = WithFieldOwner(c, fo)
113123
}
124+
if fv := options.FieldValidation; fv != "" {
125+
c = WithFieldValidation(c, FieldValidation(fv))
126+
}
114127

115128
return c, err
116129
}

pkg/client/client_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import (
4343
"k8s.io/apimachinery/pkg/runtime"
4444
"k8s.io/apimachinery/pkg/runtime/schema"
4545
"k8s.io/apimachinery/pkg/types"
46+
"k8s.io/apimachinery/pkg/util/rand"
4647
appsv1applyconfigurations "k8s.io/client-go/applyconfigurations/apps/v1"
4748
autoscaling1applyconfigurations "k8s.io/client-go/applyconfigurations/autoscaling/v1"
4849
corev1applyconfigurations "k8s.io/client-go/applyconfigurations/core/v1"
@@ -378,6 +379,52 @@ U5wwSivyi7vmegHKmblOzNVKA5qPO8zWzqBC
378379
Expect(actual.ManagedFields).To(HaveLen(1))
379380
Expect(actual.ManagedFields[0].Manager).To(Equal("test-owner"))
380381
})
382+
383+
Context("with the FieldValidation option", func() {
384+
It("should log warnings with FieldValidation equal to Warn", func(ctx SpecContext) {
385+
restCfg := rest.CopyConfig(cfg)
386+
var testLog bytes.Buffer
387+
restCfg.WarningHandler = rest.NewWarningWriter(&testLog, rest.WarningWriterOptions{})
388+
389+
warnClient, err := client.New(restCfg, client.Options{FieldValidation: metav1.FieldValidationWarn})
390+
Expect(err).NotTo(HaveOccurred())
391+
Expect(warnClient).NotTo(BeNil())
392+
393+
unstrContent, err := runtime.DefaultUnstructuredConverter.ToUnstructured(
394+
corev1applyconfigurations.ConfigMap("test-cm-"+rand.String(3), "default").
395+
WithData(map[string]string{"foo": "bar"}),
396+
)
397+
Expect(err).NotTo(HaveOccurred())
398+
unstrContent["additionalField"] = "test"
399+
cm := &unstructured.Unstructured{Object: unstrContent}
400+
401+
err = warnClient.Create(ctx, cm)
402+
Expect(err).NotTo(HaveOccurred())
403+
Expect(testLog.String()).To(ContainSubstring(`Warning: unknown field "additionalField"`))
404+
405+
})
406+
It("should fail write operation if FieldValidation equals Strict", func(ctx SpecContext) {
407+
restCfg := rest.CopyConfig(cfg)
408+
var testLog bytes.Buffer
409+
restCfg.WarningHandler = rest.NewWarningWriter(&testLog, rest.WarningWriterOptions{})
410+
strictClient, err := client.New(restCfg, client.Options{FieldValidation: metav1.FieldValidationStrict})
411+
Expect(err).NotTo(HaveOccurred())
412+
413+
unstrContent, err := runtime.DefaultUnstructuredConverter.ToUnstructured(
414+
corev1applyconfigurations.ConfigMap("test-cm-"+rand.String(3), "default").
415+
WithData(map[string]string{"foo": "bar"}),
416+
)
417+
Expect(err).NotTo(HaveOccurred())
418+
unstrContent["additionalField"] = "test"
419+
cm := &unstructured.Unstructured{Object: unstrContent}
420+
421+
err = strictClient.Create(ctx, cm)
422+
Expect(err).To(HaveOccurred())
423+
Expect(err).To(MatchError(ContainSubstring("unknown field \"additionalField\"")))
424+
Expect(err).To(MatchError(ContainSubstring("strict decoding error")))
425+
Expect(testLog.String()).To(BeEmpty())
426+
})
427+
})
381428
})
382429

383430
Describe("Create", func() {

0 commit comments

Comments
 (0)