Skip to content

feat: Add configuration option to scope application listing / getting to particular namespace #1112

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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 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***

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(appName)
}

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)
}
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