Skip to content

Commit

Permalink
Use content negotiation in the dynamic client.
Browse files Browse the repository at this point in the history
Kubernetes-commit: 97958d96a2911c8b1df324868f4730b1c622c798
  • Loading branch information
benluddy authored and k8s-publishing-bot committed Feb 15, 2024
1 parent c38ce11 commit ee51eaf
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 123 deletions.
30 changes: 23 additions & 7 deletions dynamic/scheme.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,18 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/apimachinery/pkg/runtime/serializer/json"
)

var watchScheme = runtime.NewScheme()
var basicScheme = runtime.NewScheme()
var deleteScheme = runtime.NewScheme()
var parameterScheme = runtime.NewScheme()
var deleteOptionsCodec = serializer.NewCodecFactory(deleteScheme)
var dynamicParameterCodec = runtime.NewParameterCodec(parameterScheme)

var versionV1 = schema.GroupVersion{Version: "v1"}

func init() {
metav1.AddToGroupVersion(watchScheme, versionV1)
metav1.AddToGroupVersion(basicScheme, versionV1)
metav1.AddToGroupVersion(parameterScheme, versionV1)
metav1.AddToGroupVersion(deleteScheme, versionV1)
}

// basicNegotiatedSerializer is used to handle discovery and error handling serialization
Expand Down Expand Up @@ -66,7 +60,7 @@ func (s basicNegotiatedSerializer) EncoderForVersion(encoder runtime.Encoder, gv
return runtime.WithVersionEncoder{
Version: gv,
Encoder: encoder,
ObjectTyper: unstructuredTyper{basicScheme},
ObjectTyper: permissiveTyper{basicScheme},
}
}

Expand Down Expand Up @@ -106,3 +100,25 @@ func (t unstructuredTyper) ObjectKinds(obj runtime.Object) ([]schema.GroupVersio
func (t unstructuredTyper) Recognizes(gvk schema.GroupVersionKind) bool {
return true
}

// The dynamic client has historically accepted Unstructured objects with missing or empty
// apiVersion and/or kind as arguments to its write request methods. This typer will return the type
// of a runtime.Unstructured with no error, even if the type is missing or empty.
type permissiveTyper struct {
nested runtime.ObjectTyper
}

func (t permissiveTyper) ObjectKinds(obj runtime.Object) ([]schema.GroupVersionKind, bool, error) {
kinds, unversioned, err := t.nested.ObjectKinds(obj)
if err == nil {
return kinds, unversioned, nil
}
if _, ok := obj.(runtime.Unstructured); ok {
return []schema.GroupVersionKind{obj.GetObjectKind().GroupVersionKind()}, false, nil
}
return nil, false, err
}

func (t permissiveTyper) Recognizes(gvk schema.GroupVersionKind) bool {
return true
}
157 changes: 41 additions & 116 deletions dynamic/simple.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,10 @@ func NewForConfig(inConfig *rest.Config) (*DynamicClient, error) {
// Note the http client provided takes precedence over the configured transport values.
func NewForConfigAndClient(inConfig *rest.Config, h *http.Client) (*DynamicClient, error) {
config := ConfigFor(inConfig)
// for serializing the options
config.GroupVersion = &schema.GroupVersion{}
config.GroupVersion = nil
config.APIPath = "/if-you-see-this-search-for-the-break"

restClient, err := rest.RESTClientForConfigAndClient(config, h)
restClient, err := rest.UnversionedRESTClientForConfigAndClient(config, h)
if err != nil {
return nil, err
}
Expand All @@ -114,10 +113,6 @@ func (c *dynamicResourceClient) Namespace(ns string) ResourceInterface {
}

func (c *dynamicResourceClient) Create(ctx context.Context, obj *unstructured.Unstructured, opts metav1.CreateOptions, subresources ...string) (*unstructured.Unstructured, error) {
outBytes, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj)
if err != nil {
return nil, err
}
name := ""
if len(subresources) > 0 {
accessor, err := meta.Accessor(obj)
Expand All @@ -133,26 +128,17 @@ func (c *dynamicResourceClient) Create(ctx context.Context, obj *unstructured.Un
return nil, err
}

result := c.client.client.
var out unstructured.Unstructured
if err := c.client.client.
Post().
AbsPath(append(c.makeURLSegments(name), subresources...)...).
SetHeader("Content-Type", runtime.ContentTypeJSON).
Body(outBytes).
Body(obj).
SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).
Do(ctx)
if err := result.Error(); err != nil {
Do(ctx).Into(&out); 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
return &out, nil
}

func (c *dynamicResourceClient) Update(ctx context.Context, obj *unstructured.Unstructured, opts metav1.UpdateOptions, subresources ...string) (*unstructured.Unstructured, error) {
Expand All @@ -167,31 +153,18 @@ func (c *dynamicResourceClient) Update(ctx context.Context, obj *unstructured.Un
if err := validateNamespaceWithOptionalName(c.namespace, name); err != nil {
return nil, err
}
outBytes, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj)
if err != nil {
return nil, err
}

result := c.client.client.
var out unstructured.Unstructured
if err := c.client.client.
Put().
AbsPath(append(c.makeURLSegments(name), subresources...)...).
SetHeader("Content-Type", runtime.ContentTypeJSON).
Body(outBytes).
Body(obj).
SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).
Do(ctx)
if err := result.Error(); err != nil {
Do(ctx).Into(&out); 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
return &out, nil
}

func (c *dynamicResourceClient) UpdateStatus(ctx context.Context, obj *unstructured.Unstructured, opts metav1.UpdateOptions) (*unstructured.Unstructured, error) {
Expand All @@ -206,31 +179,18 @@ func (c *dynamicResourceClient) UpdateStatus(ctx context.Context, obj *unstructu
if err := validateNamespaceWithOptionalName(c.namespace, name); err != nil {
return nil, err
}
outBytes, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj)
if err != nil {
return nil, err
}

result := c.client.client.
var out unstructured.Unstructured
if err := c.client.client.
Put().
AbsPath(append(c.makeURLSegments(name), "status")...).
SetHeader("Content-Type", runtime.ContentTypeJSON).
Body(outBytes).
Body(obj).
SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).
Do(ctx)
if err := result.Error(); err != nil {
Do(ctx).Into(&out); 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
return &out, nil
}

func (c *dynamicResourceClient) Delete(ctx context.Context, name string, opts metav1.DeleteOptions, subresources ...string) error {
Expand All @@ -240,16 +200,11 @@ func (c *dynamicResourceClient) Delete(ctx context.Context, name string, opts me
if err := validateNamespaceWithOptionalName(c.namespace, name); err != nil {
return err
}
deleteOptionsByte, err := runtime.Encode(deleteOptionsCodec.LegacyCodec(schema.GroupVersion{Version: "v1"}), &opts)
if err != nil {
return err
}

result := c.client.client.
Delete().
AbsPath(append(c.makeURLSegments(name), subresources...)...).
SetHeader("Content-Type", runtime.ContentTypeJSON).
Body(deleteOptionsByte).
Body(&opts).
Do(ctx)
return result.Error()
}
Expand All @@ -259,16 +214,10 @@ func (c *dynamicResourceClient) DeleteCollection(ctx context.Context, opts metav
return err
}

deleteOptionsByte, err := runtime.Encode(deleteOptionsCodec.LegacyCodec(schema.GroupVersion{Version: "v1"}), &opts)
if err != nil {
return err
}

result := c.client.client.
Delete().
AbsPath(c.makeURLSegments("")...).
SetHeader("Content-Type", runtime.ContentTypeJSON).
Body(deleteOptionsByte).
Body(&opts).
SpecificallyVersionedParams(&listOptions, dynamicParameterCodec, versionV1).
Do(ctx)
return result.Error()
Expand All @@ -281,20 +230,15 @@ func (c *dynamicResourceClient) Get(ctx context.Context, name string, opts metav
if err := validateNamespaceWithOptionalName(c.namespace, name); err != nil {
return nil, err
}
result := c.client.client.Get().AbsPath(append(c.makeURLSegments(name), subresources...)...).SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).Do(ctx)
if err := result.Error(); err != nil {
return nil, err
}
retBytes, err := result.Raw()
if err != nil {
return nil, err
}
obj := &unstructured.Unstructured{}
err = runtime.DecodeInto(unstructured.UnstructuredJSONScheme, retBytes, obj)
if err != nil {
var out unstructured.Unstructured
if err := c.client.client.
Get().
AbsPath(append(c.makeURLSegments(name), subresources...)...).
SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).
Do(ctx).Into(&out); err != nil {
return nil, err
}
return obj, nil
return &out, nil
}

func (c *dynamicResourceClient) List(ctx context.Context, opts metav1.ListOptions) (*unstructured.UnstructuredList, error) {
Expand All @@ -319,27 +263,15 @@ func (c *dynamicResourceClient) list(ctx context.Context, opts metav1.ListOption
if err := validateNamespaceWithOptionalName(c.namespace); err != nil {
return nil, err
}
result := c.client.client.Get().AbsPath(c.makeURLSegments("")...).SpecificallyVersionedParams(&opts, 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
}
if list, ok := uncastObj.(*unstructured.UnstructuredList); ok {
return list, nil
}

list, err := uncastObj.(*unstructured.Unstructured).ToList()
if err != nil {
var out unstructured.UnstructuredList
if err := c.client.client.
Get().
AbsPath(c.makeURLSegments("")...).
SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).
Do(ctx).Into(&out); err != nil {
return nil, err
}
return list, nil
return &out, nil
}

// watchList establishes a watch stream with the server and returns an unstructured list.
Expand Down Expand Up @@ -380,24 +312,16 @@ func (c *dynamicResourceClient) Patch(ctx context.Context, name string, pt types
if err := validateNamespaceWithOptionalName(c.namespace, name); err != nil {
return nil, err
}
result := c.client.client.
var out unstructured.Unstructured
if err := c.client.client.
Patch(pt).
AbsPath(append(c.makeURLSegments(name), subresources...)...).
Body(data).
SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).
Do(ctx)
if err := result.Error(); err != nil {
return nil, err
}
retBytes, err := result.Raw()
if err != nil {
Do(ctx).Into(&out); err != nil {
return nil, err
}
uncastObj, err := runtime.Decode(unstructured.UnstructuredJSONScheme, retBytes)
if err != nil {
return nil, err
}
return uncastObj.(*unstructured.Unstructured), nil
return &out, nil
}

func (c *dynamicResourceClient) Apply(ctx context.Context, name string, obj *unstructured.Unstructured, opts metav1.ApplyOptions, subresources ...string) (*unstructured.Unstructured, error) {
Expand Down Expand Up @@ -435,12 +359,13 @@ func (c *dynamicResourceClient) Apply(ctx context.Context, name string, obj *uns
if err != nil {
return nil, err
}
uncastObj, err := runtime.Decode(unstructured.UnstructuredJSONScheme, retBytes)
if err != nil {
var out unstructured.Unstructured
if err := runtime.DecodeInto(unstructured.UnstructuredJSONScheme, retBytes, &out); err != nil {
return nil, err
}
return uncastObj.(*unstructured.Unstructured), nil
return &out, 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")
}
Expand Down

0 comments on commit ee51eaf

Please sign in to comment.