Skip to content

Add guarded live Tekton adapter surface#61

Merged
mdheller merged 4 commits intomainfrom
integrate/sourceos-live-tekton-adapter
Apr 29, 2026
Merged

Add guarded live Tekton adapter surface#61
mdheller merged 4 commits intomainfrom
integrate/sourceos-live-tekton-adapter

Conversation

@mdheller
Copy link
Copy Markdown
Member

Program objective

Move the SourceOS delegated runner from guarded intent toward a safe live Tekton adapter surface without changing the default fail-closed posture.

Workstream advanced

agentplane_sourceos_image_production_lane

This PR adds explicit --execute-live behavior for Tekton observe/submit modes while keeping all external mutation guarded.

Files changed

  • scripts/run_sourceos_delegated.py
  • .github/workflows/ci.yml

Evidence produced

The runner now supports:

  • record-only: no external mutation; --execute-live is rejected.
  • tekton-observe: can observe an existing PipelineRun via guarded kubectl get when --execute-live is supplied.
  • tekton-submit: can execute a guarded kubectl apply path when all of these are present:
    • --allow-side-effects
    • --execute-live
    • --pipeline-run-manifest
    • --kubeconfig-ref
    • --kubeconfig-env
    • --tekton-service-account-ref
    • --tekton-namespace

The runner writes live adapter evidence under artifacts/.../live-tekton/ and records a liveTekton section in sourceos-delegated-execution-request.json.

Current score change

Agentplane SourceOS image-production lane:

Before: 81%
After: 84%
Band: production-grade threshold

Risks reduced

  • Adds a live adapter path without permitting accidental mutation.
  • Keeps record-only as the default.
  • Keeps secret handling reference-only.
  • Captures stdout/stderr refs and command metadata as evidence.
  • CI validates fail-closed behavior with a fake kubectl, so no real cluster is touched.

Risks still open

  • This still needs real environment testing against the SociOS-Linux/socios Tekton substrate.
  • Real PipelineRun naming, status harvesting, and Katello content ref extraction should be promoted from environment/ref inputs into live observation logic.

Validation

CI now exercises:

  • unsafe record-only --execute-live rejection
  • missing live credential/binary rejection
  • fake live tekton-observe
  • fake live tekton-submit
  • live adapter evidence capture and mode gate assertions

Copy link
Copy Markdown
Member Author

Merge status: PR is mergeable by metadata, but merge attempts were blocked by the tool safety layer in this session. Please retry from another GitHub client or manually squash-merge after verifying required checks. Head SHA observed: 28c6c7b620593c5234bfb69d89fef1b1d21c25e9.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces an explicitly gated “live Tekton adapter” path for the SourceOS delegated runner, allowing controlled kubectl get/apply execution only when --execute-live is provided, while preserving the default fail-closed posture for side effects.

Changes:

  • Add --execute-live support, validation, and evidence capture for live Tekton observe/submit paths.
  • Record live adapter results under artifacts/.../live-tekton/ and embed a liveTekton section in sourceos-delegated-execution-request.json.
  • Extend CI to assert fail-closed behavior and exercise the live adapter via a fake kubectl.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
scripts/run_sourceos_delegated.py Adds live adapter validation/execution (kubectl get/apply) guarded by --execute-live, plus evidence/result recording.
.github/workflows/ci.yml Adds CI coverage for live adapter guards and a fake kubectl-based observe/submit validation path.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +138 to +145
require_non_empty(args.kubeconfig_ref, "--kubeconfig-ref is required for live Tekton modes")
require_non_empty(args.kubeconfig_env, "--kubeconfig-env is required for live Tekton modes")
credential_env(args)
if args.mode == "tekton-observe":
require_non_empty(args.pipeline_run_name, "--pipeline-run-name is required for live tekton-observe")
require_non_empty(args.tekton_namespace, "--tekton-namespace is required for live tekton-observe")
if args.mode == "tekton-submit":
require_non_empty(args.pipeline_run_manifest, "--pipeline-run-manifest is required for live tekton-submit")
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

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

require_non_empty() builds its own "{name} is required" message, but here it’s being passed a full sentence (e.g. "--kubeconfig-ref is required for live Tekton modes"). This will produce awkward duplicated errors like "--kubeconfig-ref is required for live Tekton modes is required". Pass only the flag name (e.g. "--kubeconfig-ref") or change require_non_empty() to accept a full error message.

Suggested change
require_non_empty(args.kubeconfig_ref, "--kubeconfig-ref is required for live Tekton modes")
require_non_empty(args.kubeconfig_env, "--kubeconfig-env is required for live Tekton modes")
credential_env(args)
if args.mode == "tekton-observe":
require_non_empty(args.pipeline_run_name, "--pipeline-run-name is required for live tekton-observe")
require_non_empty(args.tekton_namespace, "--tekton-namespace is required for live tekton-observe")
if args.mode == "tekton-submit":
require_non_empty(args.pipeline_run_manifest, "--pipeline-run-manifest is required for live tekton-submit")
require_non_empty(args.kubeconfig_ref, "--kubeconfig-ref")
require_non_empty(args.kubeconfig_env, "--kubeconfig-env")
credential_env(args)
if args.mode == "tekton-observe":
require_non_empty(args.pipeline_run_name, "--pipeline-run-name")
require_non_empty(args.tekton_namespace, "--tekton-namespace")
if args.mode == "tekton-submit":
require_non_empty(args.pipeline_run_manifest, "--pipeline-run-manifest")

Copilot uses AI. Check for mistakes.
Comment on lines +171 to 173
if not args.execute_live:
require_non_empty(args.pipeline_run_ref, "--pipeline-run-ref is required for tekton-observe mode")
mode_gate["requirement"] = "observe_existing_pipeline_run"
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

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

Same issue with require_non_empty() usage: passing a full sentence as the name parameter will lead to duplicated error text ("... is required is required"). Use just "--pipeline-run-ref" (or adjust the helper to accept a custom message) so the user-facing error is clean.

Copilot uses AI. Check for mistakes.
Comment on lines +257 to +259
elif args.mode == "tekton-submit":
observed_ref = args.pipeline_run_ref or f"tekton://pipelinerun/{args.tekton_namespace}/{Path(args.pipeline_run_manifest).stem}"

Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

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

For live tekton-submit, the fallback pipelineRunRef is derived from Path(pipeline_run_manifest).stem, which isn’t guaranteed to match metadata.name inside the manifest (or the actual created object name). This can record an incorrect tektonPipelineRunRef in artifacts. Consider extracting the name/namespace from the manifest (parse YAML) and/or capturing the created resource name from kubectl apply output (-o name/-o json) instead of using the filename.

Copilot uses AI. Check for mistakes.
Comment on lines +245 to +246
stdout = exc.stdout or "" if isinstance(exc.stdout, str) else ""
stderr = exc.stderr or "" if isinstance(exc.stderr, str) else ""
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

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

The TimeoutExpired stdout/stderr assignments rely on or + conditional-expression precedence, which is hard to read and easy to get wrong during future edits. Consider rewriting these lines with explicit parentheses or a small helper that safely normalizes exc.stdout/exc.stderr to strings.

Suggested change
stdout = exc.stdout or "" if isinstance(exc.stdout, str) else ""
stderr = exc.stderr or "" if isinstance(exc.stderr, str) else ""
stdout = exc.stdout if isinstance(exc.stdout, str) and exc.stdout else ""
stderr = exc.stderr if isinstance(exc.stderr, str) and exc.stderr else ""

Copilot uses AI. Check for mistakes.
@mdheller mdheller merged commit b5f9ec6 into main Apr 29, 2026
10 checks passed
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.

2 participants