From 68ada429f016e9368fff9538a741fc70f6273350 Mon Sep 17 00:00:00 2001 From: "Jian.Li" Date: Tue, 8 Feb 2022 15:44:38 +0800 Subject: [PATCH] Feat: application support controller requirement (#3192) * application controller version control Signed-off-by: Jian.Li * modify command arg name Signed-off-by: Jian.Li --- cmd/core/main.go | 1 + .../core.oam.dev/oamruntime_controller.go | 3 + .../application/application_controller.go | 22 ++++++ .../application_controller_test.go | 68 +++++++++++++++++++ pkg/oam/labels.go | 3 + 5 files changed, 97 insertions(+) diff --git a/cmd/core/main.go b/cmd/core/main.go index b5e977459..9c9929e17 100644 --- a/cmd/core/main.go +++ b/cmd/core/main.go @@ -130,6 +130,7 @@ func main() { "The duration the LeaderElector clients should wait between tries of actions") flag.BoolVar(&enableClusterGateway, "enable-cluster-gateway", false, "Enable cluster-gateway to use multicluster, disabled by default.") flag.BoolVar(&controllerArgs.EnableCompatibility, "enable-asi-compatibility", false, "enable compatibility for asi") + flag.BoolVar(&controllerArgs.IgnoreAppWithoutControllerRequirement, "ignore-app-without-controller-version", false, "If true, application controller will not process the app without 'app.oam.dev/controller-version-require' annotation") standardcontroller.AddOptimizeFlags() flag.Parse() diff --git a/pkg/controller/core.oam.dev/oamruntime_controller.go b/pkg/controller/core.oam.dev/oamruntime_controller.go index 0d5da3feb..ba6f919bc 100644 --- a/pkg/controller/core.oam.dev/oamruntime_controller.go +++ b/pkg/controller/core.oam.dev/oamruntime_controller.go @@ -83,4 +83,7 @@ type Args struct { // EnableCompatibility indicates that will change some functions of controller to adapt to multiple platforms, such as asi. EnableCompatibility bool + + // IgnoreAppWithoutControllerRequirement indicates that application controller will not process the app without 'app.oam.dev/controller-version-require' annotation. + IgnoreAppWithoutControllerRequirement bool } diff --git a/pkg/controller/core.oam.dev/v1alpha2/application/application_controller.go b/pkg/controller/core.oam.dev/v1alpha2/application/application_controller.go index 2759e5191..9d3c2f85e 100644 --- a/pkg/controller/core.oam.dev/v1alpha2/application/application_controller.go +++ b/pkg/controller/core.oam.dev/v1alpha2/application/application_controller.go @@ -92,6 +92,8 @@ type options struct { appRevisionLimit int concurrentReconciles int disableStatusUpdate bool + ignoreAppNoCtrlReq bool + controllerVersion string } // +kubebuilder:rbac:groups=core.oam.dev,resources=applications,verbs=get;list;watch;create;update;patch;delete @@ -100,6 +102,7 @@ type options struct { // Reconcile process app event // nolint:gocyclo func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + ctx, cancel := context.WithTimeout(ctx, common2.ReconcileTimeout) defer cancel() @@ -117,6 +120,11 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu return r.result(client.IgnoreNotFound(err)).ret() } + if !r.matchControllerRequirement(app) { + logCtx.Info("skip app: not match the controller requirement of app") + return ctrl.Result{}, nil + } + timeReporter := timeReconcile(app) defer timeReporter() @@ -590,5 +598,19 @@ func parseOptions(args core.Args) options { disableStatusUpdate: args.EnableCompatibility, appRevisionLimit: args.AppRevisionLimit, concurrentReconciles: args.ConcurrentReconciles, + ignoreAppNoCtrlReq: args.IgnoreAppWithoutControllerRequirement, + controllerVersion: version.VelaVersion, } } + +func (r *Reconciler) matchControllerRequirement(app *v1beta1.Application) bool { + if app.Annotations != nil { + if requireVersion, ok := app.Annotations[oam.AnnotationControllerRequirement]; ok { + return requireVersion == r.controllerVersion + } + } + if r.ignoreAppNoCtrlReq { + return false + } + return true +} diff --git a/pkg/controller/core.oam.dev/v1alpha2/application/application_controller_test.go b/pkg/controller/core.oam.dev/v1alpha2/application/application_controller_test.go index 0cc63da3c..150842662 100644 --- a/pkg/controller/core.oam.dev/v1alpha2/application/application_controller_test.go +++ b/pkg/controller/core.oam.dev/v1alpha2/application/application_controller_test.go @@ -2277,6 +2277,74 @@ var _ = Describe("Test Application Controller", func() { Expect(*(checkWeb.Spec.Replicas)).Should(BeEquivalentTo(int32(0))) }) + + It("test controller requirement", func() { + + ns := corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-controller-requirement", + }, + } + Expect(k8sClient.Create(context.Background(), &ns)).Should(BeNil()) + + appWithoutCtrlReq := appwithNoTrait.DeepCopy() + appWithoutCtrlReq.SetNamespace(ns.Name) + appWithoutCtrlReq.SetName("app-no-ctrl-req") + Expect(k8sClient.Create(context.Background(), appWithoutCtrlReq)).Should(BeNil()) + + appWithCtrlReqV1 := appwithNoTrait.DeepCopy() + appWithCtrlReqV1.SetNamespace(ns.Name) + appWithCtrlReqV1.SetName("app-with-ctrl-v1") + appWithCtrlReqV1.Annotations = map[string]string{ + oam.AnnotationControllerRequirement: "v1", + } + Expect(k8sClient.Create(context.Background(), appWithCtrlReqV1)).Should(BeNil()) + + appWithCtrlReqV2 := appwithNoTrait.DeepCopy() + appWithCtrlReqV2.SetNamespace(ns.Name) + appWithCtrlReqV2.SetName("app-with-ctrl-v2") + appWithCtrlReqV2.Annotations = map[string]string{ + oam.AnnotationControllerRequirement: "v2", + } + Expect(k8sClient.Create(context.Background(), appWithCtrlReqV2)).Should(BeNil()) + + v1OREmptyReconciler := *reconciler + v1OREmptyReconciler.ignoreAppNoCtrlReq = false + v1OREmptyReconciler.controllerVersion = "v1" + + v2OnlyReconciler := *reconciler + v2OnlyReconciler.ignoreAppNoCtrlReq = true + v2OnlyReconciler.controllerVersion = "v2" + + check := func(r reconcile.Reconciler, app *v1beta1.Application, do bool) { + testutil.ReconcileOnceAfterFinalizer(r, reconcile.Request{NamespacedName: client.ObjectKey{ + Name: app.Name, + Namespace: app.Namespace, + }}) + checkApp := &v1beta1.Application{} + Expect(k8sClient.Get(context.Background(), client.ObjectKey{ + Name: app.Name, + Namespace: app.Namespace, + }, checkApp)).Should(BeNil()) + + if do { + Expect(checkApp.Annotations[oam.AnnotationKubeVelaVersion]).ShouldNot(BeEmpty()) + } else { + if checkApp.Annotations == nil { + return + } + Expect(checkApp.Annotations[oam.AnnotationKubeVelaVersion]).Should(BeEmpty()) + } + } + + check(&v2OnlyReconciler, appWithoutCtrlReq, false) + check(&v2OnlyReconciler, appWithCtrlReqV1, false) + check(&v1OREmptyReconciler, appWithCtrlReqV2, false) + + check(&v1OREmptyReconciler, appWithoutCtrlReq, true) + check(&v1OREmptyReconciler, appWithCtrlReqV1, true) + check(&v2OnlyReconciler, appWithCtrlReqV2, true) + }) }) const ( diff --git a/pkg/oam/labels.go b/pkg/oam/labels.go index cc56a6afb..3f1ba06d8 100644 --- a/pkg/oam/labels.go +++ b/pkg/oam/labels.go @@ -179,4 +179,7 @@ const ( // AnnotationWorkloadName indicates the managed workload's name by trait AnnotationWorkloadName = "trait.oam.dev/workload-name" + + // AnnotationControllerRequirement indicates the controller version that can process the application. + AnnotationControllerRequirement = "app.oam.dev/controller-version-require" )