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}