code wiki / _hdl_build / nx_blue_green.nx
nx_blue_green.nx source
↩ module page · 116 lines · 4853 B
1// nx_blue_green.nx -- DEP-002 (CALLOUT-012 deploy-exceed). Sovereign BLUE-GREEN deploy: two slots
2// (blue/green), a "live" pointer flipped ATOMICALLY between them via sys_renameat (the DEP-001
3// primitive). Deploy = write the new version to the INACTIVE slot, HEALTH-CHECK it, then atomically
4// flip live -> new slot. Rollback = atomic flip back (the old slot is untouched = INSTANT). A bad
5// (unhealthy) new version is NEVER flipped live. k8s/Spinnaker class, no 3rd-party stack.
6// Self-validating gate (BLUEGREENGATE): initial=v1, deploy=v2, rollback=v1, bad-deploy-blocked.
7// license_tier: ORIGINAL
8import "nx_syscalls.nx"
9import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
10
11const BG_LIVE: *u8 = "/tmp/nx_bg_live"
12const BG_TMP: *u8 = "/tmp/nx_bg_live_tmp"
13const BG_BLUE: *u8 = "/tmp/nx_bg_blue"
14const BG_GREEN: *u8 = "/tmp/nx_bg_green"
15
16func bg_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
17// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
18// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
19// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
20// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
21func bg_putn(v: i64) -> i64 { nxi_out(v); return 0 }
22func bg_strlen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
23
24func bg_write(path: *u8, content: *u8) -> i64 {
25 let fd: i64 = sys_openat_wr(path, 420)
26 if fd < 0 { return 0 - 1 }
27 sys_write(fd, content, bg_strlen(content))
28 sys_close(fd)
29 return 0
30}
31func bg_read(path: *u8, buf: *u8, cap: i64) -> i64 {
32 let fd: i64 = sys_openat_rd(path)
33 if fd < 0 { return 0 - 1 }
34 var tot: i64 = 0
35 var r: i64 = 1
36 while r > 0 { let dst: *u8 = ((buf as i64) + tot) as *u8; r = sys_read(fd, dst, cap - tot); if r > 0 { tot = tot + r } }
37 sys_close(fd)
38 return tot
39}
40func bg_has(buf: *u8, n: i64, needle: *u8) -> i64 {
41 var m: i64 = 0; while needle[m] != (0 as u8) { m = m + 1 }
42 if m == 0 { return 0 }
43 var i: i64 = 0
44 while i + m <= n { var j: i64 = 0; var ok: i64 = 1; while j < m { if buf[i + j] != needle[j] { ok = 0; j = m } else { j = j + 1 } } if ok == 1 { return 1 } i = i + 1 }
45 return 0
46}
47
48// ATOMIC flip: write the target slot path into the live pointer via temp+renameat.
49func bg_flip(slotpath: *u8) -> i64 {
50 bg_write(BG_TMP, slotpath)
51 return sys_renameat(BG_TMP, BG_LIVE)
52}
53// serve: read live pointer -> slot path -> slot content. (what a request sees)
54func bg_serve(buf: *u8, cap: i64) -> i64 {
55 let pb: *u8 = sys_mmap(256)
56 let pn: i64 = bg_read(BG_LIVE, pb, 255)
57 if pn <= 0 { return 0 - 1 }
58 pb[pn] = 0 as u8
59 return bg_read(pb, buf, cap)
60}
61// health: a slot is healthy iff its content carries the -OK marker.
62func bg_health(slotpath: *u8) -> i64 {
63 let hb: *u8 = sys_mmap(256)
64 let hn: i64 = bg_read(slotpath, hb, 255)
65 if hn <= 0 { return 0 }
66 return bg_has(hb, hn, "-OK" as *u8)
67}
68
69func main() -> i64 {
70 let b: *u8 = sys_mmap(256)
71
72 // initial: blue = v1, live -> blue
73 bg_write(BG_BLUE, "BLUE-v1-OK" as *u8)
74 bg_flip(BG_BLUE)
75 let n1: i64 = bg_serve(b, 255)
76 let initial_v1: i64 = bg_has(b, n1, "BLUE-v1-OK" as *u8)
77
78 // deploy v2 to the INACTIVE slot (green), health-check, atomic flip live -> green
79 bg_write(BG_GREEN, "GREEN-v2-OK" as *u8)
80 let hg: i64 = bg_health(BG_GREEN)
81 if hg == 1 { bg_flip(BG_GREEN) }
82 let n2: i64 = bg_serve(b, 255)
83 let deploy_v2: i64 = bg_has(b, n2, "GREEN-v2-OK" as *u8)
84
85 // INSTANT rollback: atomic flip back to blue (untouched slot)
86 bg_flip(BG_BLUE)
87 let n3: i64 = bg_serve(b, 255)
88 let rollback_v1: i64 = bg_has(b, n3, "BLUE-v1-OK" as *u8)
89
90 // bad new deploy: write an unhealthy version to green; health fails -> DO NOT flip
91 bg_write(BG_GREEN, "GREEN-BAD-corrupt" as *u8)
92 let hb: i64 = bg_health(BG_GREEN)
93 var bad_blocked: i64 = 0
94 if hb == 0 {
95 let n4: i64 = bg_serve(b, 255)
96 if bg_has(b, n4, "BLUE-v1-OK" as *u8) == 1 { bad_blocked = 1 }
97 }
98
99 bg_puts("BLUEGREENGATE initial_v1=" as *u8); bg_putn(initial_v1)
100 bg_puts(" deploy_v2=" as *u8); bg_putn(deploy_v2)
101 bg_puts(" rollback_v1=" as *u8); bg_putn(rollback_v1)
102 bg_puts(" green_health=" as *u8); bg_putn(hg)
103 bg_puts(" bad_health=" as *u8); bg_putn(hb)
104 bg_puts(" bad_deploy_blocked=" as *u8); bg_putn(bad_blocked)
105
106 var ok: i64 = 1
107 if initial_v1 != 1 { ok = 0 }
108 if deploy_v2 != 1 { ok = 0 }
109 if rollback_v1 != 1 { ok = 0 }
110 if hg != 1 { ok = 0 }
111 if hb != 0 { ok = 0 }
112 if bad_blocked != 1 { ok = 0 }
113 if ok == 1 { bg_puts(" verdict=GREEN\n" as *u8); return 0 }
114 bg_puts(" verdict=RED\n" as *u8)
115 return 1
116}