11//
2- // Copyright 2024- 2025 The Chainloop Authors.
2+ // Copyright 2025 The Chainloop Authors.
33//
44// Licensed under the Apache License, Version 2.0 (the "License");
55// you may not use this file except in compliance with the License.
@@ -18,14 +18,27 @@ package runners
1818import (
1919 "fmt"
2020 "os"
21+ "strings"
2122
2223 schemaapi "github.com/chainloop-dev/chainloop/app/controlplane/api/workflowcontract/v1"
2324)
2425
25- type TektonPipeline struct {}
26+ const (
27+ // Default path for Downward API labels
28+ defaultLabelsPath = "/etc/podinfo/labels"
29+ // Default Tekton dashboard URL
30+ defaultDashboardURL = "https://dashboard.tekton.dev"
31+ )
32+
33+ type TektonPipeline struct {
34+ // Path to the Downward API labels file (configurable for testing)
35+ labelsPath string
36+ }
2637
2738func NewTektonPipeline () * TektonPipeline {
28- return & TektonPipeline {}
39+ return & TektonPipeline {
40+ labelsPath : defaultLabelsPath ,
41+ }
2942}
3043
3144func (r * TektonPipeline ) ID () schemaapi.CraftingSchema_Runner_RunnerType {
@@ -42,70 +55,143 @@ func (r *TektonPipeline) CheckEnv() bool {
4255 return false
4356}
4457
58+ // ListEnvVars returns environment variables collected from Downward API labels
4559func (r * TektonPipeline ) ListEnvVars () []* EnvVarDefinition {
46- return []* EnvVarDefinition {
47- // PipelineRun context (optional - only present when running in a Pipeline)
48- {"TEKTON_PIPELINE_RUN" , true },
49- {"TEKTON_PIPELINE_RUN_UID" , true },
50- {"TEKTON_PIPELINE" , true },
60+ // Parse labels and convert to environment variable definitions
61+ labels := r .parseLabels ()
62+ if len (labels ) == 0 {
63+ return []* EnvVarDefinition {}
64+ }
65+
66+ var envVars []* EnvVarDefinition
67+
68+ // Map Tekton labels to environment variables (all optional)
69+ labelMappings := map [string ]string {
70+ "tekton.dev/pipelineRun" : "TEKTON_PIPELINE_RUN" ,
71+ "tekton.dev/pipelineRunUID" : "TEKTON_PIPELINE_RUN_UID" ,
72+ "tekton.dev/pipeline" : "TEKTON_PIPELINE" ,
73+ "tekton.dev/taskRun" : "TEKTON_TASKRUN_NAME" ,
74+ "tekton.dev/taskRunUID" : "TEKTON_TASKRUN_UID" ,
75+ "tekton.dev/task" : "TEKTON_TASK_NAME" ,
76+ }
77+
78+ for labelKey , envVarName := range labelMappings {
79+ if _ , exists := labels [labelKey ]; exists {
80+ envVars = append (envVars , & EnvVarDefinition {
81+ Name : envVarName ,
82+ Optional : true ,
83+ })
84+ }
85+ }
86+
87+ // Add namespace if available
88+ if ns := r .getNamespace (); ns != "" {
89+ envVars = append (envVars , & EnvVarDefinition {
90+ Name : "TEKTON_NAMESPACE" ,
91+ Optional : true ,
92+ })
93+ }
94+
95+ return envVars
96+ }
97+
98+ // parseLabels reads and parses the Downward API labels file
99+ // Returns a map of label key-value pairs
100+ func (r * TektonPipeline ) parseLabels () map [string ]string {
101+ labels := make (map [string ]string )
102+
103+ data , err := os .ReadFile (r .labelsPath )
104+ if err != nil {
105+ return labels
106+ }
107+
108+ // Parse labels in format: key="value"
109+ // Labels are separated by newlines
110+ lines := strings .Split (string (data ), "\n " )
111+ for _ , line := range lines {
112+ line = strings .TrimSpace (line )
113+ if line == "" {
114+ continue
115+ }
51116
52- // TaskRun context (optional - should be set by users following best practices)
53- {"TEKTON_TASKRUN_NAME" , true },
54- {"TEKTON_TASKRUN_UID" , true },
55- {"TEKTON_TASK_NAME" , true },
117+ // Split on first = to get key and quoted value
118+ parts := strings .SplitN (line , "=" , 2 )
119+ if len (parts ) != 2 {
120+ continue
121+ }
56122
57- // Namespace (optional - can be read from service account or set explicitly)
58- {"TEKTON_NAMESPACE" , true },
123+ key := strings .TrimSpace (parts [0 ])
124+ value := strings .Trim (strings .TrimSpace (parts [1 ]), `"` )
125+ labels [key ] = value
59126 }
127+
128+ return labels
60129}
61130
62131func (r * TektonPipeline ) RunURI () string {
132+ labels := r .parseLabels ()
133+ namespace := r .getNamespace ()
134+
135+ if namespace == "" {
136+ return ""
137+ }
138+
139+ // Get dashboard URL from environment variable or use default
140+ dashboardURL := os .Getenv ("TEKTON_DASHBOARD_URL" )
141+ if dashboardURL == "" {
142+ dashboardURL = defaultDashboardURL
143+ }
144+
63145 // Priority 1: If we have PipelineRun context, construct PipelineRun URL
64- if pipelineRun := os .Getenv ("TEKTON_PIPELINE_RUN" ); pipelineRun != "" {
65- namespace := r .getNamespace ()
66- if namespace != "" {
67- // Use Tekton Dashboard URL format
68- // Users can customize dashboard URL via environment variable
69- dashboardURL := os .Getenv ("TEKTON_DASHBOARD_URL" )
70- if dashboardURL == "" {
71- dashboardURL = "https://dashboard.tekton.dev"
72- }
73- return fmt .Sprintf ("%s/#/namespaces/%s/pipelineruns/%s" , dashboardURL , namespace , pipelineRun )
74- }
146+ if pipelineRun , ok := labels ["tekton.dev/pipelineRun" ]; ok && pipelineRun != "" {
147+ return fmt .Sprintf ("%s/#/namespaces/%s/pipelineruns/%s" , dashboardURL , namespace , pipelineRun )
75148 }
76149
77150 // Priority 2: If we have TaskRun context, construct TaskRun URL
78- if taskRun := os .Getenv ("TEKTON_TASKRUN_NAME" ); taskRun != "" {
79- namespace := r .getNamespace ()
80- if namespace != "" {
81- dashboardURL := os .Getenv ("TEKTON_DASHBOARD_URL" )
82- if dashboardURL == "" {
83- dashboardURL = "https://dashboard.tekton.dev"
84- }
85- return fmt .Sprintf ("%s/#/namespaces/%s/taskruns/%s" , dashboardURL , namespace , taskRun )
86- }
151+ if taskRun , ok := labels ["tekton.dev/taskRun" ]; ok && taskRun != "" {
152+ return fmt .Sprintf ("%s/#/namespaces/%s/taskruns/%s" , dashboardURL , namespace , taskRun )
87153 }
88154
89155 return ""
90156}
91157
92- // getNamespace attempts to get the namespace from multiple sources
158+ // getNamespace attempts to get the namespace from the service account
93159func (r * TektonPipeline ) getNamespace () string {
94- // Priority 1: Environment variable
95- if namespace := os .Getenv ("TEKTON_NAMESPACE" ); namespace != "" {
96- return namespace
97- }
98-
99- // Priority 2: Read from service account (standard Kubernetes location)
160+ // Read from service account (standard Kubernetes location)
100161 if data , err := os .ReadFile ("/var/run/secrets/kubernetes.io/serviceaccount/namespace" ); err == nil {
101- return string (data )
162+ return strings . TrimSpace ( string (data ) )
102163 }
103164
104165 return ""
105166}
106167
107168func (r * TektonPipeline ) ResolveEnvVars () (map [string ]string , []* error ) {
108- return resolveEnvVars (r .ListEnvVars ())
169+ result := make (map [string ]string )
170+ labels := r .parseLabels ()
171+
172+ // Map Tekton labels to environment variable names
173+ labelMappings := map [string ]string {
174+ "tekton.dev/pipelineRun" : "TEKTON_PIPELINE_RUN" ,
175+ "tekton.dev/pipelineRunUID" : "TEKTON_PIPELINE_RUN_UID" ,
176+ "tekton.dev/pipeline" : "TEKTON_PIPELINE" ,
177+ "tekton.dev/taskRun" : "TEKTON_TASKRUN_NAME" ,
178+ "tekton.dev/taskRunUID" : "TEKTON_TASKRUN_UID" ,
179+ "tekton.dev/task" : "TEKTON_TASK_NAME" ,
180+ }
181+
182+ for labelKey , envVarName := range labelMappings {
183+ if value , ok := labels [labelKey ]; ok && value != "" {
184+ result [envVarName ] = value
185+ }
186+ }
187+
188+ // Add namespace if available
189+ if ns := r .getNamespace (); ns != "" {
190+ result ["TEKTON_NAMESPACE" ] = ns
191+ }
192+
193+ // No errors since all variables are optional
194+ return result , nil
109195}
110196
111197func (r * TektonPipeline ) WorkflowFilePath () string {
0 commit comments