diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index d28738f0..cd0ab773 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -43,8 +43,14 @@ services: [ "$$SECONDS" -ge "$$deadline" ] && { echo "Timed out waiting for syslog-ng control socket"; kill "$$pid" 2>/dev/null; exit 1; } sleep 0.1 done - chmod 0777 /var/lib/syslog-ng/syslog-ng.ctl - wait + # Watchdog: syslog-ng RELOAD recreates the ctl socket with default 755 perms; + # the BDD behave container connects as a non-root user, so keep the socket + # world-writable across reloads. + ( while kill -0 "$$pid" 2>/dev/null; do + chmod 0777 /var/lib/syslog-ng/syslog-ng.ctl 2>/dev/null + sleep 0.1 + done ) & + wait $$pid behave: image: ghcr.io/davidcozens/behave:sha-3faff14 diff --git a/Bdd/features/environment.py b/Bdd/features/environment.py index bc0c33ac..17cd2242 100644 --- a/Bdd/features/environment.py +++ b/Bdd/features/environment.py @@ -133,10 +133,11 @@ def after_scenario(context, scenario): process.wait() del context.interactive_process - # Restore syslog-ng config if it was changed during the scenario + # Restore syslog-ng config if it was changed during the scenario. + # copyfile (data only, no chmod) — see syslog_ng_swap_config rationale. if hasattr(context, "syslog_ng_config_changed") and context.syslog_ng_config_changed: - shutil.copy(SYSLOG_NG_FULL_CONF, SYSLOG_NG_CONF) try: + shutil.copyfile(SYSLOG_NG_FULL_CONF, SYSLOG_NG_CONF) with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as sock: sock.connect(SYSLOG_NG_CTL) sock.sendall(b"RELOAD\n") diff --git a/Bdd/features/steps/syslog_steps.py b/Bdd/features/steps/syslog_steps.py index d7c563a9..e8040b0c 100644 --- a/Bdd/features/steps/syslog_steps.py +++ b/Bdd/features/steps/syslog_steps.py @@ -355,13 +355,13 @@ def wait_for_messages(context, expected_messages): received_log = context.received_log oracle_format = context.oracle_format expected_total = context.lines_before + expected_messages - deadline = time.monotonic() + 5 + deadline = time.monotonic() + 10 while oracle_record_count(received_log, oracle_format) < expected_total: if time.monotonic() > deadline: actual = oracle_record_count(received_log, oracle_format) - context.lines_before raise AssertionError( f"oracle received {actual} of {expected_messages} " - f"messages within 5 seconds" + f"messages within 10 seconds" ) time.sleep(0.1) @@ -468,8 +468,15 @@ def syslog_ng_reload(): def syslog_ng_swap_config(config_path): - """Replace the active syslog-ng config and reload.""" - shutil.copy(config_path, SYSLOG_NG_CONF) + """Replace the active syslog-ng config and reload. + + Uses copyfile (data only, no chmod) rather than copy. The destination + is bind-mounted from the host on developer machines; chmod fails with + 'Operation not permitted' when the host owner is not the container's + `developer` user. The file already exists with the correct mode + so copying just the contents is sufficient. + """ + shutil.copyfile(config_path, SYSLOG_NG_CONF) syslog_ng_reload() @@ -713,12 +720,17 @@ def step_oracle_stops_tcp(context): probe.settimeout(1) probe.connect(("syslog-ng", 5514)) + # Set the restore flag *before* the swap so any failure inside + # syslog_ng_swap_config (or the subsequent wait/probe-teardown) + # still triggers after_scenario's config restore — otherwise a + # half-applied swap would leave the on-disk config in UDP-only + # mode and contaminate the next scenario. + context.syslog_ng_config_changed = True syslog_ng_swap_config(SYSLOG_NG_UDP_ONLY_CONF) wait_for_tcp_port_closed() wait_for_connection_teardown(probe) # Allow time for the sender's existing connection to receive RST time.sleep(0.5) - context.syslog_ng_config_changed = True else: # Windows OTel: kill the otelcol-contrib process. The TCP listener # disappears as the process exits — the resume step will start a diff --git a/Bdd/features/store_capacity.feature b/Bdd/features/store_capacity.feature index f3643d85..dde9c038 100644 --- a/Bdd/features/store_capacity.feature +++ b/Bdd/features/store_capacity.feature @@ -4,16 +4,6 @@ Feature: Store capacity limit and discard policy When the store is full, the discard policy determines whether the oldest or newest messages are dropped. - Note: scenarios are tagged @windows_wip pending S12.14 (architecture - refactor that decouples buffer drain from sender state). The tight-loop - `client sends N messages` step fills the in-memory buffer faster than - the service thread can drain it on Windows, so records never reach the - store and the discard policy can't engage. Linux compose runner happens - to schedule producer and service so the buffer stays small enough that - the assertions pass; that is timing-dependent and will be re-tuned in - the PR #275 finale once S12.14 is on main. - - @windows_wip Scenario: Discard-oldest drops oldest messages when store overflows Given the syslog oracle is running And the block store is enabled with max-blocks 2 and max-block-size 520 and discard-policy oldest @@ -29,7 +19,6 @@ Feature: Store capacity limit and discard policy And the syslog oracle did not receive sequenceId 2 And the outage messages have contiguous sequenceIds - @windows_wip Scenario: Discard-newest preserves oldest messages when store overflows Given the syslog oracle is running And the block store is enabled with max-blocks 2 and max-block-size 520 and discard-policy newest @@ -45,7 +34,6 @@ Feature: Store capacity limit and discard policy And the syslog oracle did not receive sequenceId 11 And the outage messages have contiguous sequenceIds - @windows_wip Scenario: Halt stops the application when store overflows Given the syslog oracle is running And the block store is enabled with max-blocks 2 and max-block-size 520 and discard-policy halt @@ -57,7 +45,6 @@ Feature: Store capacity limit and discard policy And the client sends 8 messages And the client attempts to send it exits with code 2 - @windows_wip Scenario: Halt prevents further service after store overflows Given the syslog oracle is running And the block store is enabled with max-blocks 2 and max-block-size 520 and discard-policy halt