From 0ad5b94ff483bd2045e7adb3f54b1ca4c700d3da Mon Sep 17 00:00:00 2001 From: jnovick Date: Wed, 14 Aug 2024 04:18:58 +0300 Subject: feat!: Filter labels on the server instead of client to allow more label filtering options (#832) Signed-off-by: Joshua Novick --- pkg/argocd/argocd.go | 56 +++++----------------------------------------- pkg/argocd/argocd_test.go | 48 +++------------------------------------ pkg/argocd/mocks/ArgoCD.go | 2 +- 3 files changed, 10 insertions(+), 96 deletions(-) (limited to 'pkg') diff --git a/pkg/argocd/argocd.go b/pkg/argocd/argocd.go index 155d01a..7ad8068 100644 --- a/pkg/argocd/argocd.go +++ b/pkg/argocd/argocd.go @@ -29,8 +29,8 @@ func (client *k8sClient) GetApplication(ctx context.Context, appName string) (*v return client.kubeClient.ApplicationsClientset.ArgoprojV1alpha1().Applications(client.kubeClient.Namespace).Get(ctx, appName, v1.GetOptions{}) } -func (client *k8sClient) ListApplications() ([]v1alpha1.Application, error) { - list, err := client.kubeClient.ApplicationsClientset.ArgoprojV1alpha1().Applications(client.kubeClient.Namespace).List(context.TODO(), v1.ListOptions{}) +func (client *k8sClient) ListApplications(labelSelector string) ([]v1alpha1.Application, error) { + list, err := client.kubeClient.ApplicationsClientset.ArgoprojV1alpha1().Applications(client.kubeClient.Namespace).List(context.TODO(), v1.ListOptions{LabelSelector: labelSelector}) if err != nil { return nil, err } @@ -71,7 +71,7 @@ type argoCD struct { // ArgoCD is the interface for accessing Argo CD functions we need type ArgoCD interface { GetApplication(ctx context.Context, appName string) (*v1alpha1.Application, error) - ListApplications() ([]v1alpha1.Application, error) + ListApplications(labelSelector string) ([]v1alpha1.Application, error) UpdateSpec(ctx context.Context, spec *application.ApplicationUpdateSpecRequest) (*v1alpha1.ApplicationSpec, error) } @@ -145,34 +145,10 @@ func nameMatchesPattern(name string, patterns []string) bool { return false } -// Match app labels against provided filter label -func matchAppLabels(appName string, appLabels map[string]string, filterLabel string) bool { - - if filterLabel == "" { - return true - } - - filterLabelMap, err := parseLabel(filterLabel) - if err != nil { - log.Errorf("Unable match app labels against %s: %s", filterLabel, err) - return false - } - - for filterLabelKey, filterLabelValue := range filterLabelMap { - log.Tracef("Matching application name %s against label %s", appName, filterLabel) - if appLabelValue, ok := appLabels[filterLabelKey]; ok { - if appLabelValue == filterLabelValue { - return true - } - } - } - return false -} - // Retrieve a list of applications from ArgoCD that qualify for image updates // Application needs either to be of type Kustomize or Helm and must have the // correct annotation in order to be considered. -func FilterApplicationsForUpdate(apps []v1alpha1.Application, patterns []string, appLabel string) (map[string]ApplicationImages, error) { +func FilterApplicationsForUpdate(apps []v1alpha1.Application, patterns []string) (map[string]ApplicationImages, error) { var appsForUpdate = make(map[string]ApplicationImages) for _, app := range apps { @@ -199,12 +175,6 @@ func FilterApplicationsForUpdate(apps []v1alpha1.Application, patterns []string, continue } - // Check if application carries requested label - if !matchAppLabels(app.GetName(), app.GetLabels(), appLabel) { - logCtx.Debugf("Skipping app '%s' because it does not carry requested label", appNSName) - continue - } - logCtx.Tracef("processing app '%s' of type '%v'", appNSName, sourceType) imageList := parseImageList(annotations) appImages := ApplicationImages{} @@ -231,20 +201,6 @@ func parseImageList(annotations map[string]string) *image.ContainerImageList { return &results } -func parseLabel(inputLabel string) (map[string]string, error) { - var selectedLabels map[string]string - const labelFieldDelimiter = "=" - if inputLabel != "" { - selectedLabels = map[string]string{} - fields := strings.Split(inputLabel, labelFieldDelimiter) - if len(fields) != 2 { - return nil, fmt.Errorf("labels should have key%svalue, but instead got: %s", labelFieldDelimiter, inputLabel) - } - selectedLabels[fields[0]] = fields[1] - } - return selectedLabels, nil -} - // GetApplication gets the application named appName from Argo CD API func (client *argoCD) GetApplication(ctx context.Context, appName string) (*v1alpha1.Application, error) { conn, appClient, err := client.Client.NewApplicationClient() @@ -267,7 +223,7 @@ func (client *argoCD) GetApplication(ctx context.Context, appName string) (*v1al // ListApplications returns a list of all application names that the API user // has access to. -func (client *argoCD) ListApplications() ([]v1alpha1.Application, error) { +func (client *argoCD) ListApplications(labelSelector string) ([]v1alpha1.Application, error) { conn, appClient, err := client.Client.NewApplicationClient() metrics.Clients().IncreaseArgoCDClientRequest(client.Client.ClientOptions().ServerAddr, 1) if err != nil { @@ -277,7 +233,7 @@ func (client *argoCD) ListApplications() ([]v1alpha1.Application, error) { defer conn.Close() metrics.Clients().IncreaseArgoCDClientRequest(client.Client.ClientOptions().ServerAddr, 1) - apps, err := appClient.List(context.TODO(), &application.ApplicationQuery{}) + apps, err := appClient.List(context.TODO(), &application.ApplicationQuery{Selector: &labelSelector}) if err != nil { metrics.Clients().IncreaseArgoCDClientError(client.Client.ClientOptions().ServerAddr, 1) return nil, err diff --git a/pkg/argocd/argocd_test.go b/pkg/argocd/argocd_test.go index a24cc4e..317875c 100644 --- a/pkg/argocd/argocd_test.go +++ b/pkg/argocd/argocd_test.go @@ -491,7 +491,7 @@ func Test_FilterApplicationsForUpdate(t *testing.T) { }, }, } - filtered, err := FilterApplicationsForUpdate(applicationList, []string{}, "") + filtered, err := FilterApplicationsForUpdate(applicationList, []string{}) require.NoError(t, err) require.Len(t, filtered, 1) require.Contains(t, filtered, "argocd/app1") @@ -543,55 +543,13 @@ func Test_FilterApplicationsForUpdate(t *testing.T) { }, }, } - filtered, err := FilterApplicationsForUpdate(applicationList, []string{"app*"}, "") + filtered, err := FilterApplicationsForUpdate(applicationList, []string{"app*"}) require.NoError(t, err) require.Len(t, filtered, 2) require.Contains(t, filtered, "argocd/app1") require.Contains(t, filtered, "argocd/app2") assert.Len(t, filtered["argocd/app1"].Images, 2) }) - - t.Run("Filter for applications with label", func(t *testing.T) { - applicationList := []v1alpha1.Application{ - // Annotated and carries required label - { - ObjectMeta: v1.ObjectMeta{ - Name: "app1", - Namespace: "argocd", - Annotations: map[string]string{ - common.ImageUpdaterAnnotation: "nginx, quay.io/dexidp/dex:v1.23.0", - }, - Labels: map[string]string{ - "custom.label/name": "xyz", - }, - }, - Spec: v1alpha1.ApplicationSpec{}, - Status: v1alpha1.ApplicationStatus{ - SourceType: v1alpha1.ApplicationSourceTypeKustomize, - }, - }, - // Annotated but does not carry required label - { - ObjectMeta: v1.ObjectMeta{ - Name: "app2", - Namespace: "argocd", - Annotations: map[string]string{ - common.ImageUpdaterAnnotation: "nginx, quay.io/dexidp/dex:v1.23.0", - }, - }, - Spec: v1alpha1.ApplicationSpec{}, - Status: v1alpha1.ApplicationStatus{ - SourceType: v1alpha1.ApplicationSourceTypeHelm, - }, - }, - } - filtered, err := FilterApplicationsForUpdate(applicationList, []string{}, "custom.label/name=xyz") - require.NoError(t, err) - require.Len(t, filtered, 1) - require.Contains(t, filtered, "argocd/app1") - assert.Len(t, filtered["argocd/app1"].Images, 2) - }) - } func Test_GetHelmParamAnnotations(t *testing.T) { @@ -1064,7 +1022,7 @@ func TestKubernetesClient(t *testing.T) { require.NoError(t, err) t.Run("List applications", func(t *testing.T) { - apps, err := client.ListApplications() + apps, err := client.ListApplications("") require.NoError(t, err) require.Len(t, apps, 1) diff --git a/pkg/argocd/mocks/ArgoCD.go b/pkg/argocd/mocks/ArgoCD.go index f4b8130..7a41323 100644 --- a/pkg/argocd/mocks/ArgoCD.go +++ b/pkg/argocd/mocks/ArgoCD.go @@ -48,7 +48,7 @@ func (_m *ArgoCD) GetApplication(ctx context.Context, appName string) (*v1alpha1 } // ListApplications provides a mock function with given fields: -func (_m *ArgoCD) ListApplications() ([]v1alpha1.Application, error) { +func (_m *ArgoCD) ListApplications(labelSelector string) ([]v1alpha1.Application, error) { ret := _m.Called() if len(ret) == 0 { -- cgit v1.2.3