fix(ui): reduce loaded application data on list operation (Relates [#15509]) (#25451)

Signed-off-by: Antoine Veuiller <[email protected]>
Signed-off-by: Michael Crenshaw <[email protected]>
Co-authored-by: Michael Crenshaw <[email protected]>
Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Antoine Veuiller
2026-06-10 21:42:25 +00:00
committed by GitHub
co-authored by Michael Crenshaw Cursor
parent c4095370d5
commit 8259401ed4
18 changed files with 951 additions and 843 deletions
+1 -1
View File
@@ -37,4 +37,4 @@ cmd/argocd-server/argocd-server
# ignore generated `.argocd-helm-dep-up` marker file; this should not be committed to git
reposerver/repository/testdata/**/.argocd-helm-dep-up
.claude
.claude
+4
View File
@@ -8049,6 +8049,10 @@
"items": {
"type": "string"
}
},
"isAppOfApps": {
"description": "IsAppOfApps holds true if the application has any application for child resource.",
"type": "boolean"
}
}
},
+4
View File
@@ -6132,6 +6132,10 @@ spec:
items:
type: string
type: array
isAppOfApps:
description: IsAppOfApps holds true if the application has any
application for child resource.
type: boolean
type: object
sync:
description: Sync contains information about the application's current
+4
View File
@@ -6132,6 +6132,10 @@ spec:
items:
type: string
type: array
isAppOfApps:
description: IsAppOfApps holds true if the application has any
application for child resource.
type: boolean
type: object
sync:
description: Sync contains information about the application's current
+4
View File
@@ -6131,6 +6131,10 @@ spec:
items:
type: string
type: array
isAppOfApps:
description: IsAppOfApps holds true if the application has any
application for child resource.
type: boolean
type: object
sync:
description: Sync contains information about the application's current
+4
View File
@@ -6132,6 +6132,10 @@ spec:
items:
type: string
type: array
isAppOfApps:
description: IsAppOfApps holds true if the application has any
application for child resource.
type: boolean
type: object
sync:
description: Sync contains information about the application's current
+4
View File
@@ -6132,6 +6132,10 @@ spec:
items:
type: string
type: array
isAppOfApps:
description: IsAppOfApps holds true if the application has any
application for child resource.
type: boolean
type: object
sync:
description: Sync contains information about the application's current
+4
View File
@@ -6132,6 +6132,10 @@ spec:
items:
type: string
type: array
isAppOfApps:
description: IsAppOfApps holds true if the application has any
application for child resource.
type: boolean
type: object
sync:
description: Sync contains information about the application's current
+4
View File
@@ -6132,6 +6132,10 @@ spec:
items:
type: string
type: array
isAppOfApps:
description: IsAppOfApps holds true if the application has any
application for child resource.
type: boolean
type: object
sync:
description: Sync contains information about the application's current
@@ -29,10 +29,17 @@ var appFields = map[string]func(app *v1alpha1.Application) any{
"metadata.creationTimestamp": func(app *v1alpha1.Application) any { return app.CreationTimestamp },
"metadata.deletionTimestamp": func(app *v1alpha1.Application) any { return app.DeletionTimestamp },
"spec": func(app *v1alpha1.Application) any { return app.Spec },
"spec.destination": func(app *v1alpha1.Application) any { return app.Spec.Destination },
"spec.project": func(app *v1alpha1.Application) any { return app.Spec.Project },
"spec.source": func(app *v1alpha1.Application) any { return app.Spec.Source },
"spec.sources": func(app *v1alpha1.Application) any { return app.Spec.Sources },
"spec.sourceHydrator": func(app *v1alpha1.Application) any { return app.Spec.SourceHydrator },
"spec.syncPolicy": func(app *v1alpha1.Application) any { return app.Spec.SyncPolicy },
"status.sourceHydrator": func(app *v1alpha1.Application) any { return app.Status.SourceHydrator },
"status.sync.status": func(app *v1alpha1.Application) any { return app.Status.Sync.Status },
"status.health": func(app *v1alpha1.Application) any { return app.Status.Health },
"status.summary": func(app *v1alpha1.Application) any { return app.Status.Summary },
"status.sync.status": func(app *v1alpha1.Application) any { return app.Status.Sync.Status },
"status.sync.revision": func(app *v1alpha1.Application) any { return app.Status.Sync.Revision },
"status.health": func(app *v1alpha1.Application) any { return app.Status.Health },
"status.operationState.startedAt": func(app *v1alpha1.Application) any {
if app.Status.OperationState != nil {
return app.Status.OperationState.StartedAt
File diff suppressed because it is too large Load Diff
@@ -766,6 +766,9 @@ message ApplicationSummary {
// Images holds all images of application child resources.
repeated string images = 2;
// IsAppOfApps holds true if the application has any application for child resource.
optional bool isAppOfApps = 3;
}
// ApplicationTree represents the hierarchical structure of resources associated with an Argo CD application.
+10 -2
View File
@@ -2104,6 +2104,8 @@ type ApplicationSummary struct {
ExternalURLs []string `json:"externalURLs,omitempty" protobuf:"bytes,1,opt,name=externalURLs"`
// Images holds all images of application child resources.
Images []string `json:"images,omitempty" protobuf:"bytes,2,opt,name=images"`
// IsAppOfApps holds true if the application has any application for child resource.
IsAppOfApps bool `json:"isAppOfApps,omitempty" protobuf:"bytes,3,opt,name=isAppOfApps"`
}
// FindNode searches for a resource node in the application tree.
@@ -2126,11 +2128,14 @@ func (t *ApplicationTree) FindNode(group string, kind string, namespace string,
// - It extracts container images referenced in the application's resources.
// - Additionally, it includes links from application annotations that start with `common.AnnotationKeyLinkPrefix`.
// - The collected URLs and images are sorted alphabetically before being returned.
// - It also determines if the Application is managing other Applications (App of Apps pattern)
//
// Returns an `ApplicationSummary` containing a list of unique external URLs and container images.
// Returns an `ApplicationSummary` containing a list of unique external URLs, container images,
// and metadata such as app of apps pattern.
func (t *ApplicationTree) GetSummary(app *Application) ApplicationSummary {
urlsSet := make(map[string]bool)
imagesSet := make(map[string]bool)
appOfApps := false
// Collect external URLs and container images from application nodes
for _, node := range t.Nodes {
@@ -2142,6 +2147,9 @@ func (t *ApplicationTree) GetSummary(app *Application) ApplicationSummary {
for _, image := range node.Images {
imagesSet[image] = true
}
if node.Group == "argoproj.io" && node.Kind == "Application" {
appOfApps = true
}
}
// Include application-specific links from annotations
@@ -2163,7 +2171,7 @@ func (t *ApplicationTree) GetSummary(app *Application) ApplicationSummary {
}
sort.Strings(images)
return ApplicationSummary{ExternalURLs: urls, Images: images}
return ApplicationSummary{ExternalURLs: urls, Images: images, IsAppOfApps: appOfApps}
}
// ResourceRef includes fields which uniquely identify a resource
@@ -4233,6 +4233,29 @@ func TestGetSummary(t *testing.T) {
assert.Equal(t, url, summary.ExternalURLs[0])
}
func TestGetAppOfAppSummary(t *testing.T) {
app := newTestApp()
standardTree := &ApplicationTree{
Nodes: []ResourceNode{
{ResourceRef: ResourceRef{Name: "any-service", Kind: "Service"}},
},
}
summary := standardTree.GetSummary(app)
assert.Empty(t, summary.ExternalURLs)
assert.Empty(t, summary.Images)
assert.False(t, summary.IsAppOfApps)
appOfAppsTree := &ApplicationTree{
Nodes: []ResourceNode{
{ResourceRef: ResourceRef{Name: "children-app", Kind: "Application", Group: "argoproj.io"}},
{ResourceRef: ResourceRef{Name: "any-service", Kind: "Service", Group: ""}},
},
}
summary = appOfAppsTree.GetSummary(app)
assert.True(t, summary.IsAppOfApps)
}
func TestApplicationSourcePluginParameters_Environ_string(t *testing.T) {
params := ApplicationSourcePluginParameters{
{
@@ -48,7 +48,7 @@ export const ApplicationSyncPanel = ({application, selectedResource, hide}: {app
<Spinner show={isPending} style={{marginRight: '5px'}} />
Synchronize
</button>{' '}
<button onClick={() => hide()} className='argo-button argo-button--base-o'>
<button onClick={() => hide()} qe-id='application-sync-panel-button-cancel' className='argo-button argo-button--base-o'>
Cancel
</button>
</div>
@@ -38,17 +38,21 @@ const APP_FIELDS = [
'metadata.labels',
'metadata.creationTimestamp',
'metadata.deletionTimestamp',
'spec',
'spec.destination',
'spec.project',
'spec.source',
'spec.sources',
'spec.sourceHydrator',
'spec.syncPolicy',
'operation.sync',
'status.sourceHydrator',
'status.summary',
'status.sync.status',
'status.sync.revision',
'status.health',
'status.operationState.phase',
'status.operationState.finishedAt',
'status.operationState.operation.sync',
'status.summary',
'status.resources'
'status.operationState.startedAt',
'status.operationState.finishedAt'
];
const APP_LIST_FIELDS = ['metadata.resourceVersion', ...APP_FIELDS.map(field => `items.${field}`)];
const APP_WATCH_FIELDS = ['result.type', ...APP_FIELDS.map(field => `result.application.${field}`)];
@@ -192,13 +196,8 @@ const ViewPref = ({children}: {children: (pref: AppsListPreferences & {page: num
function filterApplications(applications: models.Application[], pref: AppsListPreferences, search: string): {filteredApps: models.Application[]; filterResults: FilteredApp[]} {
const processedApps = applications.map(app => {
let isAppOfAppsPattern = false;
if (app.status?.resources) {
for (const resource of app.status.resources) {
if (resource.kind === 'Application') {
isAppOfAppsPattern = true;
break;
}
}
if (app.status.summary?.isAppOfApps === true) {
isAppOfAppsPattern = true;
}
return {...app, isAppOfAppsPattern};
});
@@ -57,11 +57,15 @@ export const ApplicationsSyncPanel = ({show, apps, hide}: {show: boolean; apps:
onClose={() => hide()}
header={
<div>
<button className='argo-button argo-button--base' disabled={isPending} onClick={() => syncHandler(form, ctx, apps)}>
<button
qe-id='applications-sync-panel-button-synchronize'
className='argo-button argo-button--base'
disabled={isPending}
onClick={() => syncHandler(form, ctx, apps)}>
<Spinner show={isPending} style={{marginRight: '5px'}} />
Sync
</button>{' '}
<button onClick={() => hide()} className='argo-button argo-button--base-o'>
<button onClick={() => hide()} qe-id='applications-sync-panel-button-cancel' className='argo-button argo-button--base-o'>
Cancel
</button>
</div>
+1
View File
@@ -524,6 +524,7 @@ export interface ApplicationCondition {
export interface ApplicationSummary {
externalURLs?: string[];
images?: string[];
isAppOfApps?: boolean;
}
export interface ApplicationStatus {