You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
287 lines
8.9 KiB
287 lines
8.9 KiB
/* |
|
Copyright 2018 The Kubernetes Authors. |
|
|
|
Licensed under the Apache License, Version 2.0 (the "License"); |
|
you may not use this file except in compliance with the License. |
|
You may obtain a copy of the License at |
|
|
|
http://www.apache.org/licenses/LICENSE-2.0 |
|
|
|
Unless required by applicable law or agreed to in writing, software |
|
distributed under the License is distributed on an "AS IS" BASIS, |
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
See the License for the specific language governing permissions and |
|
limitations under the License. |
|
*/ |
|
|
|
package dynamic |
|
|
|
import ( |
|
"io" |
|
|
|
"k8s.io/apimachinery/pkg/api/meta" |
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
|
"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/streaming" |
|
"k8s.io/apimachinery/pkg/types" |
|
"k8s.io/apimachinery/pkg/watch" |
|
"k8s.io/client-go/rest" |
|
) |
|
|
|
type dynamicClient struct { |
|
client *rest.RESTClient |
|
} |
|
|
|
var _ Interface = &dynamicClient{} |
|
|
|
func NewForConfig(inConfig *rest.Config) (Interface, error) { |
|
config := rest.CopyConfig(inConfig) |
|
// for serializing the options |
|
config.GroupVersion = &schema.GroupVersion{} |
|
config.APIPath = "/if-you-see-this-search-for-the-break" |
|
config.AcceptContentTypes = "application/json" |
|
config.ContentType = "application/json" |
|
config.NegotiatedSerializer = basicNegotiatedSerializer{} // this gets used for discovery and error handling types |
|
if config.UserAgent == "" { |
|
config.UserAgent = rest.DefaultKubernetesUserAgent() |
|
} |
|
|
|
restClient, err := rest.RESTClientFor(config) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
return &dynamicClient{client: restClient}, nil |
|
} |
|
|
|
type dynamicResourceClient struct { |
|
client *dynamicClient |
|
namespace string |
|
resource schema.GroupVersionResource |
|
} |
|
|
|
func (c *dynamicClient) Resource(resource schema.GroupVersionResource) NamespaceableResourceInterface { |
|
return &dynamicResourceClient{client: c, resource: resource} |
|
} |
|
|
|
func (c *dynamicResourceClient) Namespace(ns string) ResourceInterface { |
|
ret := *c |
|
ret.namespace = ns |
|
return &ret |
|
} |
|
|
|
func (c *dynamicResourceClient) Create(obj *unstructured.Unstructured, 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) |
|
if err != nil { |
|
return nil, err |
|
} |
|
name = accessor.GetName() |
|
} |
|
|
|
result := c.client.client.Post().AbsPath(append(c.makeURLSegments(name), subresources...)...).Body(outBytes).Do() |
|
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) Update(obj *unstructured.Unstructured, subresources ...string) (*unstructured.Unstructured, error) { |
|
accessor, err := meta.Accessor(obj) |
|
if err != nil { |
|
return nil, err |
|
} |
|
outBytes, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
result := c.client.client.Put().AbsPath(append(c.makeURLSegments(accessor.GetName()), subresources...)...).Body(outBytes).Do() |
|
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) UpdateStatus(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) { |
|
accessor, err := meta.Accessor(obj) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
outBytes, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
result := c.client.client.Put().AbsPath(append(c.makeURLSegments(accessor.GetName()), "status")...).Body(outBytes).Do() |
|
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) Delete(name string, opts *metav1.DeleteOptions, subresources ...string) error { |
|
if opts == nil { |
|
opts = &metav1.DeleteOptions{} |
|
} |
|
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...)...).Body(deleteOptionsByte).Do() |
|
return result.Error() |
|
} |
|
|
|
func (c *dynamicResourceClient) DeleteCollection(opts *metav1.DeleteOptions, listOptions metav1.ListOptions) error { |
|
if opts == nil { |
|
opts = &metav1.DeleteOptions{} |
|
} |
|
deleteOptionsByte, err := runtime.Encode(deleteOptionsCodec.LegacyCodec(schema.GroupVersion{Version: "v1"}), opts) |
|
if err != nil { |
|
return err |
|
} |
|
|
|
result := c.client.client.Delete().AbsPath(c.makeURLSegments("")...).Body(deleteOptionsByte).SpecificallyVersionedParams(&listOptions, dynamicParameterCodec, versionV1).Do() |
|
return result.Error() |
|
} |
|
|
|
func (c *dynamicResourceClient) Get(name string, opts metav1.GetOptions, subresources ...string) (*unstructured.Unstructured, error) { |
|
result := c.client.client.Get().AbsPath(append(c.makeURLSegments(name), subresources...)...).SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).Do() |
|
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) List(opts metav1.ListOptions) (*unstructured.UnstructuredList, error) { |
|
result := c.client.client.Get().AbsPath(c.makeURLSegments("")...).SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).Do() |
|
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 { |
|
return nil, err |
|
} |
|
return list, nil |
|
} |
|
|
|
func (c *dynamicResourceClient) Watch(opts metav1.ListOptions) (watch.Interface, error) { |
|
internalGV := schema.GroupVersions{ |
|
{Group: c.resource.Group, Version: runtime.APIVersionInternal}, |
|
// always include the legacy group as a decoding target to handle non-error `Status` return types |
|
{Group: "", Version: runtime.APIVersionInternal}, |
|
} |
|
s := &rest.Serializers{ |
|
Encoder: watchNegotiatedSerializerInstance.EncoderForVersion(watchJsonSerializerInfo.Serializer, c.resource.GroupVersion()), |
|
Decoder: watchNegotiatedSerializerInstance.DecoderToVersion(watchJsonSerializerInfo.Serializer, internalGV), |
|
|
|
RenegotiatedDecoder: func(contentType string, params map[string]string) (runtime.Decoder, error) { |
|
return watchNegotiatedSerializerInstance.DecoderToVersion(watchJsonSerializerInfo.Serializer, internalGV), nil |
|
}, |
|
StreamingSerializer: watchJsonSerializerInfo.StreamSerializer.Serializer, |
|
Framer: watchJsonSerializerInfo.StreamSerializer.Framer, |
|
} |
|
|
|
wrappedDecoderFn := func(body io.ReadCloser) streaming.Decoder { |
|
framer := s.Framer.NewFrameReader(body) |
|
return streaming.NewDecoder(framer, s.StreamingSerializer) |
|
} |
|
|
|
opts.Watch = true |
|
return c.client.client.Get().AbsPath(c.makeURLSegments("")...). |
|
SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1). |
|
WatchWithSpecificDecoders(wrappedDecoderFn, unstructured.UnstructuredJSONScheme) |
|
} |
|
|
|
func (c *dynamicResourceClient) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (*unstructured.Unstructured, error) { |
|
result := c.client.client.Patch(pt).AbsPath(append(c.makeURLSegments(name), subresources...)...).Body(data).Do() |
|
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) makeURLSegments(name string) []string { |
|
url := []string{} |
|
if len(c.resource.Group) == 0 { |
|
url = append(url, "api") |
|
} else { |
|
url = append(url, "apis", c.resource.Group) |
|
} |
|
url = append(url, c.resource.Version) |
|
|
|
if len(c.namespace) > 0 { |
|
url = append(url, "namespaces", c.namespace) |
|
} |
|
url = append(url, c.resource.Resource) |
|
|
|
if len(name) > 0 { |
|
url = append(url, name) |
|
} |
|
|
|
return url |
|
}
|
|
|