Skip to content
This repository was archived by the owner on Jul 7, 2024. It is now read-only.

Commit 05878d2

Browse files
committed
node: runtime/trace until specified CNs reified
1 parent 0f27695 commit 05878d2

File tree

7 files changed

+157
-0
lines changed

7 files changed

+157
-0
lines changed

cmd/runner-node/main.go

+1
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ func main() {
9494
util.ShowCurrent()
9595
profile.Profile()
9696

97+
node.TraceInit()
9798
var c config
9899
configPath := os.Getenv("CONFIG_PATH")
99100
data, err := os.ReadFile(configPath)

node/api.go

+1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ func (n *Node) setupClient(cl *rpc2.Client) {
7474
}
7575
util.Notify(fmt.Sprintf("STATUS=Synced to new CC (%d CNs)...", len(cc.Networks)))
7676
util.Notify("READY=1")
77+
n.traceCheck()
7778
return nil
7879
})
7980
go cl.Run()

node/main.go

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ type NodeConfig struct {
2222
EndpointOverride string
2323
}
2424

25+
// There must be only one Node instance as a Node can trigger a trace to stop.
2526
func NewNode(cfg NodeConfig) (*Node, error) {
2627
mh, err := newMio(cfg.MioAddr, cfg.MioToken)
2728
if err != nil {

node/trace.go

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package node
2+
3+
import (
4+
"encoding/json"
5+
"os"
6+
"runtime/trace"
7+
"sync"
8+
9+
"github.com/nyiyui/qrystal/util"
10+
)
11+
12+
var traceUntil []string
13+
var traceCheckLock sync.Mutex
14+
var traceFile *os.File
15+
16+
func TraceInit() {
17+
outputPath := os.Getenv("QRYSTAL_TRACE_OUTPUT_PATH")
18+
if outputPath == "" {
19+
return
20+
}
21+
{
22+
traceUntilCNs := os.Getenv("QRYSTAL_TRACE_UNTIL_CNS")
23+
traceUntil = make([]string, 0)
24+
err := json.Unmarshal([]byte(traceUntilCNs), &traceUntil)
25+
if err != nil {
26+
util.S.Errorf("trace: failed parse QRYSTAL_TRACE_UNTIL_CNS: %s", err)
27+
return
28+
}
29+
}
30+
var err error
31+
traceFile, err = os.Create(outputPath)
32+
if err != nil {
33+
util.S.Errorf("trace: failed to create file %s: %s", outputPath, err)
34+
return
35+
}
36+
err = trace.Start(traceFile)
37+
if err != nil {
38+
util.S.Errorf("trace: failed to start trace: %s", err)
39+
return
40+
}
41+
util.S.Info("trace: ready.")
42+
}
43+
44+
func (n *Node) traceCheck() {
45+
if traceUntil == nil {
46+
// trace not enabled
47+
return
48+
}
49+
traceCheckLock.Lock()
50+
defer traceCheckLock.Unlock()
51+
matches := 0
52+
for key := range n.cc.Networks {
53+
for _, key2 := range traceUntil {
54+
if key == key2 {
55+
matches++
56+
}
57+
}
58+
}
59+
if matches != len(traceUntil) {
60+
return
61+
}
62+
if !trace.IsEnabled() {
63+
util.S.Info("trace: already stopped.")
64+
return
65+
}
66+
trace.Stop()
67+
err := traceFile.Close()
68+
if err != nil {
69+
util.S.Errorf("trace: failed to close file (while checking): %s", err)
70+
}
71+
util.S.Info("trace: stopped due to QRYSTAL_TRACE_UNTIL_CNS being satisfied")
72+
}

result

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/nix/store/76nxn1yk05p6307aafvfrjxizg2zim2a-vm-test-run-trace

test.nix

+71
Original file line numberDiff line numberDiff line change
@@ -428,4 +428,75 @@ in {
428428
break
429429
'';
430430
});
431+
trace = let
432+
networkName = "testnet";
433+
eoPath = pkgs.writeShellScript "eo.sh" ''
434+
while IFS= read -r line
435+
do
436+
echo '{"endpoint":"1.2.3.4:5678"}'
437+
done
438+
'';
439+
tracePath = "/etc/qrystal-trace";
440+
node = { token }:
441+
{ pkgs, ... }: {
442+
imports = [
443+
base
444+
self.outputs.nixosModules.${system}.node
445+
({ ... }: {
446+
environment.etc."qrystal-trace" = {
447+
text = "not yet";
448+
mode = "0666";
449+
};
450+
systemd.services.qrystal-node.environment = {
451+
"QRYSTAL_TRACE_OUTPUT_PATH" = tracePath;
452+
"QRYSTAL_TRACE_UNTIL_CNS" = builtins.toJSON [networkName];
453+
};
454+
environment.systemPackages = with pkgs; [ go ];
455+
})
456+
];
457+
458+
networking.firewall.allowedTCPPorts = [ 39251 ];
459+
qrystal.services.node = csConfig [ networkName ] token;
460+
};
461+
in lib.runTest ({
462+
name = "trace";
463+
hostPkgs = pkgs;
464+
nodes = {
465+
node1 = node { token = node1Token; };
466+
cs = { pkgs, ... }: {
467+
imports = [ base self.outputs.nixosModules.${system}.cs ];
468+
469+
networking.firewall.allowedTCPPorts = [ 39252 ];
470+
qrystal.services.cs = {
471+
enable = true;
472+
config = {
473+
tls = csTls;
474+
tokens = [
475+
(nodeToken "node1" node1Hash [ networkName ])
476+
];
477+
central.networks.${networkName} = networkBase // {
478+
peers.node1 = {
479+
host = "node1:58120";
480+
allowedIPs = [ "10.123.0.1/32" ];
481+
canSee.only = [ "node1" ];
482+
};
483+
};
484+
};
485+
};
486+
};
487+
};
488+
testScript = { nodes, ... }: ''
489+
import time
490+
491+
cs.start()
492+
cs.wait_for_unit("qrystal-cs.service")
493+
node1.start()
494+
node1.wait_for_unit("qrystal-node.service", timeout=20)
495+
start = time.time()
496+
node1.wait_until_succeeds("cat ${tracePath}", timeout=20)
497+
# verify trace is valid (e.g. if it finished writing correctly)
498+
node1.succeed("go tool trace -pprof=net ${tracePath}")
499+
# pprof type doesn't matter
500+
'';
501+
});
431502
}

trace.md

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Execution Trace (Node)
2+
3+
Node can provide an execution trace (using [Go's `runtime/trace`](https://pkg.go.dev/runtime/trace)) from startup until a set of CNs are initially reified (i.e. the WireGuard networks for the CNs are configured and Hokuto DNS is configured for them, if applicable).
4+
5+
## Configuration
6+
7+
Set the `QRYSTAL_TRACE_OUTPUT_PATH` envvar for the output path of the trace. Note that the NixOS modules set `PrivateTmp=yes` on the systemd unit (e.g. if you set `QRYSTAL_TRACE_OUTPUT_PATH=/tmp/qrystal-trace`, then the trace would be in something like `/etc/systemd-private-abc-qrystal-node.service-def/tmp/qrystal-trace`).
8+
9+
Set the `QRYSTAL_TRACE_UNTIL_CNS` envvar to a JSON list of the CNs. Once all of the CNs specified in here are reified, the trace will be stopped (and saved).
10+
Example: `["examplenet","othernet"]`

0 commit comments

Comments
 (0)