diff --git a/dynamic/fake/simple.go b/dynamic/fake/simple.go index dee16b245..197508c81 100644 --- a/dynamic/fake/simple.go +++ b/dynamic/fake/simple.go @@ -454,6 +454,51 @@ func (c *dynamicResourceClient) Patch(ctx context.Context, name string, pt types return ret, err } +// TODO: opts are currently ignored. +func (c *dynamicResourceClient) Apply(ctx context.Context, name string, obj *unstructured.Unstructured, options metav1.ApplyOptions, subresources ...string) (*unstructured.Unstructured, error) { + outBytes, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj) + if err != nil { + return nil, err + } + var uncastRet runtime.Object + switch { + case len(c.namespace) == 0 && len(subresources) == 0: + uncastRet, err = c.client.Fake. + Invokes(testing.NewRootPatchAction(c.resource, name, types.ApplyPatchType, outBytes), &metav1.Status{Status: "dynamic patch fail"}) + + case len(c.namespace) == 0 && len(subresources) > 0: + uncastRet, err = c.client.Fake. + Invokes(testing.NewRootPatchSubresourceAction(c.resource, name, types.ApplyPatchType, outBytes, subresources...), &metav1.Status{Status: "dynamic patch fail"}) + + case len(c.namespace) > 0 && len(subresources) == 0: + uncastRet, err = c.client.Fake. + Invokes(testing.NewPatchAction(c.resource, c.namespace, name, types.ApplyPatchType, outBytes), &metav1.Status{Status: "dynamic patch fail"}) + + case len(c.namespace) > 0 && len(subresources) > 0: + uncastRet, err = c.client.Fake. + Invokes(testing.NewPatchSubresourceAction(c.resource, c.namespace, name, types.ApplyPatchType, outBytes, subresources...), &metav1.Status{Status: "dynamic patch fail"}) + + } + + if err != nil { + return nil, err + } + if uncastRet == nil { + return nil, err + } + + ret := &unstructured.Unstructured{} + if err := c.client.scheme.Convert(uncastRet, ret, nil); err != nil { + return nil, err + } + return ret, err + return nil, nil +} + +func (c *dynamicResourceClient) ApplyStatus(ctx context.Context, name string, obj *unstructured.Unstructured, options metav1.ApplyOptions) (*unstructured.Unstructured, error) { + return c.Apply(ctx, name, obj, options, "status") +} + func convertObjectsToUnstructured(s *runtime.Scheme, objs []runtime.Object) ([]runtime.Object, error) { ul := make([]runtime.Object, 0, len(objs)) diff --git a/dynamic/interface.go b/dynamic/interface.go index b08067c34..a310b63e5 100644 --- a/dynamic/interface.go +++ b/dynamic/interface.go @@ -40,6 +40,8 @@ type ResourceInterface interface { List(ctx context.Context, opts metav1.ListOptions) (*unstructured.UnstructuredList, error) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, options metav1.PatchOptions, subresources ...string) (*unstructured.Unstructured, error) + Apply(ctx context.Context, name string, obj *unstructured.Unstructured, options metav1.ApplyOptions, subresources ...string) (*unstructured.Unstructured, error) + ApplyStatus(ctx context.Context, name string, obj *unstructured.Unstructured, options metav1.ApplyOptions) (*unstructured.Unstructured, error) } type NamespaceableResourceInterface interface { diff --git a/dynamic/simple.go b/dynamic/simple.go index 87594bf2e..9dc0fb5c0 100644 --- a/dynamic/simple.go +++ b/dynamic/simple.go @@ -324,6 +324,48 @@ func (c *dynamicResourceClient) Patch(ctx context.Context, name string, pt types return uncastObj.(*unstructured.Unstructured), nil } +func (c *dynamicResourceClient) Apply(ctx context.Context, name string, obj *unstructured.Unstructured, opts metav1.ApplyOptions, subresources ...string) (*unstructured.Unstructured, error) { + if len(name) == 0 { + return nil, fmt.Errorf("name is required") + } + outBytes, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj) + if err != nil { + return nil, err + } + accessor, err := meta.Accessor(obj) + if err != nil { + return nil, err + } + managedFields := accessor.GetManagedFields() + if len(managedFields) > 0 { + return nil, fmt.Errorf(`cannot apply an object with managed fields already set. + Use the client-go/applyconfigurations "UnstructructuredExtractor" to obtain the unstructured ApplyConfiguration for the given field manager that you can use/modify here to apply`) + } + patchOpts := opts.ToPatchOptions() + + result := c.client.client. + Patch(types.ApplyPatchType). + AbsPath(append(c.makeURLSegments(name), subresources...)...). + Body(outBytes). + SpecificallyVersionedParams(&patchOpts, dynamicParameterCodec, versionV1). + Do(ctx) + if err := result.Error(); err != nil { + return nil, err + } + retBytes, err := result.Raw() + if err != nil { + return nil, err + } + uncastObj, err := runtime.Decode(unstructured.UnstructuredJSONScheme, retBytes) + if err != nil { + return nil, err + } + return uncastObj.(*unstructured.Unstructured), nil +} +func (c *dynamicResourceClient) ApplyStatus(ctx context.Context, name string, obj *unstructured.Unstructured, opts metav1.ApplyOptions) (*unstructured.Unstructured, error) { + return c.Apply(ctx, name, obj, opts, "status") +} + func (c *dynamicResourceClient) makeURLSegments(name string) []string { url := []string{} if len(c.resource.Group) == 0 { diff --git a/go.mod b/go.mod index 0e0023c9d..cfd35ecf4 100644 --- a/go.mod +++ b/go.mod @@ -34,8 +34,8 @@ require ( golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 google.golang.org/protobuf v1.27.1 - k8s.io/api v0.0.0-20220504022944-af4f75e1994c - k8s.io/apimachinery v0.0.0-20220406001655-080c0c77fab5 + k8s.io/api v0.0.0-20220504062940-f2f8c15fc29f + k8s.io/apimachinery v0.0.0-20220504062721-f71cc2793493 k8s.io/klog/v2 v2.60.1 k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42 k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 @@ -44,6 +44,6 @@ require ( ) replace ( - k8s.io/api => k8s.io/api v0.0.0-20220504022944-af4f75e1994c - k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20220406001655-080c0c77fab5 + k8s.io/api => k8s.io/api v0.0.0-20220504062940-f2f8c15fc29f + k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20220504062721-f71cc2793493 ) diff --git a/go.sum b/go.sum index 687376460..8445c3400 100644 --- a/go.sum +++ b/go.sum @@ -628,10 +628,10 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.0.0-20220504022944-af4f75e1994c h1:4chKbSSvIxpT/ls7NTHZyE0xnKewL0HSbdaYyesfrmQ= -k8s.io/api v0.0.0-20220504022944-af4f75e1994c/go.mod h1:jimmvAe8QfauNx6CM87E20mTsK3CPgiOzVhfaNASkJc= -k8s.io/apimachinery v0.0.0-20220406001655-080c0c77fab5 h1:nG9Zc74nUlkVHMapMJVjlH8hT3z/fBVixlVfrsQgi24= -k8s.io/apimachinery v0.0.0-20220406001655-080c0c77fab5/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= +k8s.io/api v0.0.0-20220504062940-f2f8c15fc29f h1:yahIBtSoM2GJrTtmkge28xngCHtPiW3CyhjcrGSojQg= +k8s.io/api v0.0.0-20220504062940-f2f8c15fc29f/go.mod h1:Vj1A561tKmF3X1XV1/cGWVWkImJjDjTF6HwinifdHTU= +k8s.io/apimachinery v0.0.0-20220504062721-f71cc2793493 h1:82cE3vib2ZIhoyz153bSAnuxMUdXg1OpLaUM6pzES7M= +k8s.io/apimachinery v0.0.0-20220504062721-f71cc2793493/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=