如何让GrandPa控制器的Reconcile()在GrandSon对象变更时触发?
实现GrandPa控制器监听GrandSon对象变更的方案
因为.Owns()只能监听控制器直接拥有的资源,而GrandSon属于Son的子资源,没法通过链式所有权自动触发GrandPa的Reconcile。但可以通过直接监听GrandSon资源事件+反向追溯所有权链的方式实现需求,具体步骤如下:
1. 给GrandPa控制器添加GrandSon资源监听
在控制器的SetupWithManager方法里,除了原本的For(&v1.GrandPa{}),额外用Watches()方法注册对GrandSon的监听,同时指定自定义的所有者解析器:
import ( mygroupv1 "your-module/api/v1" // 替换成实际的GrandSon API包路径 "sigs.k8s.io/controller-runtime/pkg/event" "sigs.k8s.io/controller-runtime/pkg/predicate" ) func (r *GrandPaReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&mygroupv1.GrandPa{}). // 注册GrandSon监听,用自定义解析器找对应的GrandPa Watches( &source.Kind{Type: &mygroupv1.GrandSon{}}, &handler.EnqueueRequestForOwner{ IsController: false, // GrandSon的直接所有者是Son,不是GrandPa OwnerType: &mygroupv1.GrandPa{}, OwnerResolver: &grandSonToGrandPaResolver{Client: r.Client}, }, // 可选:过滤事件类型,只处理需要的变更 predicate.Funcs{ CreateFunc: func(e event.CreateEvent) bool { return true }, UpdateFunc: func(e event.UpdateEvent) bool { return true }, DeleteFunc: func(e event.DeleteEvent) bool { return true }, GenericFunc: func(e event.GenericEvent) bool { return false }, }, ). Complete(r) }
2. 实现自定义所有者解析器
这个解析器的作用是从GrandSon对象出发,先找到它的父资源Son,再从Son找到对应的GrandPa,最后生成GrandPa的Reconcile请求:
import ( "context" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/reconcile" ) type grandSonToGrandPaResolver struct { client.Client // 注入客户端,用来查询Son对象 } func (r *grandSonToGrandPaResolver) GetOwner(obj client.Object) []reconcile.Request { grandSon := obj.(*mygroupv1.GrandSon) var requests []reconcile.Request // 第一步:从GrandSon的OwnerReferences里找到对应的Son var sonRef metav1.OwnerReference for _, ref := range grandSon.OwnerReferences { if ref.Kind == "Son" && ref.Controller != nil && *ref.Controller { sonRef = ref break } } if sonRef.Name == "" { return requests // 找不到Son,直接返回空请求 } // 第二步:查询Son对象 son := &mygroupv1.Son{} err := r.Client.Get(context.TODO(), types.NamespacedName{ Namespace: grandSon.Namespace, Name: sonRef.Name, }, son) if err != nil { log.Error(err, "查询Son对象失败", "grandson", grandSon.Name) return requests } // 第三步:从Son的OwnerReferences里找到对应的GrandPa var grandpaRef metav1.OwnerReference for _, ref := range son.OwnerReferences { if ref.Kind == "GrandPa" && ref.Controller != nil && *ref.Controller { grandpaRef = ref break } } if grandpaRef.Name == "" { return requests // 找不到GrandPa,返回空请求 } // 生成GrandPa的Reconcile请求 requests = append(requests, reconcile.Request{ NamespacedName: types.NamespacedName{ Namespace: grandSon.Namespace, Name: grandpaRef.Name, }, }) return requests }
3. 补充控制器的RBAC权限
需要给GrandPa控制器的ClusterRole添加Son和GrandSon的访问权限,否则查询资源会失败:
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: grandpa-controller-role rules: # 原有GrandPa权限保留 - apiGroups: ["mygroup.example.com"] resources: ["grandpas"] verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] # 添加Son的查询权限 - apiGroups: ["mygroup.example.com"] resources: ["sons"] verbs: ["get", "list", "watch"] # 添加GrandSon的监听和查询权限 - apiGroups: ["mygroup.example.com"] resources: ["grandsons"] verbs: ["get", "list", "watch"]
注意事项
- 可以根据业务需求调整事件过滤规则,比如只在GrandSon的特定状态字段变更时触发Reconcile,减少无效执行。
- 要处理资源查询失败的场景,避免因为Son/GrandPa已被删除导致的报错影响控制器运行。
内容的提问来源于stack exchange,提问作者guettli
相关产品推荐
相关产品推荐

