Skip to content

Commit

Permalink
Add filtered cache and status controller test (#14)
Browse files Browse the repository at this point in the history
Signed-off-by: Mike Ng <ming@redhat.com>
  • Loading branch information
mikeshng authored Apr 30, 2023
1 parent a2bf0f6 commit d93d643
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 23 deletions.
13 changes: 7 additions & 6 deletions controllers/application/application_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const (
AnnotationKeyHubApplicationName = "apps.open-cluster-management.io/hub-application-name"
// Application and ManifestWork label that shows that ApplicationSet is the grand parent of this work
LabelKeyAppSet = "apps.open-cluster-management.io/application-set"
// Application label that enables the pull controller to wrap the Application in ManifestWork payload
// Application and ManifestWork label that enables the pull controller to wrap the Application in ManifestWork payload
LabelKeyPull = "apps.open-cluster-management.io/pull-to-ocm-managed-cluster"
)

Expand All @@ -66,17 +66,17 @@ type ApplicationReconciler struct {
var ApplicationPredicateFunctions = predicate.Funcs{
UpdateFunc: func(e event.UpdateEvent) bool {
newApp := e.ObjectNew.(*argov1alpha1.Application)
return containsValidPullLabel(*newApp) && containsValidPullAnnotation(*newApp)
return containsValidPullLabel(newApp.Labels) && containsValidPullAnnotation(*newApp)

},
CreateFunc: func(e event.CreateEvent) bool {
app := e.Object.(*argov1alpha1.Application)
return containsValidPullLabel(*app) && containsValidPullAnnotation(*app)
return containsValidPullLabel(app.Labels) && containsValidPullAnnotation(*app)
},

DeleteFunc: func(e event.DeleteEvent) bool {
app := e.Object.(*argov1alpha1.Application)
return containsValidPullLabel(*app) && containsValidPullAnnotation(*app)
return containsValidPullLabel(app.Labels) && containsValidPullAnnotation(*app)
},
}

Expand Down Expand Up @@ -164,8 +164,9 @@ func (r *ApplicationReconciler) Reconcile(ctx context.Context, req ctrl.Request)
return ctrl.Result{}, err
}
} else if err == nil {
app := prepareApplicationForWorkPayload(application)
mw.Spec.Workload.Manifests = []workv1.Manifest{{RawExtension: runtime.RawExtension{Object: &app}}}
mw.Spec = w.Spec
mw.Annotations = w.Annotations
mw.Labels = w.Labels
err = r.Client.Update(ctx, &mw)
if err != nil {
log.Error(err, "unable to update ManifestWork")
Expand Down
13 changes: 2 additions & 11 deletions controllers/application/application_status_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package application

import (
"context"
"fmt"

"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
Expand Down Expand Up @@ -46,12 +45,12 @@ type ApplicationStatusReconciler struct {
var ManifestWorkPredicateFunctions = predicate.Funcs{
UpdateFunc: func(e event.UpdateEvent) bool {
newManifestWork := e.ObjectNew.(*workv1.ManifestWork)
return containsValidManifestWorkHubApplicationAnnotations(*newManifestWork)
return containsValidPullLabel(newManifestWork.Labels) && containsValidManifestWorkHubApplicationAnnotations(*newManifestWork)

},
CreateFunc: func(e event.CreateEvent) bool {
manifestWork := e.Object.(*workv1.ManifestWork)
return containsValidManifestWorkHubApplicationAnnotations(*manifestWork)
return containsValidPullLabel(manifestWork.Labels) && containsValidManifestWorkHubApplicationAnnotations(*manifestWork)
},

DeleteFunc: func(e event.DeleteEvent) bool {
Expand Down Expand Up @@ -119,14 +118,6 @@ func (r *ApplicationStatusReconciler) Reconcile(ctx context.Context, req ctrl.Re
application.Status.Health.Status = health.HealthStatusCode(healthStatus)
log.Info("updating Application status with ManifestWork status feedbacks")

appSetName := getAppSetOwnerName(application)
if appSetName != "" && len(application.Status.Conditions) == 0 {
application.Status.Conditions = []argov1alpha1.ApplicationCondition{{
Type: "AdditionalStatusReport",
Message: fmt.Sprintf("kubectl get multiclusterapplicationsetreports -n %s %s", application.Namespace, appSetName),
}}
}

err := r.Client.Update(ctx, &application)
if err != nil {
log.Error(err, "unable to update Application")
Expand Down
104 changes: 104 additions & 0 deletions controllers/application/application_status_controller_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
Copyright 2023.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package application

import (
"context"
"strconv"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"

argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
workv1 "open-cluster-management.io/api/work/v1"
)

var _ = Describe("Application Pull controller", func() {

const (
appName = "app-5"
workName = "work-1"
appNamespace = "default"
clusterName = "default"
)

appKey := types.NamespacedName{Name: appName, Namespace: appNamespace}
workKey := types.NamespacedName{Name: workName, Namespace: clusterName}
ctx := context.Background()

Context("When ManifestWork is created/updated", func() {
It("Should update Application status", func() {
By("Creating the Application")
app1 := argov1alpha1.Application{
ObjectMeta: metav1.ObjectMeta{
Name: appName,
Namespace: appNamespace,
},
}
Expect(k8sClient.Create(ctx, &app1)).Should(Succeed())

By("Creating the ManifestWork")
work1 := workv1.ManifestWork{
ObjectMeta: metav1.ObjectMeta{
Name: workName,
Namespace: clusterName,
Annotations: map[string]string{
AnnotationKeyHubApplicationNamespace: appNamespace,
AnnotationKeyHubApplicationName: appName,
},
Labels: map[string]string{
LabelKeyPull: strconv.FormatBool(true),
},
},
}
Expect(k8sClient.Create(ctx, &work1)).Should(Succeed())
Expect(k8sClient.Get(ctx, workKey, &work1)).Should(Succeed())

By("Updating the ManifestWork status")
healthy := "Healthy"
synced := "Synced"
work1.Status = workv1.ManifestWorkStatus{
ResourceStatus: workv1.ManifestResourceStatus{
Manifests: []workv1.ManifestCondition{{
Conditions: []metav1.Condition{{
Type: workv1.WorkApplied,
Status: metav1.ConditionTrue,
Reason: workv1.WorkApplied,
Message: workv1.WorkApplied,
LastTransitionTime: work1.CreationTimestamp,
}},
StatusFeedbacks: workv1.StatusFeedbackResult{
Values: []workv1.FeedbackValue{
{Name: "healthStatus", Value: workv1.FieldValue{String: &healthy, Type: "String"}},
{Name: "syncStatus", Value: workv1.FieldValue{String: &synced, Type: "String"}},
}},
}},
},
}
Expect(k8sClient.Status().Update(ctx, &work1)).Should(Succeed())
Eventually(func() bool {
if err := k8sClient.Get(ctx, appKey, &app1); err != nil {
return false
}
return app1.Status.Health.Status == "Healthy" && app1.Status.Sync.Status == "Synced"
}).Should(BeTrue())
})
})
})
9 changes: 5 additions & 4 deletions controllers/application/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ import (
workv1 "open-cluster-management.io/api/work/v1"
)

func containsValidPullLabel(application argov1alpha1.Application) bool {
labels := application.GetLabels()
func containsValidPullLabel(labels map[string]string) bool {
if len(labels) == 0 {
return false
}
Expand Down Expand Up @@ -163,7 +162,9 @@ func prepareApplicationForWorkPayload(application argov1alpha1.Application) argo
// The Application payload Spec Destination values are modified so that the Application is always performing in-cluster resource deployments.
// If the Application is generated from an ApplicationSet, custom label and annotation are inserted.
func generateManifestWork(name, namespace string, application argov1alpha1.Application) *workv1.ManifestWork {
var workLabels map[string]string
workLabels := map[string]string{
LabelKeyPull: strconv.FormatBool(true),
}

workAnnos := map[string]string{
AnnotationKeyHubApplicationNamespace: application.Namespace,
Expand All @@ -172,7 +173,7 @@ func generateManifestWork(name, namespace string, application argov1alpha1.Appli

appSetOwnerName := getAppSetOwnerName(application)
if appSetOwnerName != "" {
workLabels = map[string]string{LabelKeyAppSet: strconv.FormatBool(true)}
workLabels[LabelKeyAppSet] = strconv.FormatBool(true)
workAnnos[AnnotationKeyAppSet] = application.Namespace + "/" + appSetOwnerName
}

Expand Down
7 changes: 5 additions & 2 deletions controllers/application/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ func Test_containsValidPullLabel(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := containsValidPullLabel(tt.args.application); got != tt.want {
if got := containsValidPullLabel(tt.args.application.Labels); got != tt.want {
t.Errorf("containsValidPullLabel() = %v, want %v", got, tt.want)
}
})
Expand Down Expand Up @@ -429,7 +429,10 @@ func Test_generateManifestWork(t *testing.T) {
application: app,
},
want: results{
workLabel: map[string]string{LabelKeyAppSet: "true"},
workLabel: map[string]string{
LabelKeyPull: "true",
LabelKeyAppSet: "true",
},
workAnno: map[string]string{
AnnotationKeyAppSet: "argocd/appset1",
AnnotationKeyHubApplicationNamespace: "argocd",
Expand Down
6 changes: 6 additions & 0 deletions controllers/application/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@ var _ = BeforeSuite(func() {
}).SetupWithManager(k8sManager)
Expect(err).ToNot(HaveOccurred())

err = (&ApplicationStatusReconciler{
Client: k8sManager.GetClient(),
Scheme: k8sManager.GetScheme(),
}).SetupWithManager(k8sManager)
Expect(err).ToNot(HaveOccurred())

go func() {
defer GinkgoRecover()
err = k8sManager.Start(ctx)
Expand Down
13 changes: 13 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,18 @@ package main
import (
"flag"
"os"
"strconv"

// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
// to ensure that exec-entrypoint and run can make use of them.
_ "k8s.io/client-go/plugin/pkg/client/auth"

"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/cache"
"sigs.k8s.io/controller-runtime/pkg/log/zap"

argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
Expand Down Expand Up @@ -83,6 +86,16 @@ func main() {
// if you are doing or is intended to do any operation such as perform cleanups
// after the manager stops then its usage might be unsafe.
// LeaderElectionReleaseOnCancel: true,
NewCache: cache.BuilderWithOptions(cache.Options{
SelectorsByObject: cache.SelectorsByObject{
&workv1.ManifestWork{}: {
Label: labels.SelectorFromSet(labels.Set{application.LabelKeyPull: strconv.FormatBool(true)}),
},
&argov1alpha1.Application{}: {
Label: labels.SelectorFromSet(labels.Set{application.LabelKeyPull: strconv.FormatBool(true)}),
},
},
}),
})
if err != nil {
setupLog.Error(err, "unable to start manager")
Expand Down

0 comments on commit d93d643

Please sign in to comment.