feat(deploy): add typed command transport releases
This commit is contained in:
@@ -112,6 +112,10 @@ TAILSCALE_RETIREMENT_ACCEPTED_PATCH = (
|
||||
TAILSCALE_RETIREMENT_ACCEPTED_SHA256 = (
|
||||
"e7b61ec9c83122fa5631467010eff871b98935746df6a1326b6ff6bb9713d877"
|
||||
)
|
||||
TRACKER_INGRESS_ACCEPTED_PATCH = "device-edge-vps-tracker-ingress-20260812-012"
|
||||
TRACKER_INGRESS_ACCEPTED_SHA256 = (
|
||||
"290acef118839c6b0c31aac864c47da1832a289537366af9322d4624a1dd81ec"
|
||||
)
|
||||
|
||||
FOUNDATION_ENTRIES = (
|
||||
"vps/config/00-nodedc-b2-vps.conf",
|
||||
@@ -162,6 +166,21 @@ TRACKER_INGRESS_ENTRIES = (
|
||||
"vps/systemd/nodedc-device-edge-runtime.service",
|
||||
"deployment/device-edge-vps-tracker-ingress-v1.json",
|
||||
)
|
||||
COMMAND_TRANSPORT_ENTRIES = (
|
||||
"packages/device-edge-channel-contract/package.json",
|
||||
"packages/device-edge-channel-contract/src",
|
||||
"services/device-edge-channel/package.json",
|
||||
"services/device-edge-channel/src",
|
||||
"packages/device-adapter-runtime/package.json",
|
||||
"packages/device-adapter-runtime/src",
|
||||
"packages/device-adapter-catalog/package.json",
|
||||
"packages/device-adapter-catalog/src",
|
||||
"packages/arusnavi-b2-adapter/package.json",
|
||||
"packages/arusnavi-b2-adapter/src",
|
||||
"services/device-gateway/src/runtime.mjs",
|
||||
"vps/edge-process/device-edge-runtime.mjs",
|
||||
"deployment/device-edge-vps-command-transport-v1.json",
|
||||
)
|
||||
|
||||
PHASE_ENTRIES = {
|
||||
"foundation": FOUNDATION_ENTRIES,
|
||||
@@ -171,6 +190,7 @@ PHASE_ENTRIES = {
|
||||
"core-channel": CORE_CHANNEL_ENTRIES,
|
||||
"tailscale-retirement": TAILSCALE_RETIREMENT_ENTRIES,
|
||||
"tracker-ingress": TRACKER_INGRESS_ENTRIES,
|
||||
"command-transport": COMMAND_TRANSPORT_ENTRIES,
|
||||
}
|
||||
|
||||
SUPERSEDED_TRANSPORT_PHASES = frozenset({"backhaul", "relay"})
|
||||
@@ -262,6 +282,36 @@ PHASE_FILE_SHA256 = {
|
||||
"deployment/device-edge-vps-tracker-ingress-v1.json":
|
||||
"faecfa0317ad0e3d102c3636c0547b65257b930e9b48f34d8679c00ceb3d323b",
|
||||
},
|
||||
"command-transport": {
|
||||
"packages/device-edge-channel-contract/package.json":
|
||||
"57d5349b5dcef2cacd4f3e4fad010359a65d59f5f903eff07d89f67c497f97c0",
|
||||
"packages/device-edge-channel-contract/src/index.mjs":
|
||||
"a49bfe31476a08f8a912c10c82b982fae28a29e2c0eb5828ced390b01f3a10bc",
|
||||
"services/device-edge-channel/package.json":
|
||||
"bdf502be43b62bdd6db05b022a532d93ba954277ac5143d6058d2f27f6a2e9d2",
|
||||
"services/device-edge-channel/src/runtime.mjs":
|
||||
"39579d85edf50be07573873a1f59045f62a74469c85588db1fd4e5151b3b778d",
|
||||
"services/device-edge-channel/src/server.mjs":
|
||||
"a82057218bb368ab926404f90a19cc17c0359b57dc890f38a1324ab8c497c17b",
|
||||
"packages/device-adapter-runtime/package.json":
|
||||
"293ee6010f255c511df40b34cf8019fcd0d1b06babe30761c2a842c7da7af778",
|
||||
"packages/device-adapter-runtime/src/index.mjs":
|
||||
"199fcdd775d1ea15ddc9f85ab24fc2c165bb7757008de26749f1b7e84481dfeb",
|
||||
"packages/device-adapter-catalog/package.json":
|
||||
"60dbfc551ca49719e0851755058c159968411c490621eac0bf96ca76bf8b78d1",
|
||||
"packages/device-adapter-catalog/src/index.mjs":
|
||||
"614749e763c0624bf0de7905343632d3edb4e026316f427a2f52ce12c4225dca",
|
||||
"packages/arusnavi-b2-adapter/package.json":
|
||||
"136fc997f5d6757ba81d370f03037e0a93fd4750dc624def1af593e7ba8b9098",
|
||||
"packages/arusnavi-b2-adapter/src/index.mjs":
|
||||
"790060720639e3835bbf60fb2bdc67172a7dac79a8f8e4bf1558ab51ed130c91",
|
||||
"services/device-gateway/src/runtime.mjs":
|
||||
"f1898baef2a964ee04adc55f7e26ae8a637fcc61701cf28d7cf4b3bda1579b25",
|
||||
"vps/edge-process/device-edge-runtime.mjs":
|
||||
"4443876d4652de5d953840896acccec93119f9ec642b5d5cdaeaac19a2022017",
|
||||
"deployment/device-edge-vps-command-transport-v1.json":
|
||||
"971166143fe954b9c5043cce9a464d17efbc87933da1405b2517a4693a7bed09",
|
||||
},
|
||||
}
|
||||
|
||||
# Exact immutable baselines from terminally accepted predecessor artifacts.
|
||||
@@ -421,8 +471,9 @@ def validate_payload(payload: Path, phase: str):
|
||||
if (
|
||||
descriptor.get("component") != COMPONENT
|
||||
or descriptor.get("runtimeHost") != RUNTIME_HOST
|
||||
or descriptor.get("commandTransport") != "disabled"
|
||||
or descriptor.get("gelios") != "untouched"
|
||||
or descriptor.get("commandTransport")
|
||||
!= ("typed-service-ping-v1" if phase == "command-transport" else "disabled")
|
||||
or not str(descriptor.get("gelios", "")).startswith("untouched")
|
||||
or not descriptor.get("rollback")
|
||||
):
|
||||
die(f"VPS {phase} descriptor mismatch")
|
||||
@@ -674,6 +725,18 @@ def current_phase_preflight(phase: str):
|
||||
if (LIVE_ROOT / TRACKER_INGRESS_ENTRIES[-1]).exists():
|
||||
die("VPS tracker ingress target path already exists")
|
||||
return {"predecessor": "accepted-tailscale-retirement-011"}
|
||||
if phase == "command-transport":
|
||||
tracker_record = applied_phase_record("tracker-ingress")
|
||||
if (
|
||||
tracker_record.get("patch") != TRACKER_INGRESS_ACCEPTED_PATCH
|
||||
or tracker_record.get("sha256") != TRACKER_INGRESS_ACCEPTED_SHA256
|
||||
):
|
||||
die("VPS command transport tracker ingress predecessor mismatch")
|
||||
source_file_state("tracker-ingress")
|
||||
validate_tracker_ingress_runtime()
|
||||
if (LIVE_ROOT / COMMAND_TRANSPORT_ENTRIES[-1]).exists():
|
||||
die("VPS command transport target path already exists")
|
||||
return {"predecessor": "accepted-tracker-ingress-012"}
|
||||
validate_foundation_runtime(
|
||||
require_running_tailnet=phase in {"backhaul", "relay"},
|
||||
expected_key_user=BACKHAUL_USER if phase == "relay" else SERVICE_USER,
|
||||
@@ -783,6 +846,8 @@ def backup_targets_for_phase(phase: str):
|
||||
]
|
||||
if phase == "tracker-ingress":
|
||||
return common + [CHANNEL_UNIT, NFTABLES_CONFIG]
|
||||
if phase == "command-transport":
|
||||
return common + [CHANNEL_UNIT, NFTABLES_CONFIG]
|
||||
return common + [RELAY_UNIT, NFTABLES_CONFIG]
|
||||
|
||||
|
||||
@@ -1265,6 +1330,12 @@ def apply_tracker_ingress(_payload: Path):
|
||||
validate_tracker_ingress_runtime()
|
||||
|
||||
|
||||
def apply_command_transport(_payload: Path):
|
||||
systemctl("daemon-reload")
|
||||
systemctl("restart", "nodedc-device-edge-channel.service")
|
||||
validate_command_transport_runtime()
|
||||
|
||||
|
||||
def sshd_effective():
|
||||
return run(["/usr/sbin/sshd", "-T"]).stdout.lower()
|
||||
|
||||
@@ -1620,6 +1691,92 @@ def validate_tracker_ingress_runtime():
|
||||
return health
|
||||
|
||||
|
||||
def validate_command_transport_runtime():
|
||||
source_file_state("foundation")
|
||||
source_file_state("tailscale-retirement")
|
||||
source_file_state("command-transport")
|
||||
tracker_record = applied_phase_record("tracker-ingress")
|
||||
if (
|
||||
tracker_record.get("patch") != TRACKER_INGRESS_ACCEPTED_PATCH
|
||||
or tracker_record.get("sha256") != TRACKER_INGRESS_ACCEPTED_SHA256
|
||||
):
|
||||
die("command transport tracker ingress identity mismatch")
|
||||
if run([str(NODE_BIN), "--version"]).stdout.strip() != f"v{NODE_VERSION}":
|
||||
die("command transport Node version mismatch")
|
||||
for path in (
|
||||
TAILSCALE_UNIT,
|
||||
TAILSCALE_SOCKET,
|
||||
TAILSCALE_STATE.parent,
|
||||
TAILSCALE_BIN.parent,
|
||||
TRUST_ROOT,
|
||||
):
|
||||
if path.exists() or path.is_symlink():
|
||||
die(f"command transport superseded trust boundary present: {path}")
|
||||
for service in (
|
||||
"nodedc-b2-tailscaled.service",
|
||||
"nodedc-b2-backhaul.service",
|
||||
"nodedc-b2-relay.service",
|
||||
):
|
||||
if service_active(service):
|
||||
die(f"command transport forbidden service became active: {service}")
|
||||
assert_management_key()
|
||||
assert_channel_trust(require_runtime_owner=True)
|
||||
if not service_active("nodedc-device-edge-channel.service"):
|
||||
die("VPS Device Edge command runtime is not active")
|
||||
health = core_channel_health(require_accepted=True)
|
||||
expected = {
|
||||
"ok": True,
|
||||
"service": "nodedc-device-edge-runtime",
|
||||
"channel": "accepted",
|
||||
"trackerIngress": "telemetry-ingest",
|
||||
"commandTransport": "typed-service-ping-v1",
|
||||
}
|
||||
for key, value in expected.items():
|
||||
if health.get(key) != value:
|
||||
die(f"VPS command transport health contract mismatch: {key}")
|
||||
tracker = health.get("tracker") or {}
|
||||
for key, value in {
|
||||
"adapter": "arusnavi-b2",
|
||||
"protocolProfile": "arusnavi.b2.internal.v1",
|
||||
"framing": "verified-read-only",
|
||||
"publicIngress": "telemetry-ingest",
|
||||
"commandTransport": "typed-service-ping-v1",
|
||||
}.items():
|
||||
if tracker.get(key) != value:
|
||||
die(f"VPS command adapter health contract mismatch: {key}")
|
||||
for port in (22, CHANNEL_PUBLIC_PORT, 9921):
|
||||
if not port_is_open(PUBLIC_IPV4, port, timeout=5):
|
||||
die(f"VPS command transport required listener unavailable: {port}")
|
||||
if not port_is_open("127.0.0.1", 18221, timeout=5):
|
||||
die("VPS command transport adapter health unavailable")
|
||||
if port_is_open("127.0.0.1", 1055):
|
||||
die("VPS retired Tailscale SOCKS listener returned")
|
||||
nft = run(["/usr/sbin/nft", "list", "table", "inet", "nodedc_b2_vps"]).stdout
|
||||
for required in ("policy drop", "tcp dport 22", "tcp dport 443", "tcp dport 9921"):
|
||||
if required not in nft:
|
||||
die(f"VPS command transport firewall contract mismatch: {required}")
|
||||
unit = run([
|
||||
"/usr/bin/systemctl",
|
||||
"show",
|
||||
"nodedc-device-edge-channel.service",
|
||||
"--property=User,Group,NoNewPrivileges,CapabilityBoundingSet,AmbientCapabilities,MemoryMax,MemorySwapMax,CPUQuotaPerSecUSec,TasksMax,LimitNOFILE",
|
||||
]).stdout
|
||||
for required in (
|
||||
"User=nodedc-channel",
|
||||
"Group=nodedc-channel",
|
||||
"NoNewPrivileges=yes",
|
||||
"CapabilityBoundingSet=cap_net_bind_service",
|
||||
"AmbientCapabilities=cap_net_bind_service",
|
||||
"MemoryMax=201326592",
|
||||
"MemorySwapMax=0",
|
||||
"TasksMax=128",
|
||||
"LimitNOFILE=1024",
|
||||
):
|
||||
if required not in unit:
|
||||
die(f"VPS command transport resource boundary mismatch: {required}")
|
||||
return health
|
||||
|
||||
|
||||
def validate_relay_runtime():
|
||||
validate_backhaul_runtime()
|
||||
source_file_state("relay")
|
||||
@@ -1789,7 +1946,7 @@ def plan_artifact(artifact_argument: str):
|
||||
print("tracker_tcp_9921=closed")
|
||||
print("services=preserved:nodedc-device-edge-channel")
|
||||
print("external_tailnet_machine_cleanup=required-after-deploy-ok")
|
||||
else:
|
||||
elif phase == "tracker-ingress":
|
||||
print("public_core_channel=preserved:155.212.211.15:443/tcp:tls13-mtls-h2")
|
||||
print(f"health=127.0.0.1:{CHANNEL_HEALTH_PORT}:combined-edge-runtime")
|
||||
print("public_b2_ingress=155.212.211.15:9921/tcp:telemetry-only")
|
||||
@@ -1798,8 +1955,27 @@ def plan_artifact(artifact_argument: str):
|
||||
print("runtime_composition=single-non-root-process:core-channel+universal-gateway")
|
||||
print("tailscale=preserved:absent")
|
||||
print("services=recreate:nodedc-device-edge-channel")
|
||||
print("command_transport=disabled")
|
||||
print("gelios=untouched")
|
||||
else:
|
||||
print("public_core_channel=preserved:155.212.211.15:443/tcp:tls13-mtls-h2")
|
||||
print(f"health=127.0.0.1:{CHANNEL_HEALTH_PORT}:combined-edge-runtime")
|
||||
print("public_b2_ingress=155.212.211.15:9921/tcp:bidirectional-session")
|
||||
print("tracker_adapter=arusnavi-b2:profile=arusnavi.b2.internal.v1")
|
||||
print("tracker_ack=after-core-durable-acceptance-only")
|
||||
print("runtime_composition=single-non-root-process:core-channel+universal-gateway")
|
||||
print("tailscale=preserved:absent")
|
||||
print("services=recreate:nodedc-device-edge-channel")
|
||||
print(
|
||||
"command_transport=typed-service-ping-v1:allowlisted-adapter-only"
|
||||
if phase == "command-transport"
|
||||
else "command_transport=disabled"
|
||||
)
|
||||
if phase == "command-transport":
|
||||
print("command_catalog=allowlisted-adapter-typed-commands-only")
|
||||
print(
|
||||
"gelios=untouched-legacy-only"
|
||||
if phase == "command-transport"
|
||||
else "gelios=untouched"
|
||||
)
|
||||
print("dns=unchanged")
|
||||
print("b2_routes=unchanged")
|
||||
print("state=new")
|
||||
@@ -1835,8 +2011,10 @@ def apply_artifact(artifact_argument: str):
|
||||
apply_core_channel(loaded["payload"])
|
||||
elif loaded["phase"] == "tailscale-retirement":
|
||||
apply_tailscale_retirement(loaded["payload"])
|
||||
else:
|
||||
elif loaded["phase"] == "tracker-ingress":
|
||||
apply_tracker_ingress(loaded["payload"])
|
||||
else:
|
||||
apply_command_transport(loaded["payload"])
|
||||
|
||||
archived = archive_artifact(loaded["artifact"], APPLIED_ROOT)
|
||||
record = {
|
||||
|
||||
Reference in New Issue
Block a user