code wiki / _hdl_build / nx_release_master.nx
nx_release_master.nx source
↩ module page · 113 lines · 6865 B
1// nx_release_master.nx -- the RELEASE-MASTER role (verb = BLESS): the ONE owner of coordinated, safe deploys
2// so parallel workstreams STOP CRASHING the shared live site. Today the team has deploy PRIMITIVES but no role
3// owns "bless an artifact -> deploy it"; publishes bypass the arbiter lease and race the shared sink -> corrupt
4// content / dropped site. This organ closes that gap by composing the existing primitives into ONE blessed,
5// LEASED, rollback-safe, serving-aware, audited lifecycle. Per deploy, holding the resource lease the whole time:
6// 1. LEASE fl_acquire(resource) -- serialize: two workstreams can't swap the same sink at once
7// 2. BLESS gate_passed==1 precondition -- NEVER deploy an un-gated artifact (no-bless-without-pass)
8// 3. SNAPSHOT live -> live.bak -- the rollback point (never deploy without one)
9// 4. SWAP candidate -> live.new -> renameat(live.new, live) -- atomic: no half-written live file
10// 5. HEALTH rm_health(live) -- serving-aware: is the swapped artifact actually good?
11// 6. ROLLBACK on unhealthy: live.bak -> live -- restore; production survives a bad deploy
12// 7. LEDGER append the verdict to a DURABLE release ledger (what was deployed, with what outcome)
13// 8. RELEASE fl_release -- siblings proceed
14// Returns the nx_deploy verdict: DEP_ABORTED (prod untouched) / DEP_DEPLOYED / DEP_ROLLED_BACK.
15// HEALTH NOTE: this rung's rm_health is a content-marker proxy (deterministic, gate-testable). The production
16// upgrade swaps rm_health's body for nx_url_probe (a real HTTPS/TLS GET of the BARE served URL) -- same shape
17// ("is the deployed thing actually serving?"), killing the sites.elf-outage class. license_tier: ORIGINAL
18import "nx_arbiter.nx"
19import "nx_deploy.nx"
20import "nx_syscalls.nx"
21const K_MAGIC_262144: i64 = 262144
22const K_MAGIC_1280: i64 = 1280
23
24func rm_len(s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8){i=i+1} return i }
25func rm_puts(dst: *u8, off: i64, s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8){ dst[off+i]=s[i]; i=i+1 } return off+i }
26func rm_putn(dst: *u8, off: i64, v: i64) -> i64 {
27 if v==0 { dst[off]=48 as u8; return off+1 }
28 var m: i64=v; if m<0{m=0-m}
29 let t: *u8=sys_mmap(32); var k: i64=0
30 while m>0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 }
31 var j: i64=0; while j<k { dst[off+j]=t[k-1-j]; j=j+1 }
32 return off+k
33}
34// build "base"+"suf" (NUL-terminated) into out.
35func rm_cat(base: *u8, suf: *u8, out: *u8) -> i64 { var o: i64 = rm_puts(out,0,base); o = rm_puts(out,o,suf); out[o]=0 as u8; return o }
36
37// is `path` present and non-empty? (candidate transfer check + live-existence check)
38func rm_nonempty(path: *u8) -> i64 {
39 let fd: i64 = sys_openat_rd(path); if fd < 0 { return 0 }
40 let b: *u8 = sys_mmap(8); let r: i64 = sys_read(fd, b, 1); sys_close(fd)
41 if r > 0 { return 1 }
42 return 0
43}
44// copy src -> dst (truncating). 1 ok / 0 fail.
45func rm_copy(src: *u8, dst: *u8) -> i64 {
46 let fdr: i64 = sys_openat_rd(src); if fdr < 0 { return 0 }
47 let fdw: i64 = sys_openat_wr(dst, 0x1a4); if fdw < 0 { sys_close(fdr); return 0 }
48 let buf: *u8 = sys_mmap(K_MAGIC_262144); var ok: i64 = 1; var go: i64 = 1
49 while go == 1 {
50 let r: i64 = sys_read(fdr, buf, K_MAGIC_262144)
51 if r <= 0 { go = 0 } else { let w: i64 = sys_write(fdw, buf, r); if w != r { ok = 0; go = 0 } }
52 }
53 sys_close(fdr); sys_close(fdw)
54 return ok
55}
56// does `path`'s content contain the NUL-terminated marker? (the serving-aware health proxy for this rung)
57func rm_contains(path: *u8, marker: *u8) -> i64 {
58 let fd: i64 = sys_openat_rd(path); if fd < 0 { return 0 }
59 let buf: *u8 = sys_mmap(K_MAGIC_262144); var n: i64 = 0; var go: i64 = 1
60 while go == 1 { let r: i64 = sys_read(fd, buf+n, K_MAGIC_262144-n); if r <= 0 { go = 0 } else { n = n + r; if n >= K_MAGIC_262144 { go = 0 } } }
61 sys_close(fd)
62 let ml: i64 = rm_len(marker); if ml <= 0 { return 0 }
63 var i: i64 = 0
64 while i + ml <= n {
65 var j: i64 = 0; var m: i64 = 1
66 while j < ml { if buf[i+j] != marker[j] { m = 0; j = ml } else { j = j + 1 } }
67 if m == 1 { return 1 }
68 i = i + 1
69 }
70 return 0
71}
72// SERVING-AWARE health: the swapped live artifact must be present, non-empty, and carry the serving marker.
73// (production: replace this body with nx_url_probe of the bare served URL.)
74func rm_health(live: *u8, marker: *u8) -> i64 {
75 if rm_nonempty(live) == 0 { return 0 }
76 if rm_contains(live, marker) == 0 { return 0 }
77 return 1
78}
79// append one durable release-ledger line: "verdict=<v> <tag>\n".
80func rm_ledger(ledger: *u8, verdict: i64, tag: *u8) -> i64 {
81 let fd: i64 = sys_openat_append(ledger, 0x1a4); if fd < 0 { return 0 }
82 let line: *u8 = sys_mmap(K_MAGIC_1280); var o: i64 = 0
83 o = rm_puts(line, o, "verdict=" as *u8); o = rm_putn(line, o, verdict); o = rm_puts(line, o, " " as *u8); o = rm_puts(line, o, tag)
84 line[o] = 10 as u8; o = o + 1
85 sys_write(fd, line, o); sys_close(fd)
86 return 0
87}
88
89// THE BLESSED, COORDINATED DEPLOY. Holds the lease for the whole lifecycle. Returns DEP_ABORTED/DEPLOYED/ROLLED_BACK.
90func rm_deploy(candidate: *u8, live: *u8, gate_passed: i64, resource: *u8, ledger: *u8, marker: *u8) -> i64 {
91 let lease: i64 = fl_acquire(resource, 600, 100)
92 if lease < 0 { rm_ledger(ledger, DEP_ABORTED, "LEASE-TIMEOUT" as *u8); return DEP_ABORTED } // can't coordinate -> fail-safe, don't deploy
93 // BLESS: never deploy an artifact that hasn't passed its gate.
94 if gate_passed != 1 { rm_ledger(ledger, DEP_ABORTED, "BLESS-REFUSED" as *u8); fl_release(lease); return DEP_ABORTED }
95 let transfer_ok: i64 = rm_nonempty(candidate)
96 // SNAPSHOT: back up the live artifact (the rollback point). A first-ever deploy has nothing to back up.
97 let bak: *u8 = sys_mmap(K_MAGIC_1280); rm_cat(live, ".bak" as *u8, bak)
98 var backup_taken: i64 = 1
99 if rm_nonempty(live) == 1 { backup_taken = rm_copy(live, bak) }
100 // SAFETY GATE: never touch production unless a backup exists AND the candidate is intact.
101 if dep_ready(backup_taken, transfer_ok) == 0 { rm_ledger(ledger, DEP_ABORTED, "PRECOND-FAIL" as *u8); fl_release(lease); return DEP_ABORTED }
102 // ATOMIC SWAP: stage to live.new, then rename over live (a concurrent reader never sees a half-written file).
103 let nw: *u8 = sys_mmap(K_MAGIC_1280); rm_cat(live, ".new" as *u8, nw)
104 rm_copy(candidate, nw)
105 sys_renameat(nw, live)
106 // SERVING-AWARE HEALTH + verdict.
107 let health_ok: i64 = rm_health(live, marker)
108 let verdict: i64 = dep_verdict(backup_taken, transfer_ok, health_ok)
109 if verdict == DEP_ROLLED_BACK { rm_copy(bak, live) } // restore -> production survives the bad deploy
110 rm_ledger(ledger, verdict, live)
111 fl_release(lease)
112 return verdict
113}