Introduce the shared lifecycle helper Python script
Intent: Create a single authoritative implementation for writing lifecycle status, event, and result JSON artifacts so that every Incus guest image uses identical serialization, atomic-write, and sequencing logic.
Affected files: nix/incus/pika-cloud-lifecycle.py.in
@@ -0,0 +1,204 @@
+#!@python3@
+import argparse
+import fcntl
+import json
+import os
+import pathlib
+import tempfile
+from datetime import datetime
+
+
+LIFECYCLE_SCHEMA_VERSION = 1
@@ +76,0 +76,16 @@
+def write_status(
+ status_path: pathlib.Path,
+ state: str,
+ message: str,
+ details,
+) -> None:
+ payload = {
+ "schema_version": LIFECYCLE_SCHEMA_VERSION,
+ "state": state,
+ "updated_at": finished_at(),
+ "message": message,
+ }
@@ +100,0 +100,30 @@
+def append_event(
+ events_path: pathlib.Path,
+ seq_path: pathlib.Path,
+ kind: str,
+ message: str,
+ details,
+) -> None:
+ ...
+ with seq_path.open("a+", encoding="utf-8") as seq_file:
+ fcntl.flock(seq_file.fileno(), fcntl.LOCK_EX)
@@ +148,0 +148,12 @@
+def write_result(
+ result_path: pathlib.Path,
+ status: str,
+ exit_code: int,
+ message: str,
+ details,
+) -> None:
The new file nix/incus/pika-cloud-lifecycle.py.in is a self-contained Python CLI with three subcommands: status, event, and result. Key design choices:
- Schema version is owned here (
LIFECYCLE_SCHEMA_VERSION = 1), removing it from every caller. - Atomic writes use
tempfile.NamedTemporaryFile+os.replace+os.fsync, an upgrade over the previous barewrite_textin the pikaci image. - Event sequencing is file-lock protected (
fcntl.LOCK_EX) with a dedicated.seqsidecar file, replacing the fragile in-process global counter (EVENT_SEQ) and the bashevent_seqvariable. - Boot ID is read from
/proc/sys/kernel/random/boot_idonce per invocation and included when available. - Argument validation uses
argparsewithchoicesconstraints (RUNTIME_STATES,TERMINAL_STATUSES) so invalid lifecycle states are rejected at the CLI boundary. - The
@python3@shebang placeholder is substituted at Nix build time.