Skip to content

feat(runners): implement Tekton Pipeline runner with native metadata discovery#2767

Open
waveywaves wants to merge 1 commit intochainloop-dev:mainfrom
waveywaves:feat/tekton-runner-native-discovery
Open

feat(runners): implement Tekton Pipeline runner with native metadata discovery#2767
waveywaves wants to merge 1 commit intochainloop-dev:mainfrom
waveywaves:feat/tekton-runner-native-discovery

Conversation

@waveywaves
Copy link

@waveywaves waveywaves commented Feb 21, 2026

Summary

  • Implement the TektonPipeline runner with two-tier native metadata discovery: Tier 1 reads HOSTNAME env var and ServiceAccount namespace file (always available in K8s pods); Tier 2 makes a best-effort K8s API call to discover tekton.dev/* pod labels for rich pipeline context
  • Implement all SupportedRunner interface methods: RunURI (Tekton Dashboard URL with tekton-pipeline:// fallback), Report (writes to /tekton/results/attestation-report with 3500-byte truncation), Environment (detects GKE/EKS/AKS as Managed vs self-hosted), ListEnvVars, and ResolveEnvVars (synthesizes discovered metadata as key-value entries)
  • Wire factory in runner.go to pass logger to NewTektonPipeline
  • Add 26 unit tests using testify suite pattern (7 discovery tests + 19 interface method tests), all passing with race detection

asciicast

Design decisions

  • No artificial env vars required: Unlike the previous approach (chore(runner): First cut on Tekton pipeline detection #2581), the runner discovers all metadata natively from the pod's runtime environment. Task YAML does not need to map $(context.*) variables as env vars
  • Graceful degradation: If K8s API access fails (RBAC denied, missing SA token), the runner continues with Tier 1 data only — it never blocks attestation
  • Functional options pattern: WithHTTPClient, WithSATokenPath, WithNamespacePath, WithCACertPath, WithResultsDir allow test injection without polluting the production API

Testing

  • 26 unit tests covering discovery success, RBAC denial, missing SA token/namespace, all RunURI variants, Report truncation, Environment detection, and ResolveEnvVars
  • E2E validated in a kind cluster with Tekton v1.6.0 (9/9 checks pass)
  • No regressions in existing runner test suites (GitHub Actions, GitLab, etc.)

@waveywaves waveywaves force-pushed the feat/tekton-runner-native-discovery branch 2 times, most recently from cbcf765 to 229b0c9 Compare February 22, 2026 02:00
@migmartri
Copy link
Member

@cubic-dev-ai review

@cubic-dev-ai
Copy link

cubic-dev-ai bot commented Feb 24, 2026

@cubic-dev-ai review

@migmartri I have started the AI code review. It will take a few minutes to complete.

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 3 files

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="pkg/attestation/crafter/runners/tektonpipeline_test.go">

<violation number="1" location="pkg/attestation/crafter/runners/tektonpipeline_test.go:111">
P3: Unused `tmpDir` in test: `t.TempDir()` is called and immediately suppressed with `_ = tmpDir`. The test only exercises `taskRunNameFromHostname()` via a struct literal — no temp directory is needed. Remove both lines to avoid unnecessary filesystem allocations.</violation>
</file>

Since this is your first cubic review, here's how it works:

  • cubic automatically reviews your code and comments on bugs and improvements
  • Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
  • Add one-off context when rerunning by tagging @cubic-dev-ai with guidance or docs links (including llms.txt)
  • Ask questions if you need clarification on any suggestion

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@javirln
Copy link
Member

javirln commented Feb 25, 2026

Thanks @waveywaves for the PR! Could you please provide an example of a completed chainloop attestation that has been processed on Tekton?

Copy link
Member

@javirln javirln left a comment

Choose a reason for hiding this comment

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

That's basically why I would to see this: #2767 (comment) 👀

// All metadata is discovered from K8s API labels and filesystem.
// Return HOSTNAME as the only env var we consume (for traceability in attestation).
return []*EnvVarDefinition{
{"HOSTNAME", true},
Copy link
Member

Choose a reason for hiding this comment

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

Although the environment variables could not be auto resolved automatically, I think it would be good to have TEKTON_TASKRUN_NAME, TEKTON_PIPELINE_NAME, TEKTON_PIPELINERUN_NAME, TEKTON_TASK_NAME, TEKTON_PIPELINE_TASK_NAME, and TEKTON_NAMESPACE as we do with the rest of the runners.

In the Dagger client for example, we "promote" the underlying environment variables because of the encapsulation of Dagger

Copy link
Author

Choose a reason for hiding this comment

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

I have added support for promoting env vars like it's done for dagger and have 2 required and 5 optional env vars as inline tasks and pipeline names might not necessarily be present.

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

3 issues found across 4 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="test/e2e/tekton-runner/main.go">

<violation number="1" location="test/e2e/tekton-runner/main.go:137">
P2: The `Environment` check always reports PASS regardless of the returned value. Unlike every other check in this e2e test, there's no conditional validation — if `r.Environment()` returns `Unknown` (e.g., due to a detection bug), the test silently passes. Add validation for the expected environment value.</violation>
</file>

<file name="pkg/attestation/crafter/runners/tektonpipeline.go">

<violation number="1" location="pkg/attestation/crafter/runners/tektonpipeline.go:63">
P2: Storing `context.Context` in a struct is a Go anti-pattern, and here `r.ctx` is only used during construction (in `discoverLabelsFromKubeAPI`, called from `NewTektonPipeline`). After the constructor returns, the field is never read again — it's dead state. Pass `ctx` as a parameter to `discoverLabelsFromKubeAPI(ctx)` and remove the struct field.

(Based on your team's feedback about removing unused code, methods, and variables.) [FEEDBACK_USED]</violation>

<violation number="2" location="pkg/attestation/crafter/runners/tektonpipeline.go:247">
P1: `TEKTON_TASKRUN_NAME` is marked as required, but its value comes exclusively from Tier 2 K8s API discovery (`r.labels["tekton.dev/taskRun"]`). If the K8s API call fails (RBAC denied, missing SA token), this label will be empty and `ResolveEnvVars` will return an error — blocking attestation. This contradicts the PR's "graceful degradation" design goal. Consider either making it optional (with `optionalVar`) or falling back to the `taskRunNameFromHostname()` derivation that `RunURI` already uses.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@migmartri
Copy link
Member

@waveywaves could you make sure you sign-off the commit, thanks!

image

javirln
javirln previously requested changes Mar 3, 2026
Copy link
Member

@javirln javirln left a comment

Choose a reason for hiding this comment

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

I'm sorry to block the PR but I have high concerns about the blockade of the attestation when one environment variable couldn't be resolved due to RBAC restrictions.

Also please, could you please share a result of a Chainloop attestation, I would like to see the populated runner environment variables.

Thanks!

optionalVar("HOSTNAME", os.Getenv("HOSTNAME"))

// Required: always available in any TaskRun (needs RBAC for pod get)
requireVar("TEKTON_TASKRUN_NAME", r.labels["tekton.dev/taskRun"])
Copy link
Member

Choose a reason for hiding this comment

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

Please pay attention to what https://github.com/chainloop-dev/chainloop/pull/2767/changes#r2864820538 is saying. If we get blocked by RBAC the attestation will be blocked.

The taskRunNameFromHostname() method already exists as a fallback and is used in RunURI(). The fix is to also use it here:

taskRunName := r.labels["tekton.dev/taskRun"]
if taskRunName == "" {
    taskRunName = r.taskRunNameFromHostname()
}
requireVar("TEKTON_TASKRUN_NAME", taskRunName)

Copy link
Author

Choose a reason for hiding this comment

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

I have added a fallback as you have mentioned.

}

r := &TektonPipeline{
ctx: ctx,
Copy link
Member

Choose a reason for hiding this comment

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

Why do we need the context as part of the struct?

Copy link
Author

Choose a reason for hiding this comment

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

Updated to pass the context from the top.

@waveywaves
Copy link
Author

waveywaves commented Mar 3, 2026

@javirln I appreciate the collaboration to make the best of this feature and to ship it in a way that works well. Thanks for reviewing. Please don't apologize for reviewing the PR. Shall reply soon.

@waveywaves waveywaves force-pushed the feat/tekton-runner-native-discovery branch from 2c6dec3 to a2d6a8b Compare March 5, 2026 11:05
@waveywaves
Copy link
Author

Here is a completed Chainloop attestation processed on a real Tekton Pipeline running on a kind cluster.

Runner detection and initialization:

--- Initializing Attestation ---
INF Attestation initialized! now you can check its status or add materials to it
+-----------------------------------------------------------+
| Attestation ID            | 21664fe0-7cc0-4e65-ab86-fd9e2ec024e1                |
| Organization              | tekton-demo-org                                     |
| Name                      | tekton-demo                                         |
| Project                   | tekton-integration                                  |
| Contract                  | tekton-integration-tekton-demo (revision 1)         |
| Runner Type               | TEKTON_PIPELINE                                     |
| Runner URL                | tekton://demo/pipelineruns/chainloop-demo-run-cf65k |
+-----------------------------------------------------------+
+-----------------------------------------------------------------+
| Runner context                                                  |
+---------------------------+-------------------------------------+
| HOSTNAME                  | chainloop-demo-run-cf65k-attest-pod |
| TEKTON_TASKRUN_NAME       | chainloop-demo-run-cf65k-attest     |
| TEKTON_NAMESPACE          | demo                                |
| TEKTON_PIPELINE_NAME      | chainloop-attestation-demo          |
| TEKTON_PIPELINERUN_NAME   | chainloop-demo-run-cf65k            |
| TEKTON_PIPELINE_TASK_NAME | attest                              |
+---------------------------+-------------------------------------+

Pushing signed attestation:

--- Pushing Signed Attestation ---
INF push completed
+-----------------------------------------------------------+
| Digest                    | sha256:8be557a6ecba8f48045e934779f59b407f8570a5... |
| Runner Type               | TEKTON_PIPELINE                                     |
| Runner URL                | tekton://demo/pipelineruns/chainloop-demo-run-cf65k |
+-----------------------------------------------------------+

Verification results (9/9 pass):

PASS: Runner detected Tekton environment
PASS: Runner is TEKTON_PIPELINE, not GENERIC
PASS: HOSTNAME captured in runner output
PASS: Namespace captured in runner output
PASS: Tier 2 labels present AND runner read them via K8s API
PASS: RunURI uses tekton:// scheme
PASS: attestation-report result found in TaskRun status
PASS: PipelineRun completed successfully (Succeeded)
PASS: Environment is SelfHosted (kind cluster, no cloud provider detected)

RESULT: 9/9 checks passed

The runner auto-discovers all metadata from the K8s API pod labels and ServiceAccount namespace file, zero env var wiring needed in the Pipeline YAML.

Full asciinema recording of the E2E flow: https://asciinema.org/a/795546

@waveywaves
Copy link
Author

Done, the commit is now squashed and signed off.

@migmartri migmartri requested a review from javirln March 5, 2026 11:20
Copy link
Member

@migmartri migmartri left a comment

Choose a reason for hiding this comment

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

Very nice! Thanks for posting the tests

once we merge it we'll update the docs, feel free to suggest any changes we should add in that section :)

Thanks!

https://docs.chainloop.dev/concepts/contracts#tekton_pipeline

@waveywaves waveywaves force-pushed the feat/tekton-runner-native-discovery branch from a2d6a8b to 5389af4 Compare March 5, 2026 11:34
@waveywaves
Copy link
Author

Thanks @migmartri! Here are the suggested updates for the Tekton runner docs:

The TEKTON_PIPELINE runner now auto-discovers all metadata natively. The docs section should be updated to reflect:

Runner context variables (auto-captured):

Variable Source Required
TEKTON_TASKRUN_NAME tekton.dev/taskRun pod label Yes (falls back to hostname)
TEKTON_NAMESPACE ServiceAccount namespace file Yes
TEKTON_TASK_NAME tekton.dev/task pod label No (absent with inline taskSpec)
TEKTON_PIPELINE_NAME tekton.dev/pipeline pod label No (only in Pipeline TaskRuns)
TEKTON_PIPELINERUN_NAME tekton.dev/pipelineRun pod label No (only in Pipeline TaskRuns)
TEKTON_PIPELINE_TASK_NAME tekton.dev/pipelineTask pod label No (only in Pipeline TaskRuns)
HOSTNAME Environment variable No

Key points:

  • Zero env var wiring needed in Pipeline YAML — the runner discovers everything from K8s API pod labels and the ServiceAccount namespace file
  • RBAC: ServiceAccount needs get permission on pods in its namespace for Tier 2 label discovery (graceful degradation if missing)
  • RunURI format: tekton://<namespace>/pipelineruns/<name> (or Tekton Dashboard URL if TEKTON_DASHBOARD_URL env var is set)

The "work in progress" note can be removed — all runner context is now automatically gathered.

waveywaves added a commit to waveywaves/docs that referenced this pull request Mar 5, 2026
Add documentation for the Tekton Pipeline runner context variables
that are auto-discovered natively from K8s API pod labels and
ServiceAccount namespace file.

Ref: chainloop-dev/chainloop#2767

Signed-off-by: Vibhav Bobade <vibhav.bobde@gmail.com>
Co-Authored-By: Claude <noreply@anthropic.com>
@waveywaves
Copy link
Author

@migmartri Thanks! I noticed the docs repo (chainloop-dev/docs) is archived so I couldn't open a PR there. I've posted the suggested runner context variable table above — happy to open a docs PR wherever the docs live now if you point me to the right place.

@migmartri migmartri dismissed javirln’s stale review March 5, 2026 12:17

we synced offline

@migmartri
Copy link
Member

@migmartri Thanks! I noticed the docs repo (chainloop-dev/docs) is archived so I couldn't open a PR there. I've posted the suggested runner context variable table above — happy to open a docs PR wherever the docs live now if you point me to the right place.

Yeah it's private, I have a PR ready to be merged with the update

image

@migmartri
Copy link
Member

one last bit, could you squash your commits and make sure they are signed please?

image

@waveywaves
Copy link
Author

@migmartri CI is green now

@migmartri
Copy link
Member

@migmartri CI is green now

I still see the commit signature issue

@waveywaves
Copy link
Author

@javirln All feedback has been addressed — context refactored to function param, env var promotion with required/optional split, RBAC fallback added, and attestation example posted above. Could you re-review when you get a chance? CI is all green.

…discovery

Add a new TektonPipeline runner that discovers Tekton metadata natively
using a two-tier approach:
- Tier 1: HOSTNAME env var and ServiceAccount namespace file (always available)
- Tier 2: K8s API pod labels for rich tekton.dev/* metadata (best-effort)

The runner promotes 7 env vars (2 required, 5 optional) following the
Dagger runner pattern. TEKTON_TASK_NAME is optional since inline taskSpec
pipelines lack the tekton.dev/task label. TEKTON_TASKRUN_NAME falls back
to hostname derivation when K8s API labels are unavailable.

context.Context is passed as a function parameter to discoverLabelsFromKubeAPI()
rather than stored in the struct, following Go best practices.

Includes comprehensive unit tests, e2e test binary with Environment
validation, and tekton:// URI scheme for RunURI.

Signed-off-by: Vibhav Bobade <vibhav.bobde@gmail.com>
Co-Authored-By: Claude <noreply@anthropic.com>
@waveywaves waveywaves force-pushed the feat/tekton-runner-native-discovery branch from 5389af4 to 2f00e03 Compare March 5, 2026 15:21
@waveywaves
Copy link
Author

@migmartri I did not sign correctly earlier looks like, it should be good now.

@waveywaves
Copy link
Author

cc @migmartri CI is properly green now :D

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.

4 participants