k8s.io/client-go@v0.22.2/dynamic/simple.go (about)

     1  /*
     2  Copyright 2018 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package dynamic
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  
    23  	"k8s.io/apimachinery/pkg/api/meta"
    24  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    25  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    26  	"k8s.io/apimachinery/pkg/runtime"
    27  	"k8s.io/apimachinery/pkg/runtime/schema"
    28  	"k8s.io/apimachinery/pkg/types"
    29  	"k8s.io/apimachinery/pkg/watch"
    30  	"k8s.io/client-go/rest"
    31  )
    32  
    33  type dynamicClient struct {
    34  	client *rest.RESTClient
    35  }
    36  
    37  var _ Interface = &dynamicClient{}
    38  
    39  // ConfigFor returns a copy of the provided config with the
    40  // appropriate dynamic client defaults set.
    41  func ConfigFor(inConfig *rest.Config) *rest.Config {
    42  	config := rest.CopyConfig(inConfig)
    43  	config.AcceptContentTypes = "application/json"
    44  	config.ContentType = "application/json"
    45  	config.NegotiatedSerializer = basicNegotiatedSerializer{} // this gets used for discovery and error handling types
    46  	if config.UserAgent == "" {
    47  		config.UserAgent = rest.DefaultKubernetesUserAgent()
    48  	}
    49  	return config
    50  }
    51  
    52  // NewForConfigOrDie creates a new Interface for the given config and
    53  // panics if there is an error in the config.
    54  func NewForConfigOrDie(c *rest.Config) Interface {
    55  	ret, err := NewForConfig(c)
    56  	if err != nil {
    57  		panic(err)
    58  	}
    59  	return ret
    60  }
    61  
    62  // NewForConfig creates a new dynamic client or returns an error.
    63  func NewForConfig(inConfig *rest.Config) (Interface, error) {
    64  	config := ConfigFor(inConfig)
    65  	// for serializing the options
    66  	config.GroupVersion = &schema.GroupVersion{}
    67  	config.APIPath = "/if-you-see-this-search-for-the-break"
    68  
    69  	restClient, err := rest.RESTClientFor(config)
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  
    74  	return &dynamicClient{client: restClient}, nil
    75  }
    76  
    77  type dynamicResourceClient struct {
    78  	client    *dynamicClient
    79  	namespace string
    80  	resource  schema.GroupVersionResource
    81  }
    82  
    83  func (c *dynamicClient) Resource(resource schema.GroupVersionResource) NamespaceableResourceInterface {
    84  	return &dynamicResourceClient{client: c, resource: resource}
    85  }
    86  
    87  func (c *dynamicResourceClient) Namespace(ns string) ResourceInterface {
    88  	ret := *c
    89  	ret.namespace = ns
    90  	return &ret
    91  }
    92  
    93  func (c *dynamicResourceClient) Create(ctx context.Context, obj *unstructured.Unstructured, opts metav1.CreateOptions, subresources ...string) (*unstructured.Unstructured, error) {
    94  	outBytes, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj)
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  	name := ""
    99  	if len(subresources) > 0 {
   100  		accessor, err := meta.Accessor(obj)
   101  		if err != nil {
   102  			return nil, err
   103  		}
   104  		name = accessor.GetName()
   105  		if len(name) == 0 {
   106  			return nil, fmt.Errorf("name is required")
   107  		}
   108  	}
   109  
   110  	result := c.client.client.
   111  		Post().
   112  		AbsPath(append(c.makeURLSegments(name), subresources...)...).
   113  		Body(outBytes).
   114  		SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).
   115  		Do(ctx)
   116  	if err := result.Error(); err != nil {
   117  		return nil, err
   118  	}
   119  
   120  	retBytes, err := result.Raw()
   121  	if err != nil {
   122  		return nil, err
   123  	}
   124  	uncastObj, err := runtime.Decode(unstructured.UnstructuredJSONScheme, retBytes)
   125  	if err != nil {
   126  		return nil, err
   127  	}
   128  	return uncastObj.(*unstructured.Unstructured), nil
   129  }
   130  
   131  func (c *dynamicResourceClient) Update(ctx context.Context, obj *unstructured.Unstructured, opts metav1.UpdateOptions, subresources ...string) (*unstructured.Unstructured, error) {
   132  	accessor, err := meta.Accessor(obj)
   133  	if err != nil {
   134  		return nil, err
   135  	}
   136  	name := accessor.GetName()
   137  	if len(name) == 0 {
   138  		return nil, fmt.Errorf("name is required")
   139  	}
   140  	outBytes, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj)
   141  	if err != nil {
   142  		return nil, err
   143  	}
   144  
   145  	result := c.client.client.
   146  		Put().
   147  		AbsPath(append(c.makeURLSegments(name), subresources...)...).
   148  		Body(outBytes).
   149  		SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).
   150  		Do(ctx)
   151  	if err := result.Error(); err != nil {
   152  		return nil, err
   153  	}
   154  
   155  	retBytes, err := result.Raw()
   156  	if err != nil {
   157  		return nil, err
   158  	}
   159  	uncastObj, err := runtime.Decode(unstructured.UnstructuredJSONScheme, retBytes)
   160  	if err != nil {
   161  		return nil, err
   162  	}
   163  	return uncastObj.(*unstructured.Unstructured), nil
   164  }
   165  
   166  func (c *dynamicResourceClient) UpdateStatus(ctx context.Context, obj *unstructured.Unstructured, opts metav1.UpdateOptions) (*unstructured.Unstructured, error) {
   167  	accessor, err := meta.Accessor(obj)
   168  	if err != nil {
   169  		return nil, err
   170  	}
   171  	name := accessor.GetName()
   172  	if len(name) == 0 {
   173  		return nil, fmt.Errorf("name is required")
   174  	}
   175  
   176  	outBytes, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj)
   177  	if err != nil {
   178  		return nil, err
   179  	}
   180  
   181  	result := c.client.client.
   182  		Put().
   183  		AbsPath(append(c.makeURLSegments(name), "status")...).
   184  		Body(outBytes).
   185  		SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).
   186  		Do(ctx)
   187  	if err := result.Error(); err != nil {
   188  		return nil, err
   189  	}
   190  
   191  	retBytes, err := result.Raw()
   192  	if err != nil {
   193  		return nil, err
   194  	}
   195  	uncastObj, err := runtime.Decode(unstructured.UnstructuredJSONScheme, retBytes)
   196  	if err != nil {
   197  		return nil, err
   198  	}
   199  	return uncastObj.(*unstructured.Unstructured), nil
   200  }
   201  
   202  func (c *dynamicResourceClient) Delete(ctx context.Context, name string, opts metav1.DeleteOptions, subresources ...string) error {
   203  	if len(name) == 0 {
   204  		return fmt.Errorf("name is required")
   205  	}
   206  	deleteOptionsByte, err := runtime.Encode(deleteOptionsCodec.LegacyCodec(schema.GroupVersion{Version: "v1"}), &opts)
   207  	if err != nil {
   208  		return err
   209  	}
   210  
   211  	result := c.client.client.
   212  		Delete().
   213  		AbsPath(append(c.makeURLSegments(name), subresources...)...).
   214  		Body(deleteOptionsByte).
   215  		Do(ctx)
   216  	return result.Error()
   217  }
   218  
   219  func (c *dynamicResourceClient) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOptions metav1.ListOptions) error {
   220  	deleteOptionsByte, err := runtime.Encode(deleteOptionsCodec.LegacyCodec(schema.GroupVersion{Version: "v1"}), &opts)
   221  	if err != nil {
   222  		return err
   223  	}
   224  
   225  	result := c.client.client.
   226  		Delete().
   227  		AbsPath(c.makeURLSegments("")...).
   228  		Body(deleteOptionsByte).
   229  		SpecificallyVersionedParams(&listOptions, dynamicParameterCodec, versionV1).
   230  		Do(ctx)
   231  	return result.Error()
   232  }
   233  
   234  func (c *dynamicResourceClient) Get(ctx context.Context, name string, opts metav1.GetOptions, subresources ...string) (*unstructured.Unstructured, error) {
   235  	if len(name) == 0 {
   236  		return nil, fmt.Errorf("name is required")
   237  	}
   238  	result := c.client.client.Get().AbsPath(append(c.makeURLSegments(name), subresources...)...).SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).Do(ctx)
   239  	if err := result.Error(); err != nil {
   240  		return nil, err
   241  	}
   242  	retBytes, err := result.Raw()
   243  	if err != nil {
   244  		return nil, err
   245  	}
   246  	uncastObj, err := runtime.Decode(unstructured.UnstructuredJSONScheme, retBytes)
   247  	if err != nil {
   248  		return nil, err
   249  	}
   250  	return uncastObj.(*unstructured.Unstructured), nil
   251  }
   252  
   253  func (c *dynamicResourceClient) List(ctx context.Context, opts metav1.ListOptions) (*unstructured.UnstructuredList, error) {
   254  	result := c.client.client.Get().AbsPath(c.makeURLSegments("")...).SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).Do(ctx)
   255  	if err := result.Error(); err != nil {
   256  		return nil, err
   257  	}
   258  	retBytes, err := result.Raw()
   259  	if err != nil {
   260  		return nil, err
   261  	}
   262  	uncastObj, err := runtime.Decode(unstructured.UnstructuredJSONScheme, retBytes)
   263  	if err != nil {
   264  		return nil, err
   265  	}
   266  	if list, ok := uncastObj.(*unstructured.UnstructuredList); ok {
   267  		return list, nil
   268  	}
   269  
   270  	list, err := uncastObj.(*unstructured.Unstructured).ToList()
   271  	if err != nil {
   272  		return nil, err
   273  	}
   274  	return list, nil
   275  }
   276  
   277  func (c *dynamicResourceClient) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
   278  	opts.Watch = true
   279  	return c.client.client.Get().AbsPath(c.makeURLSegments("")...).
   280  		SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).
   281  		Watch(ctx)
   282  }
   283  
   284  func (c *dynamicResourceClient) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (*unstructured.Unstructured, error) {
   285  	if len(name) == 0 {
   286  		return nil, fmt.Errorf("name is required")
   287  	}
   288  	result := c.client.client.
   289  		Patch(pt).
   290  		AbsPath(append(c.makeURLSegments(name), subresources...)...).
   291  		Body(data).
   292  		SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).
   293  		Do(ctx)
   294  	if err := result.Error(); err != nil {
   295  		return nil, err
   296  	}
   297  	retBytes, err := result.Raw()
   298  	if err != nil {
   299  		return nil, err
   300  	}
   301  	uncastObj, err := runtime.Decode(unstructured.UnstructuredJSONScheme, retBytes)
   302  	if err != nil {
   303  		return nil, err
   304  	}
   305  	return uncastObj.(*unstructured.Unstructured), nil
   306  }
   307  
   308  func (c *dynamicResourceClient) makeURLSegments(name string) []string {
   309  	url := []string{}
   310  	if len(c.resource.Group) == 0 {
   311  		url = append(url, "api")
   312  	} else {
   313  		url = append(url, "apis", c.resource.Group)
   314  	}
   315  	url = append(url, c.resource.Version)
   316  
   317  	if len(c.namespace) > 0 {
   318  		url = append(url, "namespaces", c.namespace)
   319  	}
   320  	url = append(url, c.resource.Resource)
   321  
   322  	if len(name) > 0 {
   323  		url = append(url, name)
   324  	}
   325  
   326  	return url
   327  }