Mount secrets via projected volume into Tekton pods - #59
Conversation
Signed-off-by: avasilev <avasilev@redhat.com>
📝 WalkthroughWalkthroughThis PR implements secret copying and projected volume mounting in Fournos. Secrets from a vault-synced namespace are copied into the operator namespace with per-job naming and owner references, then mounted via a projected volume in Tekton PipelineRun pods. Changes
Sequence DiagramsequenceDiagram
participant FJ as FournosJob<br/>(Admitted)
participant HR as Handler<br/>(reconcile_admitted)
participant CR as ClusterRegistry<br/>(copy_secrets)
participant K8s as Kubernetes API
participant TC as TektonClient<br/>(create_pipeline_run)
participant PR as PipelineRun<br/>(with projected volume)
FJ->>HR: Trigger reconcile (Admitted state)
activate HR
HR->>CR: copy_secrets(secret_refs, fjob_name, owner_ref)
activate CR
CR->>K8s: Read secrets from secrets_namespace
K8s-->>CR: Secret data + keys
CR->>K8s: Create secret copy in operator namespace<br/>(name: fjob_name-ref, ownerRef: FournosJob)
K8s-->>CR: Created Secret / 409 Conflict (idempotent)
CR-->>HR: ResolvedSecret list (name, original_name, keys)
deactivate CR
HR->>TC: create_pipeline_run(..., resolved_secrets)
activate TC
TC->>TC: Build projected vault-secrets volume<br/>(from resolved_secrets entries)
TC->>K8s: Create PipelineRun with podTemplate<br/>(includes projected volume)
K8s-->>TC: PipelineRun created
deactivate TC
TC-->>HR: PipelineRun resource
deactivate HR
HR->>PR: Pods access secrets at<br/>/var/run/secrets/fournos/
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
|
Skipping CI for Draft Pull Request. |
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
|
/test fournos |
Signed-off-by: avasilev <avasilev@redhat.com>
Signed-off-by: avasilev <avasilev@redhat.com>
Signed-off-by: avasilev <avasilev@redhat.com>
Signed-off-by: avasilev <avasilev@redhat.com>
Signed-off-by: avasilev <avasilev@redhat.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
fournos/handlers/execution.py (1)
160-170: Use a distinct condition reason for API copy failures.
ApiExceptionfailures here are not always “not found”, but the condition reason is always set toSecretRefNotFound. Consider splitting reason values so status stays diagnosable (SecretRefNotFoundvsSecretCopyFailed).♻️ Proposed adjustment
- except (KeyError, client.exceptions.ApiException) as exc: - msg = str(exc).strip("'\"") if isinstance(exc, KeyError) else exc.reason + except (KeyError, client.exceptions.ApiException) as exc: + is_not_found = isinstance(exc, KeyError) + msg = str(exc).strip("'\"") if is_not_found else exc.reason + cond_reason = "SecretRefNotFound" if is_not_found else "SecretCopyFailed" patch.status["phase"] = Phase.FAILED patch.status["message"] = msg set_condition( patch, conditions, COND_PIPELINE_RUN_READY, "False", - "SecretRefNotFound", + cond_reason, msg, )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@fournos/handlers/execution.py` around lines 160 - 170, The except block currently conflates KeyError and client.exceptions.ApiException by always using "SecretRefNotFound" as the condition reason; change the handling so KeyError continues to use reason "SecretRefNotFound" while ApiException uses a distinct reason like "SecretCopyFailed" (use instanceof checks on exc or separate except clauses), keep msg for KeyError as str(exc).strip(...) and for ApiException as exc.reason, set patch.status["phase"]=Phase.FAILED and patch.status["message"]=msg in both cases, and pass the appropriate reason into the set_condition call for COND_PIPELINE_RUN_READY so callers can distinguish missing refs from API copy failures.tests/unit/test_secret_volume.py (1)
92-170: Add a regression test for non-vault secret rejection incopy_secret.Current tests cover happy-path and API-error behavior, but not the label-gate path (
fournos.dev/vault-entry=true) that should block non-vault secrets.🧪 Suggested test case
class TestCopySecret: + def test_rejects_secret_without_vault_label(self, registry): + reg, k8s = registry + source = _make_source_secret(user="x") + source.metadata = mock.MagicMock(labels={}) + k8s.read_namespaced_secret.return_value = source + + with pytest.raises(KeyError): + reg.copy_secret("creds", "my-job", OWNER_REF)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/unit/test_secret_volume.py` around lines 92 - 170, Add a regression test in tests/unit/test_secret_volume.py that covers the label-gate path: have k8s.read_namespaced_secret return a secret that does NOT include the "fournos.dev/vault-entry":"true" label, then call reg.copy_secret("name", "job", OWNER_REF) and assert that it raises (use pytest.raises(Exception)) and that k8s.create_namespaced_secret was not called; reference the existing fixtures and the reg.copy_secret, k8s.read_namespaced_secret and k8s.create_namespaced_secret calls to mirror the other tests' structure.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@dev/mock-pipelines/tasks.yaml`:
- Around line 68-69: The current loop prints secret contents by using cat inside
the while loop (find ... | while read f; do echo " $f = $(cat "$f")"; done);
update it to avoid printing values and instead emit only metadata—e.g., echo the
file path plus size and modification time or a presence marker (use stat or ls
-l) like: for each file referenced by the find/while construct, replace the
"$(cat "$f")" expansion with a safe metadata expression (stat -c '%n %s %y' "$f"
or a simple "exists" marker) so logs show filenames and metadata only, not
secret contents.
In `@fournos/core/clusters.py`:
- Around line 81-83: The copy_secret flow currently reads a Secret from
settings.secrets_namespace then copies it without re-checking the Vault label;
update the copy_secret implementation to re-validate that the fetched source
Secret has metadata.labels.get('fournos.dev/vault-entry') == "true" before
proceeding (after self._k8s.read_namespaced_secret(ref,
settings.secrets_namespace)); if the label is missing or not "true", abort the
copy (raise or return an error and do not use source.data). Ensure you reference
the source object returned by read_namespaced_secret and the copy_secret
function name when making the change.
---
Nitpick comments:
In `@fournos/handlers/execution.py`:
- Around line 160-170: The except block currently conflates KeyError and
client.exceptions.ApiException by always using "SecretRefNotFound" as the
condition reason; change the handling so KeyError continues to use reason
"SecretRefNotFound" while ApiException uses a distinct reason like
"SecretCopyFailed" (use instanceof checks on exc or separate except clauses),
keep msg for KeyError as str(exc).strip(...) and for ApiException as exc.reason,
set patch.status["phase"]=Phase.FAILED and patch.status["message"]=msg in both
cases, and pass the appropriate reason into the set_condition call for
COND_PIPELINE_RUN_READY so callers can distinguish missing refs from API copy
failures.
In `@tests/unit/test_secret_volume.py`:
- Around line 92-170: Add a regression test in tests/unit/test_secret_volume.py
that covers the label-gate path: have k8s.read_namespaced_secret return a secret
that does NOT include the "fournos.dev/vault-entry":"true" label, then call
reg.copy_secret("name", "job", OWNER_REF) and assert that it raises (use
pytest.raises(Exception)) and that k8s.create_namespaced_secret was not called;
reference the existing fixtures and the reg.copy_secret,
k8s.read_namespaced_secret and k8s.create_namespaced_secret calls to mirror the
other tests' structure.
🪄 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: ee35bfa3-10c3-42a8-8bd6-2ec31d8803f8
📒 Files selected for processing (17)
Fournos_Design_Document.mdREADME.mdconfig/forge/workflows/tasks.yamlconfig/fournos-validation/workflows/tasks.yamldev/job-secret-demo.yamldev/mock-pipelines/tasks.yamldev/mock-resolve/resolve.shdev/mock-secrets.yamlfournos/core/clusters.pyfournos/core/tekton.pyfournos/handlers/execution.pymanifests/rbac/role_fournos.yamltests/conftest.pytests/test_secret_refs.pytests/unit/__init__.pytests/unit/conftest.pytests/unit/test_secret_volume.py
Signed-off-by: avasilev <avasilev@redhat.com>
Signed-off-by: avasilev <avasilev@redhat.com>
|
/test fournos |
1 similar comment
|
/test fournos |
|
🟢 Test of 'fournos_deploy --project-source' succeeded after 00 hours 10 minutes 54 seconds 🟢 • Link to the test results. • No reports index generated... Test configuration: |
|
thanks @avasilevskii, |
Copy secrets referenced by
FJobfrompsap-secretsto work namespace (e.g.psap-automation) and mount them into the Tekton pods via projected volume.See sample FournosJob for the reference.
Closes #14.
Summary by CodeRabbit
New Features
/var/run/secrets/fournos/<entry-name>/Documentation
Tests