Feat: application support controller requirement (#3192)

* application controller version control

Signed-off-by: Jian.Li <lj176172@alibaba-inc.com>

* modify command arg name

Signed-off-by: Jian.Li <lj176172@alibaba-inc.com>
This commit is contained in:
Jian.Li
2022-02-08 15:44:38 +08:00
committed by GitHub
parent 2c41ee9c3c
commit 68ada429f0
5 changed files with 97 additions and 0 deletions
+1
View File
@@ -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()
@@ -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
}
@@ -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
}
@@ -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 (
+3
View File
@@ -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"
)