client-go之dynamic包(下篇)源码分析

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.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))
      }
      
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值