Skip to content

Commit 6d959c1

Browse files
committed
Introduce check for "out of CI credits" situation
When a Cirrus CI job started by 'cirrus-run' fails due lack of CI credits, it would look exactly like a failure in the job itself, thus the user would not be able to distinguish the problems. If a 'taks' failed due to CI minute lack, the 'notifications' field of given 'task' contains the following message: Failed to start an instance: FAILED_PRECONDITION: Monthly compute limit exceeded! To detect this, receive also task data and their notifications when waiting for a job and look for the message about CI credits. In order to see this status from scripts this will also return a new error code '3' if the out of CI credits situation is detected. Additionally in order to allow quick adaptation when the above string will be changed add '--cirrus-out-of-ci-credits-message' parameter which can configure the string. Signed-off-by: Peter Krempa <[email protected]>
1 parent fbe1ef6 commit 6d959c1

File tree

3 files changed

+28
-3
lines changed

3 files changed

+28
-3
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ return values:
9393
- 0: The cirrus job was successful
9494
- 1: The job failed
9595
- 2: Error in 'cirrus-run'
96+
- 3: The Cirrus CI job ran out of CI minutes
9697
```
9798

9899

cirrus_run/cli.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
from . import CirrusAPI
1616
from .throbber import ProgressBar
17-
from .queries import build_log, get_repo, create_build, wait_build, CirrusBuildError
17+
from .queries import build_log, get_repo, create_build, wait_build, CirrusBuildError, CirrusCreditsError
1818

1919
log = logging.getLogger(__name__)
2020

@@ -47,10 +47,12 @@ def run(args, retry_index=0):
4747
print('Build created: {}'.format(build_url))
4848
with ProgressBar('' if args.verbose else '.'):
4949
try:
50-
wait_build(api, build_id, abort=args.timeout*60)
50+
wait_build(api, build_id, abort=args.timeout*60, credits_error_message=args.cirrus_out_of_ci_credits_message)
5151
rc, status, message = 0, 'successful', ''
5252
except CirrusBuildError:
5353
rc, status, message = 1, 'failed', ''
54+
except CirrusCreditsError:
55+
rc, status, message = 3, 'error', 'Out of CI credits'
5456
except Exception as exc:
5557
rc, status, message = 2, 'error', '{exception}: {text}'.format(
5658
exception=exc.__class__.__name__,
@@ -224,6 +226,15 @@ def parse_args(*a, **ka):
224226
'the build is retried once more. Default: ${}'
225227
).format(ENVIRONMENT['flaky_markers']),
226228
)
229+
parser.add_argument(
230+
'--cirrus-out-of-ci-credits-message',
231+
default='Monthly compute limit exceeded',
232+
help=(
233+
'Error message passed via "notifications" from Cirrus CI reported'
234+
'when the CI job failed due to lack of CI credits.'
235+
'Default: "Monthly compute limit exceeded"'
236+
),
237+
)
227238
args = parser.parse_args(*a, **ka)
228239

229240
if not args.token:

cirrus_run/queries.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ class CirrusQueryError(ValueError):
2222
class CirrusBuildError(RuntimeError):
2323
'''Raised on build failures'''
2424

25+
class CirrusCreditsError(RuntimeError):
26+
'''Raised when build fails due to lack of CI credits'''
2527

2628
class CirrusTimeoutError(RuntimeError):
2729
'''Raised when build takes too long'''
@@ -85,14 +87,19 @@ def create_build(api: CirrusAPI,
8587
return answer['createBuild']['build']['id']
8688

8789

88-
def wait_build(api, build_id: str, delay=3, abort=60*60):
90+
def wait_build(api, build_id: str, delay=3, abort=60*60, credits_error_message=None):
8991
'''Wait until build finishes'''
9092
ERROR_CONFIRM_TIMES = 3
9193

9294
query = '''
9395
query GetBuild($build: ID!) {
9496
build(id: $build) {
9597
status
98+
tasks {
99+
notifications {
100+
message
101+
}
102+
}
96103
}
97104
}
98105
'''
@@ -116,6 +123,12 @@ def wait_build(api, build_id: str, delay=3, abort=60*60):
116123
sleep(2 * delay / (ERROR_CONFIRM_TIMES - 1))
117124
continue
118125
else:
126+
if credits_error_message is not None:
127+
for task in response['build']['tasks']:
128+
for notif in task['notifications']:
129+
if credits_error_message in notif['message']:
130+
raise CirrusCreditsError('build {} ran out of CI credits'.format(build_id))
131+
119132
raise CirrusBuildError('build {} was terminated: {}'.format(build_id, status))
120133
raise ValueError('build {} returned unknown status: {}'.format(build_id, status))
121134
raise CirrusTimeoutError('build {} timed out'.format(build_id))

0 commit comments

Comments
 (0)