kubebuilder-watches 一种特殊的用法

导读

今天我在将原生的controller-runtime重构成kubebuilder架构时遇到了一个这样的问题。导致我浪费了好多时间,接下来让我们看看是什么问题。

首先,前辈们在控制器的run中写了一个wait.Until事件,作用是每半小时运行一次aa() (方法名我乱写的)方法。然后aa的参数还需要控制器的queue。

这本身没什么问题,但是在将其重构时,发现该方法无法将其加入到架构中,首先kubebuilder将控制器的创建等操作进行了封装,使得我们无法进行二次重构(这时候我突然想如果他是spring的那种模板模式就好了)。

那既然这样可以写在Reconciler方法里面可以嘛?当然不行,因为Reconciler是通过事件调用而不是主动去运行,所以这个方案pass了。

Reconciler不行的话那就只能写在SetupWithManager中。

这时候又又又出来一个问题就是,我们还需要利用控制器里面的事件队列怎么办(SetupWithManager里面本身无法利用队列)

经过我的查找,我发现SetupWithManager里的Watches方法能够解决我的问题

Watches

看过我Controller-runtime 的 informer分析 文章的同学都知道。Watches其实就是将当前资源添加到 blder.watchesInput 而 blder.watchesInput 会在doWatch中被blder.ctrl.Watch调用。

// 这里我只粘贴了一块,因为下面逻辑基本一样,只是监听的资源不一样

func (blder *Builder) doWatch() error {
   // 返回 Reconcile 类型
   typeForSrc, err := blder.project(blder.forInput.object, blder.forInput.objectProjection)
   if err != nil {
      return err
   }
  // 这里注意 因为src是Kind类型,所以在调用src.Start()时是调用的Kind中的方法
   src := &source.Kind{Type: typeForSrc}
   hdler := &handler.EnqueueRequestForObject{}
   allPredicates := append(blder.globalPredicates, blder.forInput.predicates...)
  //开始监听
   if err := blder.ctrl.Watch(src, hdler, allPredicates...); err != nil {
      return err
   }
   
   ***
  }

func (c *Controller) Watch(src source.Source, evthdler handler.EventHandler, prct ...predicate.Predicate)
{
  ***
  // 在doWatch这个环节,!c.Started会变成true,那么会将src进行封装与c.startWatches进行append。资源监控会在manager.start 中进行start
  if !c.Started {
		c.startWatches = append(c.startWatches, watchDescription{src: src, handler: evthdler, predicates: prct})
		return nil
	}
  // kind类型
  return src.Start(c.ctx, evthdler, c.Queue, prct...)
}

而Watch 里的 src 最终会调用Start方法。

首先我们先根据 src = &source.Kind{Type: typeForSrc}对kind中的start进行追踪

func (ks *Kind) Start(ctx context.Context, handler handler.EventHandler, queue workqueue.RateLimitingInterface,
	prct ...predicate.Predicate) error {

	ks.started = make(chan error)
	go func() {
      // 获取资源的informer(也是在这里创建的)
		i, err := ks.cache.GetInformer(ctx, ks.Type)
		
      // 向每个资源的informer中添加监听事件
		i.AddEventHandler(internal.EventHandler{Queue: queue, EventHandler: handler, Predicates: prct})
      // 等待cache同步完成
		if !ks.cache.WaitForCacheSync(ctx) {
			// 
			ks.started <- errors.New("cache did not sync")
		}
		close(ks.started)
	}()

	return nil
}

看到这里我们应该就可以知道了。在kubebuilder中监听资源的操作其实是在src.start中完成的。并且我们还知道src.start只运行一次,并且拥有控制器的queue。 是不是到这里大家已经猜到了我的解决办法了!

解决办法

没错,我自定义了一个src。并实现source.Source 接口中的start

SetupWithManager

// SetupWithManager sets up the controller with the Manager.
func (l *aaa) SetupWithManager(mgr ctrl.Manager, controllerConfig controller.Options, informer v1listers.informer) error {

	return ctrl.NewControllerManagedBy(mgr).Watches(&TimeSource.TimeSource{informer}, nil).
		Complete(l)
}
type TimeSource struct {
	informer v1listers.informer
}

// Start implements Source.
func (l TimeSource) Start(ctx context.Context, evt handler.EventHandler, queue workqueue.RateLimitingInterface,
	pr ...predicate.Predicate) error {

	wait.Until(func() {
		if err := l.sync(queue); err != nil {
			klog.Errorf("Error periodically sync user status, %v", err)
		}
	}, time.Hour, ctx.Done())

	return nil
}

func (l *TimeSource) sync(queue workqueue.RateLimitingInterface) error {
	ins, err := l.informer.List(labels.Everything())
	if err != nil {
		return err
	}

	for _, item := range ins {
		key, err := cache.MetaNamespaceKeyFunc(item)
		if err != nil {
			return err
		}
		queue.AddRateLimited(key)
	}
	return nil
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值