From 68d92bb4a9509e1e82167301d68d450dc08563a3 Mon Sep 17 00:00:00 2001 From: Solly Ross Date: Tue, 23 Jul 2019 17:41:28 -0700 Subject: [PATCH 1/2] Switch to interface-based options These behave more-or-less like functional options, except that a given option can be applied to more than on option type, so we can deprecate the 3 different dryrunall variants. Things with arguments generally become type aliases, with the exception of MatchingField, which gets deprecated in favor of MatchFields (a type alias to fields.Set). --- pkg/cache/informer_cache.go | 2 +- pkg/cache/informertest/fake_cache.go | 2 +- pkg/cache/internal/cache_reader.go | 2 +- pkg/cache/multi_namespace_cache.go | 2 +- pkg/client/client.go | 14 +- pkg/client/client_test.go | 92 ++------ pkg/client/doc.go | 2 +- pkg/client/fake/client.go | 14 +- pkg/client/interfaces.go | 14 +- pkg/client/options.go | 333 +++++++++++++-------------- pkg/client/split.go | 2 +- pkg/client/typed_client.go | 14 +- pkg/client/unstructured_client.go | 14 +- 13 files changed, 230 insertions(+), 277 deletions(-) diff --git a/pkg/cache/informer_cache.go b/pkg/cache/informer_cache.go index 6348937466..b906a2e08f 100644 --- a/pkg/cache/informer_cache.go +++ b/pkg/cache/informer_cache.go @@ -58,7 +58,7 @@ func (ip *informerCache) Get(ctx context.Context, key client.ObjectKey, out runt } // List implements Reader -func (ip *informerCache) List(ctx context.Context, out runtime.Object, opts ...client.ListOptionFunc) error { +func (ip *informerCache) List(ctx context.Context, out runtime.Object, opts ...client.ListOption) error { gvk, err := apiutil.GVKForObject(out, ip.Scheme) if err != nil { return err diff --git a/pkg/cache/informertest/fake_cache.go b/pkg/cache/informertest/fake_cache.go index 93a8fbcf4c..8b592056bd 100644 --- a/pkg/cache/informertest/fake_cache.go +++ b/pkg/cache/informertest/fake_cache.go @@ -136,6 +136,6 @@ func (c *FakeInformers) Get(ctx context.Context, key client.ObjectKey, obj runti } // List implements Cache -func (c *FakeInformers) List(ctx context.Context, list runtime.Object, opts ...client.ListOptionFunc) error { +func (c *FakeInformers) List(ctx context.Context, list runtime.Object, opts ...client.ListOption) error { return nil } diff --git a/pkg/cache/internal/cache_reader.go b/pkg/cache/internal/cache_reader.go index 184d66cb99..a9b7ae347c 100644 --- a/pkg/cache/internal/cache_reader.go +++ b/pkg/cache/internal/cache_reader.go @@ -88,7 +88,7 @@ func (c *CacheReader) Get(_ context.Context, key client.ObjectKey, out runtime.O } // List lists items out of the indexer and writes them to out -func (c *CacheReader) List(_ context.Context, out runtime.Object, opts ...client.ListOptionFunc) error { +func (c *CacheReader) List(_ context.Context, out runtime.Object, opts ...client.ListOption) error { var objs []interface{} var err error diff --git a/pkg/cache/multi_namespace_cache.go b/pkg/cache/multi_namespace_cache.go index fedb426536..dc6e159e35 100644 --- a/pkg/cache/multi_namespace_cache.go +++ b/pkg/cache/multi_namespace_cache.go @@ -133,7 +133,7 @@ func (c *multiNamespaceCache) Get(ctx context.Context, key client.ObjectKey, obj } // List multi namespace cache will get all the objects in the namespaces that the cache is watching if asked for all namespaces. -func (c *multiNamespaceCache) List(ctx context.Context, list runtime.Object, opts ...client.ListOptionFunc) error { +func (c *multiNamespaceCache) List(ctx context.Context, list runtime.Object, opts ...client.ListOption) error { listOpts := client.ListOptions{} listOpts.ApplyOptions(opts) if listOpts.Namespace != corev1.NamespaceAll { diff --git a/pkg/client/client.go b/pkg/client/client.go index ca04975d06..820dc92b09 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -104,7 +104,7 @@ type client struct { } // Create implements client.Client -func (c *client) Create(ctx context.Context, obj runtime.Object, opts ...CreateOptionFunc) error { +func (c *client) Create(ctx context.Context, obj runtime.Object, opts ...CreateOption) error { _, ok := obj.(*unstructured.Unstructured) if ok { return c.unstructuredClient.Create(ctx, obj, opts...) @@ -113,7 +113,7 @@ func (c *client) Create(ctx context.Context, obj runtime.Object, opts ...CreateO } // Update implements client.Client -func (c *client) Update(ctx context.Context, obj runtime.Object, opts ...UpdateOptionFunc) error { +func (c *client) Update(ctx context.Context, obj runtime.Object, opts ...UpdateOption) error { _, ok := obj.(*unstructured.Unstructured) if ok { return c.unstructuredClient.Update(ctx, obj, opts...) @@ -122,7 +122,7 @@ func (c *client) Update(ctx context.Context, obj runtime.Object, opts ...UpdateO } // Delete implements client.Client -func (c *client) Delete(ctx context.Context, obj runtime.Object, opts ...DeleteOptionFunc) error { +func (c *client) Delete(ctx context.Context, obj runtime.Object, opts ...DeleteOption) error { _, ok := obj.(*unstructured.Unstructured) if ok { return c.unstructuredClient.Delete(ctx, obj, opts...) @@ -131,7 +131,7 @@ func (c *client) Delete(ctx context.Context, obj runtime.Object, opts ...DeleteO } // Patch implements client.Client -func (c *client) Patch(ctx context.Context, obj runtime.Object, patch Patch, opts ...PatchOptionFunc) error { +func (c *client) Patch(ctx context.Context, obj runtime.Object, patch Patch, opts ...PatchOption) error { _, ok := obj.(*unstructured.Unstructured) if ok { return c.unstructuredClient.Patch(ctx, obj, patch, opts...) @@ -149,7 +149,7 @@ func (c *client) Get(ctx context.Context, key ObjectKey, obj runtime.Object) err } // List implements client.Client -func (c *client) List(ctx context.Context, obj runtime.Object, opts ...ListOptionFunc) error { +func (c *client) List(ctx context.Context, obj runtime.Object, opts ...ListOption) error { _, ok := obj.(*unstructured.UnstructuredList) if ok { return c.unstructuredClient.List(ctx, obj, opts...) @@ -171,7 +171,7 @@ type statusWriter struct { var _ StatusWriter = &statusWriter{} // Update implements client.StatusWriter -func (sw *statusWriter) Update(ctx context.Context, obj runtime.Object, opts ...UpdateOptionFunc) error { +func (sw *statusWriter) Update(ctx context.Context, obj runtime.Object, opts ...UpdateOption) error { _, ok := obj.(*unstructured.Unstructured) if ok { return sw.client.unstructuredClient.UpdateStatus(ctx, obj, opts...) @@ -180,7 +180,7 @@ func (sw *statusWriter) Update(ctx context.Context, obj runtime.Object, opts ... } // Patch implements client.Client -func (sw *statusWriter) Patch(ctx context.Context, obj runtime.Object, patch Patch, opts ...PatchOptionFunc) error { +func (sw *statusWriter) Patch(ctx context.Context, obj runtime.Object, patch Patch, opts ...PatchOption) error { _, ok := obj.(*unstructured.Unstructured) if ok { return sw.client.unstructuredClient.PatchStatus(ctx, obj, patch, opts...) diff --git a/pkg/client/client_test.go b/pkg/client/client_test.go index c492aec7a0..ff4f17f2e6 100644 --- a/pkg/client/client_test.go +++ b/pkg/client/client_test.go @@ -2000,7 +2000,7 @@ var _ = Describe("Client", func() { Describe("CreateOptions", func() { It("should allow setting DryRun to 'all'", func() { co := &client.CreateOptions{} - client.CreateDryRunAll(co) + client.DryRunAll.ApplyToCreate(co) all := []string{metav1.DryRunAll} Expect(co.AsCreateOptions().DryRun).To(Equal(all)) }) @@ -2016,7 +2016,7 @@ var _ = Describe("Client", func() { Describe("DeleteOptions", func() { It("should allow setting GracePeriodSeconds", func() { do := &client.DeleteOptions{} - client.GracePeriodSeconds(1)(do) + client.GracePeriodSeconds(1).ApplyToDelete(do) gp := int64(1) Expect(do.AsDeleteOptions().GracePeriodSeconds).To(Equal(&gp)) }) @@ -2024,14 +2024,14 @@ var _ = Describe("Client", func() { It("should allow setting Precondition", func() { do := &client.DeleteOptions{} pc := metav1.NewUIDPreconditions("uid") - client.Preconditions(pc)(do) + client.Preconditions(*pc).ApplyToDelete(do) Expect(do.AsDeleteOptions().Preconditions).To(Equal(pc)) Expect(do.Preconditions).To(Equal(pc)) }) It("should allow setting PropagationPolicy", func() { do := &client.DeleteOptions{} - client.PropagationPolicy(metav1.DeletePropagationForeground)(do) + client.PropagationPolicy(metav1.DeletePropagationForeground).ApplyToDelete(do) dp := metav1.DeletePropagationForeground Expect(do.AsDeleteOptions().PropagationPolicy).To(Equal(&dp)) }) @@ -2048,9 +2048,9 @@ var _ = Describe("Client", func() { pc := metav1.NewUIDPreconditions("uid") dp := metav1.DeletePropagationForeground do := &client.DeleteOptions{} - do.ApplyOptions([]client.DeleteOptionFunc{ + do.ApplyOptions([]client.DeleteOption{ client.GracePeriodSeconds(gp), - client.Preconditions(pc), + client.Preconditions(*pc), client.PropagationPolicy(dp), }) Expect(do.GracePeriodSeconds).To(Equal(&gp)) @@ -2060,79 +2060,35 @@ var _ = Describe("Client", func() { }) Describe("ListOptions", func() { - It("should be able to set a LabelSelector", func() { - lo := &client.ListOptions{} - err := lo.SetLabelSelector("foo=bar") - Expect(err).NotTo(HaveOccurred()) - Expect(lo.LabelSelector.String()).To(Equal("foo=bar")) - }) - - It("should be able to set a FieldSelector", func() { - lo := &client.ListOptions{} - err := lo.SetFieldSelector("field1=bar") - Expect(err).NotTo(HaveOccurred()) - Expect(lo.FieldSelector.String()).To(Equal("field1=bar")) - }) - - It("should be converted to metav1.ListOptions", func() { - lo := &client.ListOptions{} - labels := map[string]string{"foo": "bar"} - mlo := lo.MatchingLabels(labels). - MatchingField("field1", "bar"). - InNamespace("test-namespace"). - AsListOptions() + It("should be convertable to metav1.ListOptions", func() { + lo := (&client.ListOptions{}).ApplyOptions([]client.ListOption{ + client.MatchingField("field1", "bar"), + client.InNamespace("test-namespace"), + client.MatchingLabels{"foo": "bar"}, + }) + mlo := lo.AsListOptions() Expect(mlo).NotTo(BeNil()) Expect(mlo.LabelSelector).To(Equal("foo=bar")) Expect(mlo.FieldSelector).To(Equal("field1=bar")) }) - It("should be able to set MatchingLabels", func() { - lo := &client.ListOptions{} - Expect(lo.LabelSelector).To(BeNil()) - labels := map[string]string{"foo": "bar"} - lo = lo.MatchingLabels(labels) - Expect(lo.LabelSelector.String()).To(Equal("foo=bar")) - }) - - It("should be able to set MatchingField", func() { - lo := &client.ListOptions{} - Expect(lo.FieldSelector).To(BeNil()) - lo = lo.MatchingField("field1", "bar") - Expect(lo.FieldSelector.String()).To(Equal("field1=bar")) - }) - - It("should be able to set InNamespace", func() { - lo := &client.ListOptions{} - lo = lo.InNamespace("test-namespace") - Expect(lo.Namespace).To(Equal("test-namespace")) - }) - - It("should be created from MatchingLabels", func() { - labels := map[string]string{"foo": "bar"} + It("should be populated by MatchingLabels", func() { lo := &client.ListOptions{} - client.MatchingLabels(labels)(lo) + client.MatchingLabels{"foo": "bar"}.ApplyToList(lo) Expect(lo).NotTo(BeNil()) Expect(lo.LabelSelector.String()).To(Equal("foo=bar")) }) - It("should be created from MatchingField", func() { + It("should be populated by MatchingField", func() { lo := &client.ListOptions{} - client.MatchingField("field1", "bar")(lo) + client.MatchingField("field1", "bar").ApplyToList(lo) Expect(lo).NotTo(BeNil()) Expect(lo.FieldSelector.String()).To(Equal("field1=bar")) }) - It("should be created from InNamespace", func() { - lo := &client.ListOptions{} - client.InNamespace("test")(lo) - Expect(lo).NotTo(BeNil()) - Expect(lo.Namespace).To(Equal("test")) - }) - - It("should allow pre-built ListOptions", func() { + It("should be populated by InNamespace", func() { lo := &client.ListOptions{} - newLo := &client.ListOptions{} - client.UseListOptions(newLo.InNamespace("test"))(lo) + client.InNamespace("test").ApplyToList(lo) Expect(lo).NotTo(BeNil()) Expect(lo.Namespace).To(Equal("test")) }) @@ -2141,7 +2097,7 @@ var _ = Describe("Client", func() { Describe("UpdateOptions", func() { It("should allow setting DryRun to 'all'", func() { uo := &client.UpdateOptions{} - client.UpdateDryRunAll(uo) + client.DryRunAll.ApplyToUpdate(uo) all := []string{metav1.DryRunAll} Expect(uo.AsUpdateOptions().DryRun).To(Equal(all)) }) @@ -2157,14 +2113,14 @@ var _ = Describe("Client", func() { Describe("PatchOptions", func() { It("should allow setting DryRun to 'all'", func() { po := &client.PatchOptions{} - client.PatchDryRunAll(po) + client.DryRunAll.ApplyToPatch(po) all := []string{metav1.DryRunAll} Expect(po.AsPatchOptions().DryRun).To(Equal(all)) }) It("should allow setting Force to 'true'", func() { po := &client.PatchOptions{} - client.ForceOwnership(po) + client.ForceOwnership.ApplyToPatch(po) mpo := po.AsPatchOptions() Expect(mpo.Force).NotTo(BeNil()) Expect(*mpo.Force).To(BeTrue()) @@ -2172,7 +2128,7 @@ var _ = Describe("Client", func() { It("should allow setting the field manager", func() { po := &client.PatchOptions{} - client.FieldOwner("some-owner")(po) + client.FieldOwner("some-owner").ApplyToPatch(po) Expect(po.AsPatchOptions().FieldManager).To(Equal("some-owner")) }) @@ -2320,7 +2276,7 @@ func (f *fakeReader) Get(ctx context.Context, key client.ObjectKey, obj runtime. return nil } -func (f *fakeReader) List(ctx context.Context, list runtime.Object, opts ...client.ListOptionFunc) error { +func (f *fakeReader) List(ctx context.Context, list runtime.Object, opts ...client.ListOption) error { f.Called = f.Called + 1 return nil } diff --git a/pkg/client/doc.go b/pkg/client/doc.go index 6c13af211f..2965e5fa94 100644 --- a/pkg/client/doc.go +++ b/pkg/client/doc.go @@ -34,7 +34,7 @@ limitations under the License. // Many client operations in Kubernetes support options. These options are // represented as variadic arguments at the end of a given method call. // For instance, to use a label selector on list, you can call -// err := someReader.List(context.Background(), &podList, client.MatchingLabels(someLabelMap)) +// err := someReader.List(context.Background(), &podList, client.MatchingLabels{"somelabel": "someval"}) // // Indexing // diff --git a/pkg/client/fake/client.go b/pkg/client/fake/client.go index 7a348a5867..ba1d85a04d 100644 --- a/pkg/client/fake/client.go +++ b/pkg/client/fake/client.go @@ -84,7 +84,7 @@ func (c *fakeClient) Get(ctx context.Context, key client.ObjectKey, obj runtime. return err } -func (c *fakeClient) List(ctx context.Context, obj runtime.Object, opts ...client.ListOptionFunc) error { +func (c *fakeClient) List(ctx context.Context, obj runtime.Object, opts ...client.ListOption) error { gvk, err := apiutil.GVKForObject(obj, c.scheme) if err != nil { return err @@ -131,7 +131,7 @@ func (c *fakeClient) List(ctx context.Context, obj runtime.Object, opts ...clien return nil } -func (c *fakeClient) Create(ctx context.Context, obj runtime.Object, opts ...client.CreateOptionFunc) error { +func (c *fakeClient) Create(ctx context.Context, obj runtime.Object, opts ...client.CreateOption) error { createOptions := &client.CreateOptions{} createOptions.ApplyOptions(opts) @@ -152,7 +152,7 @@ func (c *fakeClient) Create(ctx context.Context, obj runtime.Object, opts ...cli return c.tracker.Create(gvr, obj, accessor.GetNamespace()) } -func (c *fakeClient) Delete(ctx context.Context, obj runtime.Object, opts ...client.DeleteOptionFunc) error { +func (c *fakeClient) Delete(ctx context.Context, obj runtime.Object, opts ...client.DeleteOption) error { gvr, err := getGVRFromObject(obj, c.scheme) if err != nil { return err @@ -165,7 +165,7 @@ func (c *fakeClient) Delete(ctx context.Context, obj runtime.Object, opts ...cli return c.tracker.Delete(gvr, accessor.GetNamespace(), accessor.GetName()) } -func (c *fakeClient) Update(ctx context.Context, obj runtime.Object, opts ...client.UpdateOptionFunc) error { +func (c *fakeClient) Update(ctx context.Context, obj runtime.Object, opts ...client.UpdateOption) error { updateOptions := &client.UpdateOptions{} updateOptions.ApplyOptions(opts) @@ -186,7 +186,7 @@ func (c *fakeClient) Update(ctx context.Context, obj runtime.Object, opts ...cli return c.tracker.Update(gvr, obj, accessor.GetNamespace()) } -func (c *fakeClient) Patch(ctx context.Context, obj runtime.Object, patch client.Patch, opts ...client.PatchOptionFunc) error { +func (c *fakeClient) Patch(ctx context.Context, obj runtime.Object, patch client.Patch, opts ...client.PatchOption) error { patchOptions := &client.PatchOptions{} patchOptions.ApplyOptions(opts) @@ -244,13 +244,13 @@ type fakeStatusWriter struct { client *fakeClient } -func (sw *fakeStatusWriter) Update(ctx context.Context, obj runtime.Object, opts ...client.UpdateOptionFunc) error { +func (sw *fakeStatusWriter) Update(ctx context.Context, obj runtime.Object, opts ...client.UpdateOption) error { // TODO(droot): This results in full update of the obj (spec + status). Need // a way to update status field only. return sw.client.Update(ctx, obj, opts...) } -func (sw *fakeStatusWriter) Patch(ctx context.Context, obj runtime.Object, patch client.Patch, opts ...client.PatchOptionFunc) error { +func (sw *fakeStatusWriter) Patch(ctx context.Context, obj runtime.Object, patch client.Patch, opts ...client.PatchOption) error { // TODO(droot): This results in full update of the obj (spec + status). Need // a way to update status field only. return sw.client.Patch(ctx, obj, patch, opts...) diff --git a/pkg/client/interfaces.go b/pkg/client/interfaces.go index e4ca8be85c..601c304052 100644 --- a/pkg/client/interfaces.go +++ b/pkg/client/interfaces.go @@ -58,24 +58,24 @@ type Reader interface { // List retrieves list of objects for a given namespace and list options. On a // successful call, Items field in the list will be populated with the // result returned from the server. - List(ctx context.Context, list runtime.Object, opts ...ListOptionFunc) error + List(ctx context.Context, list runtime.Object, opts ...ListOption) error } // Writer knows how to create, delete, and update Kubernetes objects. type Writer interface { // Create saves the object obj in the Kubernetes cluster. - Create(ctx context.Context, obj runtime.Object, opts ...CreateOptionFunc) error + Create(ctx context.Context, obj runtime.Object, opts ...CreateOption) error // Delete deletes the given obj from Kubernetes cluster. - Delete(ctx context.Context, obj runtime.Object, opts ...DeleteOptionFunc) error + Delete(ctx context.Context, obj runtime.Object, opts ...DeleteOption) error // Update updates the given obj in the Kubernetes cluster. obj must be a // struct pointer so that obj can be updated with the content returned by the Server. - Update(ctx context.Context, obj runtime.Object, opts ...UpdateOptionFunc) error + Update(ctx context.Context, obj runtime.Object, opts ...UpdateOption) error // Patch patches the given obj in the Kubernetes cluster. obj must be a // struct pointer so that obj can be updated with the content returned by the Server. - Patch(ctx context.Context, obj runtime.Object, patch Patch, opts ...PatchOptionFunc) error + Patch(ctx context.Context, obj runtime.Object, patch Patch, opts ...PatchOption) error } // StatusClient knows how to create a client which can update status subresource @@ -89,12 +89,12 @@ type StatusWriter interface { // Update updates the fields corresponding to the status subresource for the // given obj. obj must be a struct pointer so that obj can be updated // with the content returned by the Server. - Update(ctx context.Context, obj runtime.Object, opts ...UpdateOptionFunc) error + Update(ctx context.Context, obj runtime.Object, opts ...UpdateOption) error // Patch patches the given object's subresource. obj must be a struct // pointer so that obj can be updated with the content returned by the // Server. - Patch(ctx context.Context, obj runtime.Object, patch Patch, opts ...PatchOptionFunc) error + Patch(ctx context.Context, obj runtime.Object, patch Patch, opts ...PatchOption) error } // Client knows how to perform CRUD operations on Kubernetes objects. diff --git a/pkg/client/options.go b/pkg/client/options.go index 395ce7838f..2748cda888 100644 --- a/pkg/client/options.go +++ b/pkg/client/options.go @@ -22,6 +22,81 @@ import ( "k8s.io/apimachinery/pkg/labels" ) +// {{{ "Functional" Option Interfaces + +// CreateOption is some configuration that modifies options for a create request. +type CreateOption interface { + // ApplyToCreate applies this configuration to the given create options. + ApplyToCreate(*CreateOptions) +} + +// DeleteOption is some configuration that modifies options for a create request. +type DeleteOption interface { + // ApplyToDelete applies this configuration to the given delete options. + ApplyToDelete(*DeleteOptions) +} + +// ListOption is some configuration that modifies options for a create request. +type ListOption interface { + // ApplyToList applies this configuration to the given list options. + ApplyToList(*ListOptions) +} + +// UpdateOption is some configuration that modifies options for a create request. +type UpdateOption interface { + // ApplyToUpdate applies this configuration to the given update options. + ApplyToUpdate(*UpdateOptions) +} + +// PatchOption is some configuration that modifies options for a create request. +type PatchOption interface { + // ApplyToPatch applies this configuration to the given patch options. + ApplyToPatch(*PatchOptions) +} + +// }}} + +// {{{ Multi-Type Options + +// DryRunAll sets the "dry run" option to "all", executing all +// validation, etc without persisting the change to storage. +var DryRunAll = dryRunAll{} + +type dryRunAll struct{} + +func (dryRunAll) ApplyToCreate(opts *CreateOptions) { + opts.DryRun = []string{metav1.DryRunAll} +} +func (dryRunAll) ApplyToUpdate(opts *UpdateOptions) { + opts.DryRun = []string{metav1.DryRunAll} +} +func (dryRunAll) ApplyToPatch(opts *PatchOptions) { + opts.DryRun = []string{metav1.DryRunAll} +} + +// FieldOwner set the field manager name for the given server-side apply patch. +type FieldOwner string + +func (f FieldOwner) ApplyToPatch(opts *PatchOptions) { + opts.FieldManager = string(f) +} + +// ForceOwnership indicates that in case of conflicts with server-side apply, +// the client should acquire ownership of the conflicting field. Most +// controllers should use this. +var ForceOwnership = forceOwnership{} + +type forceOwnership struct{} + +func (forceOwnership) ApplyToPatch(opts *PatchOptions) { + definitelyTrue := true + opts.Force = &definitelyTrue +} + +// }}} + +// {{{ Create Options + // CreateOptions contains options for create requests. It's generally a subset // of metav1.CreateOptions. type CreateOptions struct { @@ -39,7 +114,6 @@ type CreateOptions struct { // AsCreateOptions returns these options as a metav1.CreateOptions. // This may mutate the Raw field. func (o *CreateOptions) AsCreateOptions() *metav1.CreateOptions { - if o == nil { return &metav1.CreateOptions{} } @@ -51,25 +125,23 @@ func (o *CreateOptions) AsCreateOptions() *metav1.CreateOptions { return o.Raw } -// ApplyOptions executes the given CreateOptionFuncs and returns the mutated -// CreateOptions. -func (o *CreateOptions) ApplyOptions(optFuncs []CreateOptionFunc) *CreateOptions { - for _, optFunc := range optFuncs { - optFunc(o) +// ApplyOptions applies the given create options on these options, +// and then returns itself (for convenient chaining). +func (o *CreateOptions) ApplyOptions(opts []CreateOption) *CreateOptions { + for _, opt := range opts { + opt.ApplyToCreate(o) } return o } -// CreateOptionFunc is a function that mutates a CreateOptions struct. It implements -// the functional options pattern. See -// https://github.com/tmrts/go-patterns/blob/master/idiom/functional-options.md. -type CreateOptionFunc func(*CreateOptions) +// CreateDryRunAll sets the "dry run" option to "all". +// +// Deprecated: Use DryRunAll +var CreateDryRunAll = DryRunAll -// CreateDryRunAll is a functional option that sets the DryRun -// field of a CreateOptions struct to metav1.DryRunAll. -var CreateDryRunAll CreateOptionFunc = func(opts *CreateOptions) { - opts.DryRun = []string{metav1.DryRunAll} -} +// }}} + +// {{{ Delete Options // DeleteOptions contains options for delete requests. It's generally a subset // of metav1.DeleteOptions. @@ -114,44 +186,42 @@ func (o *DeleteOptions) AsDeleteOptions() *metav1.DeleteOptions { return o.Raw } -// ApplyOptions executes the given DeleteOptionFuncs and returns the mutated -// DeleteOptions. -func (o *DeleteOptions) ApplyOptions(optFuncs []DeleteOptionFunc) *DeleteOptions { - for _, optFunc := range optFuncs { - optFunc(o) +// ApplyOptions applies the given delete options on these options, +// and then returns itself (for convenient chaining). +func (o *DeleteOptions) ApplyOptions(opts []DeleteOption) *DeleteOptions { + for _, opt := range opts { + opt.ApplyToDelete(o) } return o } -// DeleteOptionFunc is a function that mutates a DeleteOptions struct. It implements -// the functional options pattern. See -// https://github.com/tmrts/go-patterns/blob/master/idiom/functional-options.md. -type DeleteOptionFunc func(*DeleteOptions) +// GracePeriodSeconds sets the grace period for the deletion +// to the given number of seconds. +type GracePeriodSeconds int64 -// GracePeriodSeconds is a functional option that sets the GracePeriodSeconds -// field of a DeleteOptions struct. -func GracePeriodSeconds(gp int64) DeleteOptionFunc { - return func(opts *DeleteOptions) { - opts.GracePeriodSeconds = &gp - } +func (s GracePeriodSeconds) ApplyToDelete(opts *DeleteOptions) { + secs := int64(s) + opts.GracePeriodSeconds = &secs } -// Preconditions is a functional option that sets the Preconditions field of a -// DeleteOptions struct. -func Preconditions(p *metav1.Preconditions) DeleteOptionFunc { - return func(opts *DeleteOptions) { - opts.Preconditions = p - } +type Preconditions metav1.Preconditions + +func (p Preconditions) ApplyToDelete(opts *DeleteOptions) { + preconds := metav1.Preconditions(p) + opts.Preconditions = &preconds } -// PropagationPolicy is a functional option that sets the PropagationPolicy -// field of a DeleteOptions struct. -func PropagationPolicy(p metav1.DeletionPropagation) DeleteOptionFunc { - return func(opts *DeleteOptions) { - opts.PropagationPolicy = &p - } +type PropagationPolicy metav1.DeletionPropagation + +func (p PropagationPolicy) ApplyToDelete(opts *DeleteOptions) { + policy := metav1.DeletionPropagation(p) + opts.PropagationPolicy = &policy } +// }}} + +// {{{ List Options + // ListOptions contains options for limiting or filtering results. // It's generally a subset of metav1.ListOptions, with support for // pre-parsed selectors (since generally, selectors will be executed @@ -175,28 +245,6 @@ type ListOptions struct { Raw *metav1.ListOptions } -// SetLabelSelector sets this the label selector of these options -// from a string form of the selector. -func (o *ListOptions) SetLabelSelector(selRaw string) error { - sel, err := labels.Parse(selRaw) - if err != nil { - return err - } - o.LabelSelector = sel - return nil -} - -// SetFieldSelector sets this the label selector of these options -// from a string form of the selector. -func (o *ListOptions) SetFieldSelector(selRaw string) error { - sel, err := fields.ParseSelector(selRaw) - if err != nil { - return err - } - o.FieldSelector = sel - return nil -} - // AsListOptions returns these options as a flattened metav1.ListOptions. // This may mutate the Raw field. func (o *ListOptions) AsListOptions() *metav1.ListOptions { @@ -215,81 +263,52 @@ func (o *ListOptions) AsListOptions() *metav1.ListOptions { return o.Raw } -// ApplyOptions executes the given ListOptionFuncs and returns the mutated -// ListOptions. -func (o *ListOptions) ApplyOptions(optFuncs []ListOptionFunc) *ListOptions { - for _, optFunc := range optFuncs { - optFunc(o) +// ApplyOptions applies the given list options on these options, +// and then returns itself (for convenient chaining). +func (o *ListOptions) ApplyOptions(opts []ListOption) *ListOptions { + for _, opt := range opts { + opt.ApplyToList(o) } return o } -// ListOptionFunc is a function that mutates a ListOptions struct. It implements -// the functional options pattern. See -// https://github.com/tmrts/go-patterns/blob/master/idiom/functional-options.md. -type ListOptionFunc func(*ListOptions) +// MatchingLabels filters the list operation on the given set of labels. +type MatchingLabels map[string]string -// MatchingLabels is a convenience function that sets the label selector -// to match the given labels, and then returns the options. -// It mutates the list options. -func (o *ListOptions) MatchingLabels(lbls map[string]string) *ListOptions { - sel := labels.SelectorFromSet(lbls) - o.LabelSelector = sel - return o +func (m MatchingLabels) ApplyToList(opts *ListOptions) { + // TODO(directxman12): can we avoid reserializing this over and over? + sel := labels.SelectorFromSet(map[string]string(m)) + opts.LabelSelector = sel } -// MatchingField is a convenience function that sets the field selector -// to match the given field, and then returns the options. -// It mutates the list options. -func (o *ListOptions) MatchingField(name, val string) *ListOptions { - sel := fields.SelectorFromSet(fields.Set{name: val}) - o.FieldSelector = sel - return o +// MatchingField filters the list operation on the given field selector +// (or index in the case of cached lists). +// +// Deprecated: Use MatchingFields +func MatchingField(name, val string) MatchingFields { + return MatchingFields{name: val} } -// InNamespace is a convenience function that sets the namespace, -// and then returns the options. It mutates the list options. -func (o *ListOptions) InNamespace(ns string) *ListOptions { - o.Namespace = ns - return o -} +// MatchingField filters the list operation on the given field selector +// (or index in the case of cached lists). +type MatchingFields fields.Set -// MatchingLabels is a functional option that sets the LabelSelector field of -// a ListOptions struct. -func MatchingLabels(lbls map[string]string) ListOptionFunc { - sel := labels.SelectorFromSet(lbls) - return func(opts *ListOptions) { - opts.LabelSelector = sel - } +func (m MatchingFields) ApplyToList(opts *ListOptions) { + // TODO(directxman12): can we avoid re-serializing this? + sel := fields.SelectorFromSet(fields.Set(m)) + opts.FieldSelector = sel } -// MatchingField is a functional option that sets the FieldSelector field of -// a ListOptions struct. -func MatchingField(name, val string) ListOptionFunc { - sel := fields.SelectorFromSet(fields.Set{name: val}) - return func(opts *ListOptions) { - opts.FieldSelector = sel - } -} +// InNamespace restricts the given operation to the given namespace. +type InNamespace string -// InNamespace is a functional option that sets the Namespace field of -// a ListOptions struct. -func InNamespace(ns string) ListOptionFunc { - return func(opts *ListOptions) { - opts.Namespace = ns - } +func (n InNamespace) ApplyToList(opts *ListOptions) { + opts.Namespace = string(n) } -// UseListOptions is a functional option that replaces the fields of a -// ListOptions struct with those of a different ListOptions struct. -// -// Example: -// cl.List(ctx, list, client.UseListOptions(lo.InNamespace(ns).MatchingLabels(labels))) -func UseListOptions(newOpts *ListOptions) ListOptionFunc { - return func(opts *ListOptions) { - *opts = *newOpts - } -} +// }}} + +// {{{ Update Options // UpdateOptions contains options for create requests. It's generally a subset // of metav1.UpdateOptions. @@ -319,25 +338,23 @@ func (o *UpdateOptions) AsUpdateOptions() *metav1.UpdateOptions { return o.Raw } -// ApplyOptions executes the given UpdateOptionFuncs and returns the mutated -// UpdateOptions. -func (o *UpdateOptions) ApplyOptions(optFuncs []UpdateOptionFunc) *UpdateOptions { - for _, optFunc := range optFuncs { - optFunc(o) +// ApplyOptions applies the given update options on these options, +// and then returns itself (for convenient chaining). +func (o *UpdateOptions) ApplyOptions(opts []UpdateOption) *UpdateOptions { + for _, opt := range opts { + opt.ApplyToUpdate(o) } return o } -// UpdateOptionFunc is a function that mutates a UpdateOptions struct. It implements -// the functional options pattern. See -// https://github.com/tmrts/go-patterns/blob/master/idiom/functional-options.md. -type UpdateOptionFunc func(*UpdateOptions) +// UpdateDryRunAll sets the "dry run" option to "all". +// +// Deprecated: Use DryRunAll +var UpdateDryRunAll = DryRunAll -// UpdateDryRunAll is a functional option that sets the DryRun -// field of a UpdateOptions struct to metav1.DryRunAll. -var UpdateDryRunAll UpdateOptionFunc = func(opts *UpdateOptions) { - opts.DryRun = []string{metav1.DryRunAll} -} +// }}} + +// {{{ Patch Options // PatchOptions contains options for patch requests. type PatchOptions struct { @@ -362,11 +379,11 @@ type PatchOptions struct { Raw *metav1.PatchOptions } -// ApplyOptions executes the given PatchOptionFuncs, mutating these PatchOptions. -// It returns the mutated PatchOptions for convenience. -func (o *PatchOptions) ApplyOptions(optFuncs []PatchOptionFunc) *PatchOptions { - for _, optFunc := range optFuncs { - optFunc(o) +// ApplyOptions applies the given patch options on these options, +// and then returns itself (for convenient chaining). +func (o *PatchOptions) ApplyOptions(opts []PatchOption) *PatchOptions { + for _, opt := range opts { + opt.ApplyToPatch(o) } return o } @@ -387,29 +404,9 @@ func (o *PatchOptions) AsPatchOptions() *metav1.PatchOptions { return o.Raw } -// PatchOptionFunc is a function that mutates a PatchOptions struct. It implements -// the functional options pattern. See -// https://github.com/tmrts/go-patterns/blob/master/idiom/functional-options.md. -type PatchOptionFunc func(*PatchOptions) - -// ForceOwnership sets the Force option, indicating that -// in case of conflicts with server-side apply, the client should -// acquire ownership of the conflicting field. Most controllers -// should use this. -var ForceOwnership PatchOptionFunc = func(opts *PatchOptions) { - definitelyTrue := true - opts.Force = &definitelyTrue -} - -// PatchDryRunAll is a functional option that sets the DryRun -// field of a PatchOptions struct to metav1.DryRunAll. -var PatchDryRunAll PatchOptionFunc = func(opts *PatchOptions) { - opts.DryRun = []string{metav1.DryRunAll} -} +// PatchDryRunAll sets the "dry run" option to "all". +// +// Deprecated: Use DryRunAll +var PatchDryRunAll = DryRunAll -// FieldOwner set the field manager name for the given server-side apply patch. -func FieldOwner(name string) PatchOptionFunc { - return func(opts *PatchOptions) { - opts.FieldManager = name - } -} +// }}} diff --git a/pkg/client/split.go b/pkg/client/split.go index db7f16a717..47cba9576d 100644 --- a/pkg/client/split.go +++ b/pkg/client/split.go @@ -52,7 +52,7 @@ func (d *DelegatingReader) Get(ctx context.Context, key ObjectKey, obj runtime.O } // List retrieves list of objects for a given namespace and list options. -func (d *DelegatingReader) List(ctx context.Context, list runtime.Object, opts ...ListOptionFunc) error { +func (d *DelegatingReader) List(ctx context.Context, list runtime.Object, opts ...ListOption) error { _, isUnstructured := list.(*unstructured.UnstructuredList) if isUnstructured { return d.ClientReader.List(ctx, list, opts...) diff --git a/pkg/client/typed_client.go b/pkg/client/typed_client.go index 76f429b650..2124b0e991 100644 --- a/pkg/client/typed_client.go +++ b/pkg/client/typed_client.go @@ -30,7 +30,7 @@ type typedClient struct { } // Create implements client.Client -func (c *typedClient) Create(ctx context.Context, obj runtime.Object, opts ...CreateOptionFunc) error { +func (c *typedClient) Create(ctx context.Context, obj runtime.Object, opts ...CreateOption) error { o, err := c.cache.getObjMeta(obj) if err != nil { return err @@ -49,7 +49,7 @@ func (c *typedClient) Create(ctx context.Context, obj runtime.Object, opts ...Cr } // Update implements client.Client -func (c *typedClient) Update(ctx context.Context, obj runtime.Object, opts ...UpdateOptionFunc) error { +func (c *typedClient) Update(ctx context.Context, obj runtime.Object, opts ...UpdateOption) error { o, err := c.cache.getObjMeta(obj) if err != nil { return err @@ -69,7 +69,7 @@ func (c *typedClient) Update(ctx context.Context, obj runtime.Object, opts ...Up } // Delete implements client.Client -func (c *typedClient) Delete(ctx context.Context, obj runtime.Object, opts ...DeleteOptionFunc) error { +func (c *typedClient) Delete(ctx context.Context, obj runtime.Object, opts ...DeleteOption) error { o, err := c.cache.getObjMeta(obj) if err != nil { return err @@ -87,7 +87,7 @@ func (c *typedClient) Delete(ctx context.Context, obj runtime.Object, opts ...De } // Patch implements client.Client -func (c *typedClient) Patch(ctx context.Context, obj runtime.Object, patch Patch, opts ...PatchOptionFunc) error { +func (c *typedClient) Patch(ctx context.Context, obj runtime.Object, patch Patch, opts ...PatchOption) error { o, err := c.cache.getObjMeta(obj) if err != nil { return err @@ -124,7 +124,7 @@ func (c *typedClient) Get(ctx context.Context, key ObjectKey, obj runtime.Object } // List implements client.Client -func (c *typedClient) List(ctx context.Context, obj runtime.Object, opts ...ListOptionFunc) error { +func (c *typedClient) List(ctx context.Context, obj runtime.Object, opts ...ListOption) error { r, err := c.cache.getResource(obj) if err != nil { return err @@ -141,7 +141,7 @@ func (c *typedClient) List(ctx context.Context, obj runtime.Object, opts ...List } // UpdateStatus used by StatusWriter to write status. -func (c *typedClient) UpdateStatus(ctx context.Context, obj runtime.Object, opts ...UpdateOptionFunc) error { +func (c *typedClient) UpdateStatus(ctx context.Context, obj runtime.Object, opts ...UpdateOption) error { o, err := c.cache.getObjMeta(obj) if err != nil { return err @@ -163,7 +163,7 @@ func (c *typedClient) UpdateStatus(ctx context.Context, obj runtime.Object, opts } // PatchStatus used by StatusWriter to write status. -func (c *typedClient) PatchStatus(ctx context.Context, obj runtime.Object, patch Patch, opts ...PatchOptionFunc) error { +func (c *typedClient) PatchStatus(ctx context.Context, obj runtime.Object, patch Patch, opts ...PatchOption) error { o, err := c.cache.getObjMeta(obj) if err != nil { return err diff --git a/pkg/client/unstructured_client.go b/pkg/client/unstructured_client.go index f13dd18854..d5bb9a47a7 100644 --- a/pkg/client/unstructured_client.go +++ b/pkg/client/unstructured_client.go @@ -37,7 +37,7 @@ type unstructuredClient struct { } // Create implements client.Client -func (uc *unstructuredClient) Create(_ context.Context, obj runtime.Object, opts ...CreateOptionFunc) error { +func (uc *unstructuredClient) Create(_ context.Context, obj runtime.Object, opts ...CreateOption) error { u, ok := obj.(*unstructured.Unstructured) if !ok { return fmt.Errorf("unstructured client did not understand object: %T", obj) @@ -57,7 +57,7 @@ func (uc *unstructuredClient) Create(_ context.Context, obj runtime.Object, opts } // Update implements client.Client -func (uc *unstructuredClient) Update(_ context.Context, obj runtime.Object, opts ...UpdateOptionFunc) error { +func (uc *unstructuredClient) Update(_ context.Context, obj runtime.Object, opts ...UpdateOption) error { u, ok := obj.(*unstructured.Unstructured) if !ok { return fmt.Errorf("unstructured client did not understand object: %T", obj) @@ -77,7 +77,7 @@ func (uc *unstructuredClient) Update(_ context.Context, obj runtime.Object, opts } // Delete implements client.Client -func (uc *unstructuredClient) Delete(_ context.Context, obj runtime.Object, opts ...DeleteOptionFunc) error { +func (uc *unstructuredClient) Delete(_ context.Context, obj runtime.Object, opts ...DeleteOption) error { u, ok := obj.(*unstructured.Unstructured) if !ok { return fmt.Errorf("unstructured client did not understand object: %T", obj) @@ -92,7 +92,7 @@ func (uc *unstructuredClient) Delete(_ context.Context, obj runtime.Object, opts } // Patch implements client.Client -func (uc *unstructuredClient) Patch(_ context.Context, obj runtime.Object, patch Patch, opts ...PatchOptionFunc) error { +func (uc *unstructuredClient) Patch(_ context.Context, obj runtime.Object, patch Patch, opts ...PatchOption) error { u, ok := obj.(*unstructured.Unstructured) if !ok { return fmt.Errorf("unstructured client did not understand object: %T", obj) @@ -135,7 +135,7 @@ func (uc *unstructuredClient) Get(_ context.Context, key ObjectKey, obj runtime. } // List implements client.Client -func (uc *unstructuredClient) List(_ context.Context, obj runtime.Object, opts ...ListOptionFunc) error { +func (uc *unstructuredClient) List(_ context.Context, obj runtime.Object, opts ...ListOption) error { u, ok := obj.(*unstructured.UnstructuredList) if !ok { return fmt.Errorf("unstructured client did not understand object: %T", obj) @@ -160,7 +160,7 @@ func (uc *unstructuredClient) List(_ context.Context, obj runtime.Object, opts . return nil } -func (uc *unstructuredClient) UpdateStatus(_ context.Context, obj runtime.Object, opts ...UpdateOptionFunc) error { +func (uc *unstructuredClient) UpdateStatus(_ context.Context, obj runtime.Object, opts ...UpdateOption) error { u, ok := obj.(*unstructured.Unstructured) if !ok { return fmt.Errorf("unstructured client did not understand object: %T", obj) @@ -177,7 +177,7 @@ func (uc *unstructuredClient) UpdateStatus(_ context.Context, obj runtime.Object return nil } -func (uc *unstructuredClient) PatchStatus(_ context.Context, obj runtime.Object, patch Patch, opts ...PatchOptionFunc) error { +func (uc *unstructuredClient) PatchStatus(_ context.Context, obj runtime.Object, patch Patch, opts ...PatchOption) error { u, ok := obj.(*unstructured.Unstructured) if !ok { return fmt.Errorf("unstructured client did not understand object: %T", obj) From 320b6b64b40c77f9641dfbd844851f39d049a2c0 Mon Sep 17 00:00:00 2001 From: Solly Ross Date: Tue, 23 Jul 2019 17:51:58 -0700 Subject: [PATCH 2/2] Add field manager & dryrun to other opts For field manager, update and create also support fieldmanager, so we allow using them there. For dryrun, it's also supported by delete, so we support setting it there. --- pkg/client/client_test.go | 19 +++++++++++++++ pkg/client/options.go | 49 ++++++++++++++++++++++++++++++--------- 2 files changed, 57 insertions(+), 11 deletions(-) diff --git a/pkg/client/client_test.go b/pkg/client/client_test.go index ff4f17f2e6..9e613d56d6 100644 --- a/pkg/client/client_test.go +++ b/pkg/client/client_test.go @@ -2005,6 +2005,12 @@ var _ = Describe("Client", func() { Expect(co.AsCreateOptions().DryRun).To(Equal(all)) }) + It("should allow setting the field manager", func() { + po := &client.CreateOptions{} + client.FieldOwner("some-owner").ApplyToCreate(po) + Expect(po.AsCreateOptions().FieldManager).To(Equal("some-owner")) + }) + It("should produce empty metav1.CreateOptions if nil", func() { var co *client.CreateOptions Expect(co.AsCreateOptions()).To(Equal(&metav1.CreateOptions{})) @@ -2036,6 +2042,13 @@ var _ = Describe("Client", func() { Expect(do.AsDeleteOptions().PropagationPolicy).To(Equal(&dp)) }) + It("should allow setting DryRun", func() { + do := &client.DeleteOptions{} + client.DryRunAll.ApplyToDelete(do) + all := []string{metav1.DryRunAll} + Expect(do.AsDeleteOptions().DryRun).To(Equal(all)) + }) + It("should produce empty metav1.DeleteOptions if nil", func() { var do *client.DeleteOptions Expect(do.AsDeleteOptions()).To(Equal(&metav1.DeleteOptions{})) @@ -2102,6 +2115,12 @@ var _ = Describe("Client", func() { Expect(uo.AsUpdateOptions().DryRun).To(Equal(all)) }) + It("should allow setting the field manager", func() { + po := &client.UpdateOptions{} + client.FieldOwner("some-owner").ApplyToUpdate(po) + Expect(po.AsUpdateOptions().FieldManager).To(Equal("some-owner")) + }) + It("should produce empty metav1.UpdateOptions if nil", func() { var co *client.UpdateOptions Expect(co.AsUpdateOptions()).To(Equal(&metav1.UpdateOptions{})) diff --git a/pkg/client/options.go b/pkg/client/options.go index 2748cda888..48dd382c5e 100644 --- a/pkg/client/options.go +++ b/pkg/client/options.go @@ -73,6 +73,9 @@ func (dryRunAll) ApplyToUpdate(opts *UpdateOptions) { func (dryRunAll) ApplyToPatch(opts *PatchOptions) { opts.DryRun = []string{metav1.DryRunAll} } +func (dryRunAll) ApplyToDelete(opts *DeleteOptions) { + opts.DryRun = []string{metav1.DryRunAll} +} // FieldOwner set the field manager name for the given server-side apply patch. type FieldOwner string @@ -80,17 +83,11 @@ type FieldOwner string func (f FieldOwner) ApplyToPatch(opts *PatchOptions) { opts.FieldManager = string(f) } - -// ForceOwnership indicates that in case of conflicts with server-side apply, -// the client should acquire ownership of the conflicting field. Most -// controllers should use this. -var ForceOwnership = forceOwnership{} - -type forceOwnership struct{} - -func (forceOwnership) ApplyToPatch(opts *PatchOptions) { - definitelyTrue := true - opts.Force = &definitelyTrue +func (f FieldOwner) ApplyToCreate(opts *CreateOptions) { + opts.FieldManager = string(f) +} +func (f FieldOwner) ApplyToUpdate(opts *UpdateOptions) { + opts.FieldManager = string(f) } // }}} @@ -107,6 +104,10 @@ type CreateOptions struct { // - All: all dry run stages will be processed DryRun []string + // FieldManager is the name of the user or component submitting + // this request. It must be set with server-side apply. + FieldManager string + // Raw represents raw CreateOptions, as passed to the API server. Raw *metav1.CreateOptions } @@ -122,6 +123,7 @@ func (o *CreateOptions) AsCreateOptions() *metav1.CreateOptions { } o.Raw.DryRun = o.DryRun + o.Raw.FieldManager = o.FieldManager return o.Raw } @@ -168,6 +170,13 @@ type DeleteOptions struct { // Raw represents raw DeleteOptions, as passed to the API server. Raw *metav1.DeleteOptions + + // When present, indicates that modifications should not be + // persisted. An invalid or unrecognized dryRun directive will + // result in an error response and no further processing of the + // request. Valid values are: + // - All: all dry run stages will be processed + DryRun []string } // AsDeleteOptions returns these options as a metav1.DeleteOptions. @@ -183,6 +192,7 @@ func (o *DeleteOptions) AsDeleteOptions() *metav1.DeleteOptions { o.Raw.GracePeriodSeconds = o.GracePeriodSeconds o.Raw.Preconditions = o.Preconditions o.Raw.PropagationPolicy = o.PropagationPolicy + o.Raw.DryRun = o.DryRun return o.Raw } @@ -320,6 +330,10 @@ type UpdateOptions struct { // - All: all dry run stages will be processed DryRun []string + // FieldManager is the name of the user or component submitting + // this request. It must be set with server-side apply. + FieldManager string + // Raw represents raw UpdateOptions, as passed to the API server. Raw *metav1.UpdateOptions } @@ -335,6 +349,7 @@ func (o *UpdateOptions) AsUpdateOptions() *metav1.UpdateOptions { } o.Raw.DryRun = o.DryRun + o.Raw.FieldManager = o.FieldManager return o.Raw } @@ -404,6 +419,18 @@ func (o *PatchOptions) AsPatchOptions() *metav1.PatchOptions { return o.Raw } +// ForceOwnership indicates that in case of conflicts with server-side apply, +// the client should acquire ownership of the conflicting field. Most +// controllers should use this. +var ForceOwnership = forceOwnership{} + +type forceOwnership struct{} + +func (forceOwnership) ApplyToPatch(opts *PatchOptions) { + definitelyTrue := true + opts.Force = &definitelyTrue +} + // PatchDryRunAll sets the "dry run" option to "all". // // Deprecated: Use DryRunAll