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..9e613d56d6 100644 --- a/pkg/client/client_test.go +++ b/pkg/client/client_test.go @@ -2000,11 +2000,17 @@ 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)) }) + 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{})) @@ -2016,7 +2022,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,18 +2030,25 @@ 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)) }) + 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{})) @@ -2048,9 +2061,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 +2073,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() { + It("should be populated by 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"} - 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() { + It("should be populated by 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() { - 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,11 +2110,17 @@ 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)) }) + 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{})) @@ -2157,14 +2132,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 +2147,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 +2295,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..48dd382c5e 100644 --- a/pkg/client/options.go +++ b/pkg/client/options.go @@ -22,6 +22,78 @@ 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} +} +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 + +func (f FieldOwner) ApplyToPatch(opts *PatchOptions) { + opts.FieldManager = string(f) +} +func (f FieldOwner) ApplyToCreate(opts *CreateOptions) { + opts.FieldManager = string(f) +} +func (f FieldOwner) ApplyToUpdate(opts *UpdateOptions) { + opts.FieldManager = string(f) +} + +// }}} + +// {{{ Create Options + // CreateOptions contains options for create requests. It's generally a subset // of metav1.CreateOptions. type CreateOptions struct { @@ -32,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 } @@ -39,7 +115,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{} } @@ -48,28 +123,27 @@ func (o *CreateOptions) AsCreateOptions() *metav1.CreateOptions { } o.Raw.DryRun = o.DryRun + o.Raw.FieldManager = o.FieldManager 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. @@ -96,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. @@ -111,47 +192,46 @@ 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 } -// 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 +255,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 +273,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. @@ -301,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 } @@ -316,28 +349,27 @@ func (o *UpdateOptions) AsUpdateOptions() *metav1.UpdateOptions { } o.Raw.DryRun = o.DryRun + o.Raw.FieldManager = o.FieldManager 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 +394,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 +419,21 @@ 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 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{} -// 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) { +func (forceOwnership) ApplyToPatch(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)