k8s controller watch other controller CR

我有 k8s 操作员,它按预期工作,我需要向其他操作员 CRD(不是我的)添加一个“手表”,为了简单起见,让我们调用它,extCR我们的操作员 cr 调用inCR,


我尝试了以下操作,但有一个问题是它如何触发协调。


func (r *Insiconciler) SetupWithManager(mgr ctrl.Manager) error {

                return ctrl.NewControllerManagedBy(mgr).

                    For(&Inv1alpha1.Iget{}}).

                    Watches(&source.Kind{Type: &ext.Se{}},  handler.EnqueueRequestsFromMapFunc(r.FWatch)).

                    Complete(r)

}

    

func (r *Insiconciler) FWatch(c client.Object) []reconcile.Request {

        val := c.(*ivi.Srv)

        req := reconcile.Request{NamespacedName: types.NamespacedName{Name: val.Name, Namespace: val.Namespace}}

        return []reconcile.Request{req}

}

这里的问题是我触发了协调extCR,我想在里面FWatch更新inCR并开始协调 inCR 而不是 extCR,我该怎么做?


我的意思是,为了避免像下面的代码这样的事情,因为有时协调是为 the 完成的inCR,有时是为 theextCR而不是我可以得到一些丑陋的 if's


func (r *Insiconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {

        var inCR FOO

        var extCR BAR

    

        if err := r.Get(ctx, req.NamespacedName, &inCR); err != nil {

            return ctrl.Result{}, err

        }

        

        if err := r.Get(ctx, req.NamespacedName, &extCR); err != nil {

            return ctrl.Result{}, err

        }

我想知道处理这种情况的正确/干净的方法是什么


当您需要收听 externalCR(不是您的控制器的一部分)和 internalCR(来自您的控制器)时的情况。


还有一件事——CR 是不同的 GVK,但是 exteranlCR 包含很多不需要的字段,只是其中的一些。但必填字段 在两个 cr 上具有相同的名称


更新


type inCR struct {

    metav1.TypeMeta   `json:",inline"`

    metav1.ObjectMeta `json:"metadata,omitempty"`


    Spec   inSpec  `json:"spec,omitempty"`  / / ————————here is the difference 

    Status InsightTargetStatus `json:"status,omitempty"`

}

//—————— 这是在不属于我们的其他程序上定义的,因此不能“重用”


type Bar struct {

    metav1.TypeMeta   `json:",inline"`

    metav1.ObjectMeta `json:"metadata,omitempty"`


    Spec   extSpec  `json:"spec,omitempty"`    // ———————here is the difference 

    Status ServiceStatus `json:"status,omitempty"`

}

并且inSpec具有以下字段(extSpec 的子集)


type inSpec struct {

    name string

    age  int

}



慕侠2389804
浏览 127回答 1
1回答

HUWWW

请记住以下几点:每个控制器只负责一个资源。协调请求包含协调 Kubernetes 对象所需的信息。这包括唯一标识对象的信息 - 它的名称和命名空间。它不包含有关任何特定事件或对象内容本身的信息。您可以在没有资源定义的情况下创建第二个控制器。在您的主文件中,两个控制器都将被注册。如果 CRD 根本不相关,或者如果外部资源引用内部资源,这可能很有用,因此您可以在外部协调器中更改内部资源。kubebuilder create api --group other --version v2 --kind External \ --resource=false --controller=true这为您提供了一个控制器,其SetupWithManager方法如下所示。func (r *ExternalReconciler) SetupWithManager(mgr ctrl.Manager) error {    return ctrl.NewControllerManagedBy(mgr).        // Uncomment the following line adding a pointer to an instance of the controlled resource as an argument        // For().        Complete(r)}请注意 For 方法是如何被注释掉的,因为您需要导入资源以从其他地方观看并引用它。import (    ...    otherv2 "other.io/external/api/v2")...func (r *ExternalReconciler) SetupWithManager(mgr ctrl.Manager) error {    return ctrl.NewControllerManagedBy(mgr).        For(&otherv2.External{}).        Complete(r)}如果您无法导入外部资源,您可以回过头来自己模拟它,但这可能不是一种非常干净的方法。您真的应该尝试从其他控制器项目中导入它。kubebuilder edit --multigroup=truekubebuilder create api --group=other --version v2 --kind External \  --resource --controller另一种方式是当资源相互关联时,内部资源在其规范中引用了外部资源,并且知道如何在其协调时获取其规范中的外部资源。可以在这里找到一个例子https://book.kubebuilder.io/reference/watching-resources/externally-managed.htmltype InternalSpec struct {    // Name of an external resource    ExternalResource string `json:"externalResource,omitempty"`}这意味着在每个协调循环中,控制器将查找外部资源并使用它来管理内部资源。func (r *InternalReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {    _ = log.FromContext(ctx)    internal := examplev1.Internal{}    if err := r.Get(context.TODO(), types.NamespacedName{        Name:      req.Name,        Namespace: req.Namespace,    }, &internal); err != nil {        return ctrl.Result{}, err    }    external := otherv2.External{}    if err := r.Get(context.TODO(), types.NamespacedName{        // note how the name is taken from the internal spec        Name:      internal.Spec.ExternalResource,        Namespace: req.Namespace,    }, &internal); err != nil {        return ctrl.Result{}, err    }    // do something with internal and external here    return ctrl.Result{}, nil}这样做的问题是,当内部资源没有变化时,即使外部资源发生变化,也不会触发协调事件。为了解决这个问题,我们可以通过观察外部资源来触发协调。注意Watches方法:func (r *InternalReconciler) SetupWithManager(mgr ctrl.Manager) error {    return ctrl.NewControllerManagedBy(mgr).        For(&examplev1.Main{}).        Watches(            &source.Kind{Type: &otherv2.ExternalResource{}},            handler.EnqueueRequestsFromMapFunc(r.triggerReconcileBecauseExternalHasChanged),            builder.WithPredicates(predicate.ResourceVersionChangedPredicate{}),        ).        Complete(r)}为了知道我们应该为哪个内部对象触发事件,我们使用映射函数来查找所有引用外部资源的内部对象。func (r *InternalReconciler) triggerReconcileBecauseExternalHasChanged(o client.Object) []reconcile.Request {    usedByInternals := &examplev1.InternalList{}    listOps := &client.ListOptions{        FieldSelector: fields.OneTermEqualSelector(".spec.ExternalResource", o.GetName()),        Namespace:     o.GetNamespace(),    }    err := r.List(context.TODO(), usedByInternals, listOps)    if err != nil {        return []reconcile.Request{}    }    requests := make([]reconcile.Request, len(usedByInternals.Items))    for i, item := range usedByInternals.Items {        requests[i] = reconcile.Request{            NamespacedName: types.NamespacedName{                Name:      item.GetName(),                Namespace: item.GetNamespace(),            },        }    }    return requests}由于您更新了问题,我建议您执行以下操作。我正在创建一个新项目和 2 个控制器。注意第二个控制器命令没有资源与控制器一起创建。这是因为控制器将监视外部资源。mkdir demo && cd demogo mod init example.io/demokubebuilder init --domain example.io --repo example.io/demo --plugins=go/v4-alphakubebuilder create api --group=demo --version v1 --kind Internal --controller --resourcekubebuilder create api --group=other --version v2 --kind External --controller --resource=false$ tree controllerscontrollers├── external_controller.go├── internal_controller.go└── suite_test.go现在我们需要一些共享逻辑,例如将其添加到控制器包中。我们将从两个调解器中调用它。// the interface may need tweaking// depending on what you want to do with// the reconilertype reconciler interface { client.Reader client.Writer client.StatusClient}func sharedLogic(r reconciler, kobj *demov1.Internal) (ctrl.Result, error) { // do your shared logic here operating on the internal object struct // this works out because the external controller will call this passing the // internal object return ctrl.Result{}, nil}这是内部协调器的示例。func (r *InternalReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { _ = log.FromContext(ctx) obj := demov1.Internal{} if err := r.Get(ctx, req.NamespacedName, &obj); err != nil {  return ctrl.Result{}, err } return sharedLogic(r, &obj)}在外部协调器中,我们也这样做。func (r *ExternalReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { _ = log.FromContext(ctx) // note, we can use the internal object here as long as the external object // does contain the same fields we want. That means when unmarshalling the extra // fields are dropped. If this cannot be done, you could first unmarshal into the external // resource and then assign the fields you need to the internal one, before passing it down obj := demov1.Internal{} if err := r.Get(ctx, req.NamespacedName, &obj); err != nil {  return ctrl.Result{}, err } return sharedLogic(r, &obj)}func (r *ExternalReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). // note the external resource is imported from another project // you may be able to watch this without import by creating a minimal // type with the right GKV  For(otherv2.External{}).  Complete(r)}
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go