1
1
#! /usr/bin/env python3
2
- from dataclasses import dataclass
3
2
4
3
import click
5
4
import logging
10
9
11
10
class SyntheticTest :
12
11
'''
13
- Attributes for a specific synthetic test and its test run under a datadog CI batch
12
+ Attributes for a Datadog synthetic test and its test run
14
13
'''
15
14
def __init__ (self , name , public_id ):
16
- self .name = name
17
- self .public_id = public_id
18
- self .test_run_id = None
15
+ self .name = name # The test's Datadog name
16
+ self .public_id = public_id # The test's Datadog Test ID
17
+ self .test_run_id = None # The run ID given by Datadog to this test's invocation
19
18
self .success = None
20
19
21
20
class DatadogClient :
@@ -26,63 +25,68 @@ class DatadogClient:
26
25
27
26
WAFFLE_SWITCH_TEST = SyntheticTest (
28
27
'''
29
- Test on edx.org
28
+ "Waffle switch" test governing CI/CD synthetic testing
30
29
''' ,
31
30
"sad-hqu-h33"
32
31
)
33
32
34
33
def __init__ (self , api_key , app_key ):
35
34
self .api_key = api_key
36
35
self .app_key = app_key
37
- self .test_batch_id = None
38
- self .trigger_time = None
39
- self .tests_by_public_id = {}
36
+ self .test_batch_id = None # A 'batch' is a set of tests intended to be run in parallel
37
+ self .trigger_time = None # The system time at which a batch's execution was requested
38
+ self .tests_by_public_id = {} # Dictionary mapping Datadog test ID to all info we have for a specific test
40
39
41
40
def trigger_synthetic_tests (self , tests_to_report : [SyntheticTest ]):
42
41
'''
43
- Trigger running of one or more synthetic tests.
44
- :param tests_to_report: List of tests to run and reported on
45
- :return: None, but saves batch ID and test run IDs as object attributes
42
+ Trigger running of a batch of synthetic tests.
43
+ :param tests_to_report: List of tests to run and report on
44
+ :return: None, but saves test info including batch ID and test run IDs in 'self'
46
45
'''
47
46
48
- # Note that the list of tests to be run is actually one longer than the list of tests to be reported
49
- # on. This is the so-called "waffle switch test". That test should be modified via the Datadog UI to either
50
- # always pass or always fail, depending on whether synthetic testing is to be enabled at runtime or not.
51
- # It's result affects how the pipeline operates, but is not treated as a reportable smoke test.
47
+ # Note that the list of tests to be run is one longer than the list of tests to be reported on.
48
+ # The extra test is the so-called "waffle switch test". That test should be modified via the Datadog UI
49
+ # to either always pass or always fail, depending on whether synthetic testing is to be enabled at runtime
50
+ # or not, respectively.
51
+ # While the test's result does affect how the pipeline operates, the result is not treated as reportable.
52
52
tests_to_run = [self .WAFFLE_SWITCH_TEST ] + tests_to_report
53
53
self ._record_requested_test_particulars (tests_to_run )
54
54
self .trigger_time = time .time () # Key timeouts off of this
55
55
logging .info (f'CI batch triggered at time { self .trigger_time } ' )
56
56
57
57
try :
58
- response = self ._trigger_batch_tests ()
58
+ response = self ._trigger_batch_tests () # Kicks off asynchronous test execution for a batch of tests
59
59
response_body = response .json ()
60
- self ._record_batch_id (response_body )
61
- self ._map_test_run_ids (response_body )
60
+ self ._record_batch_id (response_body ) # a single batch ID has now been assiged. Save for future reference
61
+ self ._map_test_run_ids (response_body ) # one test run ID per test has been assigned. Save for reference.
62
62
63
63
except Exception as e :
64
64
raise Exception ("Datadog error on triggering tests: " + str (e ))
65
65
66
66
def gate_on_waffle_switch (self ):
67
67
'''
68
68
This is a bit hacky, but there's a designated test that's used as a waffle switch might be used in
69
- a Django app. If the test passes, it means that the GoCD pipeline is to pass or fail per the result
70
- of the Datadog synthetic tests that are run. If the test fails, then it means that the Datadog synthetic
71
- test results are to be disregarded. When this is the case, the GoCD pipeline responsible for running the
72
- tests should return with a summary success.
69
+ a Django app. If the test passes, it means that the synthetic testing GoCD pipeline is enabled, and the
70
+ build should only proceed if all reportable tests pass; if the test fails, the build should proceed irrespective
71
+ of any failures among the synthetic tests (which will be allowed to run, nonetheless). When this is intended,
72
+ the GoCD pipeline responsible for running the tests should just return a success code without waiting
73
+ for the reportable tests to complete their runs.
74
+
75
+ :return: Nothing, but terminates task with a success code if the synthetic testing feature is disabled
76
+ and logs the decision to skip testing on this build
73
77
'''
74
- logging .info ("Gating on waffle switch" )
75
78
waffle_switch = self ._poll_for_test_result (self .WAFFLE_SWITCH_TEST )
76
79
if waffle_switch == False :
77
80
switch_test_name = self .WAFFLE_SWITCH_TEST .name
78
81
logging .warning (
79
- f'*** Datadog Synthetic testing disabled via failing "Waffle flag" test { switch_test_name } ***' )
82
+ f'*** Datadog Synthetic testing disabled via failing test { switch_test_name } ***' )
80
83
sys .exit (0 )
81
84
82
85
def get_and_record_test_results (self ):
83
86
'''
84
- Poll for test results for all tests that were run, and save the results
85
- in this datadog client object
87
+ Poll for pass/fail results for all batch tests
88
+
89
+ :return: Nothing, but saves pass/fail results in 'self'
86
90
'''
87
91
for test in list (self .tests_by_public_id .values ()):
88
92
test .success = self ._poll_for_test_result (test )
@@ -170,7 +174,6 @@ def _poll_for_test_result(self, test):
170
174
171
175
Returns None if still running; otherwise, returns True on test success and False on test failure.
172
176
"""
173
- logging .info (f"Polling on test { test .public_id } : { test .name } , test_run_id = { test .test_run_id } " )
174
177
test_result = None
175
178
while test_result is None and (time .time () - self .trigger_time ) < (self .MAX_ALLOWABLE_TIME_SECS ):
176
179
time .sleep (5 ) # Poll every 5 seconds
0 commit comments