client-go之dynamic包(下篇)源码分析
@TOC
dynamic包
用于动态生成informer/lister/client等
dynamiclister包
- interface.go 定义了获取(从索引器(缓存)中get/list)obj的接口
- 接口
// Lister 获取资源和获取NamespaceLister。 type Lister interface { // List 列出索引器(缓存)中的所有资源。 List(selector labels.Selector) (ret []*unstructured.Unstructured, err error) // Get 从索引器(缓存)中检索具有给定名称的资源 Get(name string) (*unstructured.Unstructured, error) // Namespace 返回一个对象,该对象可以列出和获取给定命名空间中的资源。 Namespace(namespace string) NamespaceLister } // NamespaceLister 获取命名空间下的资源。类似于controller-runtime分析client包的Reader接口 type NamespaceLister interface { // List 列出索引器(缓存)中给定命名空间的所有资源。 List(selector labels.Selector) (ret []*unstructured.Unstructured, err error) // Get 从索引器(缓存)中检索给定命名空间和名称的资源。 Get(name string) (*unstructured.Unstructured, error) }
- 接口
- lister.go
- 结构体
// dynamicLister 实现了 Lister 接口。 type dynamicLister struct { // 索引器(缓存) indexer cache.Indexer // 索引器对应的资源gvr,该索引器只存储该gvr对应的资源 gvr schema.GroupVersionResource } // List 列出索引器中的所有资源。 func (l *dynamicLister) List(selector labels.Selector) (ret []*unstructured.Unstructured, err error) { // 该方法到对应包在做具体分析,用来获取符合selector对应添加的item err = cache.ListAll(l.indexer, selector, func(m interface{}) { // 符合添加的item会追加到ret ret = append(ret, m.(*unstructured.Unstructured)) }) return ret, err } // Get 从索引器中检索具有给定名称的资源 func (l *dynamicLister) Get(name string) (*unstructured.Unstructured, error) { // 没有使用索引,这里为什么没有判断是否是l中对应的gvr? 因为在add到indexer时已经做了区分,不同的gvr在不同的indexer中 obj, exists, err := l.indexer.GetByKey(name) if err != nil { return nil, err } if !exists { return nil, errors.NewNotFound(l.gvr.GroupResource(), name) } return obj.(*unstructured.Unstructured), nil } // Namespace 返回一个对象,该对象可以从给定的命名空间中列出和获取资源. func (l *dynamicLister) Namespace(namespace string) NamespaceLister { return &dynamicNamespaceLister{indexer: l.indexer, namespace: namespace, gvr: l.gvr} } // dynamicNamespaceLister 实现了 NamespaceLister 接口。相比dynamicLister多了namespace属性,用来限定namespace type dynamicNamespaceLister struct { // 索引器(缓存) indexer cache.Indexer // 命名空间 namespace string // 索引器对应的资源gvr,该索引器只存储该gvr对应的资源 gvr schema.GroupVersionResource } // List 列出索引器中给定命名空间的所有资源。 func (l *dynamicNamespaceLister) List(selector labels.Selector) (ret []*unstructured.Unstructured, err error) { // 该方法到对应包在做具体分析,用来获取符合selector和namespace对应添加的item err = cache.ListAllByNamespace(l.indexer, l.namespace, selector, func(m interface{}) { ret = append(ret, m.(*unstructured.Unstructured)) }) return ret, err } // Get 从索引器中检索给定命名空间和名称的资源。 func (l *dynamicNamespaceLister) Get(name string) (*unstructured.Unstructured, error) { // 注意: 这里可以看到indexer中items的存放,当namespace不为空时,key是${namespace}/${name} obj, exists, err := l.indexer.GetByKey(l.namespace + "/" + name) if err != nil { return nil, err } if !exists { return nil, errors.NewNotFound(l.gvr.GroupResource(), name) } return obj.(*unstructured.Unstructured), nil }
- 函数
// New 返回一个新的 Lister. func New(indexer cache.Indexer, gvr schema.GroupVersionResource) Lister { return &dynamicLister{indexer: indexer, gvr: gvr} }
- 结构体
- shim.go
- 结构体
// dynamicListerShim 实现了 cache.GenericLister(后面章节再详细介绍) 接口。 // 包装了Lister接口,其实没有多少差别,只是List把返回slice中Unstructured对象变为object对象 type dynamicListerShim struct { lister Lister } // List 将返回跨命名空间的所有对象 func (s *dynamicListerShim) List(selector labels.Selector) (ret []runtime.Object, err error) { // objs, err := s.lister.List(selector) if err != nil { return nil, err } // 返回slice中Unstructured对象变为Object对象 ret = make([]runtime.Object, len(objs)) for index, obj := range objs { ret[index] = obj } return ret, err } func (s *dynamicListerShim) Get(name string) (runtime.Object, error) { return s.lister.Get(name) } // 获取限定命名空间的Lister func (s *dynamicListerShim) ByNamespace(namespace string) cache.GenericNamespaceLister { return &dynamicNamespaceListerShim{ namespaceLister: s.lister.Namespace(namespace), } } // dynamicNamespaceListerShim 实现了 NamespaceLister 接口。 // 它包装了 NamespaceLister 以便它实现 cache.GenericNamespaceLister 接口 // 其实没有多少差别,只是List把返回slice中Unstructured对象变为object对象 type dynamicNamespaceListerShim struct { namespaceLister NamespaceLister } // List 将返回此命名空间中的所有对象 func (ns *dynamicNamespaceListerShim) List(selector labels.Selector) (ret []runtime.Object, err error) { objs, err := ns.namespaceLister.List(selector) if err != nil { return nil, err } // 返回slice中Unstructured对象变为Object对象 ret = make([]runtime.Object, len(objs)) for index, obj := range objs { ret[index] = obj } return ret, err } // Get 将尝试按命名空间和名称检索 func (ns *dynamicNamespaceListerShim) Get(name string) (runtime.Object, error) { return ns.namespaceLister.Get(name) }
- 结构体
dynamicinformer包
- interface.go
- 接口 定义了获取Informer的方法,等待缓存同步的方法,启动所有informer的方法
// Dynamic SharedInformerFactory 为动态客户端提供对共享informer和lister的访问 type DynamicSharedInformerFactory interface { // 启动所有informer的方法 Start(stopCh <-chan struct{}) // 获取Informer的方法 ForResource(gvr schema.GroupVersionResource) informers.GenericInformer // 等待缓存同步的方法 WaitForCacheSync(stopCh <-chan struct{}) map[schema.GroupVersionResource]bool } // TweakListOptionsFunc 定义了一个辅助函数的签名,想要为 API 提供更多的列表选项 type TweakListOptionsFunc func(*metav1.ListOptions)
- 接口 定义了获取Informer的方法,等待缓存同步的方法,启动所有informer的方法
- informer.go
- 函数
// NewDynamicSharedInformerFactory 为所有命名空间构造一个 dynamicSharedInformerFactory 的新实例。 func NewDynamicSharedInformerFactory(client dynamic.Interface, defaultResync time.Duration) DynamicSharedInformerFactory { return NewFilteredDynamicSharedInformerFactory(client, defaultResync, metav1.NamespaceAll, nil) } // NewFilteredDynamicSharedInformerFactory 构造了一个 dynamicSharedInformerFactory 的新实例。 // 通过此工厂获得的lister将受到此处指定的相同过滤器的约束。 func NewFilteredDynamicSharedInformerFactory(client dynamic.Interface, defaultResync time.Duration, namespace string, tweakListOptions TweakListOptionsFunc) DynamicSharedInformerFactory { return &dynamicSharedInformerFactory{ client: client, defaultResync: defaultResync, namespace: namespace, informers: map[schema.GroupVersionResource]informers.GenericInformer{}, startedInformers: make(map[schema.GroupVersionResource]bool), tweakListOptions: tweakListOptions, } } // NewFilteredDynamicInformer 为动态类型构造一个新的 Informer。 func NewFilteredDynamicInformer(client dynamic.Interface, gvr schema.GroupVersionResource, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions TweakListOptionsFunc) informers.GenericInformer { return &dynamicInformer{ gvr: gvr, // 创建共享的SharedIndexInformer informer: cache.NewSharedIndexInformer( &cache.ListWatch{ ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { if tweakListOptions != nil { tweakListOptions(&options) } return client.Resource(gvr).Namespace(namespace).List(context.TODO(), options) }, WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { if tweakListOptions != nil { tweakListOptions(&options) } return client.Resource(gvr).Namespace(namespace).Watch(context.TODO(), options) }, }, &unstructured.Unstructured{}, resyncPeriod, indexers, ), } }
- 结构体
type dynamicSharedInformerFactory struct { // 构建ListWatch接口使用,为后来构建reflector,执行listWatch监控api resource提供client client dynamic.Interface // 同步周期,informer同步deltaFIFO中数据到listener中的chan中 defaultResync time.Duration // 命名空间 namespace string lock sync.Mutex // 缓存informer到map中 informers map[schema.GroupVersionResource]informers.GenericInformer // startInformers 用于跟踪哪些 Informers 已启动。这允许安全地多次调用 Start()。 startedInformers map[schema.GroupVersionResource]bool tweakListOptions TweakListOptionsFunc } // 实现DynamicSharedInformerFactory的ForResource方法 func (f *dynamicSharedInformerFactory) ForResource(gvr schema.GroupVersionResource) informers.GenericInformer { f.lock.Lock() defer f.lock.Unlock() key := gvr // 获取缓存map中的informer informer, exists := f.informers[key] if exists { return informer } // 不存在就创建 informer = NewFilteredDynamicInformer(f.client, gvr, f.namespace, f.defaultResync, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) f.informers[key] = informer return informer } // 实现SharedInformerFactory的Start方法,启动所有未启动的informer。 func (f *dynamicSharedInformerFactory) Start(stopCh <-chan struct{}) { f.lock.Lock() defer f.lock.Unlock() // 遍历所有informer for informerType, informer := range f.informers { // 判断该informer是否已经启动 if !f.startedInformers[informerType] { // 启动informer go informer.Informer().Run(stopCh) // 设置对应gvr的informer已经启动 f.startedInformers[informerType] = true } } } // 实现SharedInformerFactory的WaitForCacheSync方法,等待所有启动的informer的缓存同步。 func (f *dynamicSharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[schema.GroupVersionResource]bool { informers := func() map[schema.GroupVersionResource]cache.SharedIndexInformer { f.lock.Lock() defer f.lock.Unlock() // 定义map,用于接收所有已经启动的informer informers := map[schema.GroupVersionResource]cache.SharedIndexInformer{} for informerType, informer := range f.informers { if f.startedInformers[informerType] { informers[informerType] = informer.Informer() } } return informers }() // 定义map,用于接收所有同步完成的informer res := map[schema.GroupVersionResource]bool{} // 遍历已经启动的所有informer for informType, informer := range informers { // 执行同步方法 // (1) 如果informer中controller为空,返回false, // (2) 如果informer.controller的queue还没有调用过Add/Update/Delete/AddIfNotPresent或者queue的initialPopulationCount != 0 (队列中还有数据),返回false res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced) } return res } // 动态Informer结构体,包装了SharedIndexInformer和gvr type dynamicInformer struct { informer cache.SharedIndexInformer gvr schema.GroupVersionResource } // 实现GenericInformer的Informer方法 func (d *dynamicInformer) Informer() cache.SharedIndexInformer { return d.informer } // 实现GenericInformer的Lister方法,使用dynamicInformer的indexer和gvr构造以Lister func (d *dynamicInformer) Lister() cache.GenericLister { return dynamiclister.NewRuntimeObjectShim(dynamiclister.New(d.informer.GetIndexer(), d.gvr)) }
- 函数