From 4f37d53db89cbd6a1b3f3ddd579066b476f33a30 Mon Sep 17 00:00:00 2001 From: Daniel Lipovetsky Date: Thu, 20 Jun 2024 14:43:15 -0700 Subject: [PATCH] Adds a client that enables strict field validation by default --- pkg/client/client.go | 7 +++ pkg/client/strict.go | 130 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 pkg/client/strict.go diff --git a/pkg/client/client.go b/pkg/client/client.go index e6c075eb00..671e9b44f0 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -56,6 +56,10 @@ type Options struct { // DryRun instructs the client to only perform dry run requests. DryRun *bool + + // Strict instructs the client to enable strict field validation + // for all requests. + Strict *bool } // WarningHandlerOptions are options for configuring a @@ -111,6 +115,9 @@ func New(config *rest.Config, options Options) (c Client, err error) { if err == nil && options.DryRun != nil && *options.DryRun { c = NewDryRunClient(c) } + if err == nil && options.Strict != nil && *options.Strict { + c = NewStrictClient(c) + } return c, err } diff --git a/pkg/client/strict.go b/pkg/client/strict.go new file mode 100644 index 0000000000..64fa2bb7dd --- /dev/null +++ b/pkg/client/strict.go @@ -0,0 +1,130 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package client + +import ( + "context" + + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// NewStrictClient wraps an existing client and enforces strict field validation +// on all mutating api calls. +func NewStrictClient(c Client) Client { + return &StrictClient{client: c} +} + +var _ Client = &StrictClient{} + +// StrictClient is a Client that wraps another Client in order to enforce strict field validation. +type StrictClient struct { + client Client +} + +// Scheme returns the scheme this client is using. +func (c *StrictClient) Scheme() *runtime.Scheme { + return c.client.Scheme() +} + +// RESTMapper returns the rest mapper this client is using. +func (c *StrictClient) RESTMapper() meta.RESTMapper { + return c.client.RESTMapper() +} + +// GroupVersionKindFor returns the GroupVersionKind for the given object. +func (c *StrictClient) GroupVersionKindFor(obj runtime.Object) (schema.GroupVersionKind, error) { + return c.client.GroupVersionKindFor(obj) +} + +// IsObjectNamespaced returns true if the GroupVersionKind of the object is namespaced. +func (c *StrictClient) IsObjectNamespaced(obj runtime.Object) (bool, error) { + return c.client.IsObjectNamespaced(obj) +} + +// Create implements client.Client. +func (c *StrictClient) Create(ctx context.Context, obj Object, opts ...CreateOption) error { + return c.client.Create(ctx, obj, append(opts, FieldValidationStrict)...) +} + +// Update implements client.Client. +func (c *StrictClient) Update(ctx context.Context, obj Object, opts ...UpdateOption) error { + return c.client.Update(ctx, obj, append(opts, FieldValidationStrict)...) +} + +// Delete implements client.Client. +func (c *StrictClient) Delete(ctx context.Context, obj Object, opts ...DeleteOption) error { + return c.client.Delete(ctx, obj, opts...) +} + +// DeleteAllOf implements client.Client. +func (c *StrictClient) DeleteAllOf(ctx context.Context, obj Object, opts ...DeleteAllOfOption) error { + return c.client.DeleteAllOf(ctx, obj, opts...) +} + +// Patch implements client.Client. +func (c *StrictClient) Patch(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error { + return c.client.Patch(ctx, obj, patch, append(opts, FieldValidationStrict)...) +} + +// Get implements client.Client. +func (c *StrictClient) Get(ctx context.Context, key ObjectKey, obj Object, opts ...GetOption) error { + return c.client.Get(ctx, key, obj, opts...) +} + +// List implements client.Client. +func (c *StrictClient) List(ctx context.Context, obj ObjectList, opts ...ListOption) error { + return c.client.List(ctx, obj, opts...) +} + +// Status implements client.StatusClient. +func (c *StrictClient) Status() SubResourceWriter { + return c.SubResource("status") +} + +// SubResource implements client.SubResourceClient. +func (c *StrictClient) SubResource(subResource string) SubResourceClient { + return &StrictSubResourceClient{client: c.client.SubResource(subResource)} +} + +// ensure StrictSubResourceWriter implements client.SubResourceWriter. +var _ SubResourceWriter = &StrictSubResourceClient{} + +// StrictSubResourceClient is client.SubResourceWriter that writes status subresource with strict field validation +// enforced. +type StrictSubResourceClient struct { + client SubResourceClient +} + +func (sw *StrictSubResourceClient) Get(ctx context.Context, obj, subResource Object, opts ...SubResourceGetOption) error { + return sw.client.Get(ctx, obj, subResource, opts...) +} + +func (sw *StrictSubResourceClient) Create(ctx context.Context, obj, subResource Object, opts ...SubResourceCreateOption) error { + return sw.client.Create(ctx, obj, subResource, append(opts, FieldValidationStrict)...) +} + +// Update implements client.SubResourceWriter. +func (sw *StrictSubResourceClient) Update(ctx context.Context, obj Object, opts ...SubResourceUpdateOption) error { + return sw.client.Update(ctx, obj, append(opts, FieldValidationStrict)...) +} + +// Patch implements client.SubResourceWriter. +func (sw *StrictSubResourceClient) Patch(ctx context.Context, obj Object, patch Patch, opts ...SubResourcePatchOption) error { + return sw.client.Patch(ctx, obj, patch, append(opts, FieldValidationStrict)...) +}