1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package main
import (
"context"
"flag"
"fmt"
"log/slog"
"os"
"os/signal"
"syscall"
"github.com/johnandersen777/socialweb-computer-kcp/internal/nodeagent"
)
const defaultConfigPath = "/etc/socialweb/node/join.json"
const usage = `socialweb-node - the guest node agent
socialweb-node join [--config PATH]
socialweb-node cni [--config PATH]
socialweb-node diagnose [--config PATH]
socialweb-node hostname [--config PATH]
socialweb-node dns-link [--config PATH]
socialweb-node masquerade [--config PATH]
socialweb-node install-meshd [--config PATH]
socialweb-node units install [--config PATH] [--root DIR]
socialweb-node units start [--config PATH]
The config path defaults to $SOCIALWEB_NODE_CONFIG or ` + defaultConfigPath + `.
`
func main() {
os.Exit(run(os.Args[1:]))
}
func run(args []string) int {
if len(args) == 0 {
fmt.Fprint(os.Stderr, usage)
return 2
}
command := args[0]
args = args[1:]
fs := flag.NewFlagSet("socialweb-node "+command, flag.ContinueOnError)
fs.SetOutput(os.Stderr)
configPath := fs.String("config", configPathFromEnv(), "the join config file")
root := fs.String("root", "", "the root under which the units are installed")
if command == "units" {
if len(args) == 0 {
fmt.Fprint(os.Stderr, usage)
return 2
}
mode := args[0]
if err := fs.Parse(args[1:]); err != nil {
return flagCode(err)
}
cfg, err := nodeagent.LoadConfig(*configPath)
if err != nil {
return fail(command, err)
}
log := logger()
switch mode {
case "install":
if err := nodeagent.InstallUnits(*root, cfg); err != nil {
return fail(command+" install", err)
}
log.Info("installed the node units", "root", *root)
return 0
case "start":
if err := nodeagent.StartUnits(commandContext(), cfg, log); err != nil {
return fail(command+" start", err)
}
return 0
default:
fmt.Fprint(os.Stderr, usage)
return 2
}
}
if err := fs.Parse(args); err != nil {
return flagCode(err)
}
if fs.NArg() != 0 {
fmt.Fprint(os.Stderr, usage)
return 2
}
cfg, err := nodeagent.LoadConfig(*configPath)
if err != nil {
return fail(command, err)
}
ctx := commandContext()
log := logger()
switch command {
case "join":
err = nodeagent.Join(ctx, cfg, log)
case "cni":
err = nodeagent.CNI(ctx, cfg, log)
case "diagnose":
err = nodeagent.Diagnose(ctx, cfg, log)
case "hostname":
err = nodeagent.Hostname(ctx, cfg, log)
case "dns-link":
err = nodeagent.DNSLink(ctx, cfg, log)
case "masquerade":
err = nodeagent.Masquerade(ctx, cfg, log)
case "install-meshd":
err = nodeagent.InstallMeshd(ctx, cfg, log)
default:
fmt.Fprint(os.Stderr, usage)
return 2
}
if err != nil {
return fail(command, err)
}
return 0
}
func configPathFromEnv() string {
if path := os.Getenv("SOCIALWEB_NODE_CONFIG"); path != "" {
return path
}
return defaultConfigPath
}
func logger() *slog.Logger {
return slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelInfo}))
}
func commandContext() context.Context {
ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
return ctx
}
func fail(command string, err error) int {
fmt.Fprintf(os.Stderr, "socialweb-node %s: %v\n", command, err)
return 1
}
func flagCode(err error) int {
if err == flag.ErrHelp {
return 0
}
return 2
}