|
12 | 12 | from time import sleep |
13 | 13 |
|
14 | 14 | # if GITHUB_RUN_ID is not set, use a default value that includes the user and hostname |
15 | | -RUN_ID = os.environ.get("GITHUB_RUN_ID", "unknown-ci-run-" + os.environ.get("USER", "unknown-user") + '@' + socket.gethostname()) |
16 | | -AMI_NAME = os.environ.get('AMI_NAME') |
| 15 | +RUN_ID = os.environ.get( |
| 16 | + "GITHUB_RUN_ID", |
| 17 | + "unknown-ci-run-" |
| 18 | + + os.environ.get("USER", "unknown-user") |
| 19 | + + "@" |
| 20 | + + socket.gethostname(), |
| 21 | +) |
| 22 | +AMI_NAME = os.environ.get("AMI_NAME") |
17 | 23 | postgresql_schema_sql_content = """ |
18 | 24 | ALTER DATABASE postgres SET "app.settings.jwt_secret" TO 'my_jwt_secret_which_is_not_so_secret'; |
19 | 25 | ALTER DATABASE postgres SET "app.settings.jwt_exp" TO 3600; |
|
158 | 164 |
|
159 | 165 | logger = logging.getLogger("ami-tests") |
160 | 166 | handler = logging.StreamHandler() |
161 | | -formatter = logging.Formatter( |
162 | | - '%(asctime)s %(name)-12s %(levelname)-8s %(message)s') |
| 167 | +formatter = logging.Formatter("%(asctime)s %(name)-12s %(levelname)-8s %(message)s") |
163 | 168 | handler.setFormatter(formatter) |
164 | 169 | logger.addHandler(handler) |
165 | 170 | logger.setLevel(logging.DEBUG) |
166 | 171 |
|
| 172 | + |
167 | 173 | # scope='session' uses the same container for all the tests; |
168 | 174 | # scope='function' uses a new container per test function. |
169 | 175 | @pytest.fixture(scope="session") |
@@ -232,7 +238,7 @@ def gzip_then_base64_encode(s: str) -> str: |
232 | 238 | "Tags": [ |
233 | 239 | {"Key": "Name", "Value": "ci-ami-test-nix"}, |
234 | 240 | {"Key": "creator", "Value": "testinfra-ci"}, |
235 | | - {"Key": "testinfra-run-id", "Value": RUN_ID} |
| 241 | + {"Key": "testinfra-run-id", "Value": RUN_ID}, |
236 | 242 | ], |
237 | 243 | } |
238 | 244 | ], |
@@ -264,48 +270,76 @@ def gzip_then_base64_encode(s: str) -> str: |
264 | 270 | logger.warning("waiting for ssh to be available") |
265 | 271 | sleep(10) |
266 | 272 |
|
267 | | - host = testinfra.get_host( |
| 273 | + def get_ssh_connection(instance_ip, ssh_identity_file, max_retries=10): |
| 274 | + for attempt in range(max_retries): |
| 275 | + try: |
| 276 | + return testinfra.get_host( |
| 277 | + f"paramiko://ubuntu@{instance_ip}?timeout=60", |
| 278 | + ssh_identity_file=ssh_identity_file, |
| 279 | + ) |
| 280 | + except Exception as e: |
| 281 | + if attempt == max_retries - 1: |
| 282 | + raise |
| 283 | + logger.warning( |
| 284 | + f"Ssh connection failed, retrying: {attempt + 1}/{max_retries} failed, retrying ..." |
| 285 | + ) |
| 286 | + sleep(5) |
| 287 | + |
| 288 | + host = get_ssh_connection( |
268 | 289 | # paramiko is an ssh backend |
269 | | - f"paramiko://ubuntu@{instance.public_ip_address}?timeout=60", |
270 | | - ssh_identity_file=temp_key.get_priv_key_file(), |
| 290 | + instance.public_ip_address, |
| 291 | + temp_key.get_priv_key_file(), |
271 | 292 | ) |
272 | 293 |
|
273 | | - def is_healthy(host) -> bool: |
274 | | - cmd = host.run("sudo -u postgres /usr/bin/pg_isready -U postgres") |
275 | | - if cmd.failed is True: |
276 | | - logger.warning("pg not ready") |
277 | | - return False |
278 | | - |
279 | | - cmd = host.run(f"curl -sf -k --connect-timeout 30 --max-time 60 https://localhost:8085/health -H 'apikey: {supabase_admin_key}'") |
280 | | - if cmd.failed is True: |
281 | | - logger.warning("adminapi not ready") |
282 | | - return False |
283 | | - |
284 | | - cmd = host.run("curl -sf --connect-timeout 30 --max-time 60 http://localhost:3001/ready") |
285 | | - if cmd.failed is True: |
286 | | - logger.warning("postgrest not ready") |
287 | | - return False |
288 | | - |
289 | | - cmd = host.run("curl -sf --connect-timeout 30 --max-time 60 http://localhost:8081/health") |
290 | | - if cmd.failed is True: |
291 | | - logger.warning("gotrue not ready") |
292 | | - return False |
293 | | - |
294 | | - # TODO(thebengeu): switch to checking Envoy once it's the default. |
295 | | - cmd = host.run("sudo kong health") |
296 | | - if cmd.failed is True: |
297 | | - logger.warning("kong not ready") |
298 | | - return False |
299 | | - |
300 | | - cmd = host.run("sudo fail2ban-client status") |
301 | | - if cmd.failed is True: |
302 | | - logger.warning("fail2ban not ready") |
303 | | - return False |
| 294 | + def is_healthy(host, instance_ip, ssh_identity_file) -> bool: |
| 295 | + health_checks = [ |
| 296 | + ( |
| 297 | + "postgres", |
| 298 | + lambda h: h.run("sudo -u postgres /usr/bin/pg_isready -U postgres"), |
| 299 | + ), |
| 300 | + ( |
| 301 | + "adminapi", |
| 302 | + lambda h: h.run( |
| 303 | + f"curl -sf -k --connect-timeout 30 --max-time 60 https://localhost:8085/health -H 'apikey: {supabase_admin_key}'" |
| 304 | + ), |
| 305 | + ), |
| 306 | + ( |
| 307 | + "postgrest", |
| 308 | + lambda h: h.run( |
| 309 | + "curl -sf --connect-timeout 30 --max-time 60 http://localhost:3001/ready" |
| 310 | + ), |
| 311 | + ), |
| 312 | + ( |
| 313 | + "gotrue", |
| 314 | + lambda h: h.run( |
| 315 | + "curl -sf --connect-timeout 30 --max-time 60 http://localhost:8081/health" |
| 316 | + ), |
| 317 | + ), |
| 318 | + ("kong", lambda h: h.run("sudo kong health")), |
| 319 | + ("fail2ban", lambda h: h.run("sudo fail2ban-client status")), |
| 320 | + ] |
| 321 | + |
| 322 | + for service, check in health_checks: |
| 323 | + try: |
| 324 | + cmd = check(host) |
| 325 | + if cmd.failed is True: |
| 326 | + logger.warning(f"{service} not ready") |
| 327 | + return False |
| 328 | + except Exception: |
| 329 | + logger.warning( |
| 330 | + f"Connection failed during {service} check, attempting reconnect..." |
| 331 | + ) |
| 332 | + host = get_ssh_connection(instance_ip, ssh_identity_file) |
| 333 | + return False |
304 | 334 |
|
305 | 335 | return True |
306 | 336 |
|
307 | 337 | while True: |
308 | | - if is_healthy(host): |
| 338 | + if is_healthy( |
| 339 | + host=host, |
| 340 | + instance_ip=instance.public_ip_address, |
| 341 | + ssh_identity_file=temp_key.get_priv_key_file(), |
| 342 | + ): |
309 | 343 | break |
310 | 344 | sleep(1) |
311 | 345 |
|
@@ -393,6 +427,7 @@ def test_postgrest_ending_apikey_query_parameter_is_removed(host): |
393 | 427 | ) |
394 | 428 | assert res.ok |
395 | 429 |
|
| 430 | + |
396 | 431 | # There would be an error if the empty key query parameter isn't removed, |
397 | 432 | # since PostgREST treats empty key query parameters as malformed input. |
398 | 433 | # |
|
0 commit comments