Skip to content
1 change: 1 addition & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ type ImageUpdaterConfig struct {
ApplicationsAPIKind string
ClientOpts argocd.ClientOptions
ArgocdNamespace string
AppNamespace string
DryRun bool
CheckInterval time.Duration
ArgoClient argocd.ArgoCD
Expand Down
5 changes: 4 additions & 1 deletion cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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")
Expand All @@ -256,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:
Expand Down
4 changes: 4 additions & 0 deletions docs/install/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
36 changes: 29 additions & 7 deletions pkg/argocd/argocd.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,21 @@ import (

// Kubernetes based client
type k8sClient struct {
kubeClient *kube.ImageUpdaterKubernetesClient
kubeClient *kube.ImageUpdaterKubernetesClient
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 all namespaces (using empty labelSelector)
appList, err := client.ListApplications(v1.NamespaceAll)
// 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)
}
Expand Down Expand Up @@ -61,7 +69,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)
}
Expand Down Expand Up @@ -99,9 +107,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
Expand Down
14 changes: 7 additions & 7 deletions pkg/argocd/argocd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1023,7 +1023,7 @@ func TestKubernetesClient(t *testing.T) {
Namespace: "testns1",
},
ApplicationsClientset: fake.NewSimpleClientset(app1, app2),
})
}, nil)

require.NoError(t, err)

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -1124,7 +1124,7 @@ func TestKubernetesClientUpdateSpec(t *testing.T) {

client, err := NewK8SClient(&kube.ImageUpdaterKubernetesClient{
ApplicationsClientset: clientset,
})
}, nil)
require.NoError(t, err)

appName := "test-app"
Expand All @@ -1145,7 +1145,7 @@ func TestKubernetesClientUpdateSpec(t *testing.T) {

client, err := NewK8SClient(&kube.ImageUpdaterKubernetesClient{
ApplicationsClientset: clientset,
})
}, nil)
require.NoError(t, err)

appName := "test-app"
Expand All @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down