preflight asked the GitHub release API which version it was upgrading to, so a host with no internet failed there even with the target binary already on disk - and `run` never passed its own --target-binary down to preflight, so supplying one did not help. Read the version from the binary instead when one is given, and fail if it is not the version the run targets. Found by the first preflight against the production clone, which is deliberately cut off from the network.
69 lines
2.9 KiB
Go
69 lines
2.9 KiB
Go
// SPDX-FileCopyrightText: 2026 LINUXexpert-org
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
|
|
"github.com/LINUXexpert-org/stalwart-migrator/internal/checkpoint"
|
|
"github.com/LINUXexpert-org/stalwart-migrator/internal/preflight"
|
|
)
|
|
|
|
func runPreflight(args []string) error {
|
|
fs := flag.NewFlagSet("preflight", flag.ExitOnError)
|
|
binaryPath := fs.String("binary", "/usr/local/bin/stalwart", "path to the installed stalwart binary")
|
|
configPath := fs.String("config", "/etc/stalwart/config.toml", "path to stalwart's config file")
|
|
dataDir := fs.String("data-dir", "/var/lib/stalwart", "stalwart data directory")
|
|
containerName := fs.String("container", "stalwart", "docker container name, if applicable")
|
|
adminURL := fs.String("admin-url", "", "base URL for a JMAP reachability check (optional but recommended)")
|
|
adminUser := fs.String("admin-user", "", "admin username for the reachability check")
|
|
adminPassword := fs.String("admin-password", os.Getenv("STALWART_MIGRATE_ADMIN_PASSWORD"),
|
|
"admin password for the reachability check (or set STALWART_MIGRATE_ADMIN_PASSWORD)")
|
|
targetVersion := fs.String("target", "latest", `target Stalwart version, or "latest"`)
|
|
targetBinary := fs.String("target-binary", "", "read the target version from this already-downloaded binary instead of the release API (for a host with no route to the internet)")
|
|
minFree := fs.Float64("min-free-multiple", 2.0, "required free disk space as a multiple of the data directory size")
|
|
stateDir := fs.String("state-dir", checkpoint.DefaultBaseDir, "directory to store run checkpoints in")
|
|
pythonPath := fs.String("python", "python3", "path to python3, needed by migrate_v016.py")
|
|
stalwartCLI := fs.String("stalwart-cli", "stalwart-cli", "path to stalwart-cli (v1.0.2 or later; a separate download from the server)")
|
|
if err := fs.Parse(args); err != nil {
|
|
return err
|
|
}
|
|
|
|
store := checkpoint.NewStore(*stateDir)
|
|
rs, err := store.Create("", *targetVersion)
|
|
if err != nil {
|
|
return fmt.Errorf("create run: %w", err)
|
|
}
|
|
|
|
checker := preflight.New(preflight.Options{
|
|
BinaryPath: *binaryPath,
|
|
ConfigPath: *configPath,
|
|
DataDir: *dataDir,
|
|
ContainerName: *containerName,
|
|
AdminURL: *adminURL,
|
|
AdminUser: *adminUser,
|
|
AdminPassword: *adminPassword,
|
|
TargetVersion: *targetVersion,
|
|
TargetBinaryPath: *targetBinary,
|
|
MinFreeMultiple: *minFree,
|
|
CLIPath: *stalwartCLI,
|
|
PythonPath: *pythonPath,
|
|
})
|
|
|
|
report, err := checker.Run(context.Background(), store, rs)
|
|
fmt.Print(report.String())
|
|
fmt.Printf("\nrun id: %s\n", rs.RunID)
|
|
if err != nil {
|
|
return fmt.Errorf("preflight run %s failed to complete: %w", rs.RunID, err)
|
|
}
|
|
if report.Blocking() {
|
|
return fmt.Errorf("preflight found blocking issues for run %s - see FAIL lines above", rs.RunID)
|
|
}
|
|
fmt.Println("preflight passed - safe to proceed with `stalwart-migrate run`")
|
|
return nil
|
|
}
|