fix(ops): cover partial newuser rollback failures

This commit is contained in:
gadgethd
2026-09-23 11:48:55 +00:00
parent a6b7a49580
commit 771fc6f613
2 changed files with 34 additions and 4 deletions
+6 -3
View File
@@ -617,9 +617,12 @@ else
password="$(openssl rand -hex 24)"
log 'creating persistent Mosquitto credential'
# Mark the transaction as owning this username before invoking the mutating
# command. If it changes the passwd file and then exits unsuccessfully, EXIT
# rollback must still remove the partial credential.
credential_created=1
docker exec "$MOSQUITTO_CONTAINER" \
mosquitto_passwd -b /mosquitto/config/passwd "$username" "$password"
credential_created=1
# mosquitto only re-reads password_file on SIGHUP; the reconciler reloads
# every ~60s but a device may connect before then, so reload immediately.
docker exec "$MOSQUITTO_CONTAINER" sh -c 'kill -HUP 1' >/dev/null 2>&1 \
@@ -715,10 +718,11 @@ else
new_owner_map=$new_entry
fi
log 'adding deduplicated owner grant to OWNER_MQTT_USERNAME_MAP'
replace_owner_map "$new_owner_map"
env_changed=1
replace_owner_map "$new_owner_map"
log 'inserting verified owner account and node grant rows'
db_changed=1
docker exec -i "$TIMESCALEDB_CONTAINER" \
psql -X -q -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d "$OWNER_DATABASE" \
-v "mqtt_username=${username}" -v "node_ids=${key_csv}" >/dev/null <<'SQL'
@@ -759,7 +763,6 @@ SET verification_method = 'operator-config',
updated_at = NOW();
COMMIT;
SQL
db_changed=1
log 'recreating only the backend to reconcile owner grants and reload the ACL'
apply_backend_config
+28 -1
View File
@@ -227,4 +227,31 @@ expected_map="OWNER_MQTT_USERNAME_MAP=alice=${NODE_A},bob=${NODE_C},charlie=${NO
[[ $(<"$SANDBOX/repo/.env") == "$expected_map" ]] \
|| { printf 'concurrent owner map lost a grant: %s\n' "$(<"$SANDBOX/repo/.env")" >&2; exit 1; }
printf 'newuser concurrent-discovery regression test passed\n'
# Inject failures after each mutation class and check that EXIT rollback is
# idempotent and restores the mocked broker/config/database state.
for stage in credential environment db acl; do
init_sandbox "rollback-${stage}" "alice=${NODE_A}"
if run_newuser "$SANDBOX" "$SANDBOX/output.log" alice alice "$stage" dave "$NODE_B"; then
printf 'expected injected %s failure\n' "$stage" >&2
exit 1
fi
[[ $(<"$SANDBOX/repo/.env") == "OWNER_MQTT_USERNAME_MAP=alice=${NODE_A}" ]] \
|| { printf '%s failure did not restore the environment map\n' "$stage" >&2; exit 1; }
[[ ! -e $SANDBOX/credential-state ]] \
|| { printf '%s failure left a broker credential behind\n' "$stage" >&2; exit 1; }
[[ ! -e $SANDBOX/db-state ]] \
|| { printf '%s failure left owner database state behind\n' "$stage" >&2; exit 1; }
if [[ $stage == credential ]]; then
assert_file_contains "$SANDBOX/docker.log" 'CREDENTIAL_DELETE dave'
fi
if [[ $stage == db || $stage == acl ]]; then
assert_file_contains "$SANDBOX/docker.log" 'DB_ROLLBACK dave'
fi
if [[ $stage == acl ]]; then
compose_count="$(grep -c '^COMPOSE_BACKEND$' "$SANDBOX/docker.log")"
[[ $compose_count -ge 2 ]] \
|| { printf 'ACL failure did not reapply restored backend config\n' >&2; exit 1; }
fi
done
printf 'newuser concurrency and rollback regression tests passed\n'