From 0965eaa98c060665f5eb348709f6c87b5b2d9fd8 Mon Sep 17 00:00:00 2001 From: Graham Beckley Date: Thu, 24 Apr 2025 11:14:38 -0400 Subject: [PATCH 1/6] feat: Add run config for application namespace Signed-off-by: Graham Beckley --- cmd/main.go | 1 + cmd/run.go | 3 +++ 2 files changed, 4 insertions(+) diff --git a/cmd/main.go b/cmd/main.go index 8f5c0c6b..ae36650b 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -31,6 +31,7 @@ type ImageUpdaterConfig struct { ApplicationsAPIKind string ClientOpts argocd.ClientOptions ArgocdNamespace string + AppNamespace string DryRun bool CheckInterval time.Duration ArgoClient argocd.ArgoCD diff --git a/cmd/run.go b/cmd/run.go index 947a29b7..f3eab332 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -24,6 +24,8 @@ import ( "github.com/spf13/cobra" "golang.org/x/sync/semaphore" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // newRunCommand implements "run" command @@ -235,6 +237,7 @@ func newRunCommand() *cobra.Command { runCmd.Flags().BoolVar(&disableKubernetes, "disable-kubernetes", false, "do not create and use a Kubernetes client") runCmd.Flags().IntVar(&cfg.MaxConcurrency, "max-concurrency", 10, "maximum number of update threads to run concurrently") runCmd.Flags().StringVar(&cfg.ArgocdNamespace, "argocd-namespace", "", "namespace where ArgoCD runs in (current namespace by default)") + runCmd.Flags().StringVar(&cfg.AppNamespace, "application-namespace", v1.NamespaceAll, "namespace where Argo Image Updater will manage applications (all namespaces by default)") runCmd.Flags().StringSliceVar(&cfg.AppNamePatterns, "match-application-name", nil, "patterns to match application name against") runCmd.Flags().StringVar(&cfg.AppLabel, "match-application-label", "", "label selector to match application labels against") runCmd.Flags().BoolVar(&warmUpCache, "warmup-cache", true, "whether to perform a cache warm-up on startup") From 700e3a5ec16a587a44429d86c26057caa16fb6be Mon Sep 17 00:00:00 2001 From: Graham Beckley Date: Thu, 24 Apr 2025 13:12:29 -0400 Subject: [PATCH 2/6] feat: Add k8s client config option to scope namespace Signed-off-by: Graham Beckley --- cmd/run.go | 2 +- pkg/argocd/argocd.go | 27 +++++++++++++++++++++------ pkg/argocd/argocd_test.go | 14 +++++++------- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/cmd/run.go b/cmd/run.go index f3eab332..d3e9ea96 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -259,7 +259,7 @@ func runImageUpdater(cfg *ImageUpdaterConfig, warmUp bool) (argocd.ImageUpdaterR var argoClient argocd.ArgoCD switch cfg.ApplicationsAPIKind { case applicationsAPIKindK8S: - argoClient, err = argocd.NewK8SClient(cfg.KubeClient) + argoClient, err = argocd.NewK8SClient(cfg.KubeClient, &argocd.K8SClientOptions{AppNamespace: cfg.AppNamespace}) case applicationsAPIKindArgoCD: argoClient, err = argocd.NewAPIClient(&cfg.ClientOpts) default: diff --git a/pkg/argocd/argocd.go b/pkg/argocd/argocd.go index de540f68..f2fc0543 100644 --- a/pkg/argocd/argocd.go +++ b/pkg/argocd/argocd.go @@ -25,13 +25,14 @@ import ( // Kubernetes based client type k8sClient struct { - kubeClient *kube.ImageUpdaterKubernetesClient + kubeClient *kube.ImageUpdaterKubernetesClient + appNamespace *string } // GetApplication retrieves an application by name across all namespaces. func (client *k8sClient) GetApplication(ctx context.Context, appName string) (*v1alpha1.Application, error) { - // List all applications across all namespaces (using empty labelSelector) - appList, err := client.ListApplications(v1.NamespaceAll) + // List all applications across configured namespace or all namespaces (using empty labelSelector) + appList, err := client.ListApplications("") if err != nil { return nil, fmt.Errorf("error listing applications: %w", err) } @@ -61,7 +62,7 @@ func (client *k8sClient) GetApplication(ctx context.Context, appName string) (*v // ListApplications lists all applications across all namespaces. func (client *k8sClient) ListApplications(labelSelector string) ([]v1alpha1.Application, error) { - list, err := client.kubeClient.ApplicationsClientset.ArgoprojV1alpha1().Applications(v1.NamespaceAll).List(context.TODO(), v1.ListOptions{LabelSelector: labelSelector}) + list, err := client.kubeClient.ApplicationsClientset.ArgoprojV1alpha1().Applications(*client.appNamespace).List(context.TODO(), v1.ListOptions{LabelSelector: labelSelector}) if err != nil { return nil, fmt.Errorf("error listing applications: %w", err) } @@ -99,9 +100,23 @@ func (client *k8sClient) UpdateSpec(ctx context.Context, spec *application.Appli return nil, fmt.Errorf("max retries(%d) reached while updating application: %s", maxRetries, spec.GetName()) } +type K8SClientOptions struct { + AppNamespace string +} + // NewK8SClient creates a new kubernetes client to interact with kubernetes api-server. -func NewK8SClient(kubeClient *kube.ImageUpdaterKubernetesClient) (ArgoCD, error) { - return &k8sClient{kubeClient: kubeClient}, nil +func NewK8SClient(kubeClient *kube.ImageUpdaterKubernetesClient, opts *K8SClientOptions) (ArgoCD, error) { + // Provide default options if nil + if opts == nil { + opts = &K8SClientOptions{ + AppNamespace: v1.NamespaceAll, + } + } + + return &k8sClient{ + kubeClient: kubeClient, + appNamespace: &opts.AppNamespace, + }, nil } // Native diff --git a/pkg/argocd/argocd_test.go b/pkg/argocd/argocd_test.go index 1c9b9445..b19a0278 100644 --- a/pkg/argocd/argocd_test.go +++ b/pkg/argocd/argocd_test.go @@ -1023,7 +1023,7 @@ func TestKubernetesClient(t *testing.T) { Namespace: "testns1", }, ApplicationsClientset: fake.NewSimpleClientset(app1, app2), - }) + }, nil) require.NoError(t, err) @@ -1064,7 +1064,7 @@ func TestKubernetesClient(t *testing.T) { // Create the Kubernetes client client, err := NewK8SClient(&kube.ImageUpdaterKubernetesClient{ ApplicationsClientset: clientset, - }) + }, nil) require.NoError(t, err) // Test ListApplications error handling @@ -1094,7 +1094,7 @@ func TestKubernetesClient(t *testing.T) { // Create the Kubernetes client client, err := NewK8SClient(&kube.ImageUpdaterKubernetesClient{ ApplicationsClientset: clientset, - }) + }, nil) require.NoError(t, err) // Test GetApplication with multiple matching applications @@ -1124,7 +1124,7 @@ func TestKubernetesClientUpdateSpec(t *testing.T) { client, err := NewK8SClient(&kube.ImageUpdaterKubernetesClient{ ApplicationsClientset: clientset, - }) + }, nil) require.NoError(t, err) appName := "test-app" @@ -1145,7 +1145,7 @@ func TestKubernetesClientUpdateSpec(t *testing.T) { client, err := NewK8SClient(&kube.ImageUpdaterKubernetesClient{ ApplicationsClientset: clientset, - }) + }, nil) require.NoError(t, err) appName := "test-app" @@ -1169,7 +1169,7 @@ func TestKubernetesClientUpdateSpec(t *testing.T) { client, err := NewK8SClient(&kube.ImageUpdaterKubernetesClient{ ApplicationsClientset: clientset, - }) + }, nil) require.NoError(t, err) clientset.PrependReactor("update", "applications", func(action k8stesting.Action) (handled bool, ret runtime.Object, err error) { @@ -1198,7 +1198,7 @@ func TestKubernetesClientUpdateSpec(t *testing.T) { client, err := NewK8SClient(&kube.ImageUpdaterKubernetesClient{ ApplicationsClientset: clientset, - }) + }, nil) require.NoError(t, err) clientset.PrependReactor("update", "applications", func(action k8stesting.Action) (handled bool, ret runtime.Object, err error) { From 1a44db3e7a8eb8fd8b47d62ed47a47e5605166f3 Mon Sep 17 00:00:00 2001 From: Graham Beckley Date: Thu, 8 May 2025 11:44:10 -0400 Subject: [PATCH 3/6] Avoid listing applications in all namespaces if single namespace is configured Signed-off-by: Graham Beckley --- pkg/argocd/argocd.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pkg/argocd/argocd.go b/pkg/argocd/argocd.go index f2fc0543..d115c27e 100644 --- a/pkg/argocd/argocd.go +++ b/pkg/argocd/argocd.go @@ -29,9 +29,16 @@ type k8sClient struct { appNamespace *string } -// GetApplication retrieves an application by name across all namespaces. +// GetApplication retrieves an application by name, either in a specific namespace or all namespaces depending on client configuration. func (client *k8sClient) GetApplication(ctx context.Context, appName string) (*v1alpha1.Application, error) { // List all applications across configured namespace or all namespaces (using empty labelSelector) + if *client.appNamespace != v1.NamespaceAll { + return client.kubeClient.ApplicationsClientset.ArgoprojV1alpha1().Applications(*client.appNamespace).Get(ctx, appName, v1.GetOptions{}) + } + return client.getApplicationInAllNamespaces(ctx, appName) +} + +func (client *k8sClient) getApplicationInAllNamespaces(ctx context.Context, appName string) (*v1alpha1.Application, error) { appList, err := client.ListApplications("") if err != nil { return nil, fmt.Errorf("error listing applications: %w", err) From 3f7f71e6e9d8af3d3371e4d56fa1374ded5bc2e6 Mon Sep 17 00:00:00 2001 From: Graham Beckley Date: Thu, 8 May 2025 12:14:54 -0400 Subject: [PATCH 4/6] Add documentation for new --application-namespace option Signed-off-by: Graham Beckley --- docs/install/reference.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/install/reference.md b/docs/install/reference.md index f86491c8..406f540b 100644 --- a/docs/install/reference.md +++ b/docs/install/reference.md @@ -26,6 +26,10 @@ Runs the Argo CD Image Updater, possibly in an endless loop. ### Flags +**--application-namespace *namespace* ** + +Specifies the Kubernetes namespace in which Argo Image Updater will manage Argo CD Applications when using the Kubernetes-based Application API. By default, applications in all namespaces are considered. This flag can be used to limit scope to a single namespace for performance, security, or organizational reasons. + **--argocd-auth-token *token* ** Use *token* for authenticating to the Argo CD API. This token must be a base64 From eab19bdef06599e643a5f0613c537bf50c6c88ff Mon Sep 17 00:00:00 2001 From: Graham Beckley Date: Mon, 12 May 2025 13:17:04 -0400 Subject: [PATCH 5/6] Remove unused context param from `getApplicationInAllNamespaces` Signed-off-by: Graham Beckley --- pkg/argocd/argocd.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/argocd/argocd.go b/pkg/argocd/argocd.go index d115c27e..b7d053d4 100644 --- a/pkg/argocd/argocd.go +++ b/pkg/argocd/argocd.go @@ -35,10 +35,10 @@ func (client *k8sClient) GetApplication(ctx context.Context, appName string) (*v if *client.appNamespace != v1.NamespaceAll { return client.kubeClient.ApplicationsClientset.ArgoprojV1alpha1().Applications(*client.appNamespace).Get(ctx, appName, v1.GetOptions{}) } - return client.getApplicationInAllNamespaces(ctx, appName) + return client.getApplicationInAllNamespaces(appName) } -func (client *k8sClient) getApplicationInAllNamespaces(ctx context.Context, appName string) (*v1alpha1.Application, error) { +func (client *k8sClient) getApplicationInAllNamespaces(appName string) (*v1alpha1.Application, error) { appList, err := client.ListApplications("") if err != nil { return nil, fmt.Errorf("error listing applications: %w", err) From 3acfb8d7a7558d7cf3a6874a87a8d9a0292f15fb Mon Sep 17 00:00:00 2001 From: Graham Beckley Date: Mon, 12 May 2025 13:18:50 -0400 Subject: [PATCH 6/6] Refer to `Argo CD Image Updater` in application-namespace option docs Signed-off-by: Graham Beckley --- docs/install/reference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/install/reference.md b/docs/install/reference.md index cbee9bdd..7c0603c0 100644 --- a/docs/install/reference.md +++ b/docs/install/reference.md @@ -28,7 +28,7 @@ Runs the Argo CD Image Updater, possibly in an endless loop. **--application-namespace *namespace*** -Specifies the Kubernetes namespace in which Argo Image Updater will manage Argo CD Applications when using the Kubernetes-based Application API. By default, applications in all namespaces are considered. This flag can be used to limit scope to a single namespace for performance, security, or organizational reasons. +Specifies the Kubernetes namespace in which Argo CD Image Updater will manage Argo CD Applications when using the Kubernetes-based Application API. By default, applications in all namespaces are considered. This flag can be used to limit scope to a single namespace for performance, security, or organizational reasons. **--argocd-auth-token *token***