okami.mom/albedo
git clone https://git.nkp.pet/okami.mom/albedo
cba5ff8
main
1"""Bounded Erlang inspection transport shared by isolated daemon scenarios.""" 2 3import json 4import re 5import subprocess 6import uuid 7 8 9class Inspection : 10def __init__ (self ,app ,source = None ): 11self .app = app 12self .module = source .stem if source is not None else None 13if source is not None : 14result = subprocess .run ( 15 ["erlc" ,"-Werror" ,"-o" ,str (app .root ),str (source )], 16capture_output = True , 17text = True , 18timeout = 30 , 19 ) 20if result .returncode != 0 : 21raise AssertionError ( 22f"probe compilation failed: { source } \n { result . stdout } { result . stderr } " 23 ) 24 25def evaluate (self ,expression ,* ,timeout = 40 ): 26app = self .app 27log = (app .home / "daemon.log" ).read_text (errors = "replace" ) 28nodes = re .findall (rf"inspect: node (albedo_ { app . daemon . _pid } @[^\s]+)" ,log ) 29if not nodes : 30raise AssertionError ( 31"daemon did not announce an inspection node; enable ALBEDO_INSPECT before boot" 32 ) 33cookie = (app .home / "inspect.cookie" ).read_text ().strip () 34node = nodes [- 1 ] 35prefix = ( 36f"Node = ' { node } ', " 37"Call = fun(Module, Function, Arguments) -> " 38f"case rpc:call(Node, Module, Function, Arguments, { int ( timeout * 1000 ) } ) of " 39"{badrpc, Reason} -> error({rpc_failed, Module, Function, Reason}); " 40"Value -> Value end end, " 41 ) 42if self .module is not None : 43beam = app .root / (self .module + ".beam" ) 44prefix += ( 45f"{{ok, Binary}} = file:read_file( { json . dumps ( str ( beam )) } ), " 46f"{{module, { self . module } }} = Call(code, load_binary, " 47f'[ { self . module } , "probe.erl", Binary]), ' 48 ) 49try : 50result = subprocess .run ( 51 [ 52"erl" , 53"+S" , 54"2:2" , 55"-sname" , 56f"probe_ { uuid . uuid4 (). hex } " , 57"-setcookie" , 58cookie , 59"-noshell" , 60"-eval" , 61prefix + expression + ", halt()." , 62 ], 63cwd = app .root , 64capture_output = True , 65text = True , 66timeout = timeout , 67 ) 68except subprocess .TimeoutExpired as error : 69raise AssertionError ( 70f"inspection of { node } timed out after { timeout } s" 71 )from error 72if result .returncode != 0 : 73raise AssertionError ( 74f"inspection of { node } failed:\n { result . stdout } { result . stderr } " 75 ) 76return result .stdout 77 78def call (self ,function ,arguments = "[]" ): 79if self .module is None : 80raise ValueError ("calling a probe requires its source module" ) 81return self .evaluate ( 82f"io:put_chars(Call( { self . module } , { function } , { arguments } ))" 83 ) 84 85def call_json (self ,function ,arguments = "[]" ): 86output = self .call (function ,arguments ) 87try : 88return json .loads (output ) 89except ValueError as error : 90raise AssertionError ( 91f" { self . module } : { function } returned invalid JSON: { output } " 92 )from error