@@ -87,14 +87,17 @@ def buildkite_fetch_page_build_list(
87
87
buildkite_token : str , after_cursor : str = None
88
88
) -> list [dict [str , str ]]:
89
89
"""Fetches a page of the build list using the GraphQL BuildKite API.
90
+
90
91
Returns the BUILDKITE_GRAPHQL_BUILDS_PER_PAGE last running/queued builds,
91
92
or the BUILDKITE_GRAPHQL_BUILDS_PER_PAGE running/queued builds
92
93
older than the one pointer by |after_cursor| if provided.
93
94
The |after_cursor| value is taken from the previous page returned by the
94
95
API.
96
+
95
97
Args:
96
98
buildkite_token: the secret token to authenticate GraphQL requests.
97
99
after_cursor: cursor after which to start the page fetch.
100
+
98
101
Returns:
99
102
The most recent builds after cursor (if set) with the following format:
100
103
[
@@ -130,7 +133,7 @@ def buildkite_fetch_page_build_list(
130
133
AFTER = "null" if after_cursor is None else '"{}"' .format (after_cursor ),
131
134
)
132
135
data = data .replace ("\n " , "" ).replace ('"' , '\\ "' )
133
- data = '{ "query": "' + data + '" }'
136
+ data = json . dumps ({ "query" : data }) # '{ "query": "' + data + '" }'
134
137
url = "https://graphql.buildkite.com/v1"
135
138
headers = {
136
139
"Authorization" : "Bearer " + buildkite_token ,
@@ -151,12 +154,15 @@ def buildkite_fetch_page_build_list(
151
154
152
155
def buildkite_get_build_info (build_number : str ) -> dict :
153
156
"""Returns all the info associated with the provided build number.
157
+
154
158
Note: for unknown reasons, graphql returns no jobs for a given build,
155
159
while this endpoint does, hence why this uses this API instead of graphql.
156
- Args:
157
- build_number: which build number to fetch info for.
158
- Returns:
159
- The info for the target build, a JSON dictionnary.
160
+
161
+ Args:
162
+ build_number: which build number to fetch info for.
163
+
164
+ Returns:
165
+ The info for the target build, a JSON dictionnary.
160
166
"""
161
167
162
168
URL = "https://buildkite.com/llvm-project/github-pull-requests/builds/{}.json"
@@ -165,6 +171,7 @@ def buildkite_get_build_info(build_number: str) -> dict:
165
171
166
172
def buildkite_get_incomplete_tasks (buildkite_token : str ) -> list :
167
173
"""Returns all the running/pending BuildKite builds.
174
+
168
175
Args:
169
176
buildkite_token: the secret token to authenticate GraphQL requests.
170
177
last_cursor: the cursor to stop at if set. If None, a full page is fetched.
@@ -184,13 +191,14 @@ def buildkite_get_metrics(
184
191
buildkite_token : str , previously_incomplete : set [int ]
185
192
) -> (list [JobMetrics ], set [int ]):
186
193
"""Returns a tuple with:
194
+
187
195
- the metrics recorded for newly completed workflow jobs.
188
196
- the set of workflow still running now.
189
197
190
198
Args:
191
- buildkite_token: the secret token to authenticate GraphQL requests.
192
- previously_incomplete: the set of running workflows the last time this
193
- function was called.
199
+ buildkite_token: the secret token to authenticate GraphQL requests.
200
+ previously_incomplete: the set of running workflows the last time this
201
+ function was called.
194
202
"""
195
203
196
204
running_builds = buildkite_get_incomplete_tasks (buildkite_token )
@@ -204,14 +212,31 @@ def buildkite_get_metrics(
204
212
info = buildkite_get_build_info (build_id )
205
213
metric_timestamp = dateutil .parser .isoparse (info ["finished_at" ])
206
214
for job in info ["jobs" ]:
207
- # Skip this job .
215
+ # This workflow is not interesting to us .
208
216
if job ["name" ] not in BUILDKITE_WORKFLOW_TO_TRACK :
209
217
continue
210
218
219
+ # Note: BuildKite API can return empty dates for some fields
220
+ # depending on the completion scenario. Example, a job cancelled
221
+ # before even starting will get an None date for 'started_at'.
222
+ # For this reason, if a timestamp is missing, we consider it
223
+ # skipped and keep the last event value.
211
224
created_at = dateutil .parser .isoparse (job ["created_at" ])
212
- scheduled_at = dateutil .parser .isoparse (job ["scheduled_at" ])
213
- started_at = dateutil .parser .isoparse (job ["started_at" ])
214
- finished_at = dateutil .parser .isoparse (job ["finished_at" ])
225
+ scheduled_at = (
226
+ dateutil .parser .isoparse (job ["scheduled_at" ])
227
+ if "scheduled_at" in job
228
+ else created_at
229
+ )
230
+ started_at = (
231
+ dateutil .parser .isoparse (job ["started_at" ])
232
+ if "started_at" in job
233
+ else scheduled_at
234
+ )
235
+ finished_at = (
236
+ dateutil .parser .isoparse (job ["finished_at" ])
237
+ if "finished_at" in job
238
+ else started_at
239
+ )
215
240
216
241
job_name = BUILDKITE_WORKFLOW_TO_TRACK [job ["name" ]]
217
242
queue_time = (started_at - scheduled_at ).seconds
@@ -224,7 +249,7 @@ def buildkite_get_metrics(
224
249
datetime .datetime .now (datetime .timezone .utc ) - metric_timestamp
225
250
).total_seconds () / 60
226
251
if metric_age_mn > GRAFANA_METRIC_MAX_AGE_MN :
227
- logging .info (
252
+ logging .warning (
228
253
f"Job { job ['name' ]} from workflow { build_id } dropped due"
229
254
+ f" to staleness: { metric_age_mn } mn old."
230
255
)
@@ -372,7 +397,7 @@ def github_get_metrics(
372
397
datetime .datetime .now (datetime .timezone .utc ) - completed_at
373
398
).total_seconds () / 60
374
399
if metric_age_mn > GRAFANA_METRIC_MAX_AGE_MN :
375
- logging .info (
400
+ logging .warning (
376
401
f"Job { job .id } from workflow { task .id } dropped due"
377
402
+ f" to staleness: { metric_age_mn } mn old."
378
403
)
0 commit comments