// listCollection will list the items in the specified namespace // it returns the following: // the list of items in the collection (if found) // a boolean if the operation is supported // an error if the operation is supported but could not be completed. func listCollection( dynamicClient *dynamic.Client, opCache operationNotSupportedCache, gvr unversioned.GroupVersionResource, namespace string, ) (*runtime.UnstructuredList, bool, error) { glog.V(5).Infof("namespace controller - listCollection - namespace: %s, gvr: %v", namespace, gvr) key := operationKey{op: operationList, gvr: gvr} if !opCache.isSupported(key) { glog.V(5).Infof("namespace controller - listCollection ignored since not supported - namespace: %s, gvr: %v", namespace, gvr) return nil, false, nil } apiResource := unversioned.APIResource{Name: gvr.Resource, Namespaced: true} unstructuredList, err := dynamicClient.Resource(&apiResource, namespace).List(v1.ListOptions{}) if err == nil { return unstructuredList, true, nil } // this is strange, but we need to special case for both MethodNotSupported and NotFound errors // TODO: https://github.com/kubernetes/kubernetes/issues/22413 // we have a resource returned in the discovery API that supports no top-level verbs: // /apis/extensions/v1beta1/namespaces/default/replicationcontrollers // when working with this resource type, we will get a literal not found error rather than expected method not supported // remember next time that this resource does not support delete collection... if errors.IsMethodNotSupported(err) || errors.IsNotFound(err) { glog.V(5).Infof("namespace controller - listCollection not supported - namespace: %s, gvr: %v", namespace, gvr) opCache[key] = true return nil, false, nil } return nil, true, err }
// TODO: make special List and Watch function that removes fields other than // ObjectMeta. func gcListWatcher(client *dynamic.Client, resource unversioned.GroupVersionResource) *cache.ListWatch { return &cache.ListWatch{ ListFunc: func(options api.ListOptions) (runtime.Object, error) { // APIResource.Kind is not used by the dynamic client, so // leave it empty. We want to list this resource in all // namespaces if it's namespace scoped, so leave // APIResource.Namespaced as false is all right. apiResource := unversioned.APIResource{Name: resource.Resource} // The default parameter codec used by the dynamic client cannot // encode api.ListOptions. // TODO: api.ParameterCodec doesn't support thirdparty objects. // We need a generic parameter codec. return client.ParameterCodec(api.ParameterCodec).Resource(&apiResource, api.NamespaceAll).List(&options) }, WatchFunc: func(options api.ListOptions) (watch.Interface, error) { // APIResource.Kind is not used by the dynamic client, so // leave it empty. We want to list this resource in all // namespaces if it's namespace scoped, so leave // APIResource.Namespaced as false is all right. apiResource := unversioned.APIResource{Name: resource.Resource} // The default parameter codec used by the dynamic client cannot // encode api.ListOptions. return client.ParameterCodec(api.ParameterCodec).Resource(&apiResource, api.NamespaceAll).Watch(&options) }, } }
func (r *RegisteredRateLimiter) registerIfNotPresent(gv unversioned.GroupVersion, client *dynamic.Client, prefix string) { once, found := r.rateLimiters[gv] if !found { return } once.Do(func() { if rateLimiter := client.GetRateLimiter(); rateLimiter != nil { group := strings.Replace(gv.Group, ".", ":", -1) metrics.RegisterMetricAndTrackRateLimiterUsage(fmt.Sprintf("%s_%s_%s", prefix, group, gv.Version), rateLimiter) } }) }
func (r *RegisteredRateLimiter) registerIfNotPresent(gv unversioned.GroupVersion, client *dynamic.Client, prefix string) { r.lock.RLock() _, ok := r.rateLimiters[gv] r.lock.RUnlock() if ok { return } r.lock.Lock() defer r.lock.Unlock() if _, ok := r.rateLimiters[gv]; !ok { if rateLimiter := client.GetRateLimiter(); rateLimiter != nil { group := strings.Replace(gv.Group, ".", ":", -1) metrics.RegisterMetricAndTrackRateLimiterUsage(fmt.Sprintf("%s_%s_%s", prefix, group, gv.Version), rateLimiter) } r.rateLimiters[gv] = struct{}{} } }
// 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 an error if the operation was supported on the server but was unable to complete. func deleteCollection( dynamicClient *dynamic.Client, opCache operationNotSupportedCache, gvr unversioned.GroupVersionResource, namespace string, ) (bool, error) { glog.V(5).Infof("namespace controller - deleteCollection - namespace: %s, gvr: %v", namespace, gvr) key := operationKey{op: operationDeleteCollection, gvr: gvr} if !opCache.isSupported(key) { glog.V(5).Infof("namespace controller - deleteCollection ignored since not supported - namespace: %s, gvr: %v", namespace, gvr) return false, nil } apiResource := unversioned.APIResource{Name: gvr.Resource, Namespaced: true} // 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 // namespace itself. orphanDependents := false err := dynamicClient.Resource(&apiResource, namespace).DeleteCollection(&v1.DeleteOptions{OrphanDependents: &orphanDependents}, &v1.ListOptions{}) if err == nil { return true, nil } // this is strange, but we need to special case for both MethodNotSupported and NotFound errors // TODO: https://github.com/kubernetes/kubernetes/issues/22413 // we have a resource returned in the discovery API that supports no top-level verbs: // /apis/extensions/v1beta1/namespaces/default/replicationcontrollers // when working with this resource type, we will get a literal not found error rather than expected method not supported // remember next time that this resource does not support delete collection... if errors.IsMethodNotSupported(err) || errors.IsNotFound(err) { glog.V(5).Infof("namespace controller - deleteCollection not supported - namespace: %s, gvr: %v", namespace, gvr) opCache[key] = true return false, nil } glog.V(5).Infof("namespace controller - deleteCollection unexpected error - namespace: %s, gvr: %v, error: %v", namespace, gvr, err) return true, err }
// deleteEachItem is a helper function that will list the collection of resources and delete each item 1 by 1. func deleteEachItem( dynamicClient *dynamic.Client, opCache operationNotSupportedCache, gvr unversioned.GroupVersionResource, namespace string, ) error { glog.V(5).Infof("namespace controller - deleteEachItem - namespace: %s, gvr: %v", namespace, gvr) unstructuredList, listSupported, err := listCollection(dynamicClient, opCache, gvr, namespace) if err != nil { return err } if !listSupported { return nil } apiResource := unversioned.APIResource{Name: gvr.Resource, Namespaced: true} for _, item := range unstructuredList.Items { if err = dynamicClient.Resource(&apiResource, namespace).Delete(item.Name, nil); err != nil && !errors.IsNotFound(err) && !errors.IsMethodNotSupported(err) { return err } } return nil }
func gcListWatcher(client *dynamic.Client, resource unversioned.GroupVersionResource) *cache.ListWatch { return &cache.ListWatch{ ListFunc: func(options api.ListOptions) (runtime.Object, error) { // APIResource.Kind is not used by the dynamic client, so // leave it empty. We want to list this resource in all // namespaces if it's namespace scoped, so leave // APIResource.Namespaced as false is all right. apiResource := unversioned.APIResource{Name: resource.Resource} return client.ParameterCodec(dynamic.VersionedParameterEncoderWithV1Fallback). Resource(&apiResource, api.NamespaceAll). List(&options) }, WatchFunc: func(options api.ListOptions) (watch.Interface, error) { // APIResource.Kind is not used by the dynamic client, so // leave it empty. We want to list this resource in all // namespaces if it's namespace scoped, so leave // APIResource.Namespaced as false is all right. apiResource := unversioned.APIResource{Name: resource.Resource} return client.ParameterCodec(dynamic.VersionedParameterEncoderWithV1Fallback). Resource(&apiResource, api.NamespaceAll). Watch(&options) }, } }