105 lines
3.2 KiB
Go
105 lines
3.2 KiB
Go
// Package cli implementiert die Befehlszeile von sultan:
|
||
//
|
||
// sultan server Registry, Stream-Hub, Policy-Engine, Traefik-Provider
|
||
// sultan agent (noch nicht implementiert)
|
||
// sultan token (noch nicht implementiert)
|
||
// sultan version
|
||
//
|
||
// Run ist ohne Prozessgrenzen testbar; main ruft nur Run auf.
|
||
package cli
|
||
|
||
import (
|
||
"context"
|
||
"errors"
|
||
"flag"
|
||
"fmt"
|
||
"io"
|
||
"log/slog"
|
||
"time"
|
||
|
||
"sultan/pkg/server"
|
||
)
|
||
|
||
// Version wird beim Bauen gesetzt: -ldflags "-X sultan/pkg/cli.Version=1.2.3".
|
||
var Version = "dev"
|
||
|
||
const usage = `sultan – verteiltes Ingress-Routing mit ZeroTrust-Service-Discovery
|
||
|
||
Befehle:
|
||
sultan server Server starten (Registry, Agent-Port, Traefik-Provider)
|
||
sultan agent Agent starten (noch nicht implementiert)
|
||
sultan token Mandanten, Einladungen, Lizenzen verwalten (noch nicht implementiert)
|
||
sultan version Version ausgeben
|
||
sultan help diese Hilfe
|
||
|
||
Hilfe zu einem Befehl: sultan <befehl> --help
|
||
`
|
||
|
||
// Run führt die Befehlszeile aus und liefert den Exit-Code (0 ok, 1 Fehler,
|
||
// 2 Bedienfehler). ctx endet z. B. bei SIGTERM.
|
||
func Run(ctx context.Context, args []string, stdout, stderr io.Writer) int {
|
||
if len(args) == 0 {
|
||
fmt.Fprint(stderr, usage)
|
||
return 2
|
||
}
|
||
switch args[0] {
|
||
case "help", "-h", "--help":
|
||
fmt.Fprint(stdout, usage)
|
||
return 0
|
||
case "version":
|
||
fmt.Fprintf(stdout, "sultan %s\n", Version)
|
||
return 0
|
||
case "server":
|
||
return runServer(ctx, args[1:], stdout, stderr)
|
||
case "agent", "token":
|
||
fmt.Fprintf(stderr, "sultan %s: noch nicht implementiert (siehe Roadmap)\n", args[0])
|
||
return 1
|
||
default:
|
||
fmt.Fprintf(stderr, "unbekannter Befehl %q\n\n%s", args[0], usage)
|
||
return 2
|
||
}
|
||
}
|
||
|
||
func runServer(ctx context.Context, args []string, stdout, stderr io.Writer) int {
|
||
cfg, err := parseServerFlags(args, stderr)
|
||
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))
|
||
s, err := server.New(ctx, cfg)
|
||
if err != nil {
|
||
fmt.Fprintln(stderr, "Start fehlgeschlagen:", err)
|
||
return 1
|
||
}
|
||
if err := s.Run(ctx); err != nil {
|
||
fmt.Fprintln(stderr, "Server beendet mit Fehler:", err)
|
||
return 1
|
||
}
|
||
return 0
|
||
}
|
||
|
||
// parseServerFlags liest die Flags von `sultan server`; Defaults wie in
|
||
// server.Config.
|
||
func parseServerFlags(args []string, output io.Writer) (server.Config, error) {
|
||
fs := flag.NewFlagSet("sultan server", flag.ContinueOnError)
|
||
fs.SetOutput(output)
|
||
var cfg server.Config
|
||
fs.StringVar(&cfg.DataDir, "data-dir", "./suldata", "Datenverzeichnis (sultan.db, server.key/.crt, master.key)")
|
||
fs.StringVar(&cfg.AgentAddr, "agent-addr", ":8443", "Adresse des Agent-Ports (nur TLS)")
|
||
fs.StringVar(&cfg.ProviderAddr, "provider-addr", "127.0.0.1:8080",
|
||
"Adresse für Traefik (/v1/traefik-config, Klartext – im Container :8080 und nur auf 127.0.0.1 veröffentlichen)")
|
||
fs.StringVar(&cfg.InterceptorURL, "interceptor-url", "http://127.0.0.1:8081", "Adresse des Sultan-Interceptors/Landers")
|
||
fs.DurationVar(&cfg.RefreshInterval, "refresh", time.Minute, "Intervall der Neubewertung aller Knoten")
|
||
if err := fs.Parse(args); err != nil {
|
||
return server.Config{}, err
|
||
}
|
||
if fs.NArg() > 0 {
|
||
return server.Config{}, fmt.Errorf("unerwartetes Argument %q", fs.Arg(0))
|
||
}
|
||
return cfg, nil
|
||
}
|