Transform binding to work

This commit is contained in:
chenxianpao 2020-11-16 19:42:16 +08:00 committed by Hongcai Ren
parent dbcae8c52a
commit 3a9e5cdac4
5 changed files with 622 additions and 5 deletions

View File

@ -1,13 +1,19 @@
package binding package binding
import ( import (
"context"
"fmt" "fmt"
"time" "time"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
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"
utilruntime "k8s.io/apimachinery/pkg/util/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/kubernetes/scheme"
typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1" typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
@ -17,6 +23,7 @@ import (
"k8s.io/client-go/util/workqueue" "k8s.io/client-go/util/workqueue"
"k8s.io/klog/v2" "k8s.io/klog/v2"
"github.com/huawei-cloudnative/karmada/pkg/apis/propagationstrategy/v1alpha1"
"github.com/huawei-cloudnative/karmada/pkg/controllers/util" "github.com/huawei-cloudnative/karmada/pkg/controllers/util"
clientset "github.com/huawei-cloudnative/karmada/pkg/generated/clientset/versioned" clientset "github.com/huawei-cloudnative/karmada/pkg/generated/clientset/versioned"
karmadaScheme "github.com/huawei-cloudnative/karmada/pkg/generated/clientset/versioned/scheme" karmadaScheme "github.com/huawei-cloudnative/karmada/pkg/generated/clientset/versioned/scheme"
@ -24,7 +31,27 @@ import (
listers "github.com/huawei-cloudnative/karmada/pkg/generated/listers/propagationstrategy/v1alpha1" listers "github.com/huawei-cloudnative/karmada/pkg/generated/listers/propagationstrategy/v1alpha1"
) )
const controllerAgentName = "binding-controller" const (
controllerAgentName = "binding-controller"
)
// todo: this can get by kubectl api-resources
var resourceKindMap = map[string]string{
"ConfigMap": "configmaps",
"Namespace": "namespaces",
"PersistentVolumeClaim": "persistentvolumeclaims",
"PersistentVolume": "persistentvolumes",
"Pod": "pods",
"Secret": "secrets",
"Service": "services",
"Deployment": "deployments",
"DaemonSet": "daemonsets",
"StatefulSet": "statefulsets",
"ReplicaSet": "replicasets",
"CronJob": "cronjobs",
"Job": "jobs",
"Ingress": "ingresses",
}
// Controller is the controller implementation for binding resources // Controller is the controller implementation for binding resources
type Controller struct { type Controller struct {
@ -32,8 +59,8 @@ type Controller struct {
karmadaClientSet clientset.Interface karmadaClientSet clientset.Interface
// kubeClientSet is a standard kubernetes clientset. // kubeClientSet is a standard kubernetes clientset.
kubeClientSet kubernetes.Interface kubeClientSet kubernetes.Interface
dynamicClientSet dynamic.Interface
karmadaInformerFactory informers.SharedInformerFactory karmadaInformerFactory informers.SharedInformerFactory
propagationBindingLister listers.PropagationBindingLister propagationBindingLister listers.PropagationBindingLister
propagationBindingSynced cache.InformerSynced propagationBindingSynced cache.InformerSynced
@ -73,6 +100,10 @@ func newPropagationBindingController(config *util.ControllerConfig) (*Controller
kubeClientSet := kubernetes.NewForConfigOrDie(headClusterConfig) kubeClientSet := kubernetes.NewForConfigOrDie(headClusterConfig)
karmadaClientSet := clientset.NewForConfigOrDie(headClusterConfig) karmadaClientSet := clientset.NewForConfigOrDie(headClusterConfig)
dynamicClientSet, err := dynamic.NewForConfig(headClusterConfig)
if err != nil {
return nil, err
}
karmadaInformerFactory := informers.NewSharedInformerFactory(karmadaClientSet, 0) karmadaInformerFactory := informers.NewSharedInformerFactory(karmadaClientSet, 0)
propagationBindingInformer := karmadaInformerFactory.Propagationstrategy().V1alpha1().PropagationBindings() propagationBindingInformer := karmadaInformerFactory.Propagationstrategy().V1alpha1().PropagationBindings()
// Add karmada types to the default Kubernetes Scheme so Events can be logged for karmada types. // Add karmada types to the default Kubernetes Scheme so Events can be logged for karmada types.
@ -86,6 +117,7 @@ func newPropagationBindingController(config *util.ControllerConfig) (*Controller
controller := &Controller{ controller := &Controller{
karmadaClientSet: karmadaClientSet, karmadaClientSet: karmadaClientSet,
kubeClientSet: kubeClientSet, kubeClientSet: kubeClientSet,
dynamicClientSet: dynamicClientSet,
karmadaInformerFactory: karmadaInformerFactory, karmadaInformerFactory: karmadaInformerFactory,
propagationBindingLister: propagationBindingInformer.Lister(), propagationBindingLister: propagationBindingInformer.Lister(),
propagationBindingSynced: propagationBindingInformer.Informer().HasSynced, propagationBindingSynced: propagationBindingInformer.Informer().HasSynced,
@ -226,8 +258,96 @@ func (c *Controller) syncHandler(key string) error {
return err return err
} }
klog.Infof("propagationBinding: %+v", propagationBinding) klog.V(2).Infof("Sync propagationBinding: %s/%s", propagationBinding.Namespace, propagationBinding.Name)
klog.Infof("Sync propagationBinding: %s/%s", propagationBinding.Namespace, propagationBinding.Name) err = c.transformBindingToWorks(propagationBinding)
if err != nil {
klog.Errorf("failed to transform propagationBinding %s/%s to propagationWorks. error: %+v",
propagationBinding.Namespace, propagationBinding.Name, err)
return err
}
return nil
}
// get resource yaml from kubernetes
func (c *Controller) getResourceStructure(propagationBinding *v1alpha1.PropagationBinding) (*unstructured.Unstructured, error) {
groupVersion, err := schema.ParseGroupVersion(propagationBinding.Spec.Resource.APIVersion)
if err != nil {
return nil, fmt.Errorf("can't get parse groupVersion[namespace: %s name: %s kind: %s]. error: %v", propagationBinding.Spec.Resource.Namespace,
propagationBinding.Spec.Resource.Name, resourceKindMap[propagationBinding.Spec.Resource.Kind], err)
}
dynamicResource := schema.GroupVersionResource{Group: groupVersion.Group, Version: groupVersion.Version, Resource: resourceKindMap[propagationBinding.Spec.Resource.Kind]}
result, err := c.dynamicClientSet.Resource(dynamicResource).Namespace(propagationBinding.Spec.Resource.Namespace).Get(context.TODO(), propagationBinding.Spec.Resource.Name, metav1.GetOptions{})
if err != nil {
return nil, fmt.Errorf("can't get resource[namespace: %s name: %s kind: %s]. error: %v", propagationBinding.Spec.Resource.Namespace,
propagationBinding.Spec.Resource.Name, resourceKindMap[propagationBinding.Spec.Resource.Kind], err)
}
return result, nil
}
// get clusterName list from bind clusters field
func (c *Controller) getBindingClusterNames(propagationBinding *v1alpha1.PropagationBinding) []string {
var clusterNames []string
for _, targetCluster := range propagationBinding.Spec.Clusters {
clusterNames = append(clusterNames, targetCluster.Name)
}
return clusterNames
}
// delete irrelevant field from workload. such as uid, timestamp, status
func (c *Controller) removeIrrelevantField(workload *unstructured.Unstructured) {
unstructured.RemoveNestedField(workload.Object, "metadata", "creationTimestamp")
unstructured.RemoveNestedField(workload.Object, "metadata", "generation")
unstructured.RemoveNestedField(workload.Object, "metadata", "resourceVersion")
unstructured.RemoveNestedField(workload.Object, "metadata", "selfLink")
unstructured.RemoveNestedField(workload.Object, "metadata", "uid")
unstructured.RemoveNestedField(workload.Object, "status")
}
// transform propagationBinding resource to propagationWork resources
func (c *Controller) transformBindingToWorks(propagationBinding *v1alpha1.PropagationBinding) error {
workload, err := c.getResourceStructure(propagationBinding)
if err != nil {
klog.Errorf("failed to get resource. error: %v", err)
return err
}
clusterNames := c.getBindingClusterNames(propagationBinding)
for _, clusterNameMirrorNamespace := range clusterNames {
c.removeIrrelevantField(workload)
formatWorkload, err := workload.MarshalJSON()
if err != nil {
klog.Errorf("failed to marshal workload. error: %v", err)
return err
}
rawExtension := runtime.RawExtension{
Raw: formatWorkload,
}
propagationWork := v1alpha1.PropagationWork{
ObjectMeta: metav1.ObjectMeta{
Name: propagationBinding.Name,
Namespace: clusterNameMirrorNamespace,
},
Spec: v1alpha1.PropagationWorkSpec{
Workload: v1alpha1.WorkloadTemplate{
Manifests: []v1alpha1.Manifest{
{
RawExtension: rawExtension,
},
},
},
},
}
// todo. if works exists, how to do?
result, err := c.karmadaClientSet.PropagationstrategyV1alpha1().PropagationWorks(clusterNameMirrorNamespace).Create(context.TODO(), &propagationWork, metav1.CreateOptions{})
if err != nil {
klog.Errorf("failed to create propagationWork %s/%s. error: %v", propagationWork.Namespace, propagationWork.Name, err)
return err
}
klog.V(2).Infof("create propagationWork %s/%s success. result is %+v", propagationWork.Namespace, propagationWork.Name, result)
}
return nil return nil
} }

61
vendor/k8s.io/client-go/dynamic/interface.go generated vendored Normal file
View File

@ -0,0 +1,61 @@
/*
Copyright 2016 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 (
"context"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/watch"
)
type Interface interface {
Resource(resource schema.GroupVersionResource) NamespaceableResourceInterface
}
type ResourceInterface interface {
Create(ctx context.Context, obj *unstructured.Unstructured, options metav1.CreateOptions, subresources ...string) (*unstructured.Unstructured, error)
Update(ctx context.Context, obj *unstructured.Unstructured, options metav1.UpdateOptions, subresources ...string) (*unstructured.Unstructured, error)
UpdateStatus(ctx context.Context, obj *unstructured.Unstructured, options metav1.UpdateOptions) (*unstructured.Unstructured, error)
Delete(ctx context.Context, name string, options metav1.DeleteOptions, subresources ...string) error
DeleteCollection(ctx context.Context, options metav1.DeleteOptions, listOptions metav1.ListOptions) error
Get(ctx context.Context, name string, options metav1.GetOptions, subresources ...string) (*unstructured.Unstructured, error)
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)
}
type NamespaceableResourceInterface interface {
Namespace(string) ResourceInterface
ResourceInterface
}
// APIPathResolverFunc knows how to convert a groupVersion to its API path. The Kind field is optional.
// TODO find a better place to move this for existing callers
type APIPathResolverFunc func(kind schema.GroupVersionKind) string
// LegacyAPIPathResolverFunc can resolve paths properly with the legacy API.
// TODO find a better place to move this for existing callers
func LegacyAPIPathResolverFunc(kind schema.GroupVersionKind) string {
if len(kind.Group) == 0 {
return "/api"
}
return "/apis"
}

108
vendor/k8s.io/client-go/dynamic/scheme.go generated vendored Normal file
View File

@ -0,0 +1,108 @@
/*
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 (
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"
"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
type basicNegotiatedSerializer struct{}
func (s basicNegotiatedSerializer) SupportedMediaTypes() []runtime.SerializerInfo {
return []runtime.SerializerInfo{
{
MediaType: "application/json",
MediaTypeType: "application",
MediaTypeSubType: "json",
EncodesAsText: true,
Serializer: json.NewSerializer(json.DefaultMetaFactory, unstructuredCreater{basicScheme}, unstructuredTyper{basicScheme}, false),
PrettySerializer: json.NewSerializer(json.DefaultMetaFactory, unstructuredCreater{basicScheme}, unstructuredTyper{basicScheme}, true),
StreamSerializer: &runtime.StreamSerializerInfo{
EncodesAsText: true,
Serializer: json.NewSerializer(json.DefaultMetaFactory, basicScheme, basicScheme, false),
Framer: json.Framer,
},
},
}
}
func (s basicNegotiatedSerializer) EncoderForVersion(encoder runtime.Encoder, gv runtime.GroupVersioner) runtime.Encoder {
return runtime.WithVersionEncoder{
Version: gv,
Encoder: encoder,
ObjectTyper: unstructuredTyper{basicScheme},
}
}
func (s basicNegotiatedSerializer) DecoderToVersion(decoder runtime.Decoder, gv runtime.GroupVersioner) runtime.Decoder {
return decoder
}
type unstructuredCreater struct {
nested runtime.ObjectCreater
}
func (c unstructuredCreater) New(kind schema.GroupVersionKind) (runtime.Object, error) {
out, err := c.nested.New(kind)
if err == nil {
return out, nil
}
out = &unstructured.Unstructured{}
out.GetObjectKind().SetGroupVersionKind(kind)
return out, nil
}
type unstructuredTyper struct {
nested runtime.ObjectTyper
}
func (t unstructuredTyper) 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 && !obj.GetObjectKind().GroupVersionKind().Empty() {
return []schema.GroupVersionKind{obj.GetObjectKind().GroupVersionKind()}, false, nil
}
return nil, false, err
}
func (t unstructuredTyper) Recognizes(gvk schema.GroupVersionKind) bool {
return true
}

327
vendor/k8s.io/client-go/dynamic/simple.go generated vendored Normal file
View File

@ -0,0 +1,327 @@
/*
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 (
"context"
"fmt"
"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/types"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/rest"
)
type dynamicClient struct {
client *rest.RESTClient
}
var _ Interface = &dynamicClient{}
// ConfigFor returns a copy of the provided config with the
// appropriate dynamic client defaults set.
func ConfigFor(inConfig *rest.Config) *rest.Config {
config := rest.CopyConfig(inConfig)
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()
}
return config
}
// NewForConfigOrDie creates a new Interface for the given config and
// panics if there is an error in the config.
func NewForConfigOrDie(c *rest.Config) Interface {
ret, err := NewForConfig(c)
if err != nil {
panic(err)
}
return ret
}
// NewForConfig creates a new dynamic client or returns an error.
func NewForConfig(inConfig *rest.Config) (Interface, error) {
config := ConfigFor(inConfig)
// for serializing the options
config.GroupVersion = &schema.GroupVersion{}
config.APIPath = "/if-you-see-this-search-for-the-break"
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(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)
if err != nil {
return nil, err
}
name = accessor.GetName()
if len(name) == 0 {
return nil, fmt.Errorf("name is required")
}
}
result := c.client.client.
Post().
AbsPath(append(c.makeURLSegments(name), subresources...)...).
Body(outBytes).
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
}
return uncastObj.(*unstructured.Unstructured), nil
}
func (c *dynamicResourceClient) Update(ctx context.Context, obj *unstructured.Unstructured, opts metav1.UpdateOptions, subresources ...string) (*unstructured.Unstructured, error) {
accessor, err := meta.Accessor(obj)
if err != nil {
return nil, err
}
name := accessor.GetName()
if len(name) == 0 {
return nil, fmt.Errorf("name is required")
}
outBytes, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj)
if err != nil {
return nil, err
}
result := c.client.client.
Put().
AbsPath(append(c.makeURLSegments(name), subresources...)...).
Body(outBytes).
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
}
return uncastObj.(*unstructured.Unstructured), nil
}
func (c *dynamicResourceClient) UpdateStatus(ctx context.Context, obj *unstructured.Unstructured, opts metav1.UpdateOptions) (*unstructured.Unstructured, error) {
accessor, err := meta.Accessor(obj)
if err != nil {
return nil, err
}
name := accessor.GetName()
if len(name) == 0 {
return nil, fmt.Errorf("name is required")
}
outBytes, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj)
if err != nil {
return nil, err
}
result := c.client.client.
Put().
AbsPath(append(c.makeURLSegments(name), "status")...).
Body(outBytes).
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
}
return uncastObj.(*unstructured.Unstructured), nil
}
func (c *dynamicResourceClient) Delete(ctx context.Context, name string, opts metav1.DeleteOptions, subresources ...string) error {
if len(name) == 0 {
return fmt.Errorf("name is required")
}
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(ctx)
return result.Error()
}
func (c *dynamicResourceClient) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOptions metav1.ListOptions) error {
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(ctx)
return result.Error()
}
func (c *dynamicResourceClient) Get(ctx context.Context, name string, opts metav1.GetOptions, subresources ...string) (*unstructured.Unstructured, error) {
if len(name) == 0 {
return nil, fmt.Errorf("name is required")
}
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
}
uncastObj, err := runtime.Decode(unstructured.UnstructuredJSONScheme, retBytes)
if err != nil {
return nil, err
}
return uncastObj.(*unstructured.Unstructured), nil
}
func (c *dynamicResourceClient) List(ctx context.Context, opts metav1.ListOptions) (*unstructured.UnstructuredList, error) {
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 {
return nil, err
}
return list, nil
}
func (c *dynamicResourceClient) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
opts.Watch = true
return c.client.client.Get().AbsPath(c.makeURLSegments("")...).
SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).
Watch(ctx)
}
func (c *dynamicResourceClient) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (*unstructured.Unstructured, error) {
if len(name) == 0 {
return nil, fmt.Errorf("name is required")
}
result := 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 {
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
}

1
vendor/modules.txt vendored
View File

@ -476,6 +476,7 @@ k8s.io/apiserver/plugin/pkg/authorizer/webhook
## explicit ## explicit
k8s.io/client-go/discovery k8s.io/client-go/discovery
k8s.io/client-go/discovery/fake k8s.io/client-go/discovery/fake
k8s.io/client-go/dynamic
k8s.io/client-go/informers k8s.io/client-go/informers
k8s.io/client-go/informers/admissionregistration k8s.io/client-go/informers/admissionregistration
k8s.io/client-go/informers/admissionregistration/v1 k8s.io/client-go/informers/admissionregistration/v1