diff --git a/charts/vela-core/README.md b/charts/vela-core/README.md index 80434875e..bf2d916c3 100644 --- a/charts/vela-core/README.md +++ b/charts/vela-core/README.md @@ -99,6 +99,7 @@ helm install --create-namespace -n vela-system kubevela kubevela/vela-core --wai | `featureGates.multiStageComponentApply` | if enabled, the multiStageComponentApply feature will be combined with the stage field in TraitDefinition to complete the multi-stage apply. | `false` | | `featureGates.gzipApplicationRevision` | compress apprev using gzip (good) before being stored. This is reduces network throughput when dealing with huge apprevs. | `false` | | `featureGates.zstdApplicationRevision` | compress apprev using zstd (fast and good) before being stored. This is reduces network throughput when dealing with huge apprevs. Note that zstd will be prioritized if you enable other compression options. | `true` | +| `featureGates.preDispatchDryRun` | enable dryrun before dispatching resources. Enable this flag can help prevent unsuccessful dispatch resources entering resourcetracker and improve the user experiences of gc but at the cost of increasing network requests. | `true` | ### MultiCluster parameters @@ -110,7 +111,7 @@ helm install --create-namespace -n vela-system kubevela kubevela/vela-core --wai | `multicluster.clusterGateway.replicaCount` | ClusterGateway replica count | `1` | | `multicluster.clusterGateway.port` | ClusterGateway port | `9443` | | `multicluster.clusterGateway.image.repository` | ClusterGateway image repository | `oamdev/cluster-gateway` | -| `multicluster.clusterGateway.image.tag` | ClusterGateway image tag | `v1.4.0` | +| `multicluster.clusterGateway.image.tag` | ClusterGateway image tag | `v1.7.0-alpha.3` | | `multicluster.clusterGateway.image.pullPolicy` | ClusterGateway image pull policy | `IfNotPresent` | | `multicluster.clusterGateway.resources.limits.cpu` | ClusterGateway cpu limit | `100m` | | `multicluster.clusterGateway.resources.limits.memory` | ClusterGateway memory limit | `200Mi` | diff --git a/charts/vela-core/templates/kubevela-controller.yaml b/charts/vela-core/templates/kubevela-controller.yaml index 05036478e..c78e4c110 100644 --- a/charts/vela-core/templates/kubevela-controller.yaml +++ b/charts/vela-core/templates/kubevela-controller.yaml @@ -274,6 +274,7 @@ spec: - "--feature-gates=MultiStageComponentApply= {{- .Values.featureGates.multiStageComponentApply | toString -}}" - "--feature-gates=GzipApplicationRevision={{- .Values.featureGates.gzipResourceTracker | toString -}}" - "--feature-gates=ZstdApplicationRevision={{- .Values.featureGates.zstdResourceTracker | toString -}}" + - "--feature-gates=PreDispatchDryRun={{- .Values.featureGates.preDispatchDryRun | toString -}}" {{ if .Values.authentication.enabled }} {{ if .Values.authentication.withUser }} - "--authentication-with-user" diff --git a/charts/vela-core/values.yaml b/charts/vela-core/values.yaml index dee46127c..56ec22f2a 100644 --- a/charts/vela-core/values.yaml +++ b/charts/vela-core/values.yaml @@ -113,6 +113,7 @@ optimize: ##@param featureGates.multiStageComponentApply if enabled, the multiStageComponentApply feature will be combined with the stage field in TraitDefinition to complete the multi-stage apply. ##@param featureGates.gzipApplicationRevision compress apprev using gzip (good) before being stored. This is reduces network throughput when dealing with huge apprevs. ##@param featureGates.zstdApplicationRevision compress apprev using zstd (fast and good) before being stored. This is reduces network throughput when dealing with huge apprevs. Note that zstd will be prioritized if you enable other compression options. +##@param featureGates.preDispatchDryRun enable dryrun before dispatching resources. Enable this flag can help prevent unsuccessful dispatch resources entering resourcetracker and improve the user experiences of gc but at the cost of increasing network requests. ##@param featureGates: enableLegacyComponentRevision: false @@ -122,6 +123,7 @@ featureGates: multiStageComponentApply: false gzipApplicationRevision: false zstdApplicationRevision: true + preDispatchDryRun: true ## @section MultiCluster parameters @@ -146,7 +148,7 @@ multicluster: port: 9443 image: repository: oamdev/cluster-gateway - tag: v1.4.0 + tag: v1.7.0-alpha.3 pullPolicy: IfNotPresent resources: limits: diff --git a/pkg/controller/core.oam.dev/v1alpha2/application/application_finalizer_test.go b/pkg/controller/core.oam.dev/v1alpha2/application/application_finalizer_test.go index 04caac3aa..197ce497d 100644 --- a/pkg/controller/core.oam.dev/v1alpha2/application/application_finalizer_test.go +++ b/pkg/controller/core.oam.dev/v1alpha2/application/application_finalizer_test.go @@ -94,17 +94,9 @@ var _ = Describe("Test application controller finalizer logic", func() { By("Verify latest app revision is also recorded in status") Expect(checkApp.Status.LatestRevision).ShouldNot(BeNil()) - By("Verify ResourceTracker is created") - rt := &v1beta1.ResourceTracker{} - Expect(k8sClient.Get(ctx, getTrackerKey(checkApp.Namespace, checkApp.Name, "v1"), rt)).Should(Succeed()) - By("Delete Application") Expect(k8sClient.Delete(ctx, checkApp)).Should(BeNil()) testutil.ReconcileOnceAfterFinalizer(reconciler, ctrl.Request{NamespacedName: appKey}) - - By("Verify ResourceTracker is deleted") - rt = &v1beta1.ResourceTracker{} - Expect(k8sClient.Get(ctx, getTrackerKey(checkApp.Namespace, checkApp.Name, "v1"), rt)).Should(util.NotFoundMatcher{}) }) It("Test cross namespace workload, then delete the app", func() { diff --git a/pkg/features/controller_features.go b/pkg/features/controller_features.go index f432a149b..bbaf6fdc0 100644 --- a/pkg/features/controller_features.go +++ b/pkg/features/controller_features.go @@ -84,6 +84,11 @@ const ( // MultiStageComponentApply enable multi-stage feature for component // If enabled, the dispatch of manifests is performed in batches according to the stage MultiStageComponentApply featuregate.Feature = "MultiStageComponentApply" + + // PreDispatchDryRun enable dryrun before dispatching resources + // Enable this flag can help prevent unsuccessful dispatch resources entering resourcetracker and improve the + // user experiences of gc but at the cost of increasing network requests. + PreDispatchDryRun featuregate.Feature = "PreDispatchDryRun" ) var defaultFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{ @@ -102,6 +107,7 @@ var defaultFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{ MultiStageComponentApply: {Default: false, PreRelease: featuregate.Alpha}, GzipApplicationRevision: {Default: false, PreRelease: featuregate.Alpha}, ZstdApplicationRevision: {Default: false, PreRelease: featuregate.Alpha}, + PreDispatchDryRun: {Default: true, PreRelease: featuregate.Alpha}, } func init() { diff --git a/pkg/resourcekeeper/dispatch.go b/pkg/resourcekeeper/dispatch.go index 952638003..b44b320b7 100644 --- a/pkg/resourcekeeper/dispatch.go +++ b/pkg/resourcekeeper/dispatch.go @@ -18,7 +18,9 @@ package resourcekeeper import ( "context" + "fmt" + velaslices "github.com/kubevela/pkg/util/slices" "github.com/pkg/errors" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" utilfeature "k8s.io/apiserver/pkg/util/feature" @@ -67,15 +69,23 @@ func (h *resourceKeeper) Dispatch(ctx context.Context, manifests []*unstructured if err = h.AdmissionCheck(ctx, manifests); err != nil { return err } - // 1. record manifests in resourcetracker - if err = h.record(ctx, manifests, options...); err != nil { - return err - } - // 2. apply manifests + // 1. pre-dispatch check opts := []apply.ApplyOption{apply.MustBeControlledByApp(h.app), apply.NotUpdateRenderHashEqual()} if len(applyOpts) > 0 { opts = append(opts, applyOpts...) } + if utilfeature.DefaultMutableFeatureGate.Enabled(features.PreDispatchDryRun) { + if err = h.dispatch(ctx, + velaslices.Map(manifests, func(manifest *unstructured.Unstructured) *unstructured.Unstructured { return manifest.DeepCopy() }), + append([]apply.ApplyOption{apply.DryRunAll()}, opts...)); err != nil { + return fmt.Errorf("pre-dispatch dryrun failed: %w", err) + } + } + // 2. record manifests in resourcetracker + if err = h.record(ctx, manifests, options...); err != nil { + return err + } + // 3. apply manifests if err = h.dispatch(ctx, manifests, opts); err != nil { return err } diff --git a/test/e2e-multicluster-test/multicluster_test.go b/test/e2e-multicluster-test/multicluster_test.go index c32b3214d..4cefaaa08 100644 --- a/test/e2e-multicluster-test/multicluster_test.go +++ b/test/e2e-multicluster-test/multicluster_test.go @@ -528,6 +528,9 @@ var _ = Describe("Test multicluster scenario", func() { g.Expect(app.Status.Phase).Should(Equal(common.ApplicationRunningWorkflow)) g.Expect(len(app.Status.Workflow.Steps) > 0).Should(BeTrue()) g.Expect(app.Status.Workflow.Steps[0].Message).Should(ContainSubstring("is invalid")) + rts := &v1beta1.ResourceTrackerList{} + g.Expect(k8sClient.List(hubCtx, rts, client.MatchingLabels{oam.LabelAppName: app.Name, oam.LabelAppNamespace: app.Namespace})).Should(Succeed()) + g.Expect(len(rts.Items)).Should(Equal(0)) }, 20*time.Second).Should(Succeed()) Expect(k8sClient.Delete(ctx, app)).Should(Succeed()) Eventually(func(g Gomega) {