观察特定对象的事件

huangapple go评论75阅读模式
英文:

Watch event for specific object

问题

我们有一个基于kubebuilder的k8s operator,它按预期工作,现在我们需要支持监听集群中的secrets。

以下代码是有效的,但是我得到了集群中所有secrets的事件,这是不高效的。

我想要只获取特定secret的事件,比如具有特定labels/annotation的secret,我们该如何做到?

func (r *InvReconciler) SetupWithManager(mgr ctrl.Manager) error {
	manager := ctrl.NewControllerManagedBy(mgr).
		For(&corev1alpha1.Inv{}, builder.WithPredicates(predicate.Or(predicate.GenerationChangedPredicate{}, predicate.AnnotationChangedPredicate{}))).
		WithOptions(controller.Options{
		})

	manager = manager.Watches(&source.Kind{Type: &v1.Secret{}}, handler.EnqueueRequestsFromMapFunc(func(a client.Object) []reconcile.Request {
		return r.secretHandler.GetSecret(a.GetNamespace(), a.GetName())
	}))

	return manager.Complete(r)
}

这是函数:

func (secretReq secretHandler) GetSecret(namespace string, name string) []reconcile.Request {

	fmt.Println("secret is: ", namespace, "--", name)
	return nil
}

假设有以下的secret,只有对于这个具有foo: bar标签的secret,当它被创建或修改时,我才会收到事件:

apiVersion: v1
kind: Secret
metadata:
  labels:
    foo: bar
  name: mysecret
  namespace: dev
type: Opaque
data:
  USER_NAME: YWRtaW4=
  PASSWORD: dGVzdBo=

不是在谈论在收到事件后使用if语句,因为它已经将集群中的所有secrets事件都带来了。

英文:

We have an k8s operator (based on kubebuilder) which works as expected, now we need support for listening to secrets on the cluster.

The following code is working however I got event for all the secrets in the cluster which is not efficient,

I WANT to get the event only for specific secret, lets say secret with specific labels/annotation, how we can do it?

func (r *InvReconciler) SetupWithManager(mgr ctrl.Manager) error {
	manager := ctrl.NewControllerManagedBy(mgr).
		For(&corev1alpha1.Inv{}, builder.WithPredicates(predicate.Or(predicate.GenerationChangedPredicate{}, predicate.AnnotationChangedPredicate{}))).
		WithOptions(controller.Options{
		})

	manager = manager.Watches(&source.Kind{Type: &v1.Secret{}}, handler.EnqueueRequestsFromMapFunc(func(a client.Object) []reconcile.Request {
		return r.secretHandler.GetSecret(a.GetNamespace(), a.GetName())
	}))

	return manager.Complete(r)
}

this is the function

func (secretReq secretHandler) GetSecret(namespace string, name string) []reconcile.Request {

	fmt.Println("secret is: ", namespace, "--", name)
	return nil
}

lets say secret like the following and only for this secret (with labels foo: bar )i'll get the event when it was created or modified

apiVersion: v1
kind: Secret
metadata:
  labels:
    foo: bar
  name: mysecret
  namespace: dev
type: Opaque
data:
  USER_NAME: YWRtaW4=
  PASSWORD: dGVzdBo=

Im not talking about an if statement after I got the event as it already bring all the secrets event in the cluster.

答案1

得分: 3

根据这个github的来源,你应该能够使用EnqueueRequestForObject选择特定的对象(例如,secrets)。然而,目前还不能仅监视特定的secret CRUD更改。

> 使用EnqueueRequestForObject来监视你的CRD资源更改。在你的CRD reconciler中,你可以使用基于搜索定义的标签选择器来获取所有的TLS secrets,然后运行合并逻辑与匹配的secrets。
>
> 使用EnqueueRequestFromMapFunc来监视secret的更改并触发一个或多个CR的reconcile。在你的mapper函数中,你可以获取所有的CRs。对于每个具有与传入的secret匹配的搜索定义的CR,你将为该CR创建一个新的reconcile.Request,并返回请求列表,这将触发每个匹配的CR的CRD reconciler。

最干净的方法是使用标签选择器,然后将结果与现有代码合并。在这个帖子中提供了使用标签选择器的示例:

func GetSecret(version string) (retVal interface{}, err error){
  clientset := GetClientOutOfCluster()
  labelSelector := metav1.LabelSelector{MatchLabels: map[string]string{"version":version}}

  listOptions := metav1.ListOptions{
    LabelSelector: labels.Set(labelSelector.MatchLabels).String(),
    Limit:         100,
  }
  secretList, err := clientset.CoreV1().Secrets("namespace").List(listOptions)
  retVal = secretList.Items[0]
  return retVal, err
}
英文:

According to this github source, you should be able to select specific objects (e.g. secrets) with EnqueueRequestForObject. However, it is not possible (yet) to watch only for specific secret CRUD changes.

> EnqueueRequestForObject to watch for your CRD resource changes. In
> your CRD reconciler, you'd fetch all of the TLS secrets using a label
> selector based on the search definition and then run your merge logic
> with the matched secrets.
>
> EnqueueRequestFromMapFunc to watch for
> secret changes and trigger a reconcile of one or more CRs. In your
> mapper function, you'd fetch all of the CRs. For each CR that has a
> search definition that matches the passed in secret, you'd create a
> new reconcile.Request for the CR, and return the list of requests,
> which would trigger your CRD reconciler for each CR that matched.

The cleanest way is using a label selector and then merge the results with your existing code. An example of using a label selector is given in this post:

func GetSecret(version string) (retVal interface{}, err error){
  clientset := GetClientOutOfCluster()
  labelSelector := metav1.LabelSelector{MatchLabels: map[string]string{"version":version}}

  listOptions := metav1.ListOptions{
    LabelSelector: labels.Set(labelSelector.MatchLabels).String(),
    Limit:         100,
  }
  secretList, err := clientset.CoreV1().Secrets("namespace").List(listOptions)
  retVal = secretList.Items[0]
  return retVal, err
}

答案2

得分: 1

这是可能的,但是需要通过一种迂回的方式来实现。您需要将选择器配置到您用于设置调谐器的控制器管理器中。

您可以使用标签选择器或字段选择器来实现。您可以使用DefaultSelector为所有类型的对象设置相同的选择器,或者您可以使用SelectorsByObject为不同类型的对象设置不同的选择器。

import (
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/selection"
	"k8s.io/client-go/rest"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/cache"
	"sigs.k8s.io/controller-runtime/pkg/manager"
)

func startReconciler(cnf *rest.Config) error {
	mgr, err := ctrl.NewManager(cnf, manager.Options{
		NewCache: func(conf *rest.Config, opts cache.Options) (cache.Cache, error) {
			// 对于未在SelectorsByObject中提及的所有内容,使用此选择器
			opts.DefaultSelector = cache.ObjectSelector{
				Label: labels.SelectorFromSet(labels.Set{"foo": "bar"}),
			}
			// 每种对象类型的特定选择器
			opts.SelectorsByObject[&corev1.Secret{}] = cache.ObjectSelector{
				Label: labels.SelectorFromSet(labels.Set{"foo": "bar"}),
			}
			return cache.New(conf, opts)
		},
	})
	if err != nil {
		return err
	}
	r := &InvReconciler{}
	if err := r.SetupWithManager(mgr); err != nil {
		return err
	}
}

希望对您有所帮助!

英文:

This is possible, in a roundabout way. You need to configure the selector into the controller Manager that you use to set up your reconciler.

You can use label or field selectors for this.
You can either set the same selector for all types of objects using DefaultSelector, or you can use SelectorsByObject to have different selectors for different types of objects.

import (
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/selection"
	"k8s.io/client-go/rest"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/cache"
	"sigs.k8s.io/controller-runtime/pkg/manager"
)

func startReconciler(cnf *rest.Config) error {
	mgr, err := ctrl.NewManager(cnf, manager.Options{
		NewCache: func(conf *rest.Config, opts cache.Options) (cache.Cache, error) {
            // Use this selector for everything that is not mentioned in SelectorsByObject
			opts.DefaultSelector = cache.ObjectSelector{
				Label: labels.SelectorFromSet(labels.Set{"foo": "bar"}),
			}
            // Specific selectors per type of object
			opts.SelectorsByObject[&corev1.Secret{}] = cache.ObjectSelector{
				Label: labels.SelectorFromSet(labels.Set{"foo": "bar"}),
			}
			return cache.New(conf, opts)
		},
	})
	if err != nil {
		return err
	}
	r := &InvReconciler{}
	if err := r.SetupWithManager(mgr); err != nil {
		return err
	}

huangapple
  • 本文由 发表于 2022年9月28日 23:30:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/73884118.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定