mirror of
https://github.com/techno-tim/k3s-ansible.git
synced 2026-09-14 16:13:18 +02:00
fix(verify): retry the nginx VIP assert on transient ARP miss
- convert the verify_from_outside URI assert to register/until/retries/delay - retry until the MetalLB VIP returns HTTP 200 with the welcome page - cover the host-only L2 announcement race that failed CI on the shared runner - add a regression test asserting the retry wiring and hook it into pre-commit
This commit is contained in:
committed by
Techno Tim
parent
ac77e33be0
commit
c8c3609fde
@@ -0,0 +1,92 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Regression test for the verify_from_outside nginx HTTP assert.
|
||||||
|
|
||||||
|
The "Assert that the nginx welcome page is available" task in
|
||||||
|
molecule/resources/verify_from_outside/tasks/test/deploy-example.yml performs
|
||||||
|
a plain HTTP GET (ansible.builtin.uri) against the MetalLB VIP that the load
|
||||||
|
balancer pool assigned, reaching it from the CI runner over the VirtualBox
|
||||||
|
host-only network.
|
||||||
|
|
||||||
|
The MetalLB speaker announces the VIP on the host-only network, and the runner
|
||||||
|
may briefly not see it in its ARP table yet. That manifests as a single-shot
|
||||||
|
"status -1 / No route to host" failure that fails the whole verify play even
|
||||||
|
though the cluster is healthy. Like the sibling load-balancer address wait and
|
||||||
|
the k3s_server_post metallb waits, the assert must retry until it observes
|
||||||
|
HTTP 200 with the welcome page.
|
||||||
|
|
||||||
|
A bare assert with no retry would otherwise fail an otherwise-green cluster on
|
||||||
|
a transient announcement miss.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
|
||||||
|
def repo_root():
|
||||||
|
return subprocess.check_output(
|
||||||
|
["git", "rev-parse", "--show-toplevel"], text=True
|
||||||
|
).strip()
|
||||||
|
|
||||||
|
|
||||||
|
def fail(message):
|
||||||
|
raise SystemExit("verify nginx assert test failed: " + message)
|
||||||
|
|
||||||
|
|
||||||
|
def find_task(play, name):
|
||||||
|
block = play.get("block")
|
||||||
|
if not isinstance(block, list):
|
||||||
|
fail("expected a block list of tasks")
|
||||||
|
for entry in block:
|
||||||
|
if entry.get("name") == name:
|
||||||
|
return entry
|
||||||
|
fail("could not find the '{0}' task".format(name))
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def check_retry_wiring(task, name):
|
||||||
|
if not task.get("register"):
|
||||||
|
fail("{0} does not register a result; without retry wiring a transient "
|
||||||
|
"MetalLB VIP announcement miss fails the verify play".format(name))
|
||||||
|
until = task.get("until")
|
||||||
|
if not until:
|
||||||
|
fail("{0} does not retry; a transient MetalLB VIP announcement miss "
|
||||||
|
"on the host-only network would fail the verify play".format(name))
|
||||||
|
if isinstance(until, str):
|
||||||
|
until = [until]
|
||||||
|
joined = " | ".join(str(expr) for expr in until)
|
||||||
|
if "result.status == 200" not in joined:
|
||||||
|
fail("{0} does not retry until the HTTP status is 200".format(name))
|
||||||
|
if "Welcome to nginx!" not in joined:
|
||||||
|
fail("{0} does not verify the welcome page content".format(name))
|
||||||
|
if task.get("retries") is None:
|
||||||
|
fail("{0} is missing retries".format(name))
|
||||||
|
if task.get("delay") is None:
|
||||||
|
fail("{0} is missing delay".format(name))
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
task_file = os.path.join(
|
||||||
|
repo_root(),
|
||||||
|
"molecule", "resources", "verify_from_outside", "tasks", "test",
|
||||||
|
"deploy-example.yml",
|
||||||
|
)
|
||||||
|
with open(task_file, encoding="utf-8") as handle:
|
||||||
|
plays = yaml.safe_load(handle)
|
||||||
|
|
||||||
|
play = plays[0]
|
||||||
|
task = find_task(play, "Assert that the nginx welcome page is available")
|
||||||
|
|
||||||
|
if not task.get("ansible.builtin.uri"):
|
||||||
|
fail("task does not use ansible.builtin.uri to reach the VIP")
|
||||||
|
|
||||||
|
check_retry_wiring(task, "Assert that the nginx welcome page is available")
|
||||||
|
|
||||||
|
print("verify nginx assert regression test passed")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -147,3 +147,11 @@ repos:
|
|||||||
- Jinja2>=3.1
|
- Jinja2>=3.1
|
||||||
pass_filenames: false
|
pass_filenames: false
|
||||||
files: ^inventory/sample/group_vars/all\.yml$|^\.github/scripts/test-default-interface\.py$
|
files: ^inventory/sample/group_vars/all\.yml$|^\.github/scripts/test-default-interface\.py$
|
||||||
|
- id: verify-nginx-assert-test
|
||||||
|
name: verify nginx assert test
|
||||||
|
entry: python3 .github/scripts/test-verify-nginx-assert.py
|
||||||
|
language: python
|
||||||
|
additional_dependencies:
|
||||||
|
- PyYAML
|
||||||
|
pass_filenames: false
|
||||||
|
files: ^molecule/resources/verify_from_outside/tasks/test/deploy-example\.yml$|^\.github/scripts/test-verify-nginx-assert\.py$ # noqa yaml[line-length]
|
||||||
|
|||||||
@@ -69,7 +69,16 @@
|
|||||||
url: http://{{ nginx_lb_ip | ansible.utils.ipwrap }}:{{ port_ }}/
|
url: http://{{ nginx_lb_ip | ansible.utils.ipwrap }}:{{ port_ }}/
|
||||||
return_content: true
|
return_content: true
|
||||||
register: result
|
register: result
|
||||||
failed_when: "'Welcome to nginx!' not in result.content"
|
# The MetalLB speaker announces the VIP on the host-only network, and the
|
||||||
|
# runner may briefly not see it in its ARP table yet. Retry so a transient
|
||||||
|
# announcement miss does not fail an otherwise healthy cluster, matching
|
||||||
|
# the load-balancer address wait above and the k3s_server_post metallb waits.
|
||||||
|
until:
|
||||||
|
- result.status is defined
|
||||||
|
- result.status == 200
|
||||||
|
- "'Welcome to nginx!' in result.content"
|
||||||
|
retries: 30
|
||||||
|
delay: 5
|
||||||
vars:
|
vars:
|
||||||
port_: >-
|
port_: >-
|
||||||
{{ nginx_services.resources[0].spec.ports[0].port }}
|
{{ nginx_services.resources[0].spec.ports[0].port }}
|
||||||
|
|||||||
Reference in New Issue
Block a user