Skip to content

Commit 7d9d75e

Browse files
authored
add pipeline and job attributes (#8)
* add pipeline and job attr * fix gh actions
1 parent 4b0bfac commit 7d9d75e

File tree

5 files changed

+230
-87
lines changed

5 files changed

+230
-87
lines changed

.github/workflows/test.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
go-version: ${{ env.GO_VERSION }}
1818
- shell: bash
1919
run: |
20-
cd pkg && go test -v
20+
go test -v
2121
govulncheck_job:
2222
runs-on: ubuntu-latest
2323
name: Run govulncheck

const.go

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,33 @@ const (
1212
conventionsAttributeCiCdTaskRunUrl = "cicd.pipeline.task.run.url.full"
1313

1414
//Custom Attributes - not part of Semconv 1.27.0
15-
conventionsAttributeCiCdPipelineUrl = "cicd.pipeline.url"
16-
conventionsAttributeCiCdParentPipelineId = "cicd.parent.pipeline.run.id"
17-
conventionsAttributeCiCdParentPipelineUrl = "cicd.parent.pipeline.url"
15+
16+
//General
17+
conventionsAttributeSpanSource = "span.source"
18+
19+
//Pipeline
20+
conventionsAttributeCiCdPipelineUrl = "cicd.pipeline.url"
21+
conventionsAttributeCiCdParentPipelineId = "cicd.parent.pipeline.run.id"
22+
conventionsAttributeCiCdParentPipelineUrl = "cicd.parent.pipeline.url"
23+
conventionsAttributeCiCdPipelineDuration = "cicd.pipeline.duration"
24+
conventionsAttributeCiCdPipelineQueuedDuration = "cicd.pipeline.queued.duration"
25+
conventionsAttributeCiCdPipelineVariable = "cicd.pipeline.variable"
26+
conventionsAttributeCiCdPipelineUser = "cicd.pipeline.user"
27+
conventionsAttributeCiCdPipelineUsername = "cicd.pipeline.username"
28+
conventionsAttributeCiCdPipelineUserEmail = "cicd.pipeline.user.email"
29+
30+
conventionsAttributeCiCdPipelineCommitMessage = "cicd.pipeline.commit.message"
31+
conventionsAttributeCiCdPipelineCommitTitle = "cicd.pipeline.commit.title"
32+
conventionsAttributeCiCdPipelineCommitTimestamp = "cicd.pipeline.commit.timestamp"
33+
conventionsAttributeCiCdPipelineCommitUrl = "cicd.pipeline.commit.url"
34+
conventionsAttributeCiCdPipelineCommitAuthorEmail = "cicd.pipeline.commit.author.email"
35+
36+
//Job
37+
conventionsAttributeCiCdJobEnvironment = "cicd.job.environment"
38+
39+
conventionsAttributeCiCdJobRunnerId = "cicd.job.runner.id"
40+
conventionsAttributeCiCdJobRunnerDescription = "cicd.job.runner.description"
41+
conventionsAttributeCiCdJobRunnerIsActive = "cicd.job.runner.active"
42+
conventionsAttributeCiCdJobRunnerIsShared = "cicd.job.runner.shared"
43+
conventionsAttributeCiCdJobRunnerTag = "cicd.job.runner.tag"
1844
)

gitlab.go

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ func (p *glPipelineEvent) newTrace() (*ptrace.Traces, error) {
5151
rss.EnsureCapacity(len(p.Jobs) + 1 + 1)
5252
rs := rss.AppendEmpty()
5353
rs.Resource().Attributes().PutStr(conventions.AttributeServiceName, p.Project.Path)
54+
rs.Resource().Attributes().PutStr(conventionsAttributeSpanSource, fmt.Sprintf("%s-receiver", typeStr.String()))
5455

5556
//The pipeline span is the root span, therefore 0 bytes for the parentSpanId
5657
createSpan(rs, traceId, rootSpanId, [8]byte{0, 0, 0, 0, 0, 0, 0, 0}, pipelineName, startTime, endTime, p)
@@ -77,9 +78,25 @@ func (p *glPipelineEvent) newTrace() (*ptrace.Traces, error) {
7778

7879
// CICD Pipeline semconv: https://opentelemetry.io/docs/specs/semconv/attributes-registry/cicd/#cicd-pipeline-attributes
7980
func (p glPipelineEvent) setAttributes(s ptrace.Span) {
80-
s.Attributes().EnsureCapacity(3)
81+
vc := len(p.Pipeline.Variables)
82+
s.Attributes().EnsureCapacity(12 + vc)
8183
s.Attributes().PutStr(conventionsAttributeCiCdPipelineUrl, p.Pipeline.Url)
8284
s.Attributes().PutStr(conventionsAttributeCidCPipelineRunId, strconv.Itoa(p.Pipeline.Id))
85+
s.Attributes().PutStr(conventionsAttributeCiCdPipelineDuration, strconv.Itoa(p.Pipeline.Duration))
86+
s.Attributes().PutStr(conventionsAttributeCiCdPipelineQueuedDuration, strconv.Itoa(p.Pipeline.QueuedDuration))
87+
s.Attributes().PutStr(conventionsAttributeCiCdPipelineUser, p.User.Name)
88+
s.Attributes().PutStr(conventionsAttributeCiCdPipelineUsername, p.User.Username)
89+
s.Attributes().PutStr(conventionsAttributeCiCdPipelineUserEmail, p.User.Email)
90+
91+
s.Attributes().PutStr(conventionsAttributeCiCdPipelineCommitMessage, p.Commit.Message)
92+
s.Attributes().PutStr(conventionsAttributeCiCdPipelineCommitTitle, p.Commit.Title)
93+
s.Attributes().PutStr(conventionsAttributeCiCdPipelineCommitTimestamp, p.Commit.Timestamp)
94+
s.Attributes().PutStr(conventionsAttributeCiCdPipelineCommitUrl, p.Commit.URL)
95+
s.Attributes().PutStr(conventionsAttributeCiCdPipelineCommitAuthorEmail, p.Commit.Author.Email)
96+
97+
for _, v := range p.Pipeline.Variables {
98+
s.Attributes().PutStr(fmt.Sprintf("%s.%s", conventionsAttributeCiCdPipelineVariable, v.Key), v.Value)
99+
}
83100

84101
if p.Pipeline.Source == "parent_pipeline" {
85102
s.Attributes().PutStr(conventionsAttributeCiCdParentPipelineId, strconv.Itoa(p.ParentPipeline.Id))
@@ -101,10 +118,21 @@ func (j Job) setAttributes(s ptrace.Span) {
101118
stage = "deploy"
102119
}
103120

104-
s.Attributes().EnsureCapacity(3)
121+
rtc := len(j.Runner.Tags)
122+
s.Attributes().EnsureCapacity(8 + rtc)
105123
s.Attributes().PutStr(conventionsAttributeCiCdTaskRunId, strconv.Itoa(j.Id))
106124
s.Attributes().PutStr(conventionsAttributeCiCdTaskRunUrl, j.Url)
107125
s.Attributes().PutStr(conventionsAttributeCiCdPipelineTaskType, stage)
126+
s.Attributes().PutStr(conventionsAttributeCiCdJobEnvironment, j.Environment.Name)
127+
128+
s.Attributes().PutStr(conventionsAttributeCiCdJobRunnerId, strconv.Itoa(j.Runner.Id))
129+
s.Attributes().PutStr(conventionsAttributeCiCdJobRunnerDescription, j.Runner.Description)
130+
s.Attributes().PutStr(conventionsAttributeCiCdJobRunnerIsActive, strconv.FormatBool(j.Runner.IsActive))
131+
s.Attributes().PutStr(conventionsAttributeCiCdJobRunnerIsShared, strconv.FormatBool(j.Runner.IsShared))
132+
133+
for _, t := range j.Runner.Tags {
134+
s.Attributes().PutStr(conventionsAttributeCiCdJobRunnerTag, t)
135+
}
108136

109137
setSpanStatus(s, j.Status)
110138
}

gitlab_test.go

Lines changed: 117 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package gitlabreceiver
22

33
import (
44
"encoding/hex"
5+
"fmt"
56
"io"
67
"net/http"
78
"strings"
@@ -145,42 +146,92 @@ func PipelineEvent_SetAttributes(t *testing.T) {
145146
expected map[string]string
146147
}{
147148
{
148-
name: "With parent pipeline",
149+
name: "With parent pipeline and variables",
149150
event: glPipelineEvent{
150151
Pipeline: Pipeline{
151-
Url: "https://gitlab.com/test-pipeline",
152-
Id: 123,
153-
Source: "parent_pipeline",
154-
Status: "success",
152+
Url: "https://gitlab.com/test-pipeline",
153+
Id: 123,
154+
Source: "parent_pipeline",
155+
Status: "success",
156+
Duration: 3600,
157+
QueuedDuration: 120,
158+
Variables: []Variables{
159+
{Key: "ENV", Value: "production"},
160+
{Key: "DEBUG", Value: "false"},
161+
},
155162
},
156163
ParentPipeline: ParentPipeline{
157164
Id: 456,
158165
Project: Project{
159166
Url: "https://gitlab.com/test-parent-project",
160167
},
161168
},
169+
User: User{
170+
Name: "John Doe",
171+
Username: "johndoe",
172+
173+
},
174+
Commit: Commit{
175+
Message: "Fix pipeline issue",
176+
Title: "Pipeline fix",
177+
Timestamp: "2024-10-19T12:00:00Z",
178+
URL: "https://gitlab.com/commit/789",
179+
Author: Author{Email: "[email protected]"},
180+
},
162181
},
163182
expected: map[string]string{
164-
conventionsAttributeCiCdPipelineUrl: "https://gitlab.com/test-pipeline",
165-
conventionsAttributeCidCPipelineRunId: "123",
166-
conventionsAttributeCiCdParentPipelineId: "456",
167-
conventionsAttributeCiCdParentPipelineUrl: "https://gitlab.com/test-parent-project/pipelines/456",
183+
conventionsAttributeCiCdPipelineUrl: "https://gitlab.com/test-pipeline",
184+
conventionsAttributeCidCPipelineRunId: "123",
185+
conventionsAttributeCiCdPipelineDuration: "3600",
186+
conventionsAttributeCiCdPipelineQueuedDuration: "120",
187+
conventionsAttributeCiCdPipelineUser: "John Doe",
188+
conventionsAttributeCiCdPipelineUsername: "johndoe",
189+
conventionsAttributeCiCdPipelineUserEmail: "[email protected]",
190+
conventionsAttributeCiCdPipelineCommitMessage: "Fix pipeline issue",
191+
conventionsAttributeCiCdPipelineCommitTitle: "Pipeline fix",
192+
conventionsAttributeCiCdPipelineCommitTimestamp: "2024-10-19T12:00:00Z",
193+
conventionsAttributeCiCdPipelineCommitUrl: "https://gitlab.com/commit/789",
194+
conventionsAttributeCiCdPipelineCommitAuthorEmail: "[email protected]",
195+
conventionsAttributeCiCdParentPipelineId: "456",
196+
conventionsAttributeCiCdParentPipelineUrl: "https://gitlab.com/test-parent-project/pipelines/456",
197+
198+
// Variable assertions
199+
fmt.Sprintf("%s.%s", conventionsAttributeCiCdPipelineVariable, "ENV"): "production",
200+
fmt.Sprintf("%s.%s", conventionsAttributeCiCdPipelineVariable, "DEBUG"): "false",
168201
},
169202
},
170203
{
171-
name: "Without parent pipeline",
204+
name: "Without parent pipeline and commit info",
172205
event: glPipelineEvent{
173206
Pipeline: Pipeline{
174-
Url: "https://gitlab.com/test-pipeline",
175-
Id: 124,
176-
Source: "direct",
177-
Status: "failed",
207+
Url: "https://gitlab.com/test-pipeline",
208+
Id: 124,
209+
Source: "direct",
210+
Status: "failed",
211+
Duration: 1800,
212+
QueuedDuration: 60,
213+
Variables: []Variables{
214+
{Key: "ENV", Value: "staging"},
215+
},
178216
},
179217
ParentPipeline: ParentPipeline{}, // No parent
218+
User: User{
219+
Name: "Jane Doe",
220+
Username: "janedoe",
221+
222+
},
223+
Commit: Commit{}, // No commit info
180224
},
181225
expected: map[string]string{
182-
conventionsAttributeCiCdPipelineUrl: "https://gitlab.com/test-pipeline",
183-
conventionsAttributeCidCPipelineRunId: "124",
226+
conventionsAttributeCiCdPipelineUrl: "https://gitlab.com/test-pipeline",
227+
conventionsAttributeCidCPipelineRunId: "124",
228+
conventionsAttributeCiCdPipelineDuration: "1800",
229+
conventionsAttributeCiCdPipelineQueuedDuration: "60",
230+
conventionsAttributeCiCdPipelineUser: "Jane Doe",
231+
conventionsAttributeCiCdPipelineUsername: "janedoe",
232+
conventionsAttributeCiCdPipelineUserEmail: "[email protected]",
233+
// Variable assertions
234+
fmt.Sprintf("%s.%s", conventionsAttributeCiCdPipelineVariable, "ENV"): "staging",
184235
},
185236
},
186237
}
@@ -208,71 +259,57 @@ func Job_SetAttributes(t *testing.T) {
208259
{
209260
name: "Successful job",
210261
job: Job{
211-
Id: 789,
212-
Url: "https://gitlab.com/test-job-success",
213-
Stage: "test",
214-
Status: "success",
215-
},
216-
expected: map[string]string{
217-
conventionsAttributeCiCdTaskRunId: "789",
218-
conventionsAttributeCiCdTaskRunUrl: "https://gitlab.com/test-job-success",
219-
conventionsAttributeCiCdPipelineTaskType: "test",
220-
},
221-
},
222-
{
223-
name: "Failed job",
224-
job: Job{
225-
Id: 790,
226-
Url: "https://gitlab.com/test-job-fail",
227-
Stage: "deploy",
228-
Status: "failed",
229-
},
230-
expected: map[string]string{
231-
conventionsAttributeCiCdTaskRunId: "790",
232-
conventionsAttributeCiCdTaskRunUrl: "https://gitlab.com/test-job-fail",
233-
conventionsAttributeCiCdPipelineTaskType: "deploy",
234-
},
235-
},
236-
{
237-
name: "Job with empty URL",
238-
job: Job{
239-
Id: 791,
240-
Url: "",
241-
Stage: "build",
242-
Status: "success",
243-
},
244-
expected: map[string]string{
245-
conventionsAttributeCiCdTaskRunId: "791",
246-
conventionsAttributeCiCdTaskRunUrl: "",
247-
conventionsAttributeCiCdPipelineTaskType: "build",
248-
},
249-
},
250-
{
251-
name: "Job with empty stage",
252-
job: Job{
253-
Id: 792,
254-
Url: "https://gitlab.com/test-job-empty-stage",
255-
Stage: "",
256-
Status: "success",
262+
Id: 789,
263+
Url: "https://gitlab.com/test-job-success",
264+
Stage: "test",
265+
Status: "success",
266+
Environment: Environment{Name: "prod"},
267+
Runner: Runner{
268+
Id: 101,
269+
Description: "High performance runner",
270+
IsActive: true,
271+
IsShared: false,
272+
Tags: []string{"docker", "linux"},
273+
},
257274
},
258275
expected: map[string]string{
259-
conventionsAttributeCiCdTaskRunId: "792",
260-
conventionsAttributeCiCdTaskRunUrl: "https://gitlab.com/test-job-empty-stage",
261-
conventionsAttributeCiCdPipelineTaskType: "",
276+
conventionsAttributeCiCdTaskRunId: "789",
277+
conventionsAttributeCiCdTaskRunUrl: "https://gitlab.com/test-job-success",
278+
conventionsAttributeCiCdPipelineTaskType: "test",
279+
conventionsAttributeCiCdJobEnvironment: "prod",
280+
conventionsAttributeCiCdJobRunnerId: "101",
281+
conventionsAttributeCiCdJobRunnerDescription: "High performance runner",
282+
conventionsAttributeCiCdJobRunnerIsActive: "true",
283+
conventionsAttributeCiCdJobRunnerIsShared: "false",
284+
conventionsAttributeCiCdJobRunnerTag: "docker",
262285
},
263286
},
264287
{
265-
name: "Job with special characters in URL",
288+
name: "Failed job with runner tags",
266289
job: Job{
267-
Id: 793,
268-
Url: "https://gitlab.com/test-job-@#$%&",
269-
Stage: "integration",
270-
Status: "success",
290+
Id: 790,
291+
Url: "https://gitlab.com/test-job-fail",
292+
Stage: "deploy",
293+
Status: "failed",
294+
Environment: Environment{Name: "staging"},
295+
Runner: Runner{
296+
Id: 102,
297+
Description: "Backup runner",
298+
IsActive: false,
299+
IsShared: true,
300+
Tags: []string{"backup", "windows"},
301+
},
271302
},
272303
expected: map[string]string{
273-
conventionsAttributeCiCdTaskRunId: "793",
274-
conventionsAttributeCiCdTaskRunUrl: "https://gitlab.com/test-job-@#$%&",
275-
conventionsAttributeCiCdPipelineTaskType: "integration",
304+
conventionsAttributeCiCdTaskRunId: "790",
305+
conventionsAttributeCiCdTaskRunUrl: "https://gitlab.com/test-job-fail",
306+
conventionsAttributeCiCdPipelineTaskType: "deploy",
307+
conventionsAttributeCiCdJobEnvironment: "staging",
308+
conventionsAttributeCiCdJobRunnerId: "102",
309+
conventionsAttributeCiCdJobRunnerDescription: "Backup runner",
310+
conventionsAttributeCiCdJobRunnerIsActive: "false",
311+
conventionsAttributeCiCdJobRunnerIsShared: "true",
312+
conventionsAttributeCiCdJobRunnerTag: "backup",
276313
},
277314
},
278315
}
@@ -287,6 +324,13 @@ func Job_SetAttributes(t *testing.T) {
287324
t.Errorf("expected %s to be %s, got %s", key, expectedValue, actualValue.Str())
288325
}
289326
}
327+
328+
// Special check for runner tags (since there may be multiple)
329+
for _, tag := range tt.job.Runner.Tags {
330+
if actualTagValue, exists := span.Attributes().Get(conventionsAttributeCiCdJobRunnerTag); !exists || actualTagValue.Str() != tag {
331+
t.Errorf("expected tag %s, but got %s", tag, actualTagValue.Str())
332+
}
333+
}
290334
})
291335
}
292336
}

0 commit comments

Comments
 (0)