k8s控制器运行时在更新之前读取

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

k8s controller runtime read before update

问题

我正在使用以下代码在kubebuilder控制器中读取k8s自定义资源的更新前情况。我检查对象是否存在,如果存在则检查是否需要更新,如果不存在则创建它,因为我需要在多个地方使用它。

我想问:

  • 是否有一些辅助函数可以帮助我减少这些样板代码?类似于createOrUpdate函数。
  • 我的做法正确吗?
if err := r.Get(ctx, client.ObjectKey{Name: sCrName, Namespace: sCrNs}, &eCmp); err != nil {
    if apierrors.IsNotFound(err) {
        // 如果对象不存在,则创建一个新对象
        if err := r.Create(ctx, &eCmp); err != nil {
            return ctrl.Result{}, err
        }
    } else {
        // 如果出现除了'not found'之外的错误,则返回错误
        return ctrl.Result{}, err
    }
} else {
    // 如果对象存在,则进行补丁操作
    patch := client.MergeFrom(eCmp.DeepCopy())
    if err := r.Patch(ctx, &eCmp, patch); err != nil {
        return ctrl.Result{}, err
    }
}

如果一切都按照推荐的方式进行,请告诉我。

我还需要执行策略合并,但是代码不支持。

我找到了以下内容:https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/controller/controllerutil#CreateOrUpdate,但我不希望它与时间戳相关,只是"如果有变化->更新"或"不存在->创建"。

当尝试使用以下代码更新CR时,它不起作用,有什么想法?

// 如果对象存在,则进行补丁操作
patch := client.MergeFrom(eCmp.DeepCopy())
if err := r.Patch(ctx, &eCmp, patch); err != nil {
    return ctrl.Result{}, err
}
英文:

Im using the following code inside kubebuilder controller to read before update for k8s custom resource, im checking if the object exist if yes check if need to update, if not create it , as I need to use it in several places
I want to ask:

  • if there is some helper that can help me to reduce this boilarplate
    code ? something like createOrUpdate func

  • am I doing it right ?

if err := r.Get(ctx, client.ObjectKey{Name: sCrName, Namespace: sCrNs}, &eCmp); err != nil {
	if apierrors.IsNotFound(err) {
		// If the object does not exist, create a new one
		if err := r.Create(ctx, &eCmp); err != nil {
			return ctrl.Result{}, err
		}
	} else {
		// If there was an error other than 'not found', return the error
		return ctrl.Result{}, err
	}
} else {
	// If the object exists, patch it
	patch := client.MergeFrom(eCmp.DeepCopy())
	if err := r.Patch(ctx, &eCmp, patch); err != nil {
		return ctrl.Result{}, err
	}
}

if enverything is as recomended please let me know.
I need also to do the stratgic merge but the code doesnt support it

I found the following
https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/controller/controllerutil#CreateOrUpdate
but I dont want it to be related to timestamp, just if something was change -> update it or something doesnt exist -> create it

when trying to update the CR with the following code it doesnt works , any idea?

// If the object exists, patch it
patch := client.MergeFrom(eCmp.DeepCopy())
if err := r.Patch(ctx, &eCmp, patch); err != nil {
    return ctrl.Result{}, err
}

答案1

得分: 1

你可以使用sigs.k8s.io/controller-runtime/pkg/controller/controllerutil包中的controllerutil.CreateOrUpdate()函数来减少样板代码。

使用controllerutil.CreateOrUpdate()函数:

if err := controllerutil.CreateOrUpdate(ctx, r.Client, &eCmp, func() error {
    return r.Patch(ctx, &eCmp, client.MergeFrom(eCmp.DeepCopy()))
}); err != nil {
    return ctrl.Result{}, err
}

对于策略合并(strategic merge),你可以在回调函数中添加策略合并补丁(strategic merge patch)来进行有策略地修补对象。

if err := controllerutil.CreateOrUpdate(ctx, r.Client, &eCmp, func() error {
    // 创建一个策略合并补丁
    strategicMergePatch, err := strategicpatch.CreateTwoWayMergePatch(eCmp, &newECmp, eCmp)
    if err != nil {
        return err
    }

    // 有策略地修补对象
    return r.Patch(ctx, &eCmp, client.ConstantPatch(types.StrategicMergePatchType, strategicMergePatch))
}); err != nil {
    return ctrl.Result{}, err
}
英文:

you can use the controllerutil.CreateOrUpdate() function from the sigs.k8s.io/controller-runtime/pkg/controller/controllerutil package to reduce boilerplate code.

use controllerutil.CreateOrUpdate() function:

if err := controllerutil.CreateOrUpdate(ctx, r.Client, &eCmp, func() error {
    return r.Patch(ctx, &eCmp, client.MergeFrom(eCmp.DeepCopy()))
}); err != nil {
    return ctrl.Result{}, err
}

strategic merge, you can add the strategic merge patch to the callback function to patch the object strategically

  if err := controllerutil.CreateOrUpdate(ctx, r.Client, &eCmp, func() error {
        // Create a strategic merge patch
        strategicMergePatch, err := strategicpatch.CreateTwoWayMergePatch(eCmp, &newECmp, eCmp)
        if err != nil {
            return err
        }
    
        // Patch the object strategically
        return r.Patch(ctx, &eCmp, client.ConstantPatch(types.StrategicMergePatchType, strategicMergePatch))
    }); err != nil {
        return ctrl.Result{}, err
    }

答案2

得分: 1

你的代码没有问题,但是可以稍微重构一下,减少if嵌套的层级。

err := r.Get(ctx, client.ObjectKey{Name: sCrName, Namespace: sCrNs}, &eCmp)
// 对象存在,进行更新
if err == nil {
    patch := client.MergeFrom(eCmp.DeepCopy())
    if err := r.Patch(ctx, &eCmpp, patch); err != nil {
        return ctrl.Result{}, err
    }
    return patch, nil // 根据需要进行调整
} else if apierrors.IsNotFound(err) {
    // 对象不存在,创建新的对象
    if err := r.Create(ctx, &eCmp); err != nil {
        return ctrl.Result{}, err
    }
} 
return ctrl.Result{}, err

CreateOrUpdate 函数看起来也可以满足你的需求。它并没有与时间戳进行比较。虽然示例代码中有时间戳比较的逻辑,但是如果你查看底层的函数 controllerutil.go#L195,你会发现那里并没有进行时间戳比较。

你需要做的是定义一个 mutate 函数(包含 patch 逻辑的函数)并将其作为参数传递,或者创建一个匿名函数并将其传递进去。

例如:

op, err := controllerutil.CreateOrUpdate(context.TODO(), client, object, func() error {
    
    // 在这里编写你的 mutate 逻辑

	return nil
})

如果你查看 CreateOrUpdate 函数,你会发现它实际上与你的代码做的事情基本相同,只是没有进行资源的 mutate(patch)操作。

英文:

There isn't anything wrong with how you've done it, you could potentially restructure it a little to reduce the if nesting.

err := r.Get(ctx, client.ObjectKey{Name: sCrName, Namespace: sCrNs}, &eCmp)
// Object exists, patch it
if err == nil {
    patch := client.MergeFrom(eCmp.DeepCopy())
    if err := r.Patch(ctx, &eCmpp, patch); err != nil {
        return ctrl.Result{}, err
    }
    return patch, nil // Adjust appropriately
} else if apierrors.IsNotFound(err) {
    // Object does not exist, create a new one
    if err := r.Create(ctx, &eCmp); err != nil {
        return ctrl.Result{}, err
    }
} 
return ctrl.Result{}, err

That CreateOrUpdate function looks like it should also do what you want. It doesn't do any comparison with a timestamp. The example on there is doing that specifically but if you look at the underlying function controllerutil.go#L195 you will see that there is no timestamp comparison there.

What you will need to do is either define your mutate function (a function with the patch logic) and pass that as a parameter or create an anonymous function and pass that in.

For example:

op, err := controllerutil.CreateOrUpdate(context.TODO(), client, object, func() error {
    
    // Your mutate logic goes in here

	return nil
})

If you look at the CreateOrUpdate function, it's essentially doing the same thing you are in your code except for the mutate (patching) of the resource.

huangapple
  • 本文由 发表于 2023年5月1日 14:57:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/76145219.html
匿名

发表评论

匿名网友

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

确定