Skip to content

feat: Argo CD read side - Application columns, :argocd view, fleet count, ownership tree #532

Description

@nklmilojevic

Agreed scope from #412 with @mesutoezdil. Argo CD gets the same read side Flux already has: curated Application columns, an :argocd view, a count in :fleet, and an ownership tree behind a keypress. Everything comes from the Application CRD, no Argo API or token.

Three PRs, in this order, each mergeable on its own with tests and docs. The closed drafts #518, #519, #520 already follow this split, so rebasing them onto this issue is the expected path.

Ground rules

  • CRD only. Read argoproj.io/v1alpha1 Applications. Never the Argo CD API, a token, or Argo's cluster Secrets.
  • Same shape as Flux. Pure module src/argocd.rs (unit tested without a cluster) plus a driver src/app/argocd.rs, mirroring gitops.rs / app/gitops.rs. The view reuses explain::Finding, Level, Target and ui::draw_findings. No new rendering code.
  • Reads only. Sync and suspend stay on t.
  • Tests through handle_key for every user-visible behaviour. Cluster::fake() already registers argoproj.io/applications.

PR 1: Application columns and :argocd view

Columns

columns_for("argoproj.io", "applications") returns a curated set, six columns like Flux:

Column Source
NAME
SYNC status.sync.status
HEALTH status.health.status
REVISION status.sync.revision, falling back to status.operationState.syncResult.revision
AUTO-SYNC three states, see below
AGE
  • REVISION shortens a full 40-char SHA to 7. Chart versions and branch names stay as they are.
  • AUTO-SYNC is three states, not a boolean:
    • on: spec.syncPolicy.automated is present
    • manual: no automated block and no stash annotation. Normal config, not a fault.
    • suspended: no automated block and sofka.io/argocd-automated is set (what t → Suspend writes, see app/helpers.rs)
  • SYNC / HEALTH sort as text, the way FLUX_OBJECT_COLUMNS READY does.
  • ApplicationSets keep default columns.

:argocd view

Palette names argocd and argo. Keymap mode argocd with the same bindings as gitops: enter accept, esc back, q close, j/k, g/G, r refresh.

Opens from two places:

  1. An Application. Re-read it like spawn_gitops does, then render.
  2. Anything Argo manages. Resolve the owning Application from tracking metadata and render it, with the headline stating the relationship (Deployment/web is managed by Application/guestbook).

Owner resolution:

  • argocd.argoproj.io/tracking-id annotation first (<app>:<group>/<kind>:<ns>/<name>). Only Argo writes it. If it names an Application that doesn't exist, that's a warning (dangling reference).
  • Instance label second (app.kubernetes.io/instance, then argocd.argoproj.io/instance). Helm stamps the same label on everything it installs, so a label with no matching Application renders as "not managed by Argo CD", not as an error.
  • Argo writes <namespace>_<name> for Applications outside its own namespace, bare <name> otherwise. _ is illegal in both, so the split is exact.
  • Look up by metadata.name field selector across namespaces, not a full list. Clusters hold thousands of multi-KiB Applications. If several match, pick the one whose status.resources[] lists the object.

Findings, top to bottom:

  • Headline: Application/<name>: <sync> / <health>. Level from the pair: Synced+Healthy Good, Progressing Info, OutOfSync Warn, Degraded/Missing/Unknown Critical.
  • Application: project, destination (see below) and namespace, auto-sync state, deployed revision.
  • Source: repo URL, path or chart, targetRevision. Multi-source Applications show the first source.
  • Managed resources: one line per status.resources[] entry with its own sync and health. Capped at 50 with a remainder line. Each line carries a Target resolved through the kind registry by kind and group, a Knative Service must not open the core one. Application entries (app-of-apps) jump to that Application, where :argocd opens its view again.
  • Blocking: what stops this Application from being Synced and Healthy, most serious first, or one line saying nothing does:
    1. suspended by sofka (stash annotation present)
    2. status.operationState.phase Failed / Error with its message
    3. conditions by priority: ComparisonError, InvalidSpecError, SyncError, UnknownError, then SharedResourceWarning, OrphanedResourceWarning, RepeatedResourceWarning
    4. Degraded / Missing resources by name
    5. OutOfSync resources by name
    6. Degraded with nothing above naming a cause: say so. Argo rolls health up from live state it doesn't publish per resource.

Destination

spec.destination.server (or .name) is resolved against the kubeconfig:

  • https://kubernetes.default.svc (with or without :443), or a URL equal to the connected Cluster.cluster_url, is the current cluster.
  • Otherwise match against every kubeconfig cluster's server and show the context name. Needs a small helper next to k8s::context_info, which today resolves one context at a time.
  • Unresolved: show the raw server URL.

Only a current destination yields jump targets. On a remote destination enter flashes where the object lives instead of opening a same-named object here. This reads the user's kubeconfig, never Argo's cluster Secrets.

Shared with :gitops

Request-generation guard so only the latest report updates findings. Closing the view, refreshing, changing source or context cancels pending results. A missing Application is a warning, a failed read stays an error. Never render a denied read as "not found".

Tests

  • src/argocd.rs: accessors, three auto-sync states, blocking precedence, annotation vs label, dangling vs unmanaged, group-aware targets, remote destination yields no targets, SHA shortening, the cap.
  • src/app/tests.rs through handle_key: open on an Application, open on a managed Deployment, enter jumps to a managed resource, enter on a remote line doesn't navigate, r re-gathers, esc cancels.
  • columns.rs cell tests.

Docs

docs/keys.md row, docs/features.md (GitOps and Helm section plus the palette command list), ? help in src/ui.rs, docs/keybindings.md (new argocd mode table).

PR 2: Argo CD count in :fleet

  • New FleetRow.argocd_degraded: Option<usize>. None when the cluster has no argoproj.io Application kind, rendered argo — like flux —.
  • Counts Applications that are OutOfSync, or whose health is Degraded, Missing or Unknown. Progressing is transient and not counted. Empty status is a freshly created Application, not a fault.
  • Pages with limit / continue (500 per page) and keeps only the count. list_or_warn materialises the whole list, which at thousands of multi-KiB Applications across concurrent contexts is tens of MiB. Leave the Flux path alone in this PR.
  • A failed or timed-out list is None plus a warn entry so the row goes to FleetStatus::Error, not a confident zero.
  • is_healthy() treats a non-zero count as unhealthy. draw_fleet gets an argo cell next to flux.
  • Tests: fleet row healthy / unhealthy with and without the CRD, paging count over a fake page sequence.
  • Docs: docs/providers.md#fleet-dashboard, docs/features.md.

PR 3: c expands what a managed resource owns

  • In :argocd, c (discover_children) on a managed-resource line with a current-cluster target walks ownerReferences down: Deployment → ReplicaSet → Pod, CronJob → Job → Pod, StatefulSet / DaemonSet / Job → Pod. c again collapses.
  • Reuse adjacent. Child kinds come from adjacent::children_for (BUILTIN_CHILDREN plus [views."…"].children), matching with adjacent::owned_by. No second hard-coded child list. Depth capped at 2. Cost is one GET for the parent UID plus one LIST per child kind in the namespace, reused at every depth.
  • Drop ReplicaSets scaled to zero that own no pods. With the default revisionHistoryLimit the live one is otherwise buried under ten dead ones.
  • Child rows are indented findings with their own Target and a one-word state: pod phase or waiting reason (CrashLoopBackOff), ReplicaSet ready/desired, Job Complete / Failed. Level follows the state.
  • Behind a keypress, not on open. A failed LIST is a warning line, not an empty tree. r collapses everything. Expansion results are request-guarded like the main report.
  • Tests through handle_key: expand shows the pod under the ReplicaSet, a dead ReplicaSet is hidden, c twice collapses, c on a remote or heading line flashes and does nothing.
  • Docs: docs/keys.md, docs/features.md, docs/keybindings.md (c in argocd mode), ? help in src/ui.rs.

Out of scope

ApplicationSet columns or views, diff, sync options, rollback, Argo CD API access, reading Argo cluster Secrets, a new config section. Separate discussions if wanted.

Open questions

  1. Fleet count: include Progressing or not. Spec says no.
  2. PROJECT column in the table or view only. Spec says view only, keeps the table at six columns like Flux.

Discussion: #412

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions