Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 15 additions & 58 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

## Overview

Argo CD Resource Tracker is a tool which dynamically updates the resource inclusion settings of ArgoCD by modifying the `argocd-cm` ConfigMap. It tracks resources created by ArgoCD applications, retrieves their relationships, and ensures that ArgoCD watches only those resources managed by its applications. This reduces the number of watched resources, optimizing API server load and cache memory usage.
Argo CD Resource Tracker is a **CLI tool** that helps manage ArgoCD's resource inclusion settings. It retrieves resources created by ArgoCD applications and their relationships, and ensures that ArgoCD watches only those resources managed by its applications. This reduces the number of watched resources, optimizing API server load and cache memory usage.

> **Note:** Currently available as a CLI tool. A Kubernetes controller mode is planned for future releases to enable continuous, automated resource tracking.

## Problem Statement

ArgoCD’s current implementation watches all resources in the cluster cache, leading to excessive watch connections. In Kubernetes clusters with a large number of CRDs (~200), this results in client-side throttling due to high API server load.

Static configuration settings like `resource.inclusions` and `resource.exclusions` require users to define which resource types to manage in advance, making it inflexible. This project provides a dynamic solution to watch only resources actively managed by ArgoCD applications.
Static configuration settings like `resource.inclusions` and `resource.exclusions` require users to define which resource types to manage in advance, making it inflexible. This project provides a solution to watch only resources actively managed by ArgoCD applications.

## Motivation

Expand All @@ -20,69 +22,24 @@ Static configuration settings like `resource.inclusions` and `resource.exclusion

## How It Works

* Continuously retrieve all ArgoCD applications.

* Process each application by fetching its target objects from the repo-server.

* Check if the resource relationships exist in resource-relation-lookup ConfigMap.

* If resource relations are missing:

* Dynamically obtain the resource relations and update resource-relation-lookup ConfigMap and resource inclusion settings in argocd-cm ConfigMap.
* It can process a single ArgoCD application or all applications in a namespace.

* If resource relations are present:

* Check if the inclusion settings need an update.

* Update the inclusion settings if necessary, otherwise skip.
* Processes each application by fetching its target objects from the ArgoCD repo-server.

* Retrieves all the relations of each ArgoCD application using one of two strategies:
* **Dynamic (default)**: Uses owner references to recursively discover parent-child relationships.
* **Graph**: Uses the Cyphernetes library to query resource relationships via graph queries.
## Benefits

* Dynamic resource management: Automatically tracks and manages resource inclusions settings in `argocd-cm` ConfigMap.
* Helps track and manage resource inclusions settings in `argocd-cm` ConfigMap.

* Optimized API interactions: Prevents unnecessary API calls and throttling.
* Optimised API interactions by preventing unnecessary API calls and throttling.

* Reduced operational overhead: Eliminates the need for manual resource inclusion configuration.
* Reduced operational overhead by eliminating the need for manually finding the resource relations.

## Installation & Usage

See [doc](docs/installation.md) for installation and usage instructions.

This project aims to make Argo CD more efficient and scalable by reducing unnecessary resource watches. Contributions and feedback are welcome!

There are 2 methods of determining the nested children of an Argo CD Application.

### Approach 1 : Using the cyphernetes graph querying library.

This is the most efficient way of determining the nested children of one or more Argo CD Application.

If querying for all the applications, it is efficient to use the `--global` flag which is efficient and reduces the number of calls made to the Kubernetes API server.

``` shell
argocd-resource-tracker run-query --global --tracking-method label --loglevel info
```

By enabling the flag, `--update-enabled`, it is possible to calculate the `resource.inclusions` and `resource.exclusions` and update the `argocd-cm` config map present in the namespace
determined by the parameter `--argocd-namespace` (argocd by default).

```shell
argocd-resource-tracker run-query --global --tracking-method label --loglevel info --update-enabled true
```

Based on the resource tracking method configured in Argo CD, one can specify either the `label` tracking method or the `annotation` tracking method using the `--tracking-method` parameter.
```shell
argocd-resource-tracker run-query --global --tracking-method annotation --loglevel info --update-enabled true
```

### Approach 2 : Using the Argo CD repo server

In this approach, the command makes a grpc call to the Argo CD Repo server that retrieves the immediate children, then recursively going through its owner references to determine the parent-child relationships.
Any previously known relationships are not executed again to reduce the number of calls to the Kubernetes API server.

Eg:

```shell
* **[Installation Guide](docs/installation.md)** - Installation instructions and quick start examples
* **[Command Reference](docs/reference.md)** - Complete command-line options and flags documentation

argocd-resource-tracker run --interval 2m --loglevel info --repo-server argocd-repo-server.argocd.svc.cluster --repo-server-timeout-seconds 60 \
--repo-server-plaintext true --argocd-namespace argocd
```
This project aims to make Argo CD more efficient and scalable by reducing unnecessary resource watches. Contributions and feedback are welcome!
2 changes: 1 addition & 1 deletion cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (

// newRootCommand implements the root command of argocd-resource-tracker
func newRootCommand() error {
var rootCmd = &cobra.Command{
rootCmd := &cobra.Command{
Use: "argocd-resource-tracker",
Short: "Argo CD Resource Tracker",
Long: "Argo CD Resource Tracker is a tool which analyzes the resource inclusions settings based on the resources managed by Argo Applications",
Expand Down
24 changes: 13 additions & 11 deletions cmd/tracker_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,10 @@ package main

import (
"context"
"errors"
"fmt"
"strings"

"github.com/anandf/resource-tracker/pkg/analyzer"
dynamicbackend "github.com/anandf/resource-tracker/pkg/analyzer/dynamic"
graphbackend "github.com/anandf/resource-tracker/pkg/analyzer/graph"
"github.com/anandf/resource-tracker/pkg/common"
"github.com/anandf/resource-tracker/pkg/env"
"github.com/anandf/resource-tracker/pkg/kube"
"github.com/anandf/resource-tracker/pkg/version"
argocdcommon "github.com/argoproj/argo-cd/v3/common"
kubeutil "github.com/argoproj/argo-cd/v3/util/kube"
"github.com/avitaltamir/cyphernetes/pkg/core"
Expand All @@ -21,6 +15,14 @@ import (
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"

"github.com/anandf/resource-tracker/pkg/analyzer"
dynamicbackend "github.com/anandf/resource-tracker/pkg/analyzer/dynamic"
graphbackend "github.com/anandf/resource-tracker/pkg/analyzer/graph"
"github.com/anandf/resource-tracker/pkg/common"
"github.com/anandf/resource-tracker/pkg/env"
"github.com/anandf/resource-tracker/pkg/kube"
"github.com/anandf/resource-tracker/pkg/version"
)

type queryCLIConfig struct {
Expand All @@ -45,7 +47,7 @@ func NewAnalyzeCommand() *cobra.Command {
Use: "analyze",
Short: "Analyze resource relationships and dependencies for ArgoCD applications",
Long: "Analyze resource relationships and dependencies for ArgoCD applications. Can process a single app or all apps.",
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, _ []string) error {
log.Infof("%s %s starting [loglevel:%s]",
version.BinaryName(),
version.Version(),
Expand All @@ -60,7 +62,7 @@ func NewAnalyzeCommand() *cobra.Command {

// Require --app when --all-apps is false, to avoid silently analyzing all apps.
if !cfg.allApps && cfg.applicationName == "" {
return fmt.Errorf("application name is required to analyze a single application")
return errors.New("application name is required to analyze a single application")
}

// Support app specified as "namespace/name" similar to `argocd app get ns/name`.
Expand Down Expand Up @@ -127,7 +129,7 @@ func NewAnalyzeCommand() *cobra.Command {
cmd.Flags().StringVarP(&cfg.argocdNamespace, "namespace", "n", "argocd", "ArgoCD namespace")
cmd.Flags().StringVar(&cfg.kubeConfig, "kubeconfig", "", "Path to kubeconfig file for cluster access")
cmd.Flags().BoolVar(&cfg.allApps, "all-apps", false, "Analyze all applications in the namespace")
cmd.Flags().StringVar(&cfg.strategy, "strategy", "graph", "Analysis strategy: 'dynamic' (OwnerRef walking) or 'graph' (Cyphernetes)")
cmd.Flags().StringVar(&cfg.strategy, "strategy", "dynamic", "Analysis strategy: 'dynamic' (OwnerRef walking) or 'graph' (Cyphernetes)")
return cmd
}

Expand All @@ -137,7 +139,7 @@ func printInclusions(groupedKinds *common.GroupedResourceKinds) {
log.Errorf("error generating resource.inclusions: %s", resourceInclusionString)
return
}
log.Infof("resource.inclusions: |\n%s", resourceInclusionString)
log.Infof("\nresource.inclusions: |\n%s", resourceInclusionString)
}

func ensureRepoServerAddress(restCfg *rest.Config, namespace, current string) (string, error) {
Expand Down
7 changes: 4 additions & 3 deletions cmd/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@ package main
import (
"fmt"

"github.com/anandf/resource-tracker/pkg/version"
"github.com/spf13/cobra"

"github.com/anandf/resource-tracker/pkg/version"
)

// newVersionCommand implements "version" command
func newVersionCommand() *cobra.Command {
var short bool
var versionCmd = &cobra.Command{
versionCmd := &cobra.Command{
Use: "version",
Short: "Display version information",
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(cmd *cobra.Command, _ []string) error {
out := cmd.OutOrStdout()
if !short {
fmt.Fprintf(out, "%s\n", version.Useragent())
Expand Down
3 changes: 2 additions & 1 deletion cmd/version_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/anandf/resource-tracker/pkg/version"
)
Expand Down Expand Up @@ -44,7 +45,7 @@ func TestNewVersionCommand(t *testing.T) {
cmd.SetOut(buf)
cmd.SetArgs(tt.args)
err := cmd.Execute()
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, tt.expected, buf.String())
})
}
Expand Down
101 changes: 50 additions & 51 deletions docs/installation.md
Original file line number Diff line number Diff line change
@@ -1,78 +1,77 @@
# Quick Start Guide
# Installation and Quick Start Guide

## Installation

It is required to run Argo CD Resource Tracker in the same Kubernetes namespace where Argo CD is installed.
Argo CD Resource Tracker is currently available as a CLI tool. A Kubernetes controller mode is planned for future releases.

### Follow these steps to install argocd-resource-tracker:
```
### Binary Installation

Download the latest release binary for your platform from the [releases page](https://github.com/anandf/resource-tracker/releases) or build from source:

```bash
git clone https://github.com/anandf/resource-tracker.git
cd resource-tracker
kubectl apply -f manifest/install.yaml -n argocd
make build
```

## resource-relation-lookup ConfigMap
The binary will be available at `dist/argocd-resource-tracker`.

Argo CD Resource Tracker utilizes the resource-relation-lookup ConfigMap to optimize resource tracking and reduce unnecessary API queries. This ConfigMap stores discovered parent-child relationships between Kubernetes resources, allowing Argo CD Resource Tracker to efficiently track dependencies without repeatedly querying the API server.
## Quick Start

### resource-relation-lookup ConfigMap Structure
### Analyze a Single Application

```
apiVersion: v1
data:
apps_DaemonSet: apps_ControllerRevision,core_Pod
apps_Deployment: apps_ReplicaSet
apps_ReplicaSet: core_Pod
apps_StatefulSet: apps_ControllerRevision,core_Pod
core_Node: coordination.k8s.io_Lease,core_Pod
core_Service: discovery.k8s.io_EndpointSlice
kind: ConfigMap
metadata:
name: resource-relation-lookup
namespace: argocd
**Basic usage (default: dynamic strategy):**
```shell
argocd-resource-tracker analyze --app argocd/my-app
```

### How Argo CD Resource Tracker Uses the resource-relation-lookup ConfigMap
### Analyze All Applications

* When processing an Argo CD application, the Resource Tracker first checks the resource-relation-lookup ConfigMap to determine if the resource relationships have already been discovered.
```shell
argocd-resource-tracker analyze --all-apps --namespace argocd
```

* If the necessary relationships exist in the ConfigMap, Argo CD directly utilizes them, avoiding redundant API queries.
### Using Graph Strategy

* If no relation is found in the ConfigMap, the Argo CD Resource Tracker queries all objects in the API server and, using owner references, discovers the relationships dynamically.
```shell
argocd-resource-tracker analyze --app argocd/my-app --strategy graph
```

* Once new relationships are identified, they are added to the ConfigMap to ensure that subsequent applications do not need to query the API server again, reducing API load and improving performance.
### Output Format

### Cluster-wide Inclusion Pattern
To ensure Argo CD watches all relevant resources across multiple clusters, the clusters: ['*'] wildcard is used in the resource.inclusions setting:
```
- apiGroups:
The command outputs `resource.inclusions` YAML:

```yaml
resource.inclusions: |
- apigroups:
- apps
kinds:
- ReplicaSet
- Deployment
- StatefulSet
- DaemonSet
clusters:
- '*'
- apigroups:
- ""
kinds:
- Service
- ConfigMap
- ServiceAccount
- Pod
clusters:
- '*'
- apigroups:
- rbac.authorization.k8s.io
kinds:
- Role
- RoleBinding
- ClusterRole
- ClusterRoleBinding
clusters:
- '*'

```
This wildcard enables inclusion settings to apply globally across all clusters managed by Argo CD. Due to the ConfigMap size limitation (1MB), defining inclusion rules per cluster individually is not scalable. Using the wildcard reduces redundancy and keeps the configuration compact.

***Trade-off:***

While this ensures that all necessary resources managed by Argo CD are watched, it may also include a few unrelated resources in clusters where those relationships don’t exist.

**Example:**

**In Cluster A:**
- **Resource Kind:** `Deployment`
- **Children:** `ReplicaSets`, `Services`

**In Cluster B:**
- **Resource Kind:** `Deployment`
- **Children:** `ReplicaSets`, `Services`, `Secrets`

Since resource relationships can differ across clusters, using `clusters: ['*']` ensures that all possible resource relationships are covered, even when they vary across clusters, without the need for maintaining separate configurations for each cluster.

This output can be copied into ArgoCD's `argocd-cm` ConfigMap to configure resource inclusions.

For detailed configuration options and command-line parameters, please refer to the
[Configuration and Command Line Reference](./reference.md).
For complete command-line reference, see [Configuration and Command Line Reference](./reference.md).

Loading