Skip to content

Commit 238cad0

Browse files
committed
use downward api
Signed-off-by: Javier Rodriguez <javier@chainloop.dev>
1 parent b3042bd commit 238cad0

File tree

4 files changed

+292
-164
lines changed

4 files changed

+292
-164
lines changed

app/cli/pkg/action/workflow_run_list.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ func humanizedRunnerType(in v1.CraftingSchema_Runner_RunnerType) string {
193193
*v1.CraftingSchema_Runner_CIRCLECI_BUILD.Enum(): "CircleCI Build",
194194
*v1.CraftingSchema_Runner_DAGGER_PIPELINE.Enum(): "Dagger Pipeline",
195195
*v1.CraftingSchema_Runner_TEAMCITY_PIPELINE.Enum(): "TeamCity Pipeline",
196+
*v1.CraftingSchema_Runner_TEKTON_PIPELINE.Enum(): "Tekton Pipeline",
196197
}
197198

198199
hrt, ok := mapping[in]

app/cli/pkg/action/workflow_run_list_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ func (s *workflowRunListSuite) TestHumanizedRunnerType() {
6464
name: "teamcity runner",
6565
testInput: v1.CraftingSchema_Runner_TEAMCITY_PIPELINE,
6666
expectedOutput: "TeamCity Pipeline",
67+
}, {
68+
name: "tekton runner",
69+
testInput: v1.CraftingSchema_Runner_TEKTON_PIPELINE,
70+
expectedOutput: "Tekton Pipeline",
6771
}, {
6872
name: "unknown runner",
6973
testInput: -34,

pkg/attestation/crafter/runners/tektonpipeline.go

Lines changed: 129 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
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
1818
import (
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

2738
func NewTektonPipeline() *TektonPipeline {
28-
return &TektonPipeline{}
39+
return &TektonPipeline{
40+
labelsPath: defaultLabelsPath,
41+
}
2942
}
3043

3144
func (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
4559
func (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

62131
func (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
93159
func (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

107168
func (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

111197
func (r *TektonPipeline) WorkflowFilePath() string {

0 commit comments

Comments
 (0)