1import itertools
2import os
3import subprocess
4import sys
5
6# Require Python 3.7+ for ordered dictionaries so that the order of the
7# generated tests remain the same.
8if sys.version_info[:2] < (3, 7):
9 print('ERROR: This script requires Python >= 3.7, not:')
10 print(sys.version)
11 print('Usage: python3 %s' % (sys.argv[0]))
12 exit(1)
13
14
15dirname = os.path.dirname
16DIR = dirname(os.path.realpath(__file__))
17SOURCE = dirname(dirname(dirname(DIR)))
18
19
20def template(filename):
21 fullpath = os.path.join(DIR, filename)
22 with open(fullpath, 'r') as f:
23 return f.read()
24
25
26def write_test(filename, data):
27 fullpath = os.path.join(DIR, filename + '.yml')
28 with open(fullpath, 'w') as f:
29 f.write(data)
30
31 print(f"Generated {fullpath}")
32
33
34# Maps from error_name to (error_code,)
35ERR_CODES = {
36 'InterruptedAtShutdown': (11600,),
37 'InterruptedDueToReplStateChange': (11602,),
38 'NotPrimaryOrSecondary': (13436,),
39 'PrimarySteppedDown': (189,),
40 'ShutdownInProgress': (91,),
41 'NotWritablePrimary': (10107,),
42 'NotPrimaryNoSecondaryOk': (13435,),
43 'LegacyNotPrimary': (10058,),
44}
45
46
47def create_stale_tests():
48 tmp = template('stale-topologyVersion.yml.template')
49 for error_name in ERR_CODES:
50 test_name = f'stale-topologyVersion-{error_name}'
51 error_code, = ERR_CODES[error_name]
52 data = tmp.format(**locals())
53 write_test(test_name, data)
54
55TV_GREATER = '''
56 topologyVersion:
57 processId:
58 "$oid": '000000000000000000000001'
59 counter:
60 "$numberLong": "2"'''
61TV_GREATER_FINAL = '''
62 processId:
63 "$oid": '000000000000000000000001'
64 counter:
65 "$numberLong": "2"'''
66TV_CHANGED = '''
67 topologyVersion:
68 processId:
69 "$oid": '000000000000000000000002'
70 counter:
71 "$numberLong": "1"'''
72TV_CHANGED_FINAL = '''
73 processId:
74 "$oid": '000000000000000000000002'
75 counter:
76 "$numberLong": "1"'''
77
78# Maps non-stale error description to:
79# (error_topology_version, final_topology_version)
80NON_STALE_CASES = {
81 'topologyVersion missing': ('', ' null'),
82 'topologyVersion greater': (TV_GREATER, TV_GREATER_FINAL),
83 'topologyVersion proccessId changed': (TV_CHANGED, TV_CHANGED_FINAL),
84}
85
86
87def create_non_stale_tests():
88 tmp = template('non-stale-topologyVersion.yml.template')
89 for error_name, description in itertools.product(
90 ERR_CODES, NON_STALE_CASES):
91 test_name = f'non-stale-{description.replace(" ", "-")}-{error_name}'
92 error_code, = ERR_CODES[error_name]
93 error_topology_version, final_topology_version = NON_STALE_CASES[description]
94 # On 4.2+, only ShutdownInProgress and InterruptedAtShutdown will
95 # clear the pool.
96 if error_name in ("ShutdownInProgress", "InterruptedAtShutdown"):
97 final_pool_generation = 1
98 else:
99 final_pool_generation = 0
100
101 data = tmp.format(**locals())
102 write_test(test_name, data)
103
104
105WHEN = ['beforeHandshakeCompletes', 'afterHandshakeCompletes']
106STALE_GENERATION_COMMAND_ERROR = '''
107 type: command
108 response:
109 ok: 0
110 errmsg: {error_name}
111 code: {error_code}
112 topologyVersion:
113 processId:
114 "$oid": '000000000000000000000001'
115 counter:
116 "$numberLong": "2"'''
117STALE_GENERATION_NETWORK_ERROR = '''
118 type: {network_error_type}'''
119
120
121def create_stale_generation_tests():
122 tmp = template('stale-generation.yml.template')
123 # Stale command errors
124 for error_name, when in itertools.product(ERR_CODES, WHEN):
125 test_name = f'stale-generation-{when}-{error_name}'
126 error_code, = ERR_CODES[error_name]
127 stale_error = STALE_GENERATION_COMMAND_ERROR.format(**locals())
128 data = tmp.format(**locals())
129 write_test(test_name, data)
130 # Stale network errors
131 for network_error_type, when in itertools.product(
132 ['network', 'timeout'], WHEN):
133 error_name = network_error_type
134 test_name = f'stale-generation-{when}-{network_error_type}'
135 stale_error = STALE_GENERATION_NETWORK_ERROR.format(**locals())
136 data = tmp.format(**locals())
137 write_test(test_name, data)
138
139
140def create_pre_42_tests():
141 tmp = template('pre-42.yml.template')
142 # All "not writable primary"/"node is recovering" clear the pool on <4.2
143 for error_name in ERR_CODES:
144 test_name = f'pre-42-{error_name}'
145 error_code, = ERR_CODES[error_name]
146 data = tmp.format(**locals())
147 write_test(test_name, data)
148
149
150def create_post_42_tests():
151 tmp = template('post-42.yml.template')
152 for error_name in ERR_CODES:
153 test_name = f'post-42-{error_name}'
154 error_code, = ERR_CODES[error_name]
155 # On 4.2+, only ShutdownInProgress and InterruptedAtShutdown will
156 # clear the pool.
157 if error_name in ("ShutdownInProgress", "InterruptedAtShutdown"):
158 final_pool_generation = 1
159 else:
160 final_pool_generation = 0
161 data = tmp.format(**locals())
162 write_test(test_name, data)
163
164
165create_stale_tests()
166create_non_stale_tests()
167create_stale_generation_tests()
168create_pre_42_tests()
169create_post_42_tests()
170
171print('Running make')
172subprocess.run(f'cd {SOURCE} && make', shell=True, check=True)
View as plain text