mirror of
https://github.com/kubevela/kubevela.git
synced 2026-08-18 12:06:38 +00:00
Feat: support use update instead of patch for apply (#4592)
Signed-off-by: Somefive <yd219913@alibaba-inc.com>
This commit is contained in:
@@ -43,6 +43,17 @@ const (
|
||||
// DisableReferObjectsFromURL if set, the url ref objects will be disallowed
|
||||
DisableReferObjectsFromURL featuregate.Feature = "DisableReferObjectsFromURL"
|
||||
|
||||
// ApplyResourceByUpdate enforces the modification of resource through update requests.
|
||||
// If not set, the resource modification will use patch requests (three-way-strategy-merge-patch).
|
||||
// The side effect of enabling this feature is that the request traffic will increase due to
|
||||
// the increase of bytes transferred and the more frequent resource mutation failure due to the
|
||||
// potential conflicts.
|
||||
// If set, KubeVela controller will enforce strong restriction on the managed resource that external
|
||||
// system would be unable to make modifications to the KubeVela managed resource. In other words,
|
||||
// no merge for modifications from multiple sources. Only KubeVela keeps the Source-of-Truth for the
|
||||
// resource.
|
||||
ApplyResourceByUpdate featuregate.Feature = "ApplyResourceByUpdate"
|
||||
|
||||
// Edge Features
|
||||
|
||||
// AuthenticateApplication enable the authentication for application
|
||||
@@ -58,6 +69,7 @@ var defaultFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{
|
||||
LegacyComponentRevision: {Default: false, PreRelease: featuregate.Alpha},
|
||||
LegacyResourceOwnerValidation: {Default: false, PreRelease: featuregate.Alpha},
|
||||
DisableReferObjectsFromURL: {Default: false, PreRelease: featuregate.Alpha},
|
||||
ApplyResourceByUpdate: {Default: false, PreRelease: featuregate.Alpha},
|
||||
AuthenticateApplication: {Default: false, PreRelease: featuregate.Alpha},
|
||||
}
|
||||
|
||||
|
||||
@@ -170,15 +170,26 @@ func (a *APIApplicator) Apply(ctx context.Context, desired client.Object, ao ...
|
||||
return nil
|
||||
}
|
||||
|
||||
loggingApply("patching object", desired)
|
||||
patch, err := a.patcher.patch(existing, desired, applyAct)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "cannot calculate patch by computing a three way diff")
|
||||
switch {
|
||||
case utilfeature.DefaultMutableFeatureGate.Enabled(features.ApplyResourceByUpdate) && isUpdatableResource(desired):
|
||||
loggingApply("updating object", desired)
|
||||
desired.SetResourceVersion(existing.GetResourceVersion())
|
||||
var options []client.UpdateOption
|
||||
if applyAct.dryRun {
|
||||
options = append(options, client.DryRunAll)
|
||||
}
|
||||
return errors.Wrapf(a.c.Update(ctx, desired, options...), "cannot update object")
|
||||
default:
|
||||
loggingApply("patching object", desired)
|
||||
patch, err := a.patcher.patch(existing, desired, applyAct)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "cannot calculate patch by computing a three way diff")
|
||||
}
|
||||
if applyAct.dryRun {
|
||||
return errors.Wrapf(a.c.Patch(ctx, desired, patch, client.DryRunAll), "cannot patch object")
|
||||
}
|
||||
return errors.Wrapf(a.c.Patch(ctx, desired, patch), "cannot patch object")
|
||||
}
|
||||
if applyAct.dryRun {
|
||||
return errors.Wrapf(a.c.Patch(ctx, desired, patch, client.DryRunAll), "cannot patch object")
|
||||
}
|
||||
return errors.Wrapf(a.c.Patch(ctx, desired, patch), "cannot patch object")
|
||||
}
|
||||
|
||||
func generateRenderHash(desired client.Object) (string, error) {
|
||||
@@ -397,3 +408,14 @@ func DryRunAll() ApplyOption {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// isUpdatableResource check whether the resource is updatable
|
||||
// Resource like v1.Service cannot unset the spec field (the ip spec is filled by service controller)
|
||||
func isUpdatableResource(desired client.Object) bool {
|
||||
// nolint
|
||||
switch desired.GetObjectKind().GroupVersionKind() {
|
||||
case corev1.SchemeGroupVersion.WithKind("Service"):
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ package apply
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
@@ -25,6 +27,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
@@ -34,6 +37,7 @@ import (
|
||||
"k8s.io/utils/pointer"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/pkg/features"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
oamutil "github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
)
|
||||
@@ -166,6 +170,40 @@ var _ = Describe("Test apply", func() {
|
||||
}))
|
||||
Expect(rawClient.Delete(ctx, cm2)).Should(Succeed())
|
||||
})
|
||||
|
||||
It("Test apply resources with external modifier", func() {
|
||||
deploy.SetGroupVersionKind(appsv1.SchemeGroupVersion.WithKind("Deployment"))
|
||||
originalDeploy := deploy.DeepCopy()
|
||||
bs, err := json.Marshal(deploy)
|
||||
Expect(err).Should(Succeed())
|
||||
deploy.SetAnnotations(map[string]string{oam.AnnotationLastAppliedConfig: string(bs)})
|
||||
modifiedDeploy := deploy.DeepCopy()
|
||||
modifiedDeploy.Spec.Template.Spec.Containers = append(modifiedDeploy.Spec.Template.Spec.Containers, corev1.Container{
|
||||
Name: "added-by-external-modifier",
|
||||
Image: "busybox",
|
||||
})
|
||||
Expect(rawClient.Update(ctx, modifiedDeploy)).Should(Succeed())
|
||||
|
||||
By("Test patch")
|
||||
Expect(utilfeature.DefaultMutableFeatureGate.Set(fmt.Sprintf("%s=false", features.ApplyResourceByUpdate))).Should(Succeed())
|
||||
Expect(rawClient.Get(ctx, client.ObjectKeyFromObject(deploy), deploy)).Should(Succeed())
|
||||
copy1 := originalDeploy.DeepCopy()
|
||||
copy1.SetResourceVersion(deploy.ResourceVersion)
|
||||
Expect(k8sApplicator.Apply(ctx, copy1)).Should(Succeed())
|
||||
Expect(rawClient.Get(ctx, client.ObjectKeyFromObject(deploy), deploy)).Should(Succeed())
|
||||
Expect(len(deploy.Spec.Template.Spec.Containers)).Should(Equal(2))
|
||||
|
||||
By("Test update")
|
||||
Expect(utilfeature.DefaultMutableFeatureGate.Set(fmt.Sprintf("%s=true", features.ApplyResourceByUpdate))).Should(Succeed())
|
||||
Expect(rawClient.Get(ctx, client.ObjectKeyFromObject(deploy), deploy)).Should(Succeed())
|
||||
copy2 := originalDeploy.DeepCopy()
|
||||
copy2.SetResourceVersion(deploy.ResourceVersion)
|
||||
Expect(k8sApplicator.Apply(ctx, copy2)).Should(Succeed())
|
||||
Expect(rawClient.Get(ctx, client.ObjectKeyFromObject(deploy), deploy)).Should(Succeed())
|
||||
Expect(len(deploy.Spec.Template.Spec.Containers)).Should(Equal(1))
|
||||
|
||||
Expect(utilfeature.DefaultMutableFeatureGate.Set(fmt.Sprintf("%s=false", features.ApplyResourceByUpdate))).Should(Succeed())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user