~ knotless Search/
↑↓ move↵ open⇧↵ all results
0
0
main
142 lines · 3.3 KB Raw History
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
}