Compare commits

...

10 Commits

Author SHA1 Message Date
github-actions[bot]
b8b5ea6117 Bump version to 3.3.5 on release-3.3 branch (#27004)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: reggie-k <19544836+reggie-k@users.noreply.github.com>
2026-03-25 15:52:26 +02:00
argo-cd-cherry-pick-bot[bot]
494b44ca5b fix: Hook resources not created at PostSync when configured with PreDelete PostDelete hooks (cherry-pick #26996 for 3.3) (#26999)
Signed-off-by: reggie-k <regina.voloshin@codefresh.io>
Co-authored-by: Regina Voloshin <regina.voloshin@codefresh.io>
2026-03-25 13:24:09 +02:00
argo-cd-cherry-pick-bot[bot]
06d960ddf4 fix(ui): Improve message on self-healing disabling panel (#26977) (cherry-pick #26978 for 3.3) (#26981)
Signed-off-by: Alberto Chiusole <chiusole@seqera.io>
Co-authored-by: Alberto Chiusole <1922124+bebosudo@users.noreply.github.com>
2026-03-24 17:57:52 +02:00
dudinea
476800e479 chore(deps): bump google.golang.org/grpc from 1.77.0 to 1.79.3 for release-3.3 (#26886) (#26953)
Signed-off-by: Eugene Doudine <eugene.doudine@octopus.com>
2026-03-22 15:47:39 +02:00
argo-cd-cherry-pick-bot[bot]
ff207a460b fix(server): fix find container logic for terminal (cherry-pick #26858 for 3.3) (#26934)
Signed-off-by: linghaoSu <linghao.su@daocloud.io>
Co-authored-by: Linghao Su <linghao.su@daocloud.io>
2026-03-20 13:00:32 +01:00
argo-cd-cherry-pick-bot[bot]
ea8a881c14 ci: test against k8s 1.35.0 (cherry-pick #26062 for 3.3) (#26860)
Signed-off-by: reggie-k <regina.voloshin@codefresh.io>
Co-authored-by: Regina Voloshin <regina.voloshin@codefresh.io>
2026-03-20 08:47:45 +02:00
argo-cd-cherry-pick-bot[bot]
03dda413f0 fix(ci): add .gitkeep to images dir (cherry-pick #26892 for 3.3) (#26911)
Signed-off-by: Blake Pettersson <blake.pettersson@gmail.com>
Co-authored-by: Blake Pettersson <blake.pettersson@gmail.com>
2026-03-19 15:36:15 +02:00
argo-cd-cherry-pick-bot[bot]
0dc0a66e7f fix(ui): include _-prefixed dirs in embedded assets (cherry-pick #26589 for 3.3) (#26910)
Signed-off-by: choejwoo <jaewoo45@gmail.com>
Co-authored-by: Jaewoo Choi <jaewoo45@gmail.com>
2026-03-19 15:34:59 +02:00
argo-cd-cherry-pick-bot[bot]
e53c10caec fix(UI): show RollingSync step clearly when labels match no step (cherry-pick #26877 for 3.3) (#26884)
Signed-off-by: Atif Ali <atali@redhat.com>
Co-authored-by: Atif Ali <atali@redhat.com>
2026-03-17 21:43:06 -04:00
argo-cd-cherry-pick-bot[bot]
422cabb648 fix: stack overflow when processing circular ownerrefs in resource graph (#26783) (cherry-pick #26790 for 3.3) (#26879)
Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
Signed-off-by: Jonathan Ogilvie <679297+jcogilvie@users.noreply.github.com>
Co-authored-by: Jonathan Ogilvie <679297+jcogilvie@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-17 22:55:34 +01:00
32 changed files with 532 additions and 168 deletions

View File

@@ -418,14 +418,14 @@ jobs:
# latest: true means that this version mush upload the coverage report to codecov.io
# We designate the latest version because we only collect code coverage for that version.
k3s:
- version: v1.34.2
- version: v1.35.0
latest: true
- version: v1.34.2
latest: false
- version: v1.33.1
latest: false
- version: v1.32.1
latest: false
- version: v1.31.0
latest: false
needs:
- build-go
- changes

View File

@@ -1 +1 @@
3.3.4
3.3.5

View File

@@ -76,6 +76,21 @@ func isPostDeleteHook(obj *unstructured.Unstructured) bool {
return isHookOfType(obj, PostDeleteHookType)
}
// hasGitOpsEngineSyncPhaseHook is true when gitops-engine would run the resource during a sync
// phase (PreSync, Sync, PostSync, SyncFail). PreDelete/PostDelete are not sync phases;
// without this check, state reconciliation drops such resources
// entirely because isPreDeleteHook/isPostDeleteHook match any comma-separated value.
// HookTypeSkip is omitted as it is not a sync phase.
func hasGitOpsEngineSyncPhaseHook(obj *unstructured.Unstructured) bool {
for _, t := range hook.Types(obj) {
switch t {
case common.HookTypePreSync, common.HookTypeSync, common.HookTypePostSync, common.HookTypeSyncFail:
return true
}
}
return false
}
// executeHooks is a generic function to execute hooks of a specified type
func (ctrl *ApplicationController) executeHooks(hookType HookType, app *appv1.Application, proj *appv1.AppProject, liveObjs map[kube.ResourceKey]*unstructured.Unstructured, config *rest.Config, logCtx *log.Entry) (bool, error) {
appLabelKey, err := ctrl.settingsMgr.GetAppInstanceLabelKey()

View File

@@ -192,6 +192,92 @@ func TestIsPostDeleteHook(t *testing.T) {
}
}
// TestPartitionTargetObjsForSync covers partitionTargetObjsForSync in state.go.
func TestPartitionTargetObjsForSync(t *testing.T) {
newObj := func(name string, annot map[string]string) *unstructured.Unstructured {
u := &unstructured.Unstructured{}
u.SetName(name)
u.SetAnnotations(annot)
return u
}
tests := []struct {
name string
in []*unstructured.Unstructured
wantNames []string
wantPreDelete bool
wantPostDelete bool
}{
{
name: "PostSync with PreDelete and PostDelete in same annotation stays in sync set",
in: []*unstructured.Unstructured{
newObj("combined", map[string]string{"argocd.argoproj.io/hook": "PostSync,PreDelete,PostDelete"}),
},
wantNames: []string{"combined"},
wantPreDelete: true,
wantPostDelete: true,
},
{
name: "PreDelete-only manifest excluded from sync",
in: []*unstructured.Unstructured{
newObj("pre-del", map[string]string{"argocd.argoproj.io/hook": "PreDelete"}),
},
wantNames: nil,
wantPreDelete: true,
wantPostDelete: false,
},
{
name: "PostDelete-only manifest excluded from sync",
in: []*unstructured.Unstructured{
newObj("post-del", map[string]string{"argocd.argoproj.io/hook": "PostDelete"}),
},
wantNames: nil,
wantPreDelete: false,
wantPostDelete: true,
},
{
name: "Helm pre-delete only excluded from sync",
in: []*unstructured.Unstructured{
newObj("helm-pre-del", map[string]string{"helm.sh/hook": "pre-delete"}),
},
wantNames: nil,
wantPreDelete: true,
wantPostDelete: false,
},
{
name: "Helm pre-install with pre-delete stays in sync (sync-phase hook wins)",
in: []*unstructured.Unstructured{
newObj("helm-mixed", map[string]string{"helm.sh/hook": "pre-install,pre-delete"}),
},
wantNames: []string{"helm-mixed"},
wantPreDelete: true,
wantPostDelete: false,
},
{
name: "Non-hook resource unchanged",
in: []*unstructured.Unstructured{
newObj("pod", map[string]string{"app": "x"}),
},
wantNames: []string{"pod"},
wantPreDelete: false,
wantPostDelete: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, hasPre, hasPost := partitionTargetObjsForSync(tt.in)
var names []string
for _, o := range got {
names = append(names, o.GetName())
}
assert.Equal(t, tt.wantNames, names)
assert.Equal(t, tt.wantPreDelete, hasPre, "hasPreDeleteHooks")
assert.Equal(t, tt.wantPostDelete, hasPost, "hasPostDeleteHooks")
})
}
}
func TestMultiHookOfType(t *testing.T) {
tests := []struct {
name string

View File

@@ -536,6 +536,28 @@ func isManagedNamespace(ns *unstructured.Unstructured, app *v1alpha1.Application
return ns != nil && ns.GetKind() == kubeutil.NamespaceKind && ns.GetName() == app.Spec.Destination.Namespace && app.Spec.SyncPolicy != nil && app.Spec.SyncPolicy.ManagedNamespaceMetadata != nil
}
// partitionTargetObjsForSync returns the manifest subset passed to gitops-engine sync, and whether
// the full manifest set declared PreDelete and/or PostDelete hooks (for finalizer handling).
// Uses isPreDeleteHook / isPostDeleteHook / hasGitOpsEngineSyncPhaseHook from hook.go.
func partitionTargetObjsForSync(targetObjs []*unstructured.Unstructured) (syncObjs []*unstructured.Unstructured, hasPreDeleteHooks, hasPostDeleteHooks bool) {
for _, obj := range targetObjs {
if isPreDeleteHook(obj) {
hasPreDeleteHooks = true
if !hasGitOpsEngineSyncPhaseHook(obj) {
continue
}
}
if isPostDeleteHook(obj) {
hasPostDeleteHooks = true
if !hasGitOpsEngineSyncPhaseHook(obj) {
continue
}
}
syncObjs = append(syncObjs, obj)
}
return syncObjs, hasPreDeleteHooks, hasPostDeleteHooks
}
// CompareAppState compares application git state to the live app state, using the specified
// revision and supplied source. If revision or overrides are empty, then compares against
// revision and overrides in the app spec.
@@ -763,24 +785,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *v1
}
}
}
hasPreDeleteHooks := false
hasPostDeleteHooks := false
// Filter out PreDelete and PostDelete hooks from targetObjs since they should not be synced
// as regular resources. They are only executed during deletion.
var targetObjsForSync []*unstructured.Unstructured
for _, obj := range targetObjs {
if isPreDeleteHook(obj) {
hasPreDeleteHooks = true
// Skip PreDelete hooks - they are not synced, only executed during deletion
continue
}
if isPostDeleteHook(obj) {
hasPostDeleteHooks = true
// Skip PostDelete hooks - they are not synced, only executed after deletion
continue
}
targetObjsForSync = append(targetObjsForSync, obj)
}
targetObjsForSync, hasPreDeleteHooks, hasPostDeleteHooks := partitionTargetObjsForSync(targetObjs)
reconciliation := sync.Reconcile(targetObjsForSync, liveObjByKey, app.Spec.Destination.Namespace, infoProvider)
ts.AddCheckpoint("live_ms")

View File

@@ -1,5 +1,5 @@
| Argo CD version | Kubernetes versions |
|-----------------|---------------------|
| 3.3 | v1.34, v1.33, v1.32, v1.31 |
| 3.3 | v1.35, v1.34, v1.33, v1.32 |
| 3.2 | v1.34, v1.33, v1.32, v1.31 |
| 3.1 | v1.34, v1.33, v1.32, v1.31 |

View File

@@ -92,6 +92,15 @@ const (
RespectRbacStrict
)
// callState tracks whether action() has been called on a resource during hierarchy iteration.
type callState int
const (
notCalled callState = iota // action() has not been called yet
inProgress // action() is currently being processed (in call stack)
completed // action() has been called and processing is complete
)
type apiMeta struct {
namespaced bool
// watchCancel stops the watch of all resources for this API. This gets called when the cache is invalidated or when
@@ -1186,8 +1195,11 @@ func (c *clusterCache) IterateHierarchyV2(keys []kube.ResourceKey, action func(r
c.lock.RLock()
defer c.lock.RUnlock()
// Track visited resources to avoid cycles
visited := make(map[kube.ResourceKey]int)
// Track whether action() has been called on each resource (notCalled/inProgress/completed).
// This is shared across processNamespaceHierarchy and processCrossNamespaceChildren.
// Note: This is distinct from 'crossNSTraversed' in processCrossNamespaceChildren, which tracks
// whether we've traversed a cluster-scoped key's cross-namespace children.
actionCallState := make(map[kube.ResourceKey]callState)
// Group keys by namespace for efficient processing
keysPerNamespace := make(map[string][]kube.ResourceKey)
@@ -1203,12 +1215,18 @@ func (c *clusterCache) IterateHierarchyV2(keys []kube.ResourceKey, action func(r
for namespace, namespaceKeys := range keysPerNamespace {
nsNodes := c.nsIndex[namespace]
graph := buildGraph(nsNodes)
c.processNamespaceHierarchy(namespaceKeys, nsNodes, graph, visited, action)
c.processNamespaceHierarchy(namespaceKeys, nsNodes, graph, actionCallState, action)
}
// Process pre-computed cross-namespace children
if clusterKeys, ok := keysPerNamespace[""]; ok {
c.processCrossNamespaceChildren(clusterKeys, visited, action)
// Track which cluster-scoped keys have had their cross-namespace children traversed.
// This is distinct from 'actionCallState' - a resource may have had action() called
// (i.e., its actionCallState is in the completed state) but not yet had its cross-namespace
// children traversed. This prevents infinite recursion when resources have circular
// ownerReferences.
crossNSTraversed := make(map[kube.ResourceKey]bool)
c.processCrossNamespaceChildren(clusterKeys, actionCallState, crossNSTraversed, action)
}
}
@@ -1216,12 +1234,21 @@ func (c *clusterCache) IterateHierarchyV2(keys []kube.ResourceKey, action func(r
// This enables traversing from cluster-scoped parents to their namespaced children across namespace boundaries.
// It also handles multi-level hierarchies where cluster-scoped resources own other cluster-scoped resources
// that in turn own namespaced resources (e.g., Provider -> ProviderRevision -> Deployment in Crossplane).
// The crossNSTraversed map tracks which keys have already been processed to prevent infinite recursion
// from circular ownerReferences (e.g., a resource that owns itself).
func (c *clusterCache) processCrossNamespaceChildren(
clusterScopedKeys []kube.ResourceKey,
visited map[kube.ResourceKey]int,
actionCallState map[kube.ResourceKey]callState,
crossNSTraversed map[kube.ResourceKey]bool,
action func(resource *Resource, namespaceResources map[kube.ResourceKey]*Resource) bool,
) {
for _, clusterKey := range clusterScopedKeys {
// Skip if already processed (cycle detection)
if crossNSTraversed[clusterKey] {
continue
}
crossNSTraversed[clusterKey] = true
// Get cluster-scoped resource to access its UID
clusterResource := c.resources[clusterKey]
if clusterResource == nil {
@@ -1236,16 +1263,17 @@ func (c *clusterCache) processCrossNamespaceChildren(
continue
}
alreadyVisited := visited[childKey] != 0
alreadyProcessed := actionCallState[childKey] != notCalled
// If child is cluster-scoped and was already visited by processNamespaceHierarchy,
// If child is cluster-scoped and action() was already called by processNamespaceHierarchy,
// we still need to recursively check for its cross-namespace children.
// This handles multi-level hierarchies like: ClusterScoped -> ClusterScoped -> Namespaced
// (e.g., Crossplane's Provider -> ProviderRevision -> Deployment)
if alreadyVisited {
if alreadyProcessed {
if childKey.Namespace == "" {
// Recursively process cross-namespace children of this cluster-scoped child
c.processCrossNamespaceChildren([]kube.ResourceKey{childKey}, visited, action)
// The crossNSTraversed map prevents infinite recursion on circular ownerReferences
c.processCrossNamespaceChildren([]kube.ResourceKey{childKey}, actionCallState, crossNSTraversed, action)
}
continue
}
@@ -1258,16 +1286,16 @@ func (c *clusterCache) processCrossNamespaceChildren(
// Process this child
if action(child, nsNodes) {
visited[childKey] = 1
actionCallState[childKey] = inProgress
// Recursively process descendants using index-based traversal
c.iterateChildrenUsingIndex(child, nsNodes, visited, action)
c.iterateChildrenUsingIndex(child, nsNodes, actionCallState, action)
// If this child is also cluster-scoped, recursively process its cross-namespace children
if childKey.Namespace == "" {
c.processCrossNamespaceChildren([]kube.ResourceKey{childKey}, visited, action)
c.processCrossNamespaceChildren([]kube.ResourceKey{childKey}, actionCallState, crossNSTraversed, action)
}
visited[childKey] = 2
actionCallState[childKey] = completed
}
}
}
@@ -1278,14 +1306,14 @@ func (c *clusterCache) processCrossNamespaceChildren(
func (c *clusterCache) iterateChildrenUsingIndex(
parent *Resource,
nsNodes map[kube.ResourceKey]*Resource,
visited map[kube.ResourceKey]int,
actionCallState map[kube.ResourceKey]callState,
action func(resource *Resource, namespaceResources map[kube.ResourceKey]*Resource) bool,
) {
// Look up direct children of this parent using the index
childKeys := c.parentUIDToChildren[parent.Ref.UID]
for _, childKey := range childKeys {
if visited[childKey] != 0 {
continue // Already visited or in progress
if actionCallState[childKey] != notCalled {
continue // action() already called or in progress
}
child := c.resources[childKey]
@@ -1300,10 +1328,10 @@ func (c *clusterCache) iterateChildrenUsingIndex(
}
if action(child, nsNodes) {
visited[childKey] = 1
actionCallState[childKey] = inProgress
// Recursively process this child's descendants
c.iterateChildrenUsingIndex(child, nsNodes, visited, action)
visited[childKey] = 2
c.iterateChildrenUsingIndex(child, nsNodes, actionCallState, action)
actionCallState[childKey] = completed
}
}
}
@@ -1313,22 +1341,19 @@ func (c *clusterCache) processNamespaceHierarchy(
namespaceKeys []kube.ResourceKey,
nsNodes map[kube.ResourceKey]*Resource,
graph map[kube.ResourceKey]map[types.UID]*Resource,
visited map[kube.ResourceKey]int,
actionCallState map[kube.ResourceKey]callState,
action func(resource *Resource, namespaceResources map[kube.ResourceKey]*Resource) bool,
) {
for _, key := range namespaceKeys {
visited[key] = 0
}
for _, key := range namespaceKeys {
res := c.resources[key]
if visited[key] == 2 || !action(res, nsNodes) {
if actionCallState[key] == completed || !action(res, nsNodes) {
continue
}
visited[key] = 1
actionCallState[key] = inProgress
if _, ok := graph[key]; ok {
for _, child := range graph[key] {
if visited[child.ResourceKey()] == 0 && action(child, nsNodes) {
child.iterateChildrenV2(graph, nsNodes, visited, func(err error, child *Resource, namespaceResources map[kube.ResourceKey]*Resource) bool {
if actionCallState[child.ResourceKey()] == notCalled && action(child, nsNodes) {
child.iterateChildrenV2(graph, nsNodes, actionCallState, func(err error, child *Resource, namespaceResources map[kube.ResourceKey]*Resource) bool {
if err != nil {
c.log.V(2).Info(err.Error())
return false
@@ -1338,7 +1363,7 @@ func (c *clusterCache) processNamespaceHierarchy(
}
}
}
visited[key] = 2
actionCallState[key] = completed
}
}

View File

@@ -2189,3 +2189,112 @@ func TestIterateHierarchyV2_NoDuplicatesCrossNamespace(t *testing.T) {
assert.Equal(t, 1, visitCount["namespaced-child"], "namespaced child should be visited once")
assert.Equal(t, 1, visitCount["cluster-child"], "cluster child should be visited once")
}
func TestIterateHierarchyV2_CircularOwnerReference_NoStackOverflow(t *testing.T) {
// Test that self-referencing resources (circular ownerReferences) don't cause stack overflow.
// This reproduces the bug reported in https://github.com/argoproj/argo-cd/issues/26783
// where a resource with an ownerReference pointing to itself caused infinite recursion.
// Create a cluster-scoped resource that owns itself (self-referencing)
selfReferencingResource := &corev1.Namespace{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "Namespace",
},
ObjectMeta: metav1.ObjectMeta{
Name: "self-referencing",
UID: "self-ref-uid",
ResourceVersion: "1",
OwnerReferences: []metav1.OwnerReference{{
APIVersion: "v1",
Kind: "Namespace",
Name: "self-referencing",
UID: "self-ref-uid", // Points to itself
}},
},
}
cluster := newCluster(t, selfReferencingResource).WithAPIResources([]kube.APIResourceInfo{{
GroupKind: schema.GroupKind{Group: "", Kind: "Namespace"},
GroupVersionResource: schema.GroupVersionResource{Group: "", Version: "v1", Resource: "namespaces"},
Meta: metav1.APIResource{Namespaced: false},
}})
err := cluster.EnsureSynced()
require.NoError(t, err)
visitCount := 0
// This should complete without stack overflow
cluster.IterateHierarchyV2(
[]kube.ResourceKey{kube.GetResourceKey(mustToUnstructured(selfReferencingResource))},
func(resource *Resource, _ map[kube.ResourceKey]*Resource) bool {
visitCount++
return true
},
)
// The self-referencing resource should be visited exactly once
assert.Equal(t, 1, visitCount, "self-referencing resource should be visited exactly once")
}
func TestIterateHierarchyV2_CircularOwnerChain_NoStackOverflow(t *testing.T) {
// Test that circular ownership chains (A -> B -> A) don't cause stack overflow.
// This is a more complex case where two resources own each other.
resourceA := &corev1.Namespace{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "Namespace",
},
ObjectMeta: metav1.ObjectMeta{
Name: "resource-a",
UID: "uid-a",
ResourceVersion: "1",
OwnerReferences: []metav1.OwnerReference{{
APIVersion: "v1",
Kind: "Namespace",
Name: "resource-b",
UID: "uid-b", // A is owned by B
}},
},
}
resourceB := &corev1.Namespace{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "Namespace",
},
ObjectMeta: metav1.ObjectMeta{
Name: "resource-b",
UID: "uid-b",
ResourceVersion: "1",
OwnerReferences: []metav1.OwnerReference{{
APIVersion: "v1",
Kind: "Namespace",
Name: "resource-a",
UID: "uid-a", // B is owned by A
}},
},
}
cluster := newCluster(t, resourceA, resourceB).WithAPIResources([]kube.APIResourceInfo{{
GroupKind: schema.GroupKind{Group: "", Kind: "Namespace"},
GroupVersionResource: schema.GroupVersionResource{Group: "", Version: "v1", Resource: "namespaces"},
Meta: metav1.APIResource{Namespaced: false},
}})
err := cluster.EnsureSynced()
require.NoError(t, err)
visitCount := make(map[string]int)
// This should complete without stack overflow
cluster.IterateHierarchyV2(
[]kube.ResourceKey{kube.GetResourceKey(mustToUnstructured(resourceA))},
func(resource *Resource, _ map[kube.ResourceKey]*Resource) bool {
visitCount[resource.Ref.Name]++
return true
},
)
// Each resource in the circular chain should be visited exactly once
assert.Equal(t, 1, visitCount["resource-a"], "resource-a should be visited exactly once")
assert.Equal(t, 1, visitCount["resource-b"], "resource-b should be visited exactly once")
}

View File

@@ -76,16 +76,16 @@ func (r *Resource) toOwnerRef() metav1.OwnerReference {
}
// iterateChildrenV2 is a depth-first traversal of the graph of resources starting from the current resource.
func (r *Resource) iterateChildrenV2(graph map[kube.ResourceKey]map[types.UID]*Resource, ns map[kube.ResourceKey]*Resource, visited map[kube.ResourceKey]int, action func(err error, child *Resource, namespaceResources map[kube.ResourceKey]*Resource) bool) {
func (r *Resource) iterateChildrenV2(graph map[kube.ResourceKey]map[types.UID]*Resource, ns map[kube.ResourceKey]*Resource, actionCallState map[kube.ResourceKey]callState, action func(err error, child *Resource, namespaceResources map[kube.ResourceKey]*Resource) bool) {
key := r.ResourceKey()
if visited[key] == 2 {
if actionCallState[key] == completed {
return
}
// this indicates that we've started processing this node's children
visited[key] = 1
actionCallState[key] = inProgress
defer func() {
// this indicates that we've finished processing this node's children
visited[key] = 2
actionCallState[key] = completed
}()
children, ok := graph[key]
if !ok || children == nil {
@@ -94,13 +94,13 @@ func (r *Resource) iterateChildrenV2(graph map[kube.ResourceKey]map[types.UID]*R
for _, child := range children {
childKey := child.ResourceKey()
// For cross-namespace relationships, child might not be in ns, so use it directly from graph
switch visited[childKey] {
case 1:
switch actionCallState[childKey] {
case inProgress:
// Since we encountered a node that we're currently processing, we know we have a circular dependency.
_ = action(fmt.Errorf("circular dependency detected. %s is child and parent of %s", childKey.String(), key.String()), child, ns)
case 0:
case notCalled:
if action(nil, child, ns) {
child.iterateChildrenV2(graph, ns, visited, action)
child.iterateChildrenV2(graph, ns, actionCallState, action)
}
}
}

6
go.mod
View File

@@ -99,8 +99,8 @@ require (
golang.org/x/sync v0.19.0
golang.org/x/term v0.38.0
golang.org/x/time v0.14.0
google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8
google.golang.org/grpc v1.77.0
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217
google.golang.org/grpc v1.79.3
google.golang.org/protobuf v1.36.11
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.1
@@ -291,7 +291,7 @@ require (
gomodules.xyz/notify v0.1.1 // indirect
google.golang.org/api v0.223.0 // indirect
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df // indirect

12
go.sum
View File

@@ -1360,10 +1360,10 @@ google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y=
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s=
google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8 h1:mepRgnBZa07I4TRuomDE4sTIYieg/osKmzIf4USdWS4=
google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 h1:M1rk8KBnUsBDg1oPGHNCxG4vc1f49epmTO7xscSajMk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls=
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -1378,8 +1378,8 @@ google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM=
google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig=
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=

View File

@@ -12,4 +12,4 @@ resources:
images:
- name: quay.io/argoproj/argocd
newName: quay.io/argoproj/argocd
newTag: v3.3.4
newTag: v3.3.5

View File

@@ -5,7 +5,7 @@ kind: Kustomization
images:
- name: quay.io/argoproj/argocd
newName: quay.io/argoproj/argocd
newTag: v3.3.4
newTag: v3.3.5
resources:
- ./application-controller
- ./dex

View File

@@ -31273,7 +31273,7 @@ spec:
key: applicationsetcontroller.status.max.resources.count
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -31408,7 +31408,7 @@ spec:
key: log.format.timestamp
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -31536,7 +31536,7 @@ spec:
- argocd
- admin
- redis-initial-password
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: IfNotPresent
name: secret-init
securityContext:
@@ -31833,7 +31833,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -31886,7 +31886,7 @@ spec:
command:
- sh
- -c
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -32234,7 +32234,7 @@ spec:
optional: true
- name: KUBECACHEDIR
value: /tmp/kubecache
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
name: argocd-application-controller
ports:

View File

@@ -31241,7 +31241,7 @@ spec:
key: applicationsetcontroller.status.max.resources.count
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -31370,7 +31370,7 @@ spec:
- argocd
- admin
- redis-initial-password
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: IfNotPresent
name: secret-init
securityContext:
@@ -31667,7 +31667,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -31720,7 +31720,7 @@ spec:
command:
- sh
- -c
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -32068,7 +32068,7 @@ spec:
optional: true
- name: KUBECACHEDIR
value: /tmp/kubecache
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
name: argocd-application-controller
ports:

View File

@@ -12,4 +12,4 @@ resources:
images:
- name: quay.io/argoproj/argocd
newName: quay.io/argoproj/argocd
newTag: v3.3.4
newTag: v3.3.5

View File

@@ -12,7 +12,7 @@ patches:
images:
- name: quay.io/argoproj/argocd
newName: quay.io/argoproj/argocd
newTag: v3.3.4
newTag: v3.3.5
resources:
- ../../base/application-controller
- ../../base/applicationset-controller

View File

@@ -32639,7 +32639,7 @@ spec:
key: applicationsetcontroller.status.max.resources.count
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -32774,7 +32774,7 @@ spec:
key: log.format.timestamp
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -32925,7 +32925,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -33021,7 +33021,7 @@ spec:
key: notificationscontroller.repo.server.plaintext
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -33145,7 +33145,7 @@ spec:
- argocd
- admin
- redis-initial-password
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: IfNotPresent
name: secret-init
securityContext:
@@ -33468,7 +33468,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -33521,7 +33521,7 @@ spec:
command:
- sh
- -c
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -33895,7 +33895,7 @@ spec:
key: server.sync.replace.allowed
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -34279,7 +34279,7 @@ spec:
optional: true
- name: KUBECACHEDIR
value: /tmp/kubecache
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
name: argocd-application-controller
ports:

View File

@@ -32609,7 +32609,7 @@ spec:
key: applicationsetcontroller.status.max.resources.count
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -32761,7 +32761,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -32857,7 +32857,7 @@ spec:
key: notificationscontroller.repo.server.plaintext
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -32981,7 +32981,7 @@ spec:
- argocd
- admin
- redis-initial-password
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: IfNotPresent
name: secret-init
securityContext:
@@ -33304,7 +33304,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -33357,7 +33357,7 @@ spec:
command:
- sh
- -c
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -33731,7 +33731,7 @@ spec:
key: server.sync.replace.allowed
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -34115,7 +34115,7 @@ spec:
optional: true
- name: KUBECACHEDIR
value: /tmp/kubecache
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
name: argocd-application-controller
ports:

View File

@@ -1897,7 +1897,7 @@ spec:
key: applicationsetcontroller.status.max.resources.count
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -2032,7 +2032,7 @@ spec:
key: log.format.timestamp
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -2183,7 +2183,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -2279,7 +2279,7 @@ spec:
key: notificationscontroller.repo.server.plaintext
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -2403,7 +2403,7 @@ spec:
- argocd
- admin
- redis-initial-password
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: IfNotPresent
name: secret-init
securityContext:
@@ -2726,7 +2726,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -2779,7 +2779,7 @@ spec:
command:
- sh
- -c
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -3153,7 +3153,7 @@ spec:
key: server.sync.replace.allowed
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -3537,7 +3537,7 @@ spec:
optional: true
- name: KUBECACHEDIR
value: /tmp/kubecache
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
name: argocd-application-controller
ports:

View File

@@ -1867,7 +1867,7 @@ spec:
key: applicationsetcontroller.status.max.resources.count
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -2019,7 +2019,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -2115,7 +2115,7 @@ spec:
key: notificationscontroller.repo.server.plaintext
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -2239,7 +2239,7 @@ spec:
- argocd
- admin
- redis-initial-password
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: IfNotPresent
name: secret-init
securityContext:
@@ -2562,7 +2562,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -2615,7 +2615,7 @@ spec:
command:
- sh
- -c
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -2989,7 +2989,7 @@ spec:
key: server.sync.replace.allowed
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -3373,7 +3373,7 @@ spec:
optional: true
- name: KUBECACHEDIR
value: /tmp/kubecache
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
name: argocd-application-controller
ports:

View File

@@ -31717,7 +31717,7 @@ spec:
key: applicationsetcontroller.status.max.resources.count
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -31852,7 +31852,7 @@ spec:
key: log.format.timestamp
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -32003,7 +32003,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -32099,7 +32099,7 @@ spec:
key: notificationscontroller.repo.server.plaintext
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -32201,7 +32201,7 @@ spec:
- argocd
- admin
- redis-initial-password
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: IfNotPresent
name: secret-init
securityContext:
@@ -32498,7 +32498,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -32551,7 +32551,7 @@ spec:
command:
- sh
- -c
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -32923,7 +32923,7 @@ spec:
key: server.sync.replace.allowed
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -33307,7 +33307,7 @@ spec:
optional: true
- name: KUBECACHEDIR
value: /tmp/kubecache
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
name: argocd-application-controller
ports:

16
manifests/install.yaml generated
View File

@@ -31685,7 +31685,7 @@ spec:
key: applicationsetcontroller.status.max.resources.count
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -31837,7 +31837,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -31933,7 +31933,7 @@ spec:
key: notificationscontroller.repo.server.plaintext
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -32035,7 +32035,7 @@ spec:
- argocd
- admin
- redis-initial-password
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: IfNotPresent
name: secret-init
securityContext:
@@ -32332,7 +32332,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -32385,7 +32385,7 @@ spec:
command:
- sh
- -c
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -32757,7 +32757,7 @@ spec:
key: server.sync.replace.allowed
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -33141,7 +33141,7 @@ spec:
optional: true
- name: KUBECACHEDIR
value: /tmp/kubecache
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
name: argocd-application-controller
ports:

View File

@@ -975,7 +975,7 @@ spec:
key: applicationsetcontroller.status.max.resources.count
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -1110,7 +1110,7 @@ spec:
key: log.format.timestamp
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -1261,7 +1261,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -1357,7 +1357,7 @@ spec:
key: notificationscontroller.repo.server.plaintext
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -1459,7 +1459,7 @@ spec:
- argocd
- admin
- redis-initial-password
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: IfNotPresent
name: secret-init
securityContext:
@@ -1756,7 +1756,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -1809,7 +1809,7 @@ spec:
command:
- sh
- -c
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -2181,7 +2181,7 @@ spec:
key: server.sync.replace.allowed
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -2565,7 +2565,7 @@ spec:
optional: true
- name: KUBECACHEDIR
value: /tmp/kubecache
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
name: argocd-application-controller
ports:

View File

@@ -943,7 +943,7 @@ spec:
key: applicationsetcontroller.status.max.resources.count
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -1095,7 +1095,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -1191,7 +1191,7 @@ spec:
key: notificationscontroller.repo.server.plaintext
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -1293,7 +1293,7 @@ spec:
- argocd
- admin
- redis-initial-password
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: IfNotPresent
name: secret-init
securityContext:
@@ -1590,7 +1590,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -1643,7 +1643,7 @@ spec:
command:
- sh
- -c
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -2015,7 +2015,7 @@ spec:
key: server.sync.replace.allowed
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -2399,7 +2399,7 @@ spec:
optional: true
- name: KUBECACHEDIR
value: /tmp/kubecache
image: quay.io/argoproj/argocd:v3.3.4
image: quay.io/argoproj/argocd:v3.3.5
imagePullPolicy: Always
name: argocd-application-controller
ports:

View File

@@ -209,21 +209,9 @@ func (s *terminalHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
if pod.Status.Phase != corev1.PodRunning {
http.Error(w, "Pod not running", http.StatusBadRequest)
return
}
var findContainer bool
for _, c := range pod.Spec.Containers {
if container == c.Name {
findContainer = true
break
}
}
if !findContainer {
fieldLog.Warn("terminal container not found")
http.Error(w, "Cannot find container", http.StatusBadRequest)
if !containerRunning(pod, container) {
fieldLog.Warn("terminal container not running")
http.Error(w, "container find running", http.StatusBadRequest)
return
}
@@ -272,6 +260,20 @@ func podExists(treeNodes []appv1.ResourceNode, podName, namespace string) bool {
return false
}
func containerRunning(pod *corev1.Pod, containerName string) bool {
return containerStatusRunning(pod.Status.ContainerStatuses, containerName) ||
containerStatusRunning(pod.Status.InitContainerStatuses, containerName)
}
func containerStatusRunning(statuses []corev1.ContainerStatus, containerName string) bool {
for i := range statuses {
if statuses[i].Name == containerName {
return statuses[i].State.Running != nil
}
}
return false
}
const EndOfTransmission = "\u0004"
// PtyHandler is what remotecommand expects from a pty

View File

@@ -5,9 +5,12 @@ import (
"net/http/httptest"
"strings"
"testing"
"time"
"github.com/argoproj/gitops-engine/pkg/utils/kube"
"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
appv1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v3/util/argo"
@@ -79,6 +82,115 @@ func TestPodExists(t *testing.T) {
}
}
func TestContainerRunning(t *testing.T) {
for _, tcase := range []struct {
name string
pod *corev1.Pod
containerName string
expectedResult bool
}{
{
name: "empty container",
pod: &corev1.Pod{},
containerName: "",
expectedResult: false,
},
{
name: "container not found",
pod: &corev1.Pod{},
containerName: "not-found",
expectedResult: false,
},
{
name: "container running",
pod: &corev1.Pod{
Status: corev1.PodStatus{
ContainerStatuses: []corev1.ContainerStatus{
{
Name: "test",
State: corev1.ContainerState{
Running: &corev1.ContainerStateRunning{
StartedAt: metav1.NewTime(time.Now()),
},
},
},
},
},
},
containerName: "test",
expectedResult: true,
},
{
name: "init container running",
pod: &corev1.Pod{
Status: corev1.PodStatus{
ContainerStatuses: []corev1.ContainerStatus{
{
Name: "test",
State: corev1.ContainerState{
Running: &corev1.ContainerStateRunning{
StartedAt: metav1.NewTime(time.Now()),
},
},
},
},
InitContainerStatuses: []corev1.ContainerStatus{
{
Name: "test-init",
State: corev1.ContainerState{
Running: &corev1.ContainerStateRunning{
StartedAt: metav1.NewTime(time.Now()),
},
},
},
},
},
},
containerName: "test-init",
expectedResult: true,
},
{
name: "container not running",
pod: &corev1.Pod{
Status: corev1.PodStatus{
ContainerStatuses: []corev1.ContainerStatus{
{
Name: "test",
State: corev1.ContainerState{
Running: nil,
},
},
},
},
},
containerName: "test",
expectedResult: false,
},
{
name: "init container not running",
pod: &corev1.Pod{
Status: corev1.PodStatus{
InitContainerStatuses: []corev1.ContainerStatus{
{
Name: "test-init",
State: corev1.ContainerState{
Running: nil,
},
},
},
},
},
containerName: "test-init",
expectedResult: false,
},
} {
t.Run(tcase.name, func(t *testing.T) {
result := containerRunning(tcase.pod, tcase.containerName)
assert.Equalf(t, tcase.expectedResult, result, "Expected result %v, but got %v", tcase.expectedResult, result)
})
}
}
func TestIsValidPodName(t *testing.T) {
for _, tcase := range []struct {
name string

View File

View File

@@ -5,4 +5,5 @@ import "embed"
// Embedded contains embedded UI resources
//
//go:embed dist/app
//go:embed all:dist/app/assets/images/resources
var Embedded embed.FS

View File

@@ -8,6 +8,7 @@ import {services} from '../../../shared/services';
import {
ApplicationSyncWindowStatusIcon,
ComparisonStatusIcon,
formatApplicationSetProgressiveSyncStep,
getAppDefaultSource,
getAppDefaultSyncRevisionExtra,
getAppOperationState,
@@ -134,7 +135,7 @@ const ProgressiveSyncStatus = ({application}: {application: models.Application})
<div className='application-status-panel__item-value' style={{color: getProgressiveSyncStatusColor(appResource.status)}}>
{getProgressiveSyncStatusIcon({status: appResource.status})}&nbsp;{appResource.status}
</div>
{appResource?.step && <div className='application-status-panel__item-value'>Wave: {appResource.step}</div>}
{appResource?.step !== undefined && <div className='application-status-panel__item-value'>{formatApplicationSetProgressiveSyncStep(appResource.step)}</div>}
{lastTransitionTime && (
<div className='application-status-panel__item-name' style={{marginBottom: '0.5em'}}>
Last Transition: <br />

View File

@@ -562,7 +562,7 @@ export const ApplicationSummary = (props: ApplicationSummaryProps) => {
selfHeal ? 'Enable Self Heal?' : 'Disable Self Heal?',
selfHeal
? 'If checked, application will automatically sync when changes are detected'
: 'Are you sure you want to enable automated self healing?',
: 'If unchecked, application will not automatically sync when changes are detected',
automated.prune,
selfHeal,
automated.enabled

View File

@@ -1803,6 +1803,14 @@ export function getAppUrl(app: appModels.AbstractApplication): string {
return `${basePath}/${app.metadata.namespace}/${app.metadata.name}`;
}
/** RollingSync step for display; backend uses -1 when no step matches the app's labels. */
export function formatApplicationSetProgressiveSyncStep(step: string | undefined): string {
if (step === '-1') {
return 'Step: unmatched label';
}
return `Step: ${step ?? ''}`;
}
export const getProgressiveSyncStatusIcon = ({status, isButton}: {status: string; isButton?: boolean}) => {
const getIconProps = () => {
switch (status) {