package main import ( "errors" "flag" "fmt" "os" "strings" "rocketgit.ru/rsmon/worker/internal/installer" ) func dispatchManagementCommand(args []string) (bool, int) { if len(args) == 0 { return false, 0 } switch args[0] { case "install": return true, installCommand(args[1:]) case "deploy": return true, deployCommand(args[1:]) default: return false, 0 } } func installCommand(args []string) int { fs := flag.NewFlagSet("install", flag.ContinueOnError) fs.SetOutput(os.Stderr) var opts installer.InstallOptions var tokenFile, passwordFile string fs.StringVar(&opts.Binary, "binary", "", "worker binary to install (default: this executable)") fs.StringVar(&opts.EnvFile, "env-file", "", "worker environment file (systemd-safe KEY=VALUE; overrides .env/process env)") fs.StringVar(&opts.Token, "token", "", "worker API token (RSMON_TOKEN)") fs.StringVar(&opts.Token, "api-key", "", "worker API token (alias for --token)") fs.StringVar(&tokenFile, "token-file", "", "file containing the worker API token") fs.StringVar(&opts.URL, "url", "", "RSMon server URL (RSMON_URL; default https://rsmon.ru)") fs.StringVar(&opts.Host, "host", "", "operator console bind address (WORKER_HOST; default 127.0.0.1)") fs.StringVar(&opts.Port, "port", "", "operator console port (WORKER_PORT; required with --name)") fs.StringVar(&opts.Login, "login", "", "operator console login (WORKER_LOGIN; default admin with a generated password)") fs.StringVar(&opts.Password, "password", "", "operator console password (WORKER_PASSWORD)") fs.StringVar(&passwordFile, "password-file", "", "file containing the operator console password") fs.StringVar(&opts.Name, "name", "", "instance name: installs a co-located worker (rsmon-worker-) with its own config/data/unit/port") fs.BoolVar(&opts.Docker, "docker", false, "run the prebuilt Docker image instead of the binary") fs.StringVar(&opts.Image, "image", installer.DefaultImage, "immutable Docker repository@sha256 digest required with --docker") fs.BoolVar(&opts.NoStart, "no-start", false, "install and enable without starting") fs.Usage = func() { fmt.Fprintln(fs.Output(), "Usage: rsmon-worker install [--token TOKEN|--env-file FILE] [--name NAME] [--port PORT] [options]") fs.PrintDefaults() } if err := fs.Parse(args); err != nil { if errors.Is(err, flag.ErrHelp) { return 0 } return 2 } if fs.NArg() != 0 { fs.Usage() return 2 } var err error if opts.Token, err = secretValue(opts.Token, tokenFile); err != nil { fmt.Fprintf(os.Stderr, "install failed: %v\n", err) return 1 } if opts.Password, err = secretValue(opts.Password, passwordFile); err != nil { fmt.Fprintf(os.Stderr, "install failed: %v\n", err) return 1 } if err := installer.Install(opts); err != nil { fmt.Fprintf(os.Stderr, "install failed: %v\n", err) return 1 } return 0 } func deployCommand(args []string) int { fs := flag.NewFlagSet("deploy", flag.ContinueOnError) fs.SetOutput(os.Stderr) var opts installer.DeployOptions var tokenFile, passwordFile, passphraseFile, sudoPasswordFile string fs.StringVar(&opts.Host, "host", "", "SSH server hostname or address") fs.IntVar(&opts.Port, "port", 22, "SSH server port") fs.StringVar(&opts.User, "user", "", "SSH username") fs.StringVar(&opts.IdentityFile, "identity-file", "", "SSH private key path") fs.StringVar(&opts.KeyPassphrase, "key-passphrase", "", "SSH private key passphrase") fs.StringVar(&passphraseFile, "key-passphrase-file", "", "file containing the private key passphrase") fs.StringVar(&opts.Password, "password", "", "SSH login password") fs.StringVar(&passwordFile, "password-file", "", "file containing the SSH login password") fs.StringVar(&opts.SudoPassword, "sudo-password", "", "remote sudo password") fs.StringVar(&sudoPasswordFile, "sudo-password-file", "", "file containing the remote sudo password") fs.StringVar(&opts.KnownHostsFile, "known-hosts", "", "known_hosts path (default: ~/.ssh/known_hosts)") fs.StringVar(&opts.HostKeyFingerprint, "host-key-fingerprint", "", "expected SHA256 SSH host-key fingerprint") fs.BoolVar(&opts.InsecureHostKey, "insecure-host-key", false, "disable SSH host-key verification (unsafe)") fs.StringVar(&opts.Binary, "binary", "", "worker binary to upload (default: this executable)") fs.StringVar(&opts.Token, "token", "", "worker API token") fs.StringVar(&opts.Token, "api-key", "", "worker API token (alias for --token)") fs.StringVar(&tokenFile, "token-file", "", "file containing the worker API token") fs.StringVar(&opts.URL, "url", installer.DefaultURL, "RSMon server URL") fs.BoolVar(&opts.Docker, "docker", false, "install the prebuilt Docker image remotely") fs.StringVar(&opts.Image, "image", installer.DefaultImage, "immutable Docker repository@sha256 digest required with --docker") fs.BoolVar(&opts.NoStart, "no-start", false, "install and enable without starting") fs.Usage = func() { fmt.Fprintln(fs.Output(), "Usage: rsmon-worker deploy --host HOST --user USER --token TOKEN [SSH options]") fs.PrintDefaults() } if err := fs.Parse(args); err != nil { if errors.Is(err, flag.ErrHelp) { return 0 } return 2 } if fs.NArg() != 0 { fs.Usage() return 2 } var err error if opts.Token, err = secretValue(opts.Token, tokenFile); err != nil { fmt.Fprintf(os.Stderr, "deploy failed: %v\n", err) return 1 } if opts.Password, err = secretValue(opts.Password, passwordFile); err != nil { fmt.Fprintf(os.Stderr, "deploy failed: %v\n", err) return 1 } if opts.KeyPassphrase, err = secretValue(opts.KeyPassphrase, passphraseFile); err != nil { fmt.Fprintf(os.Stderr, "deploy failed: %v\n", err) return 1 } if opts.SudoPassword, err = secretValue(opts.SudoPassword, sudoPasswordFile); err != nil { fmt.Fprintf(os.Stderr, "deploy failed: %v\n", err) return 1 } if err := installer.Deploy(opts); err != nil { fmt.Fprintf(os.Stderr, "deploy failed: %v\n", err) return 1 } fmt.Fprintln(os.Stdout, "rsmon-worker deployed") return 0 } func secretValue(direct, path string) (string, error) { if direct != "" && path != "" { return "", fmt.Errorf("a secret and its file option cannot both be set") } if path == "" { return direct, nil } b, err := os.ReadFile(path) if err != nil { return "", fmt.Errorf("read secret file: %w", err) } return strings.TrimRight(string(b), "\r\n"), nil }