mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-04 04:08:16 +00:00 
			
		
		
		
	add easy to use dynamic client
This commit is contained in:
		@@ -36,7 +36,6 @@ import (
 | 
				
			|||||||
	cacheddiscovery "k8s.io/client-go/discovery/cached"
 | 
						cacheddiscovery "k8s.io/client-go/discovery/cached"
 | 
				
			||||||
	"k8s.io/client-go/dynamic"
 | 
						"k8s.io/client-go/dynamic"
 | 
				
			||||||
	clientset "k8s.io/client-go/kubernetes"
 | 
						clientset "k8s.io/client-go/kubernetes"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/api/legacyscheme"
 | 
					 | 
				
			||||||
	"k8s.io/kubernetes/pkg/controller"
 | 
						"k8s.io/kubernetes/pkg/controller"
 | 
				
			||||||
	endpointcontroller "k8s.io/kubernetes/pkg/controller/endpoint"
 | 
						endpointcontroller "k8s.io/kubernetes/pkg/controller/endpoint"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/controller/garbagecollector"
 | 
						"k8s.io/kubernetes/pkg/controller/garbagecollector"
 | 
				
			||||||
@@ -294,9 +293,6 @@ func startResourceQuotaController(ctx ControllerContext) (bool, error) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func startNamespaceController(ctx ControllerContext) (bool, error) {
 | 
					func startNamespaceController(ctx ControllerContext) (bool, error) {
 | 
				
			||||||
	// TODO: should use a dynamic RESTMapper built from the discovery results.
 | 
					 | 
				
			||||||
	restMapper := legacyscheme.Registry.RESTMapper()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// the namespace cleanup controller is very chatty.  It makes lots of discovery calls and then it makes lots of delete calls
 | 
						// the namespace cleanup controller is very chatty.  It makes lots of discovery calls and then it makes lots of delete calls
 | 
				
			||||||
	// the ratelimiter negatively affects its speed.  Deleting 100 total items in a namespace (that's only a few of each resource
 | 
						// the ratelimiter negatively affects its speed.  Deleting 100 total items in a namespace (that's only a few of each resource
 | 
				
			||||||
	// including events), takes ~10 seconds by default.
 | 
						// including events), takes ~10 seconds by default.
 | 
				
			||||||
@@ -304,13 +300,17 @@ func startNamespaceController(ctx ControllerContext) (bool, error) {
 | 
				
			|||||||
	nsKubeconfig.QPS *= 10
 | 
						nsKubeconfig.QPS *= 10
 | 
				
			||||||
	nsKubeconfig.Burst *= 10
 | 
						nsKubeconfig.Burst *= 10
 | 
				
			||||||
	namespaceKubeClient := clientset.NewForConfigOrDie(nsKubeconfig)
 | 
						namespaceKubeClient := clientset.NewForConfigOrDie(nsKubeconfig)
 | 
				
			||||||
	namespaceClientPool := dynamic.NewClientPool(nsKubeconfig, restMapper, dynamic.LegacyAPIPathResolverFunc)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	discoverResourcesFn := namespaceKubeClient.Discovery().ServerPreferredNamespacedResources
 | 
						discoverResourcesFn := namespaceKubeClient.Discovery().ServerPreferredNamespacedResources
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dynamicClient, err := dynamic.NewForConfig(nsKubeconfig)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return true, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	namespaceController := namespacecontroller.NewNamespaceController(
 | 
						namespaceController := namespacecontroller.NewNamespaceController(
 | 
				
			||||||
		namespaceKubeClient,
 | 
							namespaceKubeClient,
 | 
				
			||||||
		namespaceClientPool,
 | 
							dynamicClient,
 | 
				
			||||||
		discoverResourcesFn,
 | 
							discoverResourcesFn,
 | 
				
			||||||
		ctx.InformerFactory.Core().V1().Namespaces(),
 | 
							ctx.InformerFactory.Core().V1().Namespaces(),
 | 
				
			||||||
		ctx.ComponentConfig.NamespaceController.NamespaceSyncPeriod.Duration,
 | 
							ctx.ComponentConfig.NamespaceController.NamespaceSyncPeriod.Duration,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -303,7 +303,7 @@ func (gc *GarbageCollector) isDangling(reference metav1.OwnerReference, item *no
 | 
				
			|||||||
	// TODO: It's only necessary to talk to the API server if the owner node
 | 
						// TODO: It's only necessary to talk to the API server if the owner node
 | 
				
			||||||
	// is a "virtual" node. The local graph could lag behind the real
 | 
						// is a "virtual" node. The local graph could lag behind the real
 | 
				
			||||||
	// status, but in practice, the difference is small.
 | 
						// status, but in practice, the difference is small.
 | 
				
			||||||
	owner, err = client.Resource(resource, item.identity.Namespace).Get(reference.Name, metav1.GetOptions{})
 | 
						owner, err = client.Resource(resource, resourceDefaultNamespace(resource, item.identity.Namespace)).Get(reference.Name, metav1.GetOptions{})
 | 
				
			||||||
	switch {
 | 
						switch {
 | 
				
			||||||
	case errors.IsNotFound(err):
 | 
						case errors.IsNotFound(err):
 | 
				
			||||||
		gc.absentOwnerCache.Add(reference.UID)
 | 
							gc.absentOwnerCache.Add(reference.UID)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -686,9 +686,13 @@ func TestOrphanDependentsFailure(t *testing.T) {
 | 
				
			|||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	err := gc.orphanDependents(objectReference{}, dependents)
 | 
						err := gc.orphanDependents(objectReference{}, dependents)
 | 
				
			||||||
	expected := `the server reported a conflict (patch pods pod)`
 | 
						expected := `the server reported a conflict`
 | 
				
			||||||
	if err == nil || !strings.Contains(err.Error(), expected) {
 | 
						if err == nil || !strings.Contains(err.Error(), expected) {
 | 
				
			||||||
		t.Errorf("expected error contains text %s, got %v", expected, err)
 | 
							if err != nil {
 | 
				
			||||||
 | 
								t.Errorf("expected error contains text %q, got %q", expected, err.Error())
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								t.Errorf("expected error contains text %q, got nil", expected)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -135,7 +135,7 @@ func listWatcher(client dynamic.Interface, resource schema.GroupVersionResource)
 | 
				
			|||||||
			// namespaces if it's namespace scoped, so leave
 | 
								// namespaces if it's namespace scoped, so leave
 | 
				
			||||||
			// APIResource.Namespaced as false is all right.
 | 
								// APIResource.Namespaced as false is all right.
 | 
				
			||||||
			apiResource := metav1.APIResource{Name: resource.Resource}
 | 
								apiResource := metav1.APIResource{Name: resource.Resource}
 | 
				
			||||||
			return client.ParameterCodec(dynamic.VersionedParameterEncoderWithV1Fallback).
 | 
								return client.
 | 
				
			||||||
				Resource(&apiResource, metav1.NamespaceAll).
 | 
									Resource(&apiResource, metav1.NamespaceAll).
 | 
				
			||||||
				List(options)
 | 
									List(options)
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
@@ -145,7 +145,7 @@ func listWatcher(client dynamic.Interface, resource schema.GroupVersionResource)
 | 
				
			|||||||
			// namespaces if it's namespace scoped, so leave
 | 
								// namespaces if it's namespace scoped, so leave
 | 
				
			||||||
			// APIResource.Namespaced as false is all right.
 | 
								// APIResource.Namespaced as false is all right.
 | 
				
			||||||
			apiResource := metav1.APIResource{Name: resource.Resource}
 | 
								apiResource := metav1.APIResource{Name: resource.Resource}
 | 
				
			||||||
			return client.ParameterCodec(dynamic.VersionedParameterEncoderWithV1Fallback).
 | 
								return client.
 | 
				
			||||||
				Resource(&apiResource, metav1.NamespaceAll).
 | 
									Resource(&apiResource, metav1.NamespaceAll).
 | 
				
			||||||
				Watch(options)
 | 
									Watch(options)
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -30,6 +30,14 @@ import (
 | 
				
			|||||||
	"k8s.io/client-go/util/retry"
 | 
						"k8s.io/client-go/util/retry"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// cluster scoped resources don't have namespaces.  Default to the item's namespace, but clear it for cluster scoped resources
 | 
				
			||||||
 | 
					func resourceDefaultNamespace(resource *metav1.APIResource, defaultNamespace string) string {
 | 
				
			||||||
 | 
						if resource.Namespaced {
 | 
				
			||||||
 | 
							return defaultNamespace
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// apiResource consults the REST mapper to translate an <apiVersion, kind,
 | 
					// apiResource consults the REST mapper to translate an <apiVersion, kind,
 | 
				
			||||||
// namespace> tuple to a unversioned.APIResource struct.
 | 
					// namespace> tuple to a unversioned.APIResource struct.
 | 
				
			||||||
func (gc *GarbageCollector) apiResource(apiVersion, kind string) (*metav1.APIResource, error) {
 | 
					func (gc *GarbageCollector) apiResource(apiVersion, kind string) (*metav1.APIResource, error) {
 | 
				
			||||||
@@ -60,7 +68,7 @@ func (gc *GarbageCollector) deleteObject(item objectReference, policy *metav1.De
 | 
				
			|||||||
	uid := item.UID
 | 
						uid := item.UID
 | 
				
			||||||
	preconditions := metav1.Preconditions{UID: &uid}
 | 
						preconditions := metav1.Preconditions{UID: &uid}
 | 
				
			||||||
	deleteOptions := metav1.DeleteOptions{Preconditions: &preconditions, PropagationPolicy: policy}
 | 
						deleteOptions := metav1.DeleteOptions{Preconditions: &preconditions, PropagationPolicy: policy}
 | 
				
			||||||
	return client.Resource(resource, item.Namespace).Delete(item.Name, &deleteOptions)
 | 
						return client.Resource(resource, resourceDefaultNamespace(resource, item.Namespace)).Delete(item.Name, &deleteOptions)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (gc *GarbageCollector) getObject(item objectReference) (*unstructured.Unstructured, error) {
 | 
					func (gc *GarbageCollector) getObject(item objectReference) (*unstructured.Unstructured, error) {
 | 
				
			||||||
@@ -73,7 +81,7 @@ func (gc *GarbageCollector) getObject(item objectReference) (*unstructured.Unstr
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return client.Resource(resource, item.Namespace).Get(item.Name, metav1.GetOptions{})
 | 
						return client.Resource(resource, resourceDefaultNamespace(resource, item.Namespace)).Get(item.Name, metav1.GetOptions{})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (gc *GarbageCollector) updateObject(item objectReference, obj *unstructured.Unstructured) (*unstructured.Unstructured, error) {
 | 
					func (gc *GarbageCollector) updateObject(item objectReference, obj *unstructured.Unstructured) (*unstructured.Unstructured, error) {
 | 
				
			||||||
@@ -86,7 +94,7 @@ func (gc *GarbageCollector) updateObject(item objectReference, obj *unstructured
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return client.Resource(resource, item.Namespace).Update(obj)
 | 
						return client.Resource(resource, resourceDefaultNamespace(resource, item.Namespace)).Update(obj)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (gc *GarbageCollector) patchObject(item objectReference, patch []byte) (*unstructured.Unstructured, error) {
 | 
					func (gc *GarbageCollector) patchObject(item objectReference, patch []byte) (*unstructured.Unstructured, error) {
 | 
				
			||||||
@@ -99,7 +107,7 @@ func (gc *GarbageCollector) patchObject(item objectReference, patch []byte) (*un
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return client.Resource(resource, item.Namespace).Patch(item.Name, types.StrategicMergePatchType, patch)
 | 
						return client.Resource(resource, resourceDefaultNamespace(resource, item.Namespace)).Patch(item.Name, types.StrategicMergePatchType, patch)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO: Using Patch when strategicmerge supports deleting an entry from a
 | 
					// TODO: Using Patch when strategicmerge supports deleting an entry from a
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,7 +31,6 @@ go_test(
 | 
				
			|||||||
    srcs = ["namespaced_resources_deleter_test.go"],
 | 
					    srcs = ["namespaced_resources_deleter_test.go"],
 | 
				
			||||||
    embed = [":go_default_library"],
 | 
					    embed = [":go_default_library"],
 | 
				
			||||||
    deps = [
 | 
					    deps = [
 | 
				
			||||||
        "//pkg/api/legacyscheme:go_default_library",
 | 
					 | 
				
			||||||
        "//pkg/apis/core:go_default_library",
 | 
					        "//pkg/apis/core:go_default_library",
 | 
				
			||||||
        "//vendor/k8s.io/api/core/v1:go_default_library",
 | 
					        "//vendor/k8s.io/api/core/v1:go_default_library",
 | 
				
			||||||
        "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
 | 
					        "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -43,12 +43,12 @@ type NamespacedResourcesDeleterInterface interface {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func NewNamespacedResourcesDeleter(nsClient v1clientset.NamespaceInterface,
 | 
					func NewNamespacedResourcesDeleter(nsClient v1clientset.NamespaceInterface,
 | 
				
			||||||
	clientPool dynamic.ClientPool, podsGetter v1clientset.PodsGetter,
 | 
						dynamicClient dynamic.DynamicInterface, podsGetter v1clientset.PodsGetter,
 | 
				
			||||||
	discoverResourcesFn func() ([]*metav1.APIResourceList, error),
 | 
						discoverResourcesFn func() ([]*metav1.APIResourceList, error),
 | 
				
			||||||
	finalizerToken v1.FinalizerName, deleteNamespaceWhenDone bool) NamespacedResourcesDeleterInterface {
 | 
						finalizerToken v1.FinalizerName, deleteNamespaceWhenDone bool) NamespacedResourcesDeleterInterface {
 | 
				
			||||||
	d := &namespacedResourcesDeleter{
 | 
						d := &namespacedResourcesDeleter{
 | 
				
			||||||
		nsClient:      nsClient,
 | 
							nsClient:      nsClient,
 | 
				
			||||||
		clientPool: clientPool,
 | 
							dynamicClient: dynamicClient,
 | 
				
			||||||
		podsGetter:    podsGetter,
 | 
							podsGetter:    podsGetter,
 | 
				
			||||||
		opCache: &operationNotSupportedCache{
 | 
							opCache: &operationNotSupportedCache{
 | 
				
			||||||
			m: make(map[operationKey]bool),
 | 
								m: make(map[operationKey]bool),
 | 
				
			||||||
@@ -68,7 +68,7 @@ type namespacedResourcesDeleter struct {
 | 
				
			|||||||
	// Client to manipulate the namespace.
 | 
						// Client to manipulate the namespace.
 | 
				
			||||||
	nsClient v1clientset.NamespaceInterface
 | 
						nsClient v1clientset.NamespaceInterface
 | 
				
			||||||
	// Dynamic client to list and delete all namespaced resources.
 | 
						// Dynamic client to list and delete all namespaced resources.
 | 
				
			||||||
	clientPool dynamic.ClientPool
 | 
						dynamicClient dynamic.DynamicInterface
 | 
				
			||||||
	// Interface to get PodInterface.
 | 
						// Interface to get PodInterface.
 | 
				
			||||||
	podsGetter v1clientset.PodsGetter
 | 
						podsGetter v1clientset.PodsGetter
 | 
				
			||||||
	// Cache of what operations are not supported on each group version resource.
 | 
						// Cache of what operations are not supported on each group version resource.
 | 
				
			||||||
@@ -328,9 +328,7 @@ func (d *namespacedResourcesDeleter) finalizeNamespace(namespace *v1.Namespace)
 | 
				
			|||||||
// deleteCollection is a helper function that will delete the collection of resources
 | 
					// deleteCollection is a helper function that will delete the collection of resources
 | 
				
			||||||
// it returns true if the operation was supported on the server.
 | 
					// it returns true if the operation was supported on the server.
 | 
				
			||||||
// it returns an error if the operation was supported on the server but was unable to complete.
 | 
					// it returns an error if the operation was supported on the server but was unable to complete.
 | 
				
			||||||
func (d *namespacedResourcesDeleter) deleteCollection(
 | 
					func (d *namespacedResourcesDeleter) deleteCollection(gvr schema.GroupVersionResource, namespace string) (bool, error) {
 | 
				
			||||||
	dynamicClient dynamic.Interface, gvr schema.GroupVersionResource,
 | 
					 | 
				
			||||||
	namespace string) (bool, error) {
 | 
					 | 
				
			||||||
	glog.V(5).Infof("namespace controller - deleteCollection - namespace: %s, gvr: %v", namespace, gvr)
 | 
						glog.V(5).Infof("namespace controller - deleteCollection - namespace: %s, gvr: %v", namespace, gvr)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	key := operationKey{operation: operationDeleteCollection, gvr: gvr}
 | 
						key := operationKey{operation: operationDeleteCollection, gvr: gvr}
 | 
				
			||||||
@@ -339,14 +337,12 @@ func (d *namespacedResourcesDeleter) deleteCollection(
 | 
				
			|||||||
		return false, nil
 | 
							return false, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	apiResource := metav1.APIResource{Name: gvr.Resource, Namespaced: true}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// namespace controller does not want the garbage collector to insert the orphan finalizer since it calls
 | 
						// namespace controller does not want the garbage collector to insert the orphan finalizer since it calls
 | 
				
			||||||
	// resource deletions generically.  it will ensure all resources in the namespace are purged prior to releasing
 | 
						// resource deletions generically.  it will ensure all resources in the namespace are purged prior to releasing
 | 
				
			||||||
	// namespace itself.
 | 
						// namespace itself.
 | 
				
			||||||
	background := metav1.DeletePropagationBackground
 | 
						background := metav1.DeletePropagationBackground
 | 
				
			||||||
	opts := &metav1.DeleteOptions{PropagationPolicy: &background}
 | 
						opts := &metav1.DeleteOptions{PropagationPolicy: &background}
 | 
				
			||||||
	err := dynamicClient.Resource(&apiResource, namespace).DeleteCollection(opts, metav1.ListOptions{})
 | 
						err := d.dynamicClient.NamespacedResource(gvr, namespace).DeleteCollection(opts, metav1.ListOptions{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err == nil {
 | 
						if err == nil {
 | 
				
			||||||
		return true, nil
 | 
							return true, nil
 | 
				
			||||||
@@ -373,8 +369,7 @@ func (d *namespacedResourcesDeleter) deleteCollection(
 | 
				
			|||||||
//  the list of items in the collection (if found)
 | 
					//  the list of items in the collection (if found)
 | 
				
			||||||
//  a boolean if the operation is supported
 | 
					//  a boolean if the operation is supported
 | 
				
			||||||
//  an error if the operation is supported but could not be completed.
 | 
					//  an error if the operation is supported but could not be completed.
 | 
				
			||||||
func (d *namespacedResourcesDeleter) listCollection(
 | 
					func (d *namespacedResourcesDeleter) listCollection(gvr schema.GroupVersionResource, namespace string) (*unstructured.UnstructuredList, bool, error) {
 | 
				
			||||||
	dynamicClient dynamic.Interface, gvr schema.GroupVersionResource, namespace string) (*unstructured.UnstructuredList, bool, error) {
 | 
					 | 
				
			||||||
	glog.V(5).Infof("namespace controller - listCollection - namespace: %s, gvr: %v", namespace, gvr)
 | 
						glog.V(5).Infof("namespace controller - listCollection - namespace: %s, gvr: %v", namespace, gvr)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	key := operationKey{operation: operationList, gvr: gvr}
 | 
						key := operationKey{operation: operationList, gvr: gvr}
 | 
				
			||||||
@@ -383,13 +378,8 @@ func (d *namespacedResourcesDeleter) listCollection(
 | 
				
			|||||||
		return nil, false, nil
 | 
							return nil, false, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	apiResource := metav1.APIResource{Name: gvr.Resource, Namespaced: true}
 | 
						unstructuredList, err := d.dynamicClient.NamespacedResource(gvr, namespace).List(metav1.ListOptions{IncludeUninitialized: true})
 | 
				
			||||||
	obj, err := dynamicClient.Resource(&apiResource, namespace).List(metav1.ListOptions{IncludeUninitialized: true})
 | 
					 | 
				
			||||||
	if err == nil {
 | 
						if err == nil {
 | 
				
			||||||
		unstructuredList, ok := obj.(*unstructured.UnstructuredList)
 | 
					 | 
				
			||||||
		if !ok {
 | 
					 | 
				
			||||||
			return nil, false, fmt.Errorf("resource: %s, expected *unstructured.UnstructuredList, got %#v", apiResource.Name, obj)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return unstructuredList, true, nil
 | 
							return unstructuredList, true, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -409,22 +399,20 @@ func (d *namespacedResourcesDeleter) listCollection(
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// deleteEachItem is a helper function that will list the collection of resources and delete each item 1 by 1.
 | 
					// deleteEachItem is a helper function that will list the collection of resources and delete each item 1 by 1.
 | 
				
			||||||
func (d *namespacedResourcesDeleter) deleteEachItem(
 | 
					func (d *namespacedResourcesDeleter) deleteEachItem(gvr schema.GroupVersionResource, namespace string) error {
 | 
				
			||||||
	dynamicClient dynamic.Interface, gvr schema.GroupVersionResource, namespace string) error {
 | 
					 | 
				
			||||||
	glog.V(5).Infof("namespace controller - deleteEachItem - namespace: %s, gvr: %v", namespace, gvr)
 | 
						glog.V(5).Infof("namespace controller - deleteEachItem - namespace: %s, gvr: %v", namespace, gvr)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	unstructuredList, listSupported, err := d.listCollection(dynamicClient, gvr, namespace)
 | 
						unstructuredList, listSupported, err := d.listCollection(gvr, namespace)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if !listSupported {
 | 
						if !listSupported {
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	apiResource := metav1.APIResource{Name: gvr.Resource, Namespaced: true}
 | 
					 | 
				
			||||||
	for _, item := range unstructuredList.Items {
 | 
						for _, item := range unstructuredList.Items {
 | 
				
			||||||
		background := metav1.DeletePropagationBackground
 | 
							background := metav1.DeletePropagationBackground
 | 
				
			||||||
		opts := &metav1.DeleteOptions{PropagationPolicy: &background}
 | 
							opts := &metav1.DeleteOptions{PropagationPolicy: &background}
 | 
				
			||||||
		if err = dynamicClient.Resource(&apiResource, namespace).Delete(item.GetName(), opts); err != nil && !errors.IsNotFound(err) && !errors.IsMethodNotSupported(err) {
 | 
							if err = d.dynamicClient.NamespacedResource(gvr, namespace).Delete(item.GetName(), opts); err != nil && !errors.IsNotFound(err) && !errors.IsMethodNotSupported(err) {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -447,22 +435,15 @@ func (d *namespacedResourcesDeleter) deleteAllContentForGroupVersionResource(
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	glog.V(5).Infof("namespace controller - deleteAllContentForGroupVersionResource - estimate - namespace: %s, gvr: %v, estimate: %v", namespace, gvr, estimate)
 | 
						glog.V(5).Infof("namespace controller - deleteAllContentForGroupVersionResource - estimate - namespace: %s, gvr: %v, estimate: %v", namespace, gvr, estimate)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// get a client for this group version...
 | 
					 | 
				
			||||||
	dynamicClient, err := d.clientPool.ClientForGroupVersionResource(gvr)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		glog.V(5).Infof("namespace controller - deleteAllContentForGroupVersionResource - unable to get client - namespace: %s, gvr: %v, err: %v", namespace, gvr, err)
 | 
					 | 
				
			||||||
		return estimate, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// first try to delete the entire collection
 | 
						// first try to delete the entire collection
 | 
				
			||||||
	deleteCollectionSupported, err := d.deleteCollection(dynamicClient, gvr, namespace)
 | 
						deleteCollectionSupported, err := d.deleteCollection(gvr, namespace)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return estimate, err
 | 
							return estimate, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// delete collection was not supported, so we list and delete each item...
 | 
						// delete collection was not supported, so we list and delete each item...
 | 
				
			||||||
	if !deleteCollectionSupported {
 | 
						if !deleteCollectionSupported {
 | 
				
			||||||
		err = d.deleteEachItem(dynamicClient, gvr, namespace)
 | 
							err = d.deleteEachItem(gvr, namespace)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return estimate, err
 | 
								return estimate, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -471,7 +452,7 @@ func (d *namespacedResourcesDeleter) deleteAllContentForGroupVersionResource(
 | 
				
			|||||||
	// verify there are no more remaining items
 | 
						// verify there are no more remaining items
 | 
				
			||||||
	// it is not an error condition for there to be remaining items if local estimate is non-zero
 | 
						// it is not an error condition for there to be remaining items if local estimate is non-zero
 | 
				
			||||||
	glog.V(5).Infof("namespace controller - deleteAllContentForGroupVersionResource - checking for no more items in namespace: %s, gvr: %v", namespace, gvr)
 | 
						glog.V(5).Infof("namespace controller - deleteAllContentForGroupVersionResource - checking for no more items in namespace: %s, gvr: %v", namespace, gvr)
 | 
				
			||||||
	unstructuredList, listSupported, err := d.listCollection(dynamicClient, gvr, namespace)
 | 
						unstructuredList, listSupported, err := d.listCollection(gvr, namespace)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		glog.V(5).Infof("namespace controller - deleteAllContentForGroupVersionResource - error verifying no items in namespace: %s, gvr: %v, err: %v", namespace, gvr, err)
 | 
							glog.V(5).Infof("namespace controller - deleteAllContentForGroupVersionResource - error verifying no items in namespace: %s, gvr: %v, err: %v", namespace, gvr, err)
 | 
				
			||||||
		return estimate, err
 | 
							return estimate, err
 | 
				
			||||||
@@ -497,8 +478,7 @@ func (d *namespacedResourcesDeleter) deleteAllContentForGroupVersionResource(
 | 
				
			|||||||
// deleteAllContent will use the dynamic client to delete each resource identified in groupVersionResources.
 | 
					// deleteAllContent will use the dynamic client to delete each resource identified in groupVersionResources.
 | 
				
			||||||
// It returns an estimate of the time remaining before the remaining resources are deleted.
 | 
					// It returns an estimate of the time remaining before the remaining resources are deleted.
 | 
				
			||||||
// If estimate > 0, not all resources are guaranteed to be gone.
 | 
					// If estimate > 0, not all resources are guaranteed to be gone.
 | 
				
			||||||
func (d *namespacedResourcesDeleter) deleteAllContent(
 | 
					func (d *namespacedResourcesDeleter) deleteAllContent(namespace string, namespaceDeletedAt metav1.Time) (int64, error) {
 | 
				
			||||||
	namespace string, namespaceDeletedAt metav1.Time) (int64, error) {
 | 
					 | 
				
			||||||
	estimate := int64(0)
 | 
						estimate := int64(0)
 | 
				
			||||||
	glog.V(4).Infof("namespace controller - deleteAllContent - namespace: %s", namespace)
 | 
						glog.V(4).Infof("namespace controller - deleteAllContent - namespace: %s", namespace)
 | 
				
			||||||
	resources, err := d.discoverResourcesFn()
 | 
						resources, err := d.discoverResourcesFn()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -36,7 +36,6 @@ import (
 | 
				
			|||||||
	"k8s.io/client-go/kubernetes/fake"
 | 
						"k8s.io/client-go/kubernetes/fake"
 | 
				
			||||||
	restclient "k8s.io/client-go/rest"
 | 
						restclient "k8s.io/client-go/rest"
 | 
				
			||||||
	core "k8s.io/client-go/testing"
 | 
						core "k8s.io/client-go/testing"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/api/legacyscheme"
 | 
					 | 
				
			||||||
	api "k8s.io/kubernetes/pkg/apis/core"
 | 
						api "k8s.io/kubernetes/pkg/apis/core"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -173,14 +172,16 @@ func testSyncNamespaceThatIsTerminating(t *testing.T, versions *metav1.APIVersio
 | 
				
			|||||||
		defer srv.Close()
 | 
							defer srv.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		mockClient := fake.NewSimpleClientset(testInput.testNamespace)
 | 
							mockClient := fake.NewSimpleClientset(testInput.testNamespace)
 | 
				
			||||||
		clientPool := dynamic.NewClientPool(clientConfig, legacyscheme.Registry.RESTMapper(), dynamic.LegacyAPIPathResolverFunc)
 | 
							dynamicClient, err := dynamic.NewForConfig(clientConfig)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								t.Fatal(err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		fn := func() ([]*metav1.APIResourceList, error) {
 | 
							fn := func() ([]*metav1.APIResourceList, error) {
 | 
				
			||||||
			return resources, nil
 | 
								return resources, nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		d := NewNamespacedResourcesDeleter(mockClient.Core().Namespaces(), clientPool, mockClient.Core(), fn, v1.FinalizerKubernetes, true)
 | 
							d := NewNamespacedResourcesDeleter(mockClient.Core().Namespaces(), dynamicClient, mockClient.Core(), fn, v1.FinalizerKubernetes, true)
 | 
				
			||||||
		err := d.Delete(testInput.testNamespace.Name)
 | 
							if err := d.Delete(testInput.testNamespace.Name); err != nil {
 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			t.Errorf("scenario %s - Unexpected error when synching namespace %v", scenario, err)
 | 
								t.Errorf("scenario %s - Unexpected error when synching namespace %v", scenario, err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -63,7 +63,7 @@ type NamespaceController struct {
 | 
				
			|||||||
// NewNamespaceController creates a new NamespaceController
 | 
					// NewNamespaceController creates a new NamespaceController
 | 
				
			||||||
func NewNamespaceController(
 | 
					func NewNamespaceController(
 | 
				
			||||||
	kubeClient clientset.Interface,
 | 
						kubeClient clientset.Interface,
 | 
				
			||||||
	clientPool dynamic.ClientPool,
 | 
						dynamicClient dynamic.DynamicInterface,
 | 
				
			||||||
	discoverResourcesFn func() ([]*metav1.APIResourceList, error),
 | 
						discoverResourcesFn func() ([]*metav1.APIResourceList, error),
 | 
				
			||||||
	namespaceInformer coreinformers.NamespaceInformer,
 | 
						namespaceInformer coreinformers.NamespaceInformer,
 | 
				
			||||||
	resyncPeriod time.Duration,
 | 
						resyncPeriod time.Duration,
 | 
				
			||||||
@@ -72,7 +72,7 @@ func NewNamespaceController(
 | 
				
			|||||||
	// create the controller so we can inject the enqueue function
 | 
						// create the controller so we can inject the enqueue function
 | 
				
			||||||
	namespaceController := &NamespaceController{
 | 
						namespaceController := &NamespaceController{
 | 
				
			||||||
		queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "namespace"),
 | 
							queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "namespace"),
 | 
				
			||||||
		namespacedResourcesDeleter: deletion.NewNamespacedResourcesDeleter(kubeClient.CoreV1().Namespaces(), clientPool, kubeClient.CoreV1(), discoverResourcesFn, finalizerToken, true),
 | 
							namespacedResourcesDeleter: deletion.NewNamespacedResourcesDeleter(kubeClient.CoreV1().Namespaces(), dynamicClient, kubeClient.CoreV1(), discoverResourcesFn, finalizerToken, true),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if kubeClient != nil && kubeClient.CoreV1().RESTClient().GetRateLimiter() != nil {
 | 
						if kubeClient != nil && kubeClient.CoreV1().RESTClient().GetRateLimiter() != nil {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -197,7 +197,7 @@ func genericDescriber(clientAccessFactory ClientAccessFactory, mapping *meta.RES
 | 
				
			|||||||
	clientConfigCopy.GroupVersion = &gv
 | 
						clientConfigCopy.GroupVersion = &gv
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// used to fetch the resource
 | 
						// used to fetch the resource
 | 
				
			||||||
	dynamicClient, err := dynamic.NewClient(&clientConfigCopy)
 | 
						dynamicClient, err := dynamic.NewClient(&clientConfigCopy, gv)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -49,6 +49,9 @@ filegroup(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
filegroup(
 | 
					filegroup(
 | 
				
			||||||
    name = "all-srcs",
 | 
					    name = "all-srcs",
 | 
				
			||||||
    srcs = [":package-srcs"],
 | 
					    srcs = [
 | 
				
			||||||
 | 
					        ":package-srcs",
 | 
				
			||||||
 | 
					        "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructuredscheme:all-srcs",
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
    tags = ["automanaged"],
 | 
					    tags = ["automanaged"],
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -58,6 +58,26 @@ func (obj *Unstructured) IsList() bool {
 | 
				
			|||||||
	_, ok = field.([]interface{})
 | 
						_, ok = field.([]interface{})
 | 
				
			||||||
	return ok
 | 
						return ok
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					func (obj *Unstructured) ToList() (*UnstructuredList, error) {
 | 
				
			||||||
 | 
						if !obj.IsList() {
 | 
				
			||||||
 | 
							// return an empty list back
 | 
				
			||||||
 | 
							return &UnstructuredList{Object: obj.Object}, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret := &UnstructuredList{}
 | 
				
			||||||
 | 
						ret.Object = obj.Object
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := obj.EachListItem(func(item runtime.Object) error {
 | 
				
			||||||
 | 
							castItem := item.(*Unstructured)
 | 
				
			||||||
 | 
							ret.Items = append(ret.Items, *castItem)
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ret, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (obj *Unstructured) EachListItem(fn func(runtime.Object) error) error {
 | 
					func (obj *Unstructured) EachListItem(fn func(runtime.Object) error) error {
 | 
				
			||||||
	field, ok := obj.Object["items"]
 | 
						field, ok := obj.Object["items"]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					load("@io_bazel_rules_go//go:def.bzl", "go_library")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					go_library(
 | 
				
			||||||
 | 
					    name = "go_default_library",
 | 
				
			||||||
 | 
					    srcs = ["scheme.go"],
 | 
				
			||||||
 | 
					    importpath = "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructuredscheme",
 | 
				
			||||||
 | 
					    visibility = ["//visibility:public"],
 | 
				
			||||||
 | 
					    deps = [
 | 
				
			||||||
 | 
					        "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
 | 
				
			||||||
 | 
					        "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
 | 
				
			||||||
 | 
					        "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
 | 
				
			||||||
 | 
					        "//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
 | 
				
			||||||
 | 
					        "//vendor/k8s.io/apimachinery/pkg/runtime/serializer/json:go_default_library",
 | 
				
			||||||
 | 
					        "//vendor/k8s.io/apimachinery/pkg/runtime/serializer/versioning:go_default_library",
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					filegroup(
 | 
				
			||||||
 | 
					    name = "package-srcs",
 | 
				
			||||||
 | 
					    srcs = glob(["**"]),
 | 
				
			||||||
 | 
					    tags = ["automanaged"],
 | 
				
			||||||
 | 
					    visibility = ["//visibility:private"],
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					filegroup(
 | 
				
			||||||
 | 
					    name = "all-srcs",
 | 
				
			||||||
 | 
					    srcs = [":package-srcs"],
 | 
				
			||||||
 | 
					    tags = ["automanaged"],
 | 
				
			||||||
 | 
					    visibility = ["//visibility:public"],
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
@@ -0,0 +1,121 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					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 unstructuredscheme
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"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"
 | 
				
			||||||
 | 
						"k8s.io/apimachinery/pkg/runtime/serializer/versioning"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						scheme = runtime.NewScheme()
 | 
				
			||||||
 | 
						codecs = serializer.NewCodecFactory(scheme)
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewUnstructuredNegotiatedSerializer returns a simple, negotiated serializer
 | 
				
			||||||
 | 
					func NewUnstructuredNegotiatedSerializer() runtime.NegotiatedSerializer {
 | 
				
			||||||
 | 
						return unstructuredNegotiatedSerializer{
 | 
				
			||||||
 | 
							scheme:  scheme,
 | 
				
			||||||
 | 
							typer:   NewUnstructuredObjectTyper(),
 | 
				
			||||||
 | 
							creator: NewUnstructuredCreator(),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type unstructuredNegotiatedSerializer struct {
 | 
				
			||||||
 | 
						scheme  *runtime.Scheme
 | 
				
			||||||
 | 
						typer   runtime.ObjectTyper
 | 
				
			||||||
 | 
						creator runtime.ObjectCreater
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s unstructuredNegotiatedSerializer) SupportedMediaTypes() []runtime.SerializerInfo {
 | 
				
			||||||
 | 
						return []runtime.SerializerInfo{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								MediaType:        "application/json",
 | 
				
			||||||
 | 
								EncodesAsText:    true,
 | 
				
			||||||
 | 
								Serializer:       json.NewSerializer(json.DefaultMetaFactory, s.creator, s.typer, false),
 | 
				
			||||||
 | 
								PrettySerializer: json.NewSerializer(json.DefaultMetaFactory, s.creator, s.typer, true),
 | 
				
			||||||
 | 
								StreamSerializer: &runtime.StreamSerializerInfo{
 | 
				
			||||||
 | 
									EncodesAsText: true,
 | 
				
			||||||
 | 
									Serializer:    json.NewSerializer(json.DefaultMetaFactory, s.creator, s.typer, false),
 | 
				
			||||||
 | 
									Framer:        json.Framer,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								MediaType:     "application/yaml",
 | 
				
			||||||
 | 
								EncodesAsText: true,
 | 
				
			||||||
 | 
								Serializer:    json.NewYAMLSerializer(json.DefaultMetaFactory, s.creator, s.typer),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s unstructuredNegotiatedSerializer) EncoderForVersion(encoder runtime.Encoder, gv runtime.GroupVersioner) runtime.Encoder {
 | 
				
			||||||
 | 
						return versioning.NewDefaultingCodecForScheme(s.scheme, encoder, nil, gv, nil)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s unstructuredNegotiatedSerializer) DecoderToVersion(decoder runtime.Decoder, gv runtime.GroupVersioner) runtime.Decoder {
 | 
				
			||||||
 | 
						return versioning.NewDefaultingCodecForScheme(s.scheme, nil, decoder, nil, gv)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type unstructuredObjectTyper struct {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewUnstructuredObjectTyper returns an object typer that can deal with unstructured things
 | 
				
			||||||
 | 
					func NewUnstructuredObjectTyper() runtime.ObjectTyper {
 | 
				
			||||||
 | 
						return unstructuredObjectTyper{}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (t unstructuredObjectTyper) ObjectKinds(obj runtime.Object) ([]schema.GroupVersionKind, bool, error) {
 | 
				
			||||||
 | 
						// Delegate for things other than Unstructured.
 | 
				
			||||||
 | 
						if _, ok := obj.(runtime.Unstructured); !ok {
 | 
				
			||||||
 | 
							return nil, false, fmt.Errorf("cannot type %T", obj)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return []schema.GroupVersionKind{obj.GetObjectKind().GroupVersionKind()}, false, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (t unstructuredObjectTyper) Recognizes(gvk schema.GroupVersionKind) bool {
 | 
				
			||||||
 | 
						return true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type unstructuredCreator struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewUnstructuredCreator returns a simple object creator that always returns an unstructured
 | 
				
			||||||
 | 
					func NewUnstructuredCreator() runtime.ObjectCreater {
 | 
				
			||||||
 | 
						return unstructuredCreator{}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c unstructuredCreator) New(kind schema.GroupVersionKind) (runtime.Object, error) {
 | 
				
			||||||
 | 
						ret := &unstructured.Unstructured{}
 | 
				
			||||||
 | 
						ret.SetGroupVersionKind(kind)
 | 
				
			||||||
 | 
						return ret, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type unstructuredDefaulter struct {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewUnstructuredDefaulter returns defaulter suitable for unstructured types that doesn't default anything
 | 
				
			||||||
 | 
					func NewUnstructuredDefaulter() runtime.ObjectDefaulter {
 | 
				
			||||||
 | 
						return unstructuredDefaulter{}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d unstructuredDefaulter) Default(in runtime.Object) {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -30,25 +30,28 @@ go_test(
 | 
				
			|||||||
go_library(
 | 
					go_library(
 | 
				
			||||||
    name = "go_default_library",
 | 
					    name = "go_default_library",
 | 
				
			||||||
    srcs = [
 | 
					    srcs = [
 | 
				
			||||||
 | 
					        "bad_debt.go",
 | 
				
			||||||
        "client.go",
 | 
					        "client.go",
 | 
				
			||||||
        "client_pool.go",
 | 
					        "client_pool.go",
 | 
				
			||||||
        "dynamic_util.go",
 | 
					        "dynamic_util.go",
 | 
				
			||||||
 | 
					        "scheme.go",
 | 
				
			||||||
 | 
					        "simple.go",
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    importpath = "k8s.io/client-go/dynamic",
 | 
					    importpath = "k8s.io/client-go/dynamic",
 | 
				
			||||||
    deps = [
 | 
					    deps = [
 | 
				
			||||||
        "//vendor/k8s.io/api/core/v1:go_default_library",
 | 
					 | 
				
			||||||
        "//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
 | 
					        "//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
 | 
				
			||||||
        "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
 | 
					        "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
 | 
				
			||||||
        "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
 | 
					        "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
 | 
				
			||||||
        "//vendor/k8s.io/apimachinery/pkg/conversion/queryparams:go_default_library",
 | 
					 | 
				
			||||||
        "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
 | 
					        "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
 | 
				
			||||||
        "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
 | 
					        "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
 | 
				
			||||||
        "//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
 | 
					        "//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
 | 
				
			||||||
 | 
					        "//vendor/k8s.io/apimachinery/pkg/runtime/serializer/json:go_default_library",
 | 
				
			||||||
 | 
					        "//vendor/k8s.io/apimachinery/pkg/runtime/serializer/streaming:go_default_library",
 | 
				
			||||||
 | 
					        "//vendor/k8s.io/apimachinery/pkg/runtime/serializer/versioning:go_default_library",
 | 
				
			||||||
        "//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
 | 
					        "//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
 | 
				
			||||||
        "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
 | 
					        "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
 | 
				
			||||||
        "//vendor/k8s.io/client-go/kubernetes/scheme:go_default_library",
 | 
					        "//vendor/k8s.io/client-go/kubernetes/scheme:go_default_library",
 | 
				
			||||||
        "//vendor/k8s.io/client-go/rest:go_default_library",
 | 
					        "//vendor/k8s.io/client-go/rest:go_default_library",
 | 
				
			||||||
        "//vendor/k8s.io/client-go/util/flowcontrol:go_default_library",
 | 
					 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										79
									
								
								staging/src/k8s.io/client-go/dynamic/bad_debt.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								staging/src/k8s.io/client-go/dynamic/bad_debt.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,79 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					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 (
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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/client-go/kubernetes/scheme"
 | 
				
			||||||
 | 
						"k8s.io/client-go/rest"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// dynamicCodec is a codec that wraps the standard unstructured codec
 | 
				
			||||||
 | 
					// with special handling for Status objects.
 | 
				
			||||||
 | 
					// Deprecated only used by test code and its wrong
 | 
				
			||||||
 | 
					type dynamicCodec struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (dynamicCodec) Decode(data []byte, gvk *schema.GroupVersionKind, obj runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
 | 
				
			||||||
 | 
						obj, gvk, err := unstructured.UnstructuredJSONScheme.Decode(data, gvk, obj)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if _, ok := obj.(*metav1.Status); !ok && strings.ToLower(gvk.Kind) == "status" {
 | 
				
			||||||
 | 
							obj = &metav1.Status{}
 | 
				
			||||||
 | 
							err := json.Unmarshal(data, obj)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return obj, gvk, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (dynamicCodec) Encode(obj runtime.Object, w io.Writer) error {
 | 
				
			||||||
 | 
						return unstructured.UnstructuredJSONScheme.Encode(obj, w)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ContentConfig returns a rest.ContentConfig for dynamic types.
 | 
				
			||||||
 | 
					// Deprecated only used by test code and its wrong
 | 
				
			||||||
 | 
					func ContentConfig() rest.ContentConfig {
 | 
				
			||||||
 | 
						var jsonInfo runtime.SerializerInfo
 | 
				
			||||||
 | 
						// TODO: scheme.Codecs here should become "pkg/apis/server/scheme" which is the minimal core you need
 | 
				
			||||||
 | 
						// to talk to a kubernetes server
 | 
				
			||||||
 | 
						for _, info := range scheme.Codecs.SupportedMediaTypes() {
 | 
				
			||||||
 | 
							if info.MediaType == runtime.ContentTypeJSON {
 | 
				
			||||||
 | 
								jsonInfo = info
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						jsonInfo.Serializer = dynamicCodec{}
 | 
				
			||||||
 | 
						jsonInfo.PrettySerializer = nil
 | 
				
			||||||
 | 
						return rest.ContentConfig{
 | 
				
			||||||
 | 
							AcceptContentTypes:   runtime.ContentTypeJSON,
 | 
				
			||||||
 | 
							ContentType:          runtime.ContentTypeJSON,
 | 
				
			||||||
 | 
							NegotiatedSerializer: serializer.NegotiatedSerializerWrapper(jsonInfo),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -20,37 +20,24 @@ limitations under the License.
 | 
				
			|||||||
package dynamic
 | 
					package dynamic
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"encoding/json"
 | 
					 | 
				
			||||||
	"errors"
 | 
					 | 
				
			||||||
	"io"
 | 
					 | 
				
			||||||
	"net/url"
 | 
					 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"k8s.io/api/core/v1"
 | 
					 | 
				
			||||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
						metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 | 
						"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/conversion/queryparams"
 | 
					 | 
				
			||||||
	"k8s.io/apimachinery/pkg/runtime"
 | 
						"k8s.io/apimachinery/pkg/runtime"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/runtime/schema"
 | 
						"k8s.io/apimachinery/pkg/runtime/schema"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/runtime/serializer"
 | 
					 | 
				
			||||||
	"k8s.io/apimachinery/pkg/types"
 | 
						"k8s.io/apimachinery/pkg/types"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/watch"
 | 
						"k8s.io/apimachinery/pkg/watch"
 | 
				
			||||||
	"k8s.io/client-go/kubernetes/scheme"
 | 
					 | 
				
			||||||
	restclient "k8s.io/client-go/rest"
 | 
						restclient "k8s.io/client-go/rest"
 | 
				
			||||||
	"k8s.io/client-go/util/flowcontrol"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Interface is a Kubernetes client that allows you to access metadata
 | 
					// Interface is a Kubernetes client that allows you to access metadata
 | 
				
			||||||
// and manipulate metadata of a Kubernetes API group.
 | 
					// and manipulate metadata of a Kubernetes API group.
 | 
				
			||||||
type Interface interface {
 | 
					type Interface interface {
 | 
				
			||||||
	// GetRateLimiter returns the rate limiter for this client.
 | 
					 | 
				
			||||||
	GetRateLimiter() flowcontrol.RateLimiter
 | 
					 | 
				
			||||||
	// Resource returns an API interface to the specified resource for this client's
 | 
						// Resource returns an API interface to the specified resource for this client's
 | 
				
			||||||
	// group and version.  If resource is not a namespaced resource, then namespace
 | 
						// group and version.  If resource is not a namespaced resource, then namespace
 | 
				
			||||||
	// is ignored.  The ResourceInterface inherits the parameter codec of this client.
 | 
						// is ignored.  The ResourceInterface inherits the parameter codec of this client.
 | 
				
			||||||
	Resource(resource *metav1.APIResource, namespace string) ResourceInterface
 | 
						Resource(resource *metav1.APIResource, namespace string) ResourceInterface
 | 
				
			||||||
	// ParameterCodec returns a client with the provided parameter codec.
 | 
					 | 
				
			||||||
	ParameterCodec(parameterCodec runtime.ParameterCodec) Interface
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ResourceInterface is an API interface to a specific resource under a
 | 
					// ResourceInterface is an API interface to a specific resource under a
 | 
				
			||||||
@@ -77,303 +64,50 @@ type ResourceInterface interface {
 | 
				
			|||||||
// Client is a Kubernetes client that allows you to access metadata
 | 
					// Client is a Kubernetes client that allows you to access metadata
 | 
				
			||||||
// and manipulate metadata of a Kubernetes API group, and implements Interface.
 | 
					// and manipulate metadata of a Kubernetes API group, and implements Interface.
 | 
				
			||||||
type Client struct {
 | 
					type Client struct {
 | 
				
			||||||
	cl             *restclient.RESTClient
 | 
						version  schema.GroupVersion
 | 
				
			||||||
	parameterCodec runtime.ParameterCodec
 | 
						delegate DynamicInterface
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewClient returns a new client based on the passed in config. The
 | 
					// NewClient returns a new client based on the passed in config. The
 | 
				
			||||||
// codec is ignored, as the dynamic client uses it's own codec.
 | 
					// codec is ignored, as the dynamic client uses it's own codec.
 | 
				
			||||||
func NewClient(conf *restclient.Config) (*Client, error) {
 | 
					func NewClient(conf *restclient.Config, version schema.GroupVersion) (*Client, error) {
 | 
				
			||||||
	// avoid changing the original config
 | 
						delegate, err := NewForConfig(conf)
 | 
				
			||||||
	confCopy := *conf
 | 
					 | 
				
			||||||
	conf = &confCopy
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	contentConfig := ContentConfig()
 | 
					 | 
				
			||||||
	contentConfig.GroupVersion = conf.GroupVersion
 | 
					 | 
				
			||||||
	if conf.NegotiatedSerializer != nil {
 | 
					 | 
				
			||||||
		contentConfig.NegotiatedSerializer = conf.NegotiatedSerializer
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	conf.ContentConfig = contentConfig
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if conf.APIPath == "" {
 | 
					 | 
				
			||||||
		conf.APIPath = "/api"
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if len(conf.UserAgent) == 0 {
 | 
					 | 
				
			||||||
		conf.UserAgent = restclient.DefaultKubernetesUserAgent()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	cl, err := restclient.RESTClientFor(conf)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return &Client{cl: cl}, nil
 | 
						return &Client{version: version, delegate: delegate}, nil
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// GetRateLimiter returns rate limier.
 | 
					 | 
				
			||||||
func (c *Client) GetRateLimiter() flowcontrol.RateLimiter {
 | 
					 | 
				
			||||||
	return c.cl.GetRateLimiter()
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Resource returns an API interface to the specified resource for this client's
 | 
					// Resource returns an API interface to the specified resource for this client's
 | 
				
			||||||
// group and version. If resource is not a namespaced resource, then namespace
 | 
					// group and version. If resource is not a namespaced resource, then namespace
 | 
				
			||||||
// is ignored. The ResourceInterface inherits the parameter codec of c.
 | 
					// is ignored. The ResourceInterface inherits the parameter codec of c.
 | 
				
			||||||
func (c *Client) Resource(resource *metav1.APIResource, namespace string) ResourceInterface {
 | 
					func (c *Client) Resource(resource *metav1.APIResource, namespace string) ResourceInterface {
 | 
				
			||||||
	return &ResourceClient{
 | 
						resourceTokens := strings.SplitN(resource.Name, "/", 2)
 | 
				
			||||||
		cl:             c.cl,
 | 
						subresource := ""
 | 
				
			||||||
		resource:       resource,
 | 
						if len(resourceTokens) > 1 {
 | 
				
			||||||
		ns:             namespace,
 | 
							subresource = resourceTokens[1]
 | 
				
			||||||
		parameterCodec: c.parameterCodec,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ParameterCodec returns a client with the provided parameter codec.
 | 
						if len(namespace) == 0 {
 | 
				
			||||||
func (c *Client) ParameterCodec(parameterCodec runtime.ParameterCodec) Interface {
 | 
							return oldResourceShim(c.delegate.ClusterSubresource(c.version.WithResource(resourceTokens[0]), subresource))
 | 
				
			||||||
	return &Client{
 | 
					 | 
				
			||||||
		cl:             c.cl,
 | 
					 | 
				
			||||||
		parameterCodec: parameterCodec,
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						return oldResourceShim(c.delegate.NamespacedSubresource(c.version.WithResource(resourceTokens[0]), subresource, namespace))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ResourceClient is an API interface to a specific resource under a
 | 
					// the old interfaces used the wrong type for lists.  this fixes that
 | 
				
			||||||
// dynamic client, and implements ResourceInterface.
 | 
					func oldResourceShim(in DynamicResourceInterface) ResourceInterface {
 | 
				
			||||||
type ResourceClient struct {
 | 
						return oldResourceShimType{DynamicResourceInterface: in}
 | 
				
			||||||
	cl             *restclient.RESTClient
 | 
					 | 
				
			||||||
	resource       *metav1.APIResource
 | 
					 | 
				
			||||||
	ns             string
 | 
					 | 
				
			||||||
	parameterCodec runtime.ParameterCodec
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (rc *ResourceClient) parseResourceSubresourceName() (string, []string) {
 | 
					type oldResourceShimType struct {
 | 
				
			||||||
	var resourceName string
 | 
						DynamicResourceInterface
 | 
				
			||||||
	var subresourceName []string
 | 
					 | 
				
			||||||
	if strings.Contains(rc.resource.Name, "/") {
 | 
					 | 
				
			||||||
		resourceName = strings.Split(rc.resource.Name, "/")[0]
 | 
					 | 
				
			||||||
		subresourceName = strings.Split(rc.resource.Name, "/")[1:]
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		resourceName = rc.resource.Name
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return resourceName, subresourceName
 | 
					func (s oldResourceShimType) List(opts metav1.ListOptions) (runtime.Object, error) {
 | 
				
			||||||
 | 
						return s.DynamicResourceInterface.List(opts)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// List returns a list of objects for this resource.
 | 
					func (s oldResourceShimType) Patch(name string, pt types.PatchType, data []byte) (*unstructured.Unstructured, error) {
 | 
				
			||||||
func (rc *ResourceClient) List(opts metav1.ListOptions) (runtime.Object, error) {
 | 
						return s.DynamicResourceInterface.Patch(name, pt, data)
 | 
				
			||||||
	parameterEncoder := rc.parameterCodec
 | 
					 | 
				
			||||||
	if parameterEncoder == nil {
 | 
					 | 
				
			||||||
		parameterEncoder = defaultParameterEncoder
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
	return rc.cl.Get().
 | 
					 | 
				
			||||||
		NamespaceIfScoped(rc.ns, rc.resource.Namespaced).
 | 
					 | 
				
			||||||
		Resource(rc.resource.Name).
 | 
					 | 
				
			||||||
		VersionedParams(&opts, parameterEncoder).
 | 
					 | 
				
			||||||
		Do().
 | 
					 | 
				
			||||||
		Get()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Get gets the resource with the specified name.
 | 
					 | 
				
			||||||
func (rc *ResourceClient) Get(name string, opts metav1.GetOptions) (*unstructured.Unstructured, error) {
 | 
					 | 
				
			||||||
	parameterEncoder := rc.parameterCodec
 | 
					 | 
				
			||||||
	if parameterEncoder == nil {
 | 
					 | 
				
			||||||
		parameterEncoder = defaultParameterEncoder
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	result := new(unstructured.Unstructured)
 | 
					 | 
				
			||||||
	resourceName, subresourceName := rc.parseResourceSubresourceName()
 | 
					 | 
				
			||||||
	err := rc.cl.Get().
 | 
					 | 
				
			||||||
		NamespaceIfScoped(rc.ns, rc.resource.Namespaced).
 | 
					 | 
				
			||||||
		Resource(resourceName).
 | 
					 | 
				
			||||||
		SubResource(subresourceName...).
 | 
					 | 
				
			||||||
		VersionedParams(&opts, parameterEncoder).
 | 
					 | 
				
			||||||
		Name(name).
 | 
					 | 
				
			||||||
		Do().
 | 
					 | 
				
			||||||
		Into(result)
 | 
					 | 
				
			||||||
	return result, err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Delete deletes the resource with the specified name.
 | 
					 | 
				
			||||||
func (rc *ResourceClient) Delete(name string, opts *metav1.DeleteOptions) error {
 | 
					 | 
				
			||||||
	return rc.cl.Delete().
 | 
					 | 
				
			||||||
		NamespaceIfScoped(rc.ns, rc.resource.Namespaced).
 | 
					 | 
				
			||||||
		Resource(rc.resource.Name).
 | 
					 | 
				
			||||||
		Name(name).
 | 
					 | 
				
			||||||
		Body(opts).
 | 
					 | 
				
			||||||
		Do().
 | 
					 | 
				
			||||||
		Error()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// DeleteCollection deletes a collection of objects.
 | 
					 | 
				
			||||||
func (rc *ResourceClient) DeleteCollection(deleteOptions *metav1.DeleteOptions, listOptions metav1.ListOptions) error {
 | 
					 | 
				
			||||||
	parameterEncoder := rc.parameterCodec
 | 
					 | 
				
			||||||
	if parameterEncoder == nil {
 | 
					 | 
				
			||||||
		parameterEncoder = defaultParameterEncoder
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return rc.cl.Delete().
 | 
					 | 
				
			||||||
		NamespaceIfScoped(rc.ns, rc.resource.Namespaced).
 | 
					 | 
				
			||||||
		Resource(rc.resource.Name).
 | 
					 | 
				
			||||||
		VersionedParams(&listOptions, parameterEncoder).
 | 
					 | 
				
			||||||
		Body(deleteOptions).
 | 
					 | 
				
			||||||
		Do().
 | 
					 | 
				
			||||||
		Error()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Create creates the provided resource.
 | 
					 | 
				
			||||||
func (rc *ResourceClient) Create(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) {
 | 
					 | 
				
			||||||
	result := new(unstructured.Unstructured)
 | 
					 | 
				
			||||||
	resourceName, subresourceName := rc.parseResourceSubresourceName()
 | 
					 | 
				
			||||||
	req := rc.cl.Post().
 | 
					 | 
				
			||||||
		NamespaceIfScoped(rc.ns, rc.resource.Namespaced).
 | 
					 | 
				
			||||||
		Resource(resourceName).
 | 
					 | 
				
			||||||
		Body(obj)
 | 
					 | 
				
			||||||
	if len(subresourceName) > 0 {
 | 
					 | 
				
			||||||
		// If the provided resource is a subresource, the POST request should contain
 | 
					 | 
				
			||||||
		// object name. Examples of subresources that support Create operation:
 | 
					 | 
				
			||||||
		//	core/v1/pods/{name}/binding
 | 
					 | 
				
			||||||
		//	core/v1/pods/{name}/eviction
 | 
					 | 
				
			||||||
		//	extensions/v1beta1/deployments/{name}/rollback
 | 
					 | 
				
			||||||
		//	apps/v1beta1/deployments/{name}/rollback
 | 
					 | 
				
			||||||
		// NOTE: Currently our system assumes every subresource object has the same
 | 
					 | 
				
			||||||
		//	 name as the parent resource object. E.g. a pods/binding object having
 | 
					 | 
				
			||||||
		//	 metadada.name "foo" means pod "foo" is being bound. We may need to
 | 
					 | 
				
			||||||
		//	 change this if we break the assumption in the future.
 | 
					 | 
				
			||||||
		req = req.SubResource(subresourceName...).
 | 
					 | 
				
			||||||
			Name(obj.GetName())
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	err := req.Do().
 | 
					 | 
				
			||||||
		Into(result)
 | 
					 | 
				
			||||||
	return result, err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Update updates the provided resource.
 | 
					 | 
				
			||||||
func (rc *ResourceClient) Update(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) {
 | 
					 | 
				
			||||||
	result := new(unstructured.Unstructured)
 | 
					 | 
				
			||||||
	if len(obj.GetName()) == 0 {
 | 
					 | 
				
			||||||
		return result, errors.New("object missing name")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	resourceName, subresourceName := rc.parseResourceSubresourceName()
 | 
					 | 
				
			||||||
	err := rc.cl.Put().
 | 
					 | 
				
			||||||
		NamespaceIfScoped(rc.ns, rc.resource.Namespaced).
 | 
					 | 
				
			||||||
		Resource(resourceName).
 | 
					 | 
				
			||||||
		SubResource(subresourceName...).
 | 
					 | 
				
			||||||
		// NOTE: Currently our system assumes every subresource object has the same
 | 
					 | 
				
			||||||
		//	 name as the parent resource object. E.g. a pods/binding object having
 | 
					 | 
				
			||||||
		//	 metadada.name "foo" means pod "foo" is being bound. We may need to
 | 
					 | 
				
			||||||
		//	 change this if we break the assumption in the future.
 | 
					 | 
				
			||||||
		Name(obj.GetName()).
 | 
					 | 
				
			||||||
		Body(obj).
 | 
					 | 
				
			||||||
		Do().
 | 
					 | 
				
			||||||
		Into(result)
 | 
					 | 
				
			||||||
	return result, err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Watch returns a watch.Interface that watches the resource.
 | 
					 | 
				
			||||||
func (rc *ResourceClient) Watch(opts metav1.ListOptions) (watch.Interface, error) {
 | 
					 | 
				
			||||||
	parameterEncoder := rc.parameterCodec
 | 
					 | 
				
			||||||
	if parameterEncoder == nil {
 | 
					 | 
				
			||||||
		parameterEncoder = defaultParameterEncoder
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	opts.Watch = true
 | 
					 | 
				
			||||||
	return rc.cl.Get().
 | 
					 | 
				
			||||||
		NamespaceIfScoped(rc.ns, rc.resource.Namespaced).
 | 
					 | 
				
			||||||
		Resource(rc.resource.Name).
 | 
					 | 
				
			||||||
		VersionedParams(&opts, parameterEncoder).
 | 
					 | 
				
			||||||
		Watch()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Patch applies the patch and returns the patched resource.
 | 
					 | 
				
			||||||
func (rc *ResourceClient) Patch(name string, pt types.PatchType, data []byte) (*unstructured.Unstructured, error) {
 | 
					 | 
				
			||||||
	result := new(unstructured.Unstructured)
 | 
					 | 
				
			||||||
	resourceName, subresourceName := rc.parseResourceSubresourceName()
 | 
					 | 
				
			||||||
	err := rc.cl.Patch(pt).
 | 
					 | 
				
			||||||
		NamespaceIfScoped(rc.ns, rc.resource.Namespaced).
 | 
					 | 
				
			||||||
		Resource(resourceName).
 | 
					 | 
				
			||||||
		SubResource(subresourceName...).
 | 
					 | 
				
			||||||
		Name(name).
 | 
					 | 
				
			||||||
		Body(data).
 | 
					 | 
				
			||||||
		Do().
 | 
					 | 
				
			||||||
		Into(result)
 | 
					 | 
				
			||||||
	return result, err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// dynamicCodec is a codec that wraps the standard unstructured codec
 | 
					 | 
				
			||||||
// with special handling for Status objects.
 | 
					 | 
				
			||||||
type dynamicCodec struct{}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (dynamicCodec) Decode(data []byte, gvk *schema.GroupVersionKind, obj runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
 | 
					 | 
				
			||||||
	obj, gvk, err := unstructured.UnstructuredJSONScheme.Decode(data, gvk, obj)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if _, ok := obj.(*metav1.Status); !ok && strings.ToLower(gvk.Kind) == "status" {
 | 
					 | 
				
			||||||
		obj = &metav1.Status{}
 | 
					 | 
				
			||||||
		err := json.Unmarshal(data, obj)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return nil, nil, err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return obj, gvk, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (dynamicCodec) Encode(obj runtime.Object, w io.Writer) error {
 | 
					 | 
				
			||||||
	return unstructured.UnstructuredJSONScheme.Encode(obj, w)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ContentConfig returns a restclient.ContentConfig for dynamic types.
 | 
					 | 
				
			||||||
func ContentConfig() restclient.ContentConfig {
 | 
					 | 
				
			||||||
	var jsonInfo runtime.SerializerInfo
 | 
					 | 
				
			||||||
	// TODO: scheme.Codecs here should become "pkg/apis/server/scheme" which is the minimal core you need
 | 
					 | 
				
			||||||
	// to talk to a kubernetes server
 | 
					 | 
				
			||||||
	for _, info := range scheme.Codecs.SupportedMediaTypes() {
 | 
					 | 
				
			||||||
		if info.MediaType == runtime.ContentTypeJSON {
 | 
					 | 
				
			||||||
			jsonInfo = info
 | 
					 | 
				
			||||||
			break
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	jsonInfo.Serializer = dynamicCodec{}
 | 
					 | 
				
			||||||
	jsonInfo.PrettySerializer = nil
 | 
					 | 
				
			||||||
	return restclient.ContentConfig{
 | 
					 | 
				
			||||||
		AcceptContentTypes:   runtime.ContentTypeJSON,
 | 
					 | 
				
			||||||
		ContentType:          runtime.ContentTypeJSON,
 | 
					 | 
				
			||||||
		NegotiatedSerializer: serializer.NegotiatedSerializerWrapper(jsonInfo),
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// paramaterCodec is a codec converts an API object to query
 | 
					 | 
				
			||||||
// parameters without trying to convert to the target version.
 | 
					 | 
				
			||||||
type parameterCodec struct{}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (parameterCodec) EncodeParameters(obj runtime.Object, to schema.GroupVersion) (url.Values, error) {
 | 
					 | 
				
			||||||
	return queryparams.Convert(obj)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (parameterCodec) DecodeParameters(parameters url.Values, from schema.GroupVersion, into runtime.Object) error {
 | 
					 | 
				
			||||||
	return errors.New("DecodeParameters not implemented on dynamic parameterCodec")
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var defaultParameterEncoder runtime.ParameterCodec = parameterCodec{}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type versionedParameterEncoderWithV1Fallback struct{}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (versionedParameterEncoderWithV1Fallback) EncodeParameters(obj runtime.Object, to schema.GroupVersion) (url.Values, error) {
 | 
					 | 
				
			||||||
	ret, err := scheme.ParameterCodec.EncodeParameters(obj, to)
 | 
					 | 
				
			||||||
	if err != nil && runtime.IsNotRegisteredError(err) {
 | 
					 | 
				
			||||||
		// fallback to v1
 | 
					 | 
				
			||||||
		return scheme.ParameterCodec.EncodeParameters(obj, v1.SchemeGroupVersion)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return ret, err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (versionedParameterEncoderWithV1Fallback) DecodeParameters(parameters url.Values, from schema.GroupVersion, into runtime.Object) error {
 | 
					 | 
				
			||||||
	return errors.New("DecodeParameters not implemented on versionedParameterEncoderWithV1Fallback")
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// VersionedParameterEncoderWithV1Fallback is useful for encoding query
 | 
					 | 
				
			||||||
// parameters for custom resources. It tries to convert object to the
 | 
					 | 
				
			||||||
// specified version before converting it to query parameters, and falls back to
 | 
					 | 
				
			||||||
// converting to v1 if the object is not registered in the specified version.
 | 
					 | 
				
			||||||
// For the record, currently API server always treats query parameters sent to a
 | 
					 | 
				
			||||||
// custom resource endpoint as v1.
 | 
					 | 
				
			||||||
var VersionedParameterEncoderWithV1Fallback runtime.ParameterCodec = versionedParameterEncoderWithV1Fallback{}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -113,7 +113,7 @@ func (c *clientPoolImpl) ClientForGroupVersionKind(kind schema.GroupVersionKind)
 | 
				
			|||||||
	// we need to make a client
 | 
						// we need to make a client
 | 
				
			||||||
	conf.GroupVersion = &gv
 | 
						conf.GroupVersion = &gv
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dynamicClient, err := NewClient(conf)
 | 
						dynamicClient, err := NewClient(conf, gv)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -63,7 +63,7 @@ func getClientServer(gv *schema.GroupVersion, h func(http.ResponseWriter, *http.
 | 
				
			|||||||
	cl, err := NewClient(&restclient.Config{
 | 
						cl, err := NewClient(&restclient.Config{
 | 
				
			||||||
		Host:          srv.URL,
 | 
							Host:          srv.URL,
 | 
				
			||||||
		ContentConfig: restclient.ContentConfig{GroupVersion: gv},
 | 
							ContentConfig: restclient.ContentConfig{GroupVersion: gv},
 | 
				
			||||||
	})
 | 
						}, *gv)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		srv.Close()
 | 
							srv.Close()
 | 
				
			||||||
		return nil, nil, err
 | 
							return nil, nil, err
 | 
				
			||||||
@@ -81,7 +81,7 @@ func TestList(t *testing.T) {
 | 
				
			|||||||
	}{
 | 
						}{
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name: "normal_list",
 | 
								name: "normal_list",
 | 
				
			||||||
			path: "/api/gtest/vtest/rtest",
 | 
								path: "/apis/gtest/vtest/rtest",
 | 
				
			||||||
			resp: getListJSON("vTest", "rTestList",
 | 
								resp: getListJSON("vTest", "rTestList",
 | 
				
			||||||
				getJSON("vTest", "rTest", "item1"),
 | 
									getJSON("vTest", "rTest", "item1"),
 | 
				
			||||||
				getJSON("vTest", "rTest", "item2")),
 | 
									getJSON("vTest", "rTest", "item2")),
 | 
				
			||||||
@@ -99,7 +99,7 @@ func TestList(t *testing.T) {
 | 
				
			|||||||
		{
 | 
							{
 | 
				
			||||||
			name:      "namespaced_list",
 | 
								name:      "namespaced_list",
 | 
				
			||||||
			namespace: "nstest",
 | 
								namespace: "nstest",
 | 
				
			||||||
			path:      "/api/gtest/vtest/namespaces/nstest/rtest",
 | 
								path:      "/apis/gtest/vtest/namespaces/nstest/rtest",
 | 
				
			||||||
			resp: getListJSON("vTest", "rTestList",
 | 
								resp: getListJSON("vTest", "rTestList",
 | 
				
			||||||
				getJSON("vTest", "rTest", "item1"),
 | 
									getJSON("vTest", "rTest", "item1"),
 | 
				
			||||||
				getJSON("vTest", "rTest", "item2")),
 | 
									getJSON("vTest", "rTest", "item2")),
 | 
				
			||||||
@@ -160,7 +160,7 @@ func TestGet(t *testing.T) {
 | 
				
			|||||||
		{
 | 
							{
 | 
				
			||||||
			resource: "rtest",
 | 
								resource: "rtest",
 | 
				
			||||||
			name:     "normal_get",
 | 
								name:     "normal_get",
 | 
				
			||||||
			path:     "/api/gtest/vtest/rtest/normal_get",
 | 
								path:     "/apis/gtest/vtest/rtest/normal_get",
 | 
				
			||||||
			resp:     getJSON("vTest", "rTest", "normal_get"),
 | 
								resp:     getJSON("vTest", "rTest", "normal_get"),
 | 
				
			||||||
			want:     getObject("vTest", "rTest", "normal_get"),
 | 
								want:     getObject("vTest", "rTest", "normal_get"),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
@@ -168,14 +168,14 @@ func TestGet(t *testing.T) {
 | 
				
			|||||||
			resource:  "rtest",
 | 
								resource:  "rtest",
 | 
				
			||||||
			namespace: "nstest",
 | 
								namespace: "nstest",
 | 
				
			||||||
			name:      "namespaced_get",
 | 
								name:      "namespaced_get",
 | 
				
			||||||
			path:      "/api/gtest/vtest/namespaces/nstest/rtest/namespaced_get",
 | 
								path:      "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_get",
 | 
				
			||||||
			resp:      getJSON("vTest", "rTest", "namespaced_get"),
 | 
								resp:      getJSON("vTest", "rTest", "namespaced_get"),
 | 
				
			||||||
			want:      getObject("vTest", "rTest", "namespaced_get"),
 | 
								want:      getObject("vTest", "rTest", "namespaced_get"),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			resource: "rtest/srtest",
 | 
								resource: "rtest/srtest",
 | 
				
			||||||
			name:     "normal_subresource_get",
 | 
								name:     "normal_subresource_get",
 | 
				
			||||||
			path:     "/api/gtest/vtest/rtest/normal_subresource_get/srtest",
 | 
								path:     "/apis/gtest/vtest/rtest/normal_subresource_get/srtest",
 | 
				
			||||||
			resp:     getJSON("vTest", "srTest", "normal_subresource_get"),
 | 
								resp:     getJSON("vTest", "srTest", "normal_subresource_get"),
 | 
				
			||||||
			want:     getObject("vTest", "srTest", "normal_subresource_get"),
 | 
								want:     getObject("vTest", "srTest", "normal_subresource_get"),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
@@ -183,7 +183,7 @@ func TestGet(t *testing.T) {
 | 
				
			|||||||
			resource:  "rtest/srtest",
 | 
								resource:  "rtest/srtest",
 | 
				
			||||||
			namespace: "nstest",
 | 
								namespace: "nstest",
 | 
				
			||||||
			name:      "namespaced_subresource_get",
 | 
								name:      "namespaced_subresource_get",
 | 
				
			||||||
			path:      "/api/gtest/vtest/namespaces/nstest/rtest/namespaced_subresource_get/srtest",
 | 
								path:      "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_subresource_get/srtest",
 | 
				
			||||||
			resp:      getJSON("vTest", "srTest", "namespaced_subresource_get"),
 | 
								resp:      getJSON("vTest", "srTest", "namespaced_subresource_get"),
 | 
				
			||||||
			want:      getObject("vTest", "srTest", "namespaced_subresource_get"),
 | 
								want:      getObject("vTest", "srTest", "namespaced_subresource_get"),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
@@ -222,6 +222,9 @@ func TestGet(t *testing.T) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestDelete(t *testing.T) {
 | 
					func TestDelete(t *testing.T) {
 | 
				
			||||||
 | 
						background := metav1.DeletePropagationBackground
 | 
				
			||||||
 | 
						uid := types.UID("uid")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	statusOK := &metav1.Status{
 | 
						statusOK := &metav1.Status{
 | 
				
			||||||
		TypeMeta: metav1.TypeMeta{Kind: "Status"},
 | 
							TypeMeta: metav1.TypeMeta{Kind: "Status"},
 | 
				
			||||||
		Status:   metav1.StatusSuccess,
 | 
							Status:   metav1.StatusSuccess,
 | 
				
			||||||
@@ -230,15 +233,22 @@ func TestDelete(t *testing.T) {
 | 
				
			|||||||
		namespace     string
 | 
							namespace     string
 | 
				
			||||||
		name          string
 | 
							name          string
 | 
				
			||||||
		path          string
 | 
							path          string
 | 
				
			||||||
 | 
							deleteOptions *metav1.DeleteOptions
 | 
				
			||||||
	}{
 | 
						}{
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name: "normal_delete",
 | 
								name: "normal_delete",
 | 
				
			||||||
			path: "/api/gtest/vtest/rtest/normal_delete",
 | 
								path: "/apis/gtest/vtest/rtest/normal_delete",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			namespace: "nstest",
 | 
								namespace: "nstest",
 | 
				
			||||||
			name:      "namespaced_delete",
 | 
								name:      "namespaced_delete",
 | 
				
			||||||
			path:      "/api/gtest/vtest/namespaces/nstest/rtest/namespaced_delete",
 | 
								path:      "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_delete",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								namespace:     "nstest",
 | 
				
			||||||
 | 
								name:          "namespaced_delete_with_options",
 | 
				
			||||||
 | 
								path:          "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_delete_with_options",
 | 
				
			||||||
 | 
								deleteOptions: &metav1.DeleteOptions{Preconditions: &metav1.Preconditions{UID: &uid}, PropagationPolicy: &background},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, tc := range tcs {
 | 
						for _, tc := range tcs {
 | 
				
			||||||
@@ -262,7 +272,7 @@ func TestDelete(t *testing.T) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		defer srv.Close()
 | 
							defer srv.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		err = cl.Resource(resource, tc.namespace).Delete(tc.name, nil)
 | 
							err = cl.Resource(resource, tc.namespace).Delete(tc.name, tc.deleteOptions)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			t.Errorf("unexpected error when deleting %q: %v", tc.name, err)
 | 
								t.Errorf("unexpected error when deleting %q: %v", tc.name, err)
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
@@ -282,12 +292,12 @@ func TestDeleteCollection(t *testing.T) {
 | 
				
			|||||||
	}{
 | 
						}{
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name: "normal_delete_collection",
 | 
								name: "normal_delete_collection",
 | 
				
			||||||
			path: "/api/gtest/vtest/rtest",
 | 
								path: "/apis/gtest/vtest/rtest",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			namespace: "nstest",
 | 
								namespace: "nstest",
 | 
				
			||||||
			name:      "namespaced_delete_collection",
 | 
								name:      "namespaced_delete_collection",
 | 
				
			||||||
			path:      "/api/gtest/vtest/namespaces/nstest/rtest",
 | 
								path:      "/apis/gtest/vtest/namespaces/nstest/rtest",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, tc := range tcs {
 | 
						for _, tc := range tcs {
 | 
				
			||||||
@@ -330,28 +340,15 @@ func TestCreate(t *testing.T) {
 | 
				
			|||||||
		{
 | 
							{
 | 
				
			||||||
			resource: "rtest",
 | 
								resource: "rtest",
 | 
				
			||||||
			name:     "normal_create",
 | 
								name:     "normal_create",
 | 
				
			||||||
			path:     "/api/gtest/vtest/rtest",
 | 
								path:     "/apis/gtest/vtest/rtest",
 | 
				
			||||||
			obj:      getObject("vTest", "rTest", "normal_create"),
 | 
								obj:      getObject("gtest/vTest", "rTest", "normal_create"),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			resource:  "rtest",
 | 
								resource:  "rtest",
 | 
				
			||||||
			name:      "namespaced_create",
 | 
								name:      "namespaced_create",
 | 
				
			||||||
			namespace: "nstest",
 | 
								namespace: "nstest",
 | 
				
			||||||
			path:      "/api/gtest/vtest/namespaces/nstest/rtest",
 | 
								path:      "/apis/gtest/vtest/namespaces/nstest/rtest",
 | 
				
			||||||
			obj:       getObject("vTest", "rTest", "namespaced_create"),
 | 
								obj:       getObject("gtest/vTest", "rTest", "namespaced_create"),
 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			resource: "rtest/srtest",
 | 
					 | 
				
			||||||
			name:     "normal_subresource_create",
 | 
					 | 
				
			||||||
			path:     "/api/gtest/vtest/rtest/normal_subresource_create/srtest",
 | 
					 | 
				
			||||||
			obj:      getObject("vTest", "srTest", "normal_subresource_create"),
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			resource:  "rtest/srtest",
 | 
					 | 
				
			||||||
			name:      "namespaced_subresource_create",
 | 
					 | 
				
			||||||
			namespace: "nstest",
 | 
					 | 
				
			||||||
			path:      "/api/gtest/vtest/namespaces/nstest/rtest/namespaced_subresource_create/srtest",
 | 
					 | 
				
			||||||
			obj:       getObject("vTest", "srTest", "namespaced_subresource_create"),
 | 
					 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, tc := range tcs {
 | 
						for _, tc := range tcs {
 | 
				
			||||||
@@ -405,28 +402,28 @@ func TestUpdate(t *testing.T) {
 | 
				
			|||||||
		{
 | 
							{
 | 
				
			||||||
			resource: "rtest",
 | 
								resource: "rtest",
 | 
				
			||||||
			name:     "normal_update",
 | 
								name:     "normal_update",
 | 
				
			||||||
			path:     "/api/gtest/vtest/rtest/normal_update",
 | 
								path:     "/apis/gtest/vtest/rtest/normal_update",
 | 
				
			||||||
			obj:      getObject("vTest", "rTest", "normal_update"),
 | 
								obj:      getObject("gtest/vTest", "rTest", "normal_update"),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			resource:  "rtest",
 | 
								resource:  "rtest",
 | 
				
			||||||
			name:      "namespaced_update",
 | 
								name:      "namespaced_update",
 | 
				
			||||||
			namespace: "nstest",
 | 
								namespace: "nstest",
 | 
				
			||||||
			path:      "/api/gtest/vtest/namespaces/nstest/rtest/namespaced_update",
 | 
								path:      "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_update",
 | 
				
			||||||
			obj:       getObject("vTest", "rTest", "namespaced_update"),
 | 
								obj:       getObject("gtest/vTest", "rTest", "namespaced_update"),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			resource: "rtest/srtest",
 | 
								resource: "rtest/srtest",
 | 
				
			||||||
			name:     "normal_subresource_update",
 | 
								name:     "normal_subresource_update",
 | 
				
			||||||
			path:     "/api/gtest/vtest/rtest/normal_update/srtest",
 | 
								path:     "/apis/gtest/vtest/rtest/normal_update/srtest",
 | 
				
			||||||
			obj:      getObject("vTest", "srTest", "normal_update"),
 | 
								obj:      getObject("gtest/vTest", "srTest", "normal_update"),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			resource:  "rtest/srtest",
 | 
								resource:  "rtest/srtest",
 | 
				
			||||||
			name:      "namespaced_subresource_update",
 | 
								name:      "namespaced_subresource_update",
 | 
				
			||||||
			namespace: "nstest",
 | 
								namespace: "nstest",
 | 
				
			||||||
			path:      "/api/gtest/vtest/namespaces/nstest/rtest/namespaced_update/srtest",
 | 
								path:      "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_update/srtest",
 | 
				
			||||||
			obj:       getObject("vTest", "srTest", "namespaced_update"),
 | 
								obj:       getObject("gtest/vTest", "srTest", "namespaced_update"),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, tc := range tcs {
 | 
						for _, tc := range tcs {
 | 
				
			||||||
@@ -479,23 +476,23 @@ func TestWatch(t *testing.T) {
 | 
				
			|||||||
	}{
 | 
						}{
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name:  "normal_watch",
 | 
								name:  "normal_watch",
 | 
				
			||||||
			path:  "/api/gtest/vtest/rtest",
 | 
								path:  "/apis/gtest/vtest/rtest",
 | 
				
			||||||
			query: "watch=true",
 | 
								query: "watch=true",
 | 
				
			||||||
			events: []watch.Event{
 | 
								events: []watch.Event{
 | 
				
			||||||
				{Type: watch.Added, Object: getObject("vTest", "rTest", "normal_watch")},
 | 
									{Type: watch.Added, Object: getObject("gtest/vTest", "rTest", "normal_watch")},
 | 
				
			||||||
				{Type: watch.Modified, Object: getObject("vTest", "rTest", "normal_watch")},
 | 
									{Type: watch.Modified, Object: getObject("gtest/vTest", "rTest", "normal_watch")},
 | 
				
			||||||
				{Type: watch.Deleted, Object: getObject("vTest", "rTest", "normal_watch")},
 | 
									{Type: watch.Deleted, Object: getObject("gtest/vTest", "rTest", "normal_watch")},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name:      "namespaced_watch",
 | 
								name:      "namespaced_watch",
 | 
				
			||||||
			namespace: "nstest",
 | 
								namespace: "nstest",
 | 
				
			||||||
			path:      "/api/gtest/vtest/namespaces/nstest/rtest",
 | 
								path:      "/apis/gtest/vtest/namespaces/nstest/rtest",
 | 
				
			||||||
			query:     "watch=true",
 | 
								query:     "watch=true",
 | 
				
			||||||
			events: []watch.Event{
 | 
								events: []watch.Event{
 | 
				
			||||||
				{Type: watch.Added, Object: getObject("vTest", "rTest", "namespaced_watch")},
 | 
									{Type: watch.Added, Object: getObject("gtest/vTest", "rTest", "namespaced_watch")},
 | 
				
			||||||
				{Type: watch.Modified, Object: getObject("vTest", "rTest", "namespaced_watch")},
 | 
									{Type: watch.Modified, Object: getObject("gtest/vTest", "rTest", "namespaced_watch")},
 | 
				
			||||||
				{Type: watch.Deleted, Object: getObject("vTest", "rTest", "namespaced_watch")},
 | 
									{Type: watch.Deleted, Object: getObject("gtest/vTest", "rTest", "namespaced_watch")},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -552,32 +549,32 @@ func TestPatch(t *testing.T) {
 | 
				
			|||||||
		{
 | 
							{
 | 
				
			||||||
			resource: "rtest",
 | 
								resource: "rtest",
 | 
				
			||||||
			name:     "normal_patch",
 | 
								name:     "normal_patch",
 | 
				
			||||||
			path:     "/api/gtest/vtest/rtest/normal_patch",
 | 
								path:     "/apis/gtest/vtest/rtest/normal_patch",
 | 
				
			||||||
			patch:    getJSON("vTest", "rTest", "normal_patch"),
 | 
								patch:    getJSON("gtest/vTest", "rTest", "normal_patch"),
 | 
				
			||||||
			want:     getObject("vTest", "rTest", "normal_patch"),
 | 
								want:     getObject("gtest/vTest", "rTest", "normal_patch"),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			resource:  "rtest",
 | 
								resource:  "rtest",
 | 
				
			||||||
			name:      "namespaced_patch",
 | 
								name:      "namespaced_patch",
 | 
				
			||||||
			namespace: "nstest",
 | 
								namespace: "nstest",
 | 
				
			||||||
			path:      "/api/gtest/vtest/namespaces/nstest/rtest/namespaced_patch",
 | 
								path:      "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_patch",
 | 
				
			||||||
			patch:     getJSON("vTest", "rTest", "namespaced_patch"),
 | 
								patch:     getJSON("gtest/vTest", "rTest", "namespaced_patch"),
 | 
				
			||||||
			want:      getObject("vTest", "rTest", "namespaced_patch"),
 | 
								want:      getObject("gtest/vTest", "rTest", "namespaced_patch"),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			resource: "rtest/srtest",
 | 
								resource: "rtest/srtest",
 | 
				
			||||||
			name:     "normal_subresource_patch",
 | 
								name:     "normal_subresource_patch",
 | 
				
			||||||
			path:     "/api/gtest/vtest/rtest/normal_subresource_patch/srtest",
 | 
								path:     "/apis/gtest/vtest/rtest/normal_subresource_patch/srtest",
 | 
				
			||||||
			patch:    getJSON("vTest", "srTest", "normal_subresource_patch"),
 | 
								patch:    getJSON("gtest/vTest", "srTest", "normal_subresource_patch"),
 | 
				
			||||||
			want:     getObject("vTest", "srTest", "normal_subresource_patch"),
 | 
								want:     getObject("gtest/vTest", "srTest", "normal_subresource_patch"),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			resource:  "rtest/srtest",
 | 
								resource:  "rtest/srtest",
 | 
				
			||||||
			name:      "namespaced_subresource_patch",
 | 
								name:      "namespaced_subresource_patch",
 | 
				
			||||||
			namespace: "nstest",
 | 
								namespace: "nstest",
 | 
				
			||||||
			path:      "/api/gtest/vtest/namespaces/nstest/rtest/namespaced_subresource_patch/srtest",
 | 
								path:      "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_subresource_patch/srtest",
 | 
				
			||||||
			patch:     getJSON("vTest", "srTest", "namespaced_subresource_patch"),
 | 
								patch:     getJSON("gtest/vTest", "srTest", "namespaced_subresource_patch"),
 | 
				
			||||||
			want:      getObject("vTest", "srTest", "namespaced_subresource_patch"),
 | 
								want:      getObject("gtest/vTest", "srTest", "namespaced_subresource_patch"),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, tc := range tcs {
 | 
						for _, tc := range tcs {
 | 
				
			||||||
@@ -624,11 +621,3 @@ func TestPatch(t *testing.T) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestVersionedParameterEncoderWithV1Fallback(t *testing.T) {
 | 
					 | 
				
			||||||
	enc := VersionedParameterEncoderWithV1Fallback
 | 
					 | 
				
			||||||
	_, err := enc.EncodeParameters(&metav1.ListOptions{}, schema.GroupVersion{Group: "foo.bar.com", Version: "v4"})
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		t.Errorf("Unexpected error: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										98
									
								
								staging/src/k8s.io/client-go/dynamic/scheme.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								staging/src/k8s.io/client-go/dynamic/scheme.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,98 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					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/runtime"
 | 
				
			||||||
 | 
						"k8s.io/apimachinery/pkg/runtime/schema"
 | 
				
			||||||
 | 
						"k8s.io/apimachinery/pkg/runtime/serializer"
 | 
				
			||||||
 | 
						"k8s.io/apimachinery/pkg/runtime/serializer/json"
 | 
				
			||||||
 | 
						"k8s.io/apimachinery/pkg/runtime/serializer/versioning"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var watchJsonSerializerInfo = runtime.SerializerInfo{
 | 
				
			||||||
 | 
						MediaType:        "application/json",
 | 
				
			||||||
 | 
						EncodesAsText:    true,
 | 
				
			||||||
 | 
						Serializer:       json.NewSerializer(json.DefaultMetaFactory, watchScheme, watchScheme, false),
 | 
				
			||||||
 | 
						PrettySerializer: json.NewSerializer(json.DefaultMetaFactory, watchScheme, watchScheme, true),
 | 
				
			||||||
 | 
						StreamSerializer: &runtime.StreamSerializerInfo{
 | 
				
			||||||
 | 
							EncodesAsText: true,
 | 
				
			||||||
 | 
							Serializer:    json.NewSerializer(json.DefaultMetaFactory, watchScheme, watchScheme, false),
 | 
				
			||||||
 | 
							Framer:        json.Framer,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// watchNegotiatedSerializer is used to read the wrapper of the watch stream
 | 
				
			||||||
 | 
					type watchNegotiatedSerializer struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var watchNegotiatedSerializerInstance = watchNegotiatedSerializer{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s watchNegotiatedSerializer) SupportedMediaTypes() []runtime.SerializerInfo {
 | 
				
			||||||
 | 
						return []runtime.SerializerInfo{watchJsonSerializerInfo}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s watchNegotiatedSerializer) EncoderForVersion(encoder runtime.Encoder, gv runtime.GroupVersioner) runtime.Encoder {
 | 
				
			||||||
 | 
						return versioning.NewDefaultingCodecForScheme(watchScheme, encoder, nil, gv, nil)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s watchNegotiatedSerializer) DecoderToVersion(decoder runtime.Decoder, gv runtime.GroupVersioner) runtime.Decoder {
 | 
				
			||||||
 | 
						return versioning.NewDefaultingCodecForScheme(watchScheme, nil, decoder, nil, gv)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 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",
 | 
				
			||||||
 | 
								EncodesAsText:    true,
 | 
				
			||||||
 | 
								Serializer:       json.NewSerializer(json.DefaultMetaFactory, basicScheme, basicScheme, false),
 | 
				
			||||||
 | 
								PrettySerializer: json.NewSerializer(json.DefaultMetaFactory, basicScheme, 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 versioning.NewDefaultingCodecForScheme(watchScheme, encoder, nil, gv, nil)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s basicNegotiatedSerializer) DecoderToVersion(decoder runtime.Decoder, gv runtime.GroupVersioner) runtime.Decoder {
 | 
				
			||||||
 | 
						return versioning.NewDefaultingCodecForScheme(watchScheme, nil, decoder, nil, gv)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										322
									
								
								staging/src/k8s.io/client-go/dynamic/simple.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										322
									
								
								staging/src/k8s.io/client-go/dynamic/simple.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,322 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					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 (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"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 DynamicInterface interface {
 | 
				
			||||||
 | 
						ClusterResource(resource schema.GroupVersionResource) DynamicResourceInterface
 | 
				
			||||||
 | 
						NamespacedResource(resource schema.GroupVersionResource, namespace string) DynamicResourceInterface
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Deprecated, this isn't how we want to do it
 | 
				
			||||||
 | 
						ClusterSubresource(resource schema.GroupVersionResource, subresource string) DynamicResourceInterface
 | 
				
			||||||
 | 
						// Deprecated, this isn't how we want to do it
 | 
				
			||||||
 | 
						NamespacedSubresource(resource schema.GroupVersionResource, subresource, namespace string) DynamicResourceInterface
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type DynamicResourceInterface interface {
 | 
				
			||||||
 | 
						Create(obj *unstructured.Unstructured) (*unstructured.Unstructured, error)
 | 
				
			||||||
 | 
						Update(obj *unstructured.Unstructured) (*unstructured.Unstructured, error)
 | 
				
			||||||
 | 
						UpdateStatus(obj *unstructured.Unstructured) (*unstructured.Unstructured, error)
 | 
				
			||||||
 | 
						Delete(name string, options *metav1.DeleteOptions) error
 | 
				
			||||||
 | 
						DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error
 | 
				
			||||||
 | 
						Get(name string, options metav1.GetOptions) (*unstructured.Unstructured, error)
 | 
				
			||||||
 | 
						List(opts metav1.ListOptions) (*unstructured.UnstructuredList, error)
 | 
				
			||||||
 | 
						Watch(opts metav1.ListOptions) (watch.Interface, error)
 | 
				
			||||||
 | 
						Patch(name string, pt types.PatchType, data []byte, subresources ...string) (*unstructured.Unstructured, error)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type dynamicClient struct {
 | 
				
			||||||
 | 
						client *rest.RESTClient
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var _ DynamicInterface = &dynamicClient{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewForConfig(inConfig *rest.Config) (DynamicInterface, 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
 | 
				
			||||||
 | 
						subresource string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *dynamicClient) ClusterResource(resource schema.GroupVersionResource) DynamicResourceInterface {
 | 
				
			||||||
 | 
						return &dynamicResourceClient{client: c, resource: resource}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					func (c *dynamicClient) NamespacedResource(resource schema.GroupVersionResource, namespace string) DynamicResourceInterface {
 | 
				
			||||||
 | 
						return &dynamicResourceClient{client: c, resource: resource, namespace: namespace}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *dynamicClient) ClusterSubresource(resource schema.GroupVersionResource, subresource string) DynamicResourceInterface {
 | 
				
			||||||
 | 
						return &dynamicResourceClient{client: c, resource: resource, subresource: subresource}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					func (c *dynamicClient) NamespacedSubresource(resource schema.GroupVersionResource, subresource, namespace string) DynamicResourceInterface {
 | 
				
			||||||
 | 
						return &dynamicResourceClient{client: c, resource: resource, namespace: namespace, subresource: subresource}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *dynamicResourceClient) Create(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) {
 | 
				
			||||||
 | 
						if len(c.subresource) > 0 {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("create not supported for subresources")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						outBytes, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						result := c.client.client.Post().AbsPath(c.makeURLSegments("")...).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) (*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(c.makeURLSegments(accessor.GetName())...).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
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						result := c.client.client.Put().AbsPath(append(c.makeURLSegments(accessor.GetName()), "status")...).Body(obj).Do()
 | 
				
			||||||
 | 
						uncastObj, err := result.Get()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return uncastObj.(*unstructured.Unstructured), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *dynamicResourceClient) Delete(name string, opts *metav1.DeleteOptions) error {
 | 
				
			||||||
 | 
						if opts == nil {
 | 
				
			||||||
 | 
							opts = &metav1.DeleteOptions{}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						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(name)...).Body(deleteOptionsByte).Do()
 | 
				
			||||||
 | 
						return result.Error()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *dynamicResourceClient) DeleteCollection(opts *metav1.DeleteOptions, listOptions metav1.ListOptions) error {
 | 
				
			||||||
 | 
						if len(c.subresource) > 0 {
 | 
				
			||||||
 | 
							return fmt.Errorf("deletecollection not supported for subresources")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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) (*unstructured.Unstructured, error) {
 | 
				
			||||||
 | 
						result := c.client.client.Get().AbsPath(c.makeURLSegments(name)...).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) {
 | 
				
			||||||
 | 
						if len(c.subresource) > 0 {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("list not supported for subresources")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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) {
 | 
				
			||||||
 | 
						if len(c.subresource) > 0 {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("watch not supported for subresources")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// subresources only work on things with names
 | 
				
			||||||
 | 
							if len(c.subresource) > 0 {
 | 
				
			||||||
 | 
								url = append(url, c.subresource)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							if len(c.subresource) > 0 {
 | 
				
			||||||
 | 
								panic("somehow snuck a subresource and an empty name.  programmer error")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return url
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -317,10 +317,14 @@ func (r *Request) Param(paramName, s string) *Request {
 | 
				
			|||||||
// VersionedParams will not write query parameters that have omitempty set and are empty. If a
 | 
					// VersionedParams will not write query parameters that have omitempty set and are empty. If a
 | 
				
			||||||
// parameter has already been set it is appended to (Params and VersionedParams are additive).
 | 
					// parameter has already been set it is appended to (Params and VersionedParams are additive).
 | 
				
			||||||
func (r *Request) VersionedParams(obj runtime.Object, codec runtime.ParameterCodec) *Request {
 | 
					func (r *Request) VersionedParams(obj runtime.Object, codec runtime.ParameterCodec) *Request {
 | 
				
			||||||
 | 
						return r.SpecificallyVersionedParams(obj, codec, *r.content.GroupVersion)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *Request) SpecificallyVersionedParams(obj runtime.Object, codec runtime.ParameterCodec, version schema.GroupVersion) *Request {
 | 
				
			||||||
	if r.err != nil {
 | 
						if r.err != nil {
 | 
				
			||||||
		return r
 | 
							return r
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	params, err := codec.EncodeParameters(obj, *r.content.GroupVersion)
 | 
						params, err := codec.EncodeParameters(obj, version)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		r.err = err
 | 
							r.err = err
 | 
				
			||||||
		return r
 | 
							return r
 | 
				
			||||||
@@ -485,6 +489,19 @@ func (r *Request) tryThrottle() {
 | 
				
			|||||||
// Watch attempts to begin watching the requested location.
 | 
					// Watch attempts to begin watching the requested location.
 | 
				
			||||||
// Returns a watch.Interface, or an error.
 | 
					// Returns a watch.Interface, or an error.
 | 
				
			||||||
func (r *Request) Watch() (watch.Interface, error) {
 | 
					func (r *Request) Watch() (watch.Interface, error) {
 | 
				
			||||||
 | 
						return r.WatchWithSpecificDecoders(
 | 
				
			||||||
 | 
							func(body io.ReadCloser) streaming.Decoder {
 | 
				
			||||||
 | 
								framer := r.serializers.Framer.NewFrameReader(body)
 | 
				
			||||||
 | 
								return streaming.NewDecoder(framer, r.serializers.StreamingSerializer)
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							r.serializers.Decoder,
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// WatchWithSpecificDecoders attempts to begin watching the requested location with a *different* decoder.
 | 
				
			||||||
 | 
					// Turns out that you want one "standard" decoder for the watch event and one "personal" decoder for the content
 | 
				
			||||||
 | 
					// Returns a watch.Interface, or an error.
 | 
				
			||||||
 | 
					func (r *Request) WatchWithSpecificDecoders(wrapperDecoderFn func(io.ReadCloser) streaming.Decoder, embeddedDecoder runtime.Decoder) (watch.Interface, error) {
 | 
				
			||||||
	// We specifically don't want to rate limit watches, so we
 | 
						// We specifically don't want to rate limit watches, so we
 | 
				
			||||||
	// don't use r.throttle here.
 | 
						// don't use r.throttle here.
 | 
				
			||||||
	if r.err != nil {
 | 
						if r.err != nil {
 | 
				
			||||||
@@ -532,9 +549,8 @@ func (r *Request) Watch() (watch.Interface, error) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		return nil, fmt.Errorf("for request '%+v', got status: %v", url, resp.StatusCode)
 | 
							return nil, fmt.Errorf("for request '%+v', got status: %v", url, resp.StatusCode)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	framer := r.serializers.Framer.NewFrameReader(resp.Body)
 | 
						wrapperDecoder := wrapperDecoderFn(resp.Body)
 | 
				
			||||||
	decoder := streaming.NewDecoder(framer, r.serializers.StreamingSerializer)
 | 
						return watch.NewStreamWatcher(restclientwatch.NewDecoder(wrapperDecoder, embeddedDecoder)), nil
 | 
				
			||||||
	return watch.NewStreamWatcher(restclientwatch.NewDecoder(decoder, r.serializers.Decoder)), nil
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// updateURLMetrics is a convenience function for pushing metrics.
 | 
					// updateURLMetrics is a convenience function for pushing metrics.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,7 +23,6 @@ go_library(
 | 
				
			|||||||
        "//cmd/kube-apiserver/app:go_default_library",
 | 
					        "//cmd/kube-apiserver/app:go_default_library",
 | 
				
			||||||
        "//cmd/kube-apiserver/app/options:go_default_library",
 | 
					        "//cmd/kube-apiserver/app/options:go_default_library",
 | 
				
			||||||
        "//cmd/kubelet/app/options:go_default_library",
 | 
					        "//cmd/kubelet/app/options:go_default_library",
 | 
				
			||||||
        "//pkg/api/legacyscheme:go_default_library",
 | 
					 | 
				
			||||||
        "//pkg/controller/namespace:go_default_library",
 | 
					        "//pkg/controller/namespace:go_default_library",
 | 
				
			||||||
        "//pkg/features:go_default_library",
 | 
					        "//pkg/features:go_default_library",
 | 
				
			||||||
        "//pkg/kubelet/apis/kubeletconfig:go_default_library",
 | 
					        "//pkg/kubelet/apis/kubeletconfig:go_default_library",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,7 +24,6 @@ import (
 | 
				
			|||||||
	"k8s.io/client-go/informers"
 | 
						"k8s.io/client-go/informers"
 | 
				
			||||||
	clientset "k8s.io/client-go/kubernetes"
 | 
						clientset "k8s.io/client-go/kubernetes"
 | 
				
			||||||
	restclient "k8s.io/client-go/rest"
 | 
						restclient "k8s.io/client-go/rest"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/api/legacyscheme"
 | 
					 | 
				
			||||||
	namespacecontroller "k8s.io/kubernetes/pkg/controller/namespace"
 | 
						namespacecontroller "k8s.io/kubernetes/pkg/controller/namespace"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -56,12 +55,15 @@ func (n *NamespaceController) Start() error {
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	clientPool := dynamic.NewClientPool(config, legacyscheme.Registry.RESTMapper(), dynamic.LegacyAPIPathResolverFunc)
 | 
						dynamicClient, err := dynamic.NewForConfig(config)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	discoverResourcesFn := client.Discovery().ServerPreferredNamespacedResources
 | 
						discoverResourcesFn := client.Discovery().ServerPreferredNamespacedResources
 | 
				
			||||||
	informerFactory := informers.NewSharedInformerFactory(client, ncResyncPeriod)
 | 
						informerFactory := informers.NewSharedInformerFactory(client, ncResyncPeriod)
 | 
				
			||||||
	nc := namespacecontroller.NewNamespaceController(
 | 
						nc := namespacecontroller.NewNamespaceController(
 | 
				
			||||||
		client,
 | 
							client,
 | 
				
			||||||
		clientPool,
 | 
							dynamicClient,
 | 
				
			||||||
		discoverResourcesFn,
 | 
							discoverResourcesFn,
 | 
				
			||||||
		informerFactory.Core().V1().Namespaces(),
 | 
							informerFactory.Core().V1().Namespaces(),
 | 
				
			||||||
		ncResyncPeriod, v1.FinalizerKubernetes,
 | 
							ncResyncPeriod, v1.FinalizerKubernetes,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -45,7 +45,7 @@ func TestDynamicClient(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	client := clientset.NewForConfigOrDie(config)
 | 
						client := clientset.NewForConfigOrDie(config)
 | 
				
			||||||
	dynamicClient, err := dynamic.NewClient(config)
 | 
						dynamicClient, err := dynamic.NewClient(config, *gv)
 | 
				
			||||||
	_ = dynamicClient
 | 
						_ = dynamicClient
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatalf("unexpected error creating dynamic client: %v", err)
 | 
							t.Fatalf("unexpected error creating dynamic client: %v", err)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -153,7 +153,7 @@ func TestCRD(t *testing.T) {
 | 
				
			|||||||
	barComConfig := *result.ClientConfig
 | 
						barComConfig := *result.ClientConfig
 | 
				
			||||||
	barComConfig.GroupVersion = &schema.GroupVersion{Group: "cr.bar.com", Version: "v1"}
 | 
						barComConfig.GroupVersion = &schema.GroupVersion{Group: "cr.bar.com", Version: "v1"}
 | 
				
			||||||
	barComConfig.APIPath = "/apis"
 | 
						barComConfig.APIPath = "/apis"
 | 
				
			||||||
	barComClient, err := dynamic.NewClient(&barComConfig)
 | 
						barComClient, err := dynamic.NewClient(&barComConfig, *barComConfig.GroupVersion)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatalf("Unexpected error: %v", err)
 | 
							t.Fatalf("Unexpected error: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user