117 lines
4.2 KiB
Go
117 lines
4.2 KiB
Go
package cli
|
||
|
||
import (
|
||
"context"
|
||
"errors"
|
||
"flag"
|
||
"fmt"
|
||
"io"
|
||
"log/slog"
|
||
"os"
|
||
|
||
"sultan/pkg/agent"
|
||
)
|
||
|
||
func runAgent(ctx context.Context, args []string, stderr io.Writer) int {
|
||
if len(args) > 0 && args[0] == "maintenance" {
|
||
return runAgentMaintenance(ctx, args[1:], stderr)
|
||
}
|
||
cfg, err := parseAgentFlags(args, stderr, os.Getenv)
|
||
if errors.Is(err, flag.ErrHelp) {
|
||
return 0
|
||
}
|
||
if err != nil {
|
||
fmt.Fprintln(stderr, err)
|
||
return 2
|
||
}
|
||
cfg.Logger = slog.New(slog.NewTextHandler(stderr, nil))
|
||
a, err := agent.New(cfg)
|
||
if err != nil {
|
||
fmt.Fprintln(stderr, err)
|
||
return 2
|
||
}
|
||
if err := a.Run(ctx); err != nil {
|
||
fmt.Fprintln(stderr, "Agent beendet mit Fehler:", err)
|
||
return 1
|
||
}
|
||
return 0
|
||
}
|
||
|
||
// parseAgentFlags liest die Flags von `sultan agent`. SULTAN_TOKEN und
|
||
// SULTAN_SERVER dienen als Defaults (curl | sh, Compose; Argumente wären über
|
||
// ps für andere Benutzer sichtbar) – Flags gewinnen.
|
||
func parseAgentFlags(args []string, output io.Writer, getenv func(string) string) (agent.Config, error) {
|
||
fs := flag.NewFlagSet("sultan agent", flag.ContinueOnError)
|
||
fs.SetOutput(output)
|
||
var cfg agent.Config
|
||
fs.StringVar(&cfg.ServerURL, "server", "", "Server-Adresse, z. B. https://ingress.example.com:8443 (Override; sonst aus der Einladung)")
|
||
fs.StringVar(&cfg.Token, "token", "", "Einladung von `sultan token invite` (nur beim ersten Start)")
|
||
fs.StringVar(&cfg.DataDir, "data-dir", "./suldata", "Datenverzeichnis (node.key, node.json)")
|
||
fs.StringVar(&cfg.NodeID, "node-id", "", "Knoten-ID (Default: aus dem Hostnamen)")
|
||
fs.StringVar(&cfg.DockerSocket, "docker-socket", "/var/run/docker.sock", `Docker-Socket ("none" = ohne Docker)`)
|
||
fs.StringVar(&cfg.BackendHost, "backend-host", "", "Adresse, unter der der Ingress die Dienste erreicht (Default: automatisch)")
|
||
fs.StringVar(&cfg.BackendInterface, "backend-interface", "", "alternativ: IP dieses Interfaces verwenden (z. B. wt0 für NetBird)")
|
||
fs.StringVar(&cfg.ConfigDir, "config-dir", "/etc/sultan.d", `Dienste als YAML (*.yaml) für Hosts ohne Docker-Labels ("none" = aus)`)
|
||
fs.StringVar(&cfg.FQDN, "fqdn", "", "vollständiger Name dieses Hosts fürs Inventar (Default: automatisch über DNS)")
|
||
annotateEnv(fs)
|
||
if err := fs.Parse(args); err != nil {
|
||
return agent.Config{}, err
|
||
}
|
||
if err := applyEnv(fs, getenv); err != nil {
|
||
return agent.Config{}, err
|
||
}
|
||
if fs.NArg() > 0 {
|
||
return agent.Config{}, fmt.Errorf("unerwartetes Argument %q", fs.Arg(0))
|
||
}
|
||
return cfg, nil
|
||
}
|
||
|
||
// runAgentMaintenance: `sultan agent maintenance on|off [--for] [--reason]
|
||
// [--public-reason]` – meldet Wartung dieses Knotens beim Server an oder ab,
|
||
// z. B. aus einem Patch- oder Reboot-Skript.
|
||
func runAgentMaintenance(ctx context.Context, args []string, stderr io.Writer) int {
|
||
if len(args) == 0 || (args[0] != "on" && args[0] != "off") {
|
||
fmt.Fprintln(stderr, "Aufruf: sultan agent maintenance on|off [--for 4h|0] [--reason TEXT] [--public-reason TEXT] [--data-dir DIR]")
|
||
return 2
|
||
}
|
||
on := args[0] == "on"
|
||
fs := flag.NewFlagSet("sultan agent maintenance", flag.ContinueOnError)
|
||
fs.SetOutput(stderr)
|
||
dataDir := fs.String("data-dir", "./suldata", "Datenverzeichnis des Agenten")
|
||
server := fs.String("server", "", "Server-Adresse (Override)")
|
||
forRaw := fs.String("for", "4h", "Dauer (z. B. 30m, 4h, 2d; 0 = unbefristet)")
|
||
reason := fs.String("reason", "", "interner Grund (Log, Monitoring)")
|
||
publicReason := fs.String("public-reason", "", "öffentlicher Grund – ersetzt den Standardtext der Wartungsseite")
|
||
annotateEnv(fs)
|
||
if err := fs.Parse(args[1:]); err != nil {
|
||
if errors.Is(err, flag.ErrHelp) {
|
||
return 0
|
||
}
|
||
return 2
|
||
}
|
||
if err := applyEnv(fs, os.Getenv); err != nil {
|
||
fmt.Fprintln(stderr, err)
|
||
return 2
|
||
}
|
||
d, err := parseMaintenanceFor(*forRaw)
|
||
if err != nil {
|
||
fmt.Fprintln(stderr, "--for:", err)
|
||
return 2
|
||
}
|
||
a, err := agent.New(agent.Config{DataDir: *dataDir, ServerURL: *server, Logger: slog.New(slog.NewTextHandler(stderr, nil))})
|
||
if err != nil {
|
||
fmt.Fprintln(stderr, err)
|
||
return 2
|
||
}
|
||
if err := a.SetMaintenance(ctx, on, d, *reason, *publicReason); err != nil {
|
||
fmt.Fprintln(stderr, "Fehler:", err)
|
||
return 1
|
||
}
|
||
if on {
|
||
fmt.Fprintln(stderr, "Wartung angemeldet – Besucher sehen jetzt die Wartungsseite.")
|
||
} else {
|
||
fmt.Fprintln(stderr, "Wartung beendet.")
|
||
}
|
||
return 0
|
||
}
|