diff --git a/pkg/client/cache/listers.go b/pkg/client/cache/listers.go index 363e04f453..82c61e5fb0 100644 --- a/pkg/client/cache/listers.go +++ b/pkg/client/cache/listers.go @@ -31,6 +31,7 @@ import ( "k8s.io/kubernetes/pkg/apis/policy" "k8s.io/kubernetes/pkg/apis/storage" "k8s.io/kubernetes/pkg/labels" + "k8s.io/kubernetes/pkg/runtime" ) // AppendFunc is used to add a matching item to whatever list the caller is using @@ -92,6 +93,79 @@ func ListAllByNamespace(indexer Indexer, namespace string, selector labels.Selec return nil } +// GenericLister is a lister skin on a generic Indexer +type GenericLister interface { + // List will return all objects across namespaces + List(selector labels.Selector) (ret []runtime.Object, err error) + // Get will attempt to retrieve assuming that name==key + Get(name string) (runtime.Object, error) + // ByNamespace will give you a GenericNamespaceLister for one namespace + ByNamespace(namespace string) GenericNamespaceLister +} + +// GenericNamespaceLister is a lister skin on a generic Indexer +type GenericNamespaceLister interface { + // List will return all objects in this namespace + List(selector labels.Selector) (ret []runtime.Object, err error) + // Get will attempt to retrieve by namespace and name + Get(name string) (runtime.Object, error) +} + +func NewGenericLister(indexer Indexer, resource unversioned.GroupResource) GenericLister { + return &genericLister{indexer: indexer, resource: resource} +} + +type genericLister struct { + indexer Indexer + resource unversioned.GroupResource +} + +func (s *genericLister) List(selector labels.Selector) (ret []runtime.Object, err error) { + err = ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(runtime.Object)) + }) + return ret, err +} + +func (s *genericLister) ByNamespace(namespace string) GenericNamespaceLister { + return &genericNamespaceLister{indexer: s.indexer, namespace: namespace, resource: s.resource} +} + +func (s *genericLister) Get(name string) (runtime.Object, error) { + obj, exists, err := s.indexer.GetByKey(name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(s.resource, name) + } + return obj.(runtime.Object), nil +} + +type genericNamespaceLister struct { + indexer Indexer + namespace string + resource unversioned.GroupResource +} + +func (s *genericNamespaceLister) List(selector labels.Selector) (ret []runtime.Object, err error) { + err = ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(runtime.Object)) + }) + return ret, err +} + +func (s *genericNamespaceLister) Get(name string) (runtime.Object, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(s.resource, name) + } + return obj.(runtime.Object), nil +} + // TODO: generate these classes and methods for all resources of interest using // a script. Can use "go generate" once 1.4 is supported by all users. diff --git a/pkg/controller/informers/factory.go b/pkg/controller/informers/factory.go index 04483aab59..daf4e03ee4 100644 --- a/pkg/controller/informers/factory.go +++ b/pkg/controller/informers/factory.go @@ -21,6 +21,7 @@ import ( "sync" "time" + "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/client/cache" clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" ) @@ -31,9 +32,14 @@ type SharedInformerFactory interface { // Start starts informers that can start AFTER the API server and controllers have started Start(stopCh <-chan struct{}) + ForResource(unversioned.GroupResource) (GenericInformer, error) + + // when you update these, update generic.go/ForResource, same package + Pods() PodInformer - Nodes() NodeInformer + LimitRanges() LimitRangeInformer Namespaces() NamespaceInformer + Nodes() NodeInformer PersistentVolumeClaims() PVCInformer PersistentVolumes() PVInformer ServiceAccounts() ServiceAccountInformer @@ -42,12 +48,10 @@ type SharedInformerFactory interface { Deployments() DeploymentInformer ReplicaSets() ReplicaSetInformer - ClusterRoles() ClusterRoleInformer ClusterRoleBindings() ClusterRoleBindingInformer - Roles() RoleInformer + ClusterRoles() ClusterRoleInformer RoleBindings() RoleBindingInformer - - LimitRanges() LimitRangeInformer + Roles() RoleInformer StorageClasses() StorageClassInformer } diff --git a/pkg/controller/informers/generic.go b/pkg/controller/informers/generic.go new file mode 100644 index 0000000000..fa34fb48f6 --- /dev/null +++ b/pkg/controller/informers/generic.go @@ -0,0 +1,86 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package informers + +import ( + "fmt" + + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/apis/rbac" + "k8s.io/kubernetes/pkg/client/cache" +) + +// GenericInformer is type of SharedIndexInformer which will locate and delegate to other +// sharedInformers based on type +type GenericInformer interface { + Informer() cache.SharedIndexInformer + Lister() cache.GenericLister +} + +// ForResource gives generic access to a shared informer of the matching type +// TODO extend this to unknown resources with a client pool +func (f *sharedInformerFactory) ForResource(resource unversioned.GroupResource) (GenericInformer, error) { + switch resource { + case api.Resource("pods"): + return &genericInformer{resource: resource, informer: f.Pods().Informer()}, nil + case api.Resource("limitranges"): + return &genericInformer{resource: resource, informer: f.LimitRanges().Informer()}, nil + case api.Resource("namespaces"): + return &genericInformer{resource: resource, informer: f.Namespaces().Informer()}, nil + case api.Resource("nodes"): + return &genericInformer{resource: resource, informer: f.Nodes().Informer()}, nil + case api.Resource("persistentvolumeclaims"): + return &genericInformer{resource: resource, informer: f.PersistentVolumeClaims().Informer()}, nil + case api.Resource("persistentvolumes"): + return &genericInformer{resource: resource, informer: f.PersistentVolumes().Informer()}, nil + case api.Resource("serviceaccounts"): + return &genericInformer{resource: resource, informer: f.ServiceAccounts().Informer()}, nil + + case extensions.Resource("daemonsets"): + return &genericInformer{resource: resource, informer: f.DaemonSets().Informer()}, nil + case extensions.Resource("deployments"): + return &genericInformer{resource: resource, informer: f.Deployments().Informer()}, nil + case extensions.Resource("replicasets"): + return &genericInformer{resource: resource, informer: f.ReplicaSets().Informer()}, nil + + case rbac.Resource("clusterrolebindings"): + return &genericInformer{resource: resource, informer: f.ClusterRoleBindings().Informer()}, nil + case rbac.Resource("clusterroles"): + return &genericInformer{resource: resource, informer: f.ClusterRoles().Informer()}, nil + case rbac.Resource("rolebindings"): + return &genericInformer{resource: resource, informer: f.RoleBindings().Informer()}, nil + case rbac.Resource("roles"): + return &genericInformer{resource: resource, informer: f.Roles().Informer()}, nil + } + + return nil, fmt.Errorf("no informer found for %v", resource) +} + +type genericInformer struct { + informer cache.SharedIndexInformer + resource unversioned.GroupResource +} + +func (f *genericInformer) Informer() cache.SharedIndexInformer { + return f.informer +} + +func (f *genericInformer) Lister() cache.GenericLister { + return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource) +}