Skip to content

feat: sync credential templates to managed agents#878

Open
jparsai wants to merge 2 commits intoargoproj-labs:mainfrom
jparsai:repo-cred
Open

feat: sync credential templates to managed agents#878
jparsai wants to merge 2 commits intoargoproj-labs:mainfrom
jparsai:repo-cred

Conversation

@jparsai
Copy link
Copy Markdown
Collaborator

@jparsai jparsai commented Apr 8, 2026

This PR is to add support for syncing Argo CD credential templates (repo-creds) from principal to
managed agents. Repository secret's project-scoped distribution logic is reused for repo-creds as well.
It also has an fix where deleting an AppProject on the principal did not
clean up the associated repository secrets from agents. Same is followed for repo-creds as well.

Fixes #820

How to test changes / Special notes to the reviewer:

  • Create an AppProject on the principal.
  • Verify AppProject is synced to managed agent
  • Create a repo-creds secret on the principal with argocd.argoproj.io/secret-type: repo-creds label and project field matching the AppProject name
  • Verify the repo-creds secret is synced to managed agent but not to the autonomous agent
  • Verify update/delete operations on principal are synced to managed agent.

Assisted by: Cursor

Summary by CodeRabbit

  • New Features

    • Sync repository credentials ("repo-creds") alongside repository secrets to managed agents.
  • Bug Fixes

    • Suppress noisy errors when referenced projects are missing and ensure repository mappings are cleaned on project deletion.
    • More specific error context when listing secrets per secret type fails.
  • Tests

    • Added unit and E2E tests covering repo-creds sync, AppProject delete/update reconciliation, and edge cases.
  • Refactor

    • Unified handling of multiple repository secret types across sync and cleanup flows.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 8, 2026

📝 Walkthrough

Walkthrough

Adds support for syncing repository-credential secrets (repo-creds) alongside existing repository secrets: validation, informer listing, AppProject callbacks, principal->agent state sync, unit tests, and E2E coverage were extended to handle both secret types.

Changes

Cohort / File(s) Summary
Repository validation & tests
internal/backend/kubernetes/repository/kubernetes.go, internal/backend/kubernetes/repository/filters_test.go
Accept common.LabelValueSecretTypeRepoCreds as valid repository secrets for validation/filtering; added positive tests for repo-creds and adjusted nil-label handling.
Principal callbacks & unit tests
principal/callbacks.go, principal/callbacks_test.go
deleteAppProjectCallback now calls syncRepositoriesForProject; suppressed logging for NotFound when fetching projects; added tests covering repo sync on project delete/update and missing-secret edge cases.
Principal state sync & unit tests
principal/server.go, principal/server_test.go
sendCurrentStateToAgent now lists and processes both repository and repo-creds secret label types, updating mappings and enqueuing repo events per secret type; tests updated/added for repo-creds scenarios.
E2E cleanup & new E2E tests
test/e2e/fixture/fixture.go, test/e2e/repocreds_test.go
E2E cleanup now iterates both secret types when deleting; added RepoCredsTestSuite E2E tests to verify principal→managed-agent propagation, update, and AppProject-driven sync/deletion behavior.

Sequence Diagram(s)

sequenceDiagram
    participant Principal as Principal Cluster
    participant Kube as Kubernetes API
    participant PrincipalSvc as Principal Service
    participant Agent as Managed Agent

    Principal->>Kube: Delete AppProject
    Kube-->>Principal: Ack

    Principal->>PrincipalSvc: deleteAppProjectCallback
    activate PrincipalSvc
    PrincipalSvc->>PrincipalSvc: syncRepositoriesForProject(ctx, project)
    PrincipalSvc->>Kube: List secrets (label=repository)
    Kube-->>PrincipalSvc: Repo secrets list
    PrincipalSvc->>Kube: List secrets (label=repo-creds)
    Kube-->>PrincipalSvc: Repo-creds list
    PrincipalSvc->>Agent: Enqueue repository delete events for matched agents
    deactivate PrincipalSvc

    Agent->>Kube: Reconcile secrets/events
    Kube-->>Agent: Secret created/updated/deleted
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • jgwest
  • chetan-rns
  • jannfis

Poem

🐰 I hopped where secrets hide in beds,
repo-creds now hop beside repositories' threads,
From principal burrow to agent glen,
Matches checked, then sent again,
Hooray — small hops make big friend-to-friend!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding support for syncing credential templates (repo-creds) to managed agents, which aligns with the primary objective across all code changes in the PR.
Linked Issues check ✅ Passed The PR implementation fully addresses issue #820 requirements: it enables syncing secrets labeled argocd.argoproj.io/secret-type: repo-creds from principal to managed agents with parity to repository-type secrets, matching all stated objectives.
Out of Scope Changes check ✅ Passed All code changes directly support repo-creds syncing or maintain consistency with existing repository secret handling; additionally fixes cleanup on AppProject deletion as mentioned in PR objectives, all within scope.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Apr 8, 2026

Codecov Report

❌ Patch coverage is 82.35294% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 46.65%. Comparing base (a2c99e2) to head (f122afd).

Files with missing lines Patch % Lines
principal/server.go 77.77% 3 Missing and 3 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #878      +/-   ##
==========================================
+ Coverage   46.31%   46.65%   +0.34%     
==========================================
  Files         122      122              
  Lines       17410    17416       +6     
==========================================
+ Hits         8063     8126      +63     
+ Misses       8605     8537      -68     
- Partials      742      753      +11     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@jparsai jparsai marked this pull request as ready for review April 8, 2026 11:01
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
principal/callbacks.go (1)

869-881: ⚠️ Potential issue | 🟠 Major

Keep projectToRepos in sync even when one project lookup returns NotFound.

syncRepositoriesForProject now depends on this index for later AppProject-driven reconciliation, but the map is only rekeyed when both Get calls succeed. If a repo/repo-creds secret moves from project=A to project=B while A is already deleted or B does not exist yet, the entry stays under A, so later updates/deletes for B will miss it and A can still be treated as owning it.

🛠️ Proposed fix
-	if oldproject != nil && newProject != nil && oldproject.Name != newProject.Name {
-		// The project name has changed. Delete the repository from the old project.
-		s.projectToRepos.Delete(oldproject.Name, new.Name)
-		s.projectToRepos.Add(newProject.Name, new.Name)
+	if string(oldProjectName) != string(newProjectName) {
+		// Keep the project index aligned with the secret payload even if one side
+		// no longer exists yet.
+		s.projectToRepos.Delete(string(oldProjectName), new.Name)
+		s.projectToRepos.Add(string(newProjectName), new.Name)
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@principal/callbacks.go` around lines 869 - 881, The projectToRepos rekeying
only runs when both s.projectManager.Get calls succeed; instead ensure we rekey
even if one Get returned NotFound by deriving project names from whichever
Project object is available or from the requested names used in the Get call
(oldproject, newProject, and newProjectName/oldProjectName strings). Update the
logic around s.projectManager.Get and the subsequent block that calls
s.projectToRepos.Delete(oldproject.Name, new.Name) and
s.projectToRepos.Add(newProject.Name, new.Name) so it computes oldName =
(oldproject != nil ? oldproject.Name : string(oldProjectName)) and newName =
(newProject != nil ? newProject.Name : string(newProjectName)) and then, if
oldName != newName, call s.projectToRepos.Delete(oldName, new.Name) and
s.projectToRepos.Add(newName, new.Name) so the index stays in sync even when a
Get returns NotFound.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@principal/callbacks.go`:
- Around line 869-881: The projectToRepos rekeying only runs when both
s.projectManager.Get calls succeed; instead ensure we rekey even if one Get
returned NotFound by deriving project names from whichever Project object is
available or from the requested names used in the Get call (oldproject,
newProject, and newProjectName/oldProjectName strings). Update the logic around
s.projectManager.Get and the subsequent block that calls
s.projectToRepos.Delete(oldproject.Name, new.Name) and
s.projectToRepos.Add(newProject.Name, new.Name) so it computes oldName =
(oldproject != nil ? oldproject.Name : string(oldProjectName)) and newName =
(newProject != nil ? newProject.Name : string(newProjectName)) and then, if
oldName != newName, call s.projectToRepos.Delete(oldName, new.Name) and
s.projectToRepos.Add(newName, new.Name) so the index stays in sync even when a
Get returns NotFound.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8a18e6c9-8974-4963-a3bf-cc9c5bc9d4b6

📥 Commits

Reviewing files that changed from the base of the PR and between 09b7384 and 21c27ee.

📒 Files selected for processing (8)
  • internal/backend/kubernetes/repository/filters_test.go
  • internal/backend/kubernetes/repository/kubernetes.go
  • principal/callbacks.go
  • principal/callbacks_test.go
  • principal/server.go
  • principal/server_test.go
  • test/e2e/fixture/fixture.go
  • test/e2e/repocreds_test.go

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@principal/callbacks.go`:
- Around line 485-486: When cleaning up repos after an AppProject deletion,
treat a NotFound error for the project as an expected state instead of an error:
in the code paths invoked by syncRepositoriesForProject (specifically in
syncRepositoryUpdatesToAgents and any function that looks up the current
AppProject), detect the NotFound error (using the kube API error checker, e.g.
errors.IsNotFound(err)) and log it at Info/Debug level and return/continue
normally rather than logging as an error; this prevents false-positive error
logs during genuine project deletions while preserving error logging for other
failure modes.

In `@principal/server_test.go`:
- Around line 448-455: The mock expectations for selector-specific List calls on
mockRepoBackend and mockProjBackend are not being asserted; add calls to
mockRepoBackend.AssertExpectations(t) and mockProjBackend.AssertExpectations(t)
after the test logic (after the code that invokes List) so the test fails if
either selector-specific path is no longer exercised; reference the mock objects
mockRepoBackend, mockProjBackend and the AssertExpectations(t) method to ensure
the selector-specific On("List", ..., backend.RepositorySelector{...})
expectations are validated.

In `@test/e2e/repocreds_test.go`:
- Around line 85-98: The helper createRepoCredsAndWaitForSync currently returns
once any Secret with the same name exists on the managed agent; update it to
assert that the synced Secret has the expected secret-type label and value
(e.g., label key "secret-type" with value "repo-creds") before returning. Inside
the requires.Eventually check (where you Get into tempRepoCreds via
ManagedAgentClient.Get), verify tempRepoCreds.Labels is non-nil and
tempRepoCreds.Labels["secret-type"] == "repo-creds" (or your repo-creds label
key/value used elsewhere) and only return true when that condition is met so the
tests wait for a real repo-creds replica.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9649d764-be81-4348-9d36-a64e9c941470

📥 Commits

Reviewing files that changed from the base of the PR and between 21c27ee and e7a0130.

📒 Files selected for processing (8)
  • internal/backend/kubernetes/repository/filters_test.go
  • internal/backend/kubernetes/repository/kubernetes.go
  • principal/callbacks.go
  • principal/callbacks_test.go
  • principal/server.go
  • principal/server_test.go
  • test/e2e/fixture/fixture.go
  • test/e2e/repocreds_test.go
✅ Files skipped from review due to trivial changes (1)
  • internal/backend/kubernetes/repository/filters_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • internal/backend/kubernetes/repository/kubernetes.go
  • test/e2e/fixture/fixture.go

jparsai added 2 commits April 8, 2026 17:47
Assisted by: Cursor

Signed-off-by: Jayendra Parsai <jparsai@redhat.com>
Signed-off-by: Jayendra Parsai <jparsai@redhat.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
principal/server.go (1)

1009-1015: Consider centralizing the replicated secret-type allowlist.

The same repository/repo-creds set now appears in multiple call sites. Pulling it into a shared helper or const would reduce drift the next time this list changes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@principal/server.go` around lines 1009 - 1015, The repeated inline allowlist
of secret types (common.LabelValueSecretTypeRepository and
common.LabelValueSecretTypeRepoCreds) used when calling s.repoManager.List
(e.g., the loop over secretType) should be centralized; create a single
exported/packaged constant or helper (e.g., RepositorySecretAllowlist or func
AllowedRepositorySecretTypes()) and replace the duplicated inline slice in all
call sites (places that iterate secretType, call repoManager.List with Labels
using common.LabelKeySecretType) to reference that central value so future
changes are made in one place.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@principal/server.go`:
- Around line 1009-1015: The repeated inline allowlist of secret types
(common.LabelValueSecretTypeRepository and common.LabelValueSecretTypeRepoCreds)
used when calling s.repoManager.List (e.g., the loop over secretType) should be
centralized; create a single exported/packaged constant or helper (e.g.,
RepositorySecretAllowlist or func AllowedRepositorySecretTypes()) and replace
the duplicated inline slice in all call sites (places that iterate secretType,
call repoManager.List with Labels using common.LabelKeySecretType) to reference
that central value so future changes are made in one place.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e75e7fbe-da30-4986-9c23-bd5a64190876

📥 Commits

Reviewing files that changed from the base of the PR and between e7a0130 and f122afd.

📒 Files selected for processing (8)
  • internal/backend/kubernetes/repository/filters_test.go
  • internal/backend/kubernetes/repository/kubernetes.go
  • principal/callbacks.go
  • principal/callbacks_test.go
  • principal/server.go
  • principal/server_test.go
  • test/e2e/fixture/fixture.go
  • test/e2e/repocreds_test.go
✅ Files skipped from review due to trivial changes (1)
  • internal/backend/kubernetes/repository/filters_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • principal/server_test.go
  • test/e2e/repocreds_test.go

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support for credential template

2 participants