英文:
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 likecreateOrUpdate
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论