-
Notifications
You must be signed in to change notification settings - Fork 243
/
jobs.go
165 lines (138 loc) · 4.39 KB
/
jobs.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
package controller
import (
"reflect"
"regexp"
"github.com/mvisonneau/gitlab-ci-pipelines-exporter/pkg/schemas"
log "github.com/sirupsen/logrus"
)
// PullRefPipelineJobsMetrics ..
func (c *Controller) PullRefPipelineJobsMetrics(ref schemas.Ref) error {
jobs, err := c.Gitlab.ListRefPipelineJobs(ref)
if err != nil {
return err
}
for _, job := range jobs {
c.ProcessJobMetrics(ref, job)
}
return nil
}
// PullRefMostRecentJobsMetrics ..
func (c *Controller) PullRefMostRecentJobsMetrics(ref schemas.Ref) error {
if !ref.Project.Pull.Pipeline.Jobs.Enabled {
return nil
}
jobs, err := c.Gitlab.ListRefMostRecentJobs(ref)
if err != nil {
return err
}
for _, job := range jobs {
c.ProcessJobMetrics(ref, job)
}
return nil
}
// ProcessJobMetrics ..
func (c *Controller) ProcessJobMetrics(ref schemas.Ref, job schemas.Job) {
projectRefLogFields := log.Fields{
"project-name": ref.Project.Name,
"job-name": job.Name,
"job-id": job.ID,
}
labels := ref.DefaultLabelsValues()
labels["stage"] = job.Stage
labels["job_name"] = job.Name
if ref.Project.Pull.Pipeline.Jobs.RunnerDescription.Enabled {
re, err := regexp.Compile(ref.Project.Pull.Pipeline.Jobs.RunnerDescription.AggregationRegexp)
if err != nil {
log.WithFields(projectRefLogFields).WithField("error", err.Error()).Error("invalid job runner description aggregation regexp")
}
if re.MatchString(job.Runner.Description) {
labels["runner_description"] = ref.Project.Pull.Pipeline.Jobs.RunnerDescription.AggregationRegexp
} else {
labels["runner_description"] = job.Runner.Description
}
} else {
// TODO: Figure out how to completely remove it from the exporter instead of keeping it empty
labels["runner_description"] = ""
}
// Refresh ref state from the store
if err := c.Store.GetRef(&ref); err != nil {
log.WithFields(projectRefLogFields).WithField("error", err.Error()).Error("getting ref from the store")
return
}
// In case a job gets restarted, it will have an ID greated than the previous one(s)
// jobs in new pipelines should get greated IDs too
lastJob, lastJobExists := ref.LatestJobs[job.Name]
if lastJobExists && reflect.DeepEqual(lastJob, job) {
return
}
// Update the ref in the store
if ref.LatestJobs == nil {
ref.LatestJobs = make(schemas.Jobs)
}
ref.LatestJobs[job.Name] = job
if err := c.Store.SetRef(ref); err != nil {
log.WithFields(
projectRefLogFields,
).WithField("error", err.Error()).Error("writing ref in the store")
return
}
log.WithFields(projectRefLogFields).Trace("processing job metrics")
storeSetMetric(c.Store, schemas.Metric{
Kind: schemas.MetricKindJobID,
Labels: labels,
Value: float64(job.ID),
})
storeSetMetric(c.Store, schemas.Metric{
Kind: schemas.MetricKindJobTimestamp,
Labels: labels,
Value: job.Timestamp,
})
storeSetMetric(c.Store, schemas.Metric{
Kind: schemas.MetricKindJobDurationSeconds,
Labels: labels,
Value: job.DurationSeconds,
})
storeSetMetric(c.Store, schemas.Metric{
Kind: schemas.MetricKindJobQueuedDurationSeconds,
Labels: labels,
Value: job.QueuedDurationSeconds,
})
jobRunCount := schemas.Metric{
Kind: schemas.MetricKindJobRunCount,
Labels: labels,
}
// If the metric does not exist yet, start with 0 instead of 1
// this could cause some false positives in prometheus
// when restarting the exporter otherwise
jobRunCountExists, err := c.Store.MetricExists(jobRunCount.Key())
if err != nil {
log.WithFields(
projectRefLogFields,
).WithField("error", err.Error()).Error("checking if metric exists in the store")
return
}
// We want to increment this counter only once per job ID if:
// - the metric is already set
// - the job has been triggered
jobTriggeredRegexp := regexp.MustCompile("^(skipped|manual|scheduled)$")
lastJobTriggered := !jobTriggeredRegexp.MatchString(lastJob.Status)
jobTriggered := !jobTriggeredRegexp.MatchString(job.Status)
if jobRunCountExists && ((lastJob.ID != job.ID && jobTriggered) || (lastJob.ID == job.ID && jobTriggered && !lastJobTriggered)) {
storeGetMetric(c.Store, &jobRunCount)
jobRunCount.Value++
}
storeSetMetric(c.Store, jobRunCount)
storeSetMetric(c.Store, schemas.Metric{
Kind: schemas.MetricKindJobArtifactSizeBytes,
Labels: labels,
Value: job.ArtifactSize,
})
emitStatusMetric(
c.Store,
schemas.MetricKindJobStatus,
labels,
statusesList[:],
job.Status,
ref.Project.OutputSparseStatusMetrics,
)
}