feat(installer): activate source builds atomically
Все проверки выполнены успешно
CI / test (push) Successful in 4m30s
Docker / Build and publish worker image (push) Successful in 17m26s
Все проверки выполнены успешно
CI / test (push) Successful in 4m30s
Docker / Build and publish worker image (push) Successful in 17m26s
Этот коммит содержится в:
@@ -156,16 +156,21 @@ func secretValue(direct, path string) (string, error) {
|
||||
return strings.TrimRight(string(b), "\r\n"), nil
|
||||
}
|
||||
|
||||
// sourceInstallCommand drives the remote source installer (work package
|
||||
// 3 of docs/source-installation.md): prerequisites, verified Go
|
||||
// toolchain, clone/update, resolved branch/commit record, and a staging
|
||||
// build. It reuses the deploy SSH options; it never touches the running
|
||||
// service, configuration, or data directory on the remote host.
|
||||
// sourceInstallCommand drives the remote source installer
|
||||
// (docs/source-installation.md): prerequisites, verified Go toolchain,
|
||||
// clone/update, resolved branch/commit record, a staging build, and (work
|
||||
// package 4) the atomic activation of the staged binary, validated
|
||||
// environment, data dir, and the detected init's service definition,
|
||||
// followed by a start and a process + /healthz verification. It reuses
|
||||
// the deploy SSH options. Any activation/start/health failure rolls back
|
||||
// to the prior working install; --no-activate keeps the staging-only
|
||||
// behavior.
|
||||
func sourceInstallCommand(args []string) int {
|
||||
fs := flag.NewFlagSet("source-install", flag.ContinueOnError)
|
||||
fs.SetOutput(os.Stderr)
|
||||
var opts installer.SourceInstallOptions
|
||||
var passphraseFile, passwordFile, sudoPasswordFile string
|
||||
var passphraseFile, passwordFile, sudoPasswordFile, tokenFile, workerPasswordFile string
|
||||
var noActivate bool
|
||||
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")
|
||||
@@ -188,8 +193,22 @@ func sourceInstallCommand(args []string) int {
|
||||
fs.StringVar(&opts.ToolchainDir, "toolchain-dir", "", "remote Go install path ending in /go (default /usr/local/go)")
|
||||
fs.StringVar(&opts.StageBinary, "stage-binary", "", "staging binary path (default <build-dir>/rsmon-worker)")
|
||||
fs.DurationVar(&opts.SessionTimeout, "session-timeout", 0, "per-remote-command timeout (default 30m; 0 uses the default)")
|
||||
fs.BoolVar(&noActivate, "no-activate", false, "stop after the staging build and do not install/start a service (activation is the default)")
|
||||
fs.StringVar(&opts.Activation.Name, "name", "", "instance name: activates rsmon-worker-<name> with its own config/data/unit/port")
|
||||
fs.StringVar(&opts.Activation.URL, "url", installer.DefaultURL, "RSMon server URL (RSMON_URL)")
|
||||
fs.StringVar(&opts.Activation.Token, "token", "", "worker API token (RSMON_TOKEN; required for activation)")
|
||||
fs.StringVar(&opts.Activation.Token, "api-key", "", "worker API token (alias for --token)")
|
||||
fs.StringVar(&tokenFile, "token-file", "", "file containing the worker API token")
|
||||
fs.StringVar(&opts.Activation.PublicURL, "public-url", "", "advertised public origin (PUBLIC_URL; scheme + host, no path)")
|
||||
fs.StringVar(&opts.Activation.Host, "worker-host", "", "worker webapp bind address (WORKER_HOST; default 127.0.0.1)")
|
||||
fs.StringVar(&opts.Activation.Port, "worker-port", "", "worker webapp bind port (WORKER_PORT; default 27401, required with --name)")
|
||||
fs.StringVar(&opts.Activation.Login, "worker-login", "", "operator console login (WORKER_LOGIN; default admin with a generated password)")
|
||||
fs.StringVar(&opts.Activation.Password, "worker-password", "", "operator console password (WORKER_PASSWORD)")
|
||||
fs.StringVar(&workerPasswordFile, "worker-password-file", "", "file containing the operator console password")
|
||||
fs.StringVar(&opts.Activation.EnvFile, "env-file", "", "local systemd-safe env file uploaded for activation (overrides the individual knobs)")
|
||||
fs.BoolVar(&opts.Activation.NoStart, "no-start", false, "install the binary, env, data dir, and service definition without starting the worker")
|
||||
fs.Usage = func() {
|
||||
fmt.Fprintln(fs.Output(), "Usage: rsmon-worker source-install --host HOST --user USER [SSH options] [source options]")
|
||||
fmt.Fprintln(fs.Output(), "Usage: rsmon-worker source-install --host HOST --user USER [--token TOKEN|--token-file FILE] [SSH options] [source options]")
|
||||
fs.PrintDefaults()
|
||||
}
|
||||
if err := fs.Parse(args); err != nil {
|
||||
@@ -215,6 +234,15 @@ func sourceInstallCommand(args []string) int {
|
||||
fmt.Fprintf(os.Stderr, "source-install failed: %v\n", err)
|
||||
return 1
|
||||
}
|
||||
if opts.Activation.Token, err = secretValue(opts.Activation.Token, tokenFile); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "source-install failed: %v\n", err)
|
||||
return 1
|
||||
}
|
||||
if opts.Activation.Password, err = secretValue(opts.Activation.Password, workerPasswordFile); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "source-install failed: %v\n", err)
|
||||
return 1
|
||||
}
|
||||
opts.Activation.Activate = !noActivate
|
||||
res, err := installer.SourceInstall(opts)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "source-install failed: %v\n", err)
|
||||
@@ -227,6 +255,22 @@ func sourceInstallCommand(args []string) int {
|
||||
fmt.Printf(" commit: %s\n", res.ResolvedCommit)
|
||||
fmt.Printf(" record file: %s\n", res.RecordFile)
|
||||
fmt.Printf(" staged build: %s\n", res.StageBinary)
|
||||
fmt.Println(" status: not installed as a service (activation is the next milestone)")
|
||||
if res.Activation != nil {
|
||||
fmt.Printf(" installed to: %s\n", res.Activation.Binary)
|
||||
fmt.Printf(" env file: %s (mode 0600)\n", res.Activation.EnvFile)
|
||||
fmt.Printf(" data dir: %s\n", res.Activation.DataDir)
|
||||
if res.Activation.UnitFile != "" {
|
||||
fmt.Printf(" service: %s (%s)\n", res.Activation.UnitFile, res.Activation.UnitName)
|
||||
} else {
|
||||
fmt.Println(" service: none (no supported init system detected; supervisor-managed)")
|
||||
}
|
||||
if res.Activation.Started {
|
||||
fmt.Printf(" status: running (supervisor=%s, /healthz verified)\n", res.Activation.Supervisor)
|
||||
} else {
|
||||
fmt.Printf(" status: installed (not started, --no-start)\n")
|
||||
}
|
||||
} else {
|
||||
fmt.Println(" status: not installed as a service (--no-activate)")
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
Ссылка в новой задаче
Block a user