Skip to content

Commit

Permalink
feat: Add ignore-resources-tracking annotation to ignore resources up…
Browse files Browse the repository at this point in the history
…date (#18343)

* feat: Add ignore-resources-tracking annotation to ignore resources update

Signed-off-by: kahoulei <kahou.lei@okta.com>

* add doc

Signed-off-by: kahoulei <kahou.lei@okta.com>

* update annotation doc

Signed-off-by: kahoulei <kahou.lei@okta.com>

* refactor annotation usage base on comment feedback

Signed-off-by: kahoulei <kahou.lei@okta.com>

* update annotation

Signed-off-by: kahoulei <kahou.lei@okta.com>

* do not store boolean in resourceInfo

Signed-off-by: kahoulei <kahou.lei@okta.com>

* typo

Signed-off-by: kahoulei <kahou.lei@okta.com>

* update logic

Signed-off-by: kahoulei <kahou.lei@okta.com>

* refactor

Signed-off-by: kahoulei <kahou.lei@okta.com>

* add comment

Signed-off-by: kahoulei <kahou.lei@okta.com>

* add tests

Signed-off-by: kahoulei <kahou.lei@okta.com>

* update doc

Signed-off-by: kahoulei <kahou.lei@okta.com>

* update code base on comment feedback

Signed-off-by: kahoulei <kahou.lei@okta.com>

* update annotation doc

Signed-off-by: kahoulei <kahou.lei@okta.com>

* fix goimport

Signed-off-by: kahoulei <kahou.lei@okta.com>

* fix golint

Signed-off-by: kahoulei <kahou.lei@okta.com>

* update comments

Signed-off-by: kahoulei <kahou.lei@okta.com>

* update docs

Signed-off-by: kahoulei <kahou.lei@okta.com>

* update annotation name

Signed-off-by: kahoulei <kahou.lei@okta.com>

* rename annotation

Signed-off-by: kahoulei <kahou.lei@okta.com>

* lint check

Signed-off-by: kahoulei <kahou.lei@okta.com>

---------

Signed-off-by: kahoulei <kahou.lei@okta.com>
Co-authored-by: kahoulei <kahou.lei@okta.com>
Co-authored-by: Ishita Sequeira <46771830+ishitasequeira@users.noreply.github.com>
  • Loading branch information
3 people authored Jul 29, 2024
1 parent 6cc898f commit 75e7383
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 5 deletions.
30 changes: 26 additions & 4 deletions controller/cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"net/url"
"os/exec"
"reflect"
"strconv"
"strings"
"sync"
"syscall"
Expand Down Expand Up @@ -67,6 +68,10 @@ const (

// EnvClusterCacheRetryUseBackoff is the env variable to control whether to use a backoff strategy with the retry during cluster cache sync
EnvClusterCacheRetryUseBackoff = "ARGOCD_CLUSTER_CACHE_RETRY_USE_BACKOFF"

// AnnotationIgnoreResourceUpdates when set to true on an untracked resource,
// argo will apply `ignoreResourceUpdates` configuration on it.
AnnotationIgnoreResourceUpdates = "argocd.argoproj.io/ignore-resource-updates"
)

// GitOps engine cluster cache tuning options
Expand Down Expand Up @@ -353,13 +358,30 @@ func skipResourceUpdate(oldInfo, newInfo *ResourceInfo) bool {
// shouldHashManifest validates if the API resource needs to be hashed.
// If there's an app name from resource tracking, or if this is itself an app, we should generate a hash.
// Otherwise, the hashing should be skipped to save CPU time.
func shouldHashManifest(appName string, gvk schema.GroupVersionKind) bool {
// Only hash if the resource belongs to an app.
func shouldHashManifest(appName string, gvk schema.GroupVersionKind, un *unstructured.Unstructured) bool {
// Only hash if the resource belongs to an app OR argocd.argoproj.io/ignore-resource-updates is present and set to true
// Best - Only hash for resources that are part of an app or their dependencies
// (current) - Only hash for resources that are part of an app + all apps that might be from an ApplicationSet
// Orphan - If orphan is enabled, hash should be made on all resource of that namespace and a config to disable it
// Worst - Hash all resources watched by Argo
return appName != "" || (gvk.Group == application.Group && gvk.Kind == application.ApplicationKind)
isTrackedResource := appName != "" || (gvk.Group == application.Group && gvk.Kind == application.ApplicationKind)

// If the resource is not a tracked resource, we will look up argocd.argoproj.io/ignore-resource-updates and decide
// whether we generate hash or not.
// If argocd.argoproj.io/ignore-resource-updates is presented and is true, return true
// Else return false
if !isTrackedResource {
if val, ok := un.GetAnnotations()[AnnotationIgnoreResourceUpdates]; ok {
applyResourcesUpdate, err := strconv.ParseBool(val)
if err != nil {
applyResourcesUpdate = false
}
return applyResourcesUpdate
}
return false
}

return isTrackedResource
}

// isRetryableError is a helper method to see whether an error
Expand Down Expand Up @@ -508,7 +530,7 @@ func (c *liveStateCache) getCluster(server string) (clustercache.ClusterCache, e

gvk := un.GroupVersionKind()

if cacheSettings.ignoreResourceUpdatesEnabled && shouldHashManifest(appName, gvk) {
if cacheSettings.ignoreResourceUpdatesEnabled && shouldHashManifest(appName, gvk, un) {
hash, err := generateManifestHash(un, nil, cacheSettings.resourceOverrides, c.ignoreNormalizerOpts)
if err != nil {
log.Errorf("Failed to generate manifest hash: %v", err)
Expand Down
79 changes: 79 additions & 0 deletions controller/cache/cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"testing"
"time"

"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"

"github.com/stretchr/testify/assert"
v1 "k8s.io/api/core/v1"
apierr "k8s.io/apimachinery/pkg/api/errors"
Expand All @@ -25,6 +27,7 @@ import (
"github.com/argoproj/argo-cd/v2/common"
"github.com/argoproj/argo-cd/v2/controller/metrics"
"github.com/argoproj/argo-cd/v2/controller/sharding"
"github.com/argoproj/argo-cd/v2/pkg/apis/application"
appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
dbmocks "github.com/argoproj/argo-cd/v2/util/db/mocks"
argosettings "github.com/argoproj/argo-cd/v2/util/settings"
Expand Down Expand Up @@ -653,3 +656,79 @@ func TestSkipResourceUpdate(t *testing.T) {
}))
})
}

func TestShouldHashManifest(t *testing.T) {
tests := []struct {
name string
appName string
gvk schema.GroupVersionKind
un *unstructured.Unstructured
annotations map[string]string
want bool
}{
{
name: "appName not empty gvk matches",
appName: "MyApp",
gvk: schema.GroupVersionKind{Group: application.Group, Kind: application.ApplicationKind},
un: &unstructured.Unstructured{},
want: true,
},
{
name: "appName empty",
appName: "",
gvk: schema.GroupVersionKind{Group: application.Group, Kind: application.ApplicationKind},
un: &unstructured.Unstructured{},
want: true,
},
{
name: "appName empty group not match",
appName: "",
gvk: schema.GroupVersionKind{Group: "group1", Kind: application.ApplicationKind},
un: &unstructured.Unstructured{},
want: false,
},
{
name: "appName empty kind not match",
appName: "",
gvk: schema.GroupVersionKind{Group: application.Group, Kind: "kind1"},
un: &unstructured.Unstructured{},
want: false,
},
{
name: "argocd.argoproj.io/ignore-resource-updates=true",
appName: "",
gvk: schema.GroupVersionKind{Group: application.Group, Kind: "kind1"},
un: &unstructured.Unstructured{},
annotations: map[string]string{"argocd.argoproj.io/ignore-resource-updates": "true"},
want: true,
},
{
name: "argocd.argoproj.io/ignore-resource-updates=invalid",
appName: "",
gvk: schema.GroupVersionKind{Group: application.Group, Kind: "kind1"},
un: &unstructured.Unstructured{},
annotations: map[string]string{"argocd.argoproj.io/ignore-resource-updates": "invalid"},
want: false,
},
{
name: "argocd.argoproj.io/ignore-resource-updates=false",
appName: "",
gvk: schema.GroupVersionKind{Group: application.Group, Kind: "kind1"},
un: &unstructured.Unstructured{},
annotations: map[string]string{"argocd.argoproj.io/ignore-resource-updates": "false"},
want: false,
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
if test.annotations != nil {
test.un.SetAnnotations(test.annotations)
}
got := shouldHashManifest(test.appName, test.gvk, test.un)
if test.want != got {
t.Fatalf("test=%v want %v got %v", test.name, test.want, got)
}
})
}
}
55 changes: 54 additions & 1 deletion docs/operator-manual/reconcile.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ By default, an Argo CD Application is refreshed every time a resource that belon

Kubernetes controllers often update the resources they watch periodically, causing continuous reconcile operation on the Application
and a high CPU usage on the `argocd-application-controller`. Argo CD allows you to optionally ignore resource updates on specific fields
for [tracked resources](../user-guide/resource_tracking.md).
for [tracked resources](../user-guide/resource_tracking.md).
For untracked resources, you can [use the argocd.argoproj.io/ignore-resource-updates annotations](#ignoring-updates-for-untracked-resources)

When a resource update is ignored, if the resource's [health status](./health.md) does not change, the Application that this resource belongs to will not be reconciled.

Expand Down Expand Up @@ -111,3 +112,55 @@ data:
# actually changing in content.
- .status.conditions[].lastTransitionTime
```

## Ignoring updates for untracked resources

ArgoCD will only apply `ignoreResourceUpdates` configuration to tracked resources of an application. This means dependant resources, such as a `ReplicaSet` and `Pod` created by a `Deployment`, will not ignore any updates and trigger a reconcile of the application for any changes.

If you want to apply the `ignoreResourceUpdates` configuration to an untracked resource, you can add the
`argocd.argoproj.io/ignore-resource-updates=true` annotation in the dependent resources manifest.

## Example

### CronJob

```yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: hello
namespace: test-cronjob
spec:
schedule: "* * * * *"
jobTemplate:
metadata:
annotations:
argocd.argoproj.io/ignore-resource-updates: "true"
spec:
template:
metadata:
annotations:
argocd.argoproj.io/ignore-resource-updates: "true"
spec:
containers:
- name: hello
image: busybox:1.28
imagePullPolicy: IfNotPresent
command:
- /bin/sh
- -c
- date; echo Hello from the Kubernetes cluster
restartPolicy: OnFailure
```

The resource updates will be ignored based on your the `ignoreResourceUpdates` configuration in the `argocd-cm` configMap:

`argocd-cm`:
```yaml
resource.customizations.ignoreResourceUpdates.batch_Job: |
jsonPointers:
- /status
resource.customizations.ignoreResourceUpdates.Pod: |
jsonPointers:
- /status
```
1 change: 1 addition & 0 deletions docs/user-guide/annotations-and-labels.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
| argocd.argoproj.io/sync-options | any | [see sync options docs](sync-options.md) | Provides a variety of settings to determine how an Application's resources are synced. |
| argocd.argoproj.io/sync-wave | any | [see sync waves docs](sync-waves.md) | |
| argocd.argoproj.io/tracking-id | any | any | Used by Argo CD to track resources it manages. See [resource tracking docs](resource_tracking.md) for details. |
| argocd.argoproj.io/ignore-resource-updates | any | `"true"`, `false` | Used by Argo CD to ignore resource updates. See [reconcile docs](..%2Foperator-manual%2Freconcile.md)reconcile_docs for details. |
| link.argocd.argoproj.io/{some link name} | any | An http(s) URL | Adds a link to the Argo CD UI for the resource. See [external URL docs](external-url.md) for details. |
| pref.argocd.argoproj.io/default-pod-sort | Application | [see UI customization docs](../operator-manual/ui-customization.md) | Sets the Application's default grouping mechanism. |
| pref.argocd.argoproj.io/default-view | Application | [see UI customization docs](../operator-manual/ui-customization.md) | Sets the Application's default view mode (e.g. "tree" or "list") |
Expand Down

0 comments on commit 75e7383

Please sign in to comment.