mirror of
https://github.com/jmcorgan/fips.git
synced 2026-08-09 08:14:42 +00:00
A CI worker may preempt an in-flight ci-local.sh run (SIGTERM, then SIGKILL after a grace period) to restart on a newer commit. For that kill to be safe, the script must clean up after itself and never let a dying run collide with its restart. It previously had no signal handling, shared the default compose project name across runs, and tore down each suite only at the suite end. - Derive a per-run id (honoring FIPS_CI_RUN_ID, else short-sha+random) and namespace every docker resource to it: a fipsci_<run>_<suite> compose project per suite and per parallel chaos child, and per-run image tags (fips-test:<run>, fips-test-app:<run>) retagged to :latest only after both builds succeed so :latest never points at a half-built image. - Install a bounded, idempotent teardown trap on SIGTERM/SIGINT (+ EXIT): reap parallel chaos children, then force-remove this run's docker resources via the new ci-cleanup.sh, wrapped in timeout so a stuck down cannot wedge it. - Exit 143 (SIGTERM) / 130 (SIGINT), distinct from 0 (pass) / 1 (failed), so a preempting worker tells a cancelled run from a real failure. - Add ci-cleanup.sh (also ci-local.sh --reap): force-removes leftover CI resources by the com.corganlabs.fips-ci=1 label and the fipsci_ project prefix, robust to however a prior run died. - Label every per-suite docker resource so the label sweep reaps it after a SIGKILL regardless of network name: direct docker run/network resources, the sidecar compose services, and every per-suite compose network (acl-allowlist, boringtun, firewall, nat, static, both tor suites, and the chaos generator template). Parametrize the static/sidecar compose image refs so the per-run tags are honored. - Give each parallel chaos child a unique /24 from 10.30.x (a new --subnet override on the sim CLI, assigned per-child in ci-local.sh) so parallel children never collide on a shared docker subnet, and a chaos net can never span a fixed-subnet suite (sidecar/static in 172.20.x). 10.30.x sits outside docker's default-address-pool range, so an auto-assigned net cannot land on it either; node IPs derive from the subnet, so no scenario config changes.
72 lines
2.0 KiB
Python
72 lines
2.0 KiB
Python
"""CLI entry point: python -m sim <scenario.yaml>"""
|
|
|
|
import argparse
|
|
import logging
|
|
import sys
|
|
|
|
from .runner import SimRunner
|
|
from .scenario import load_scenario
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
prog="sim",
|
|
description="FIPS stochastic network simulation",
|
|
)
|
|
parser.add_argument("scenario", help="Path to scenario YAML file")
|
|
parser.add_argument(
|
|
"-v", "--verbose", action="store_true", help="Enable debug logging"
|
|
)
|
|
parser.add_argument(
|
|
"--seed", type=int, default=None,
|
|
help="Override scenario seed",
|
|
)
|
|
parser.add_argument(
|
|
"--duration", type=int, default=None,
|
|
help="Override scenario duration in seconds",
|
|
)
|
|
parser.add_argument(
|
|
"--subnet", type=str, default=None,
|
|
help="Override topology subnet CIDR (e.g. 10.30.0.0/24); node IPs "
|
|
"derive from it. Used by CI to give each parallel run a "
|
|
"non-overlapping network.",
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
level = logging.DEBUG if args.verbose else logging.INFO
|
|
logging.basicConfig(
|
|
level=level,
|
|
format="%(asctime)s %(levelname)-5s %(name)s: %(message)s",
|
|
datefmt="%H:%M:%S",
|
|
)
|
|
|
|
try:
|
|
scenario = load_scenario(args.scenario)
|
|
except (FileNotFoundError, ValueError) as e:
|
|
print(f"Error loading scenario: {e}", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
# Apply CLI overrides
|
|
if args.seed is not None:
|
|
scenario.seed = args.seed
|
|
if args.duration is not None:
|
|
if args.duration < 1:
|
|
print("Error: --duration must be >= 1", file=sys.stderr)
|
|
sys.exit(1)
|
|
scenario.duration_secs = args.duration
|
|
if args.subnet is not None:
|
|
scenario.topology.subnet = args.subnet
|
|
|
|
runner = SimRunner(scenario)
|
|
result = runner.run()
|
|
|
|
if result and result.panics:
|
|
sys.exit(2)
|
|
if runner.assertions_failed:
|
|
sys.exit(3)
|
|
sys.exit(0)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|