Make how the target version is started a seam
Both phases that bring the target version up against a not-yet-migrated store -- the recovery cycle, and the ordinary boot validate does after it -- constructed a child process from a path on this host directly. That is the one thing about them packaging changes: a container runs an image against the data volume instead. Everything either of them is started *for* is identical afterwards. So starting it is now a Launcher, returning a Supervised the callers stop and read output from. BinaryLauncher is today's behaviour and the default when Options.Launcher is nil, so every existing caller is unchanged -- no test needed editing, which is the evidence for that rather than a claim about it. Deliberately narrower than the interface sketched in issue #3. Stage and cutover also differ by packaging, but designing their interfaces now would be designing against a guess: there is no second implementation yet to shape them, and the shape a container needs is what PR 3 and PR 4 find out. This seam is different because it already had two callers doing the same thing for the same reason, so extracting it describes the code rather than predicting it. outputSuffix now takes Supervised. It only ever needed Output(), and the diagnosis it exists to preserve -- the server's own words about a bind conflict or a rejected config value, which a bare timeout loses -- matters whatever started the process.
This commit is contained in:
@@ -29,6 +29,11 @@ type Options struct {
|
||||
StartupTimeout time.Duration
|
||||
StopGrace time.Duration
|
||||
HTTPClient *http.Client
|
||||
|
||||
// Launcher starts the target version. Nil means BinaryPath as a child
|
||||
// process, which is what every caller wants today; a container
|
||||
// deployment supplies its own (issue #3).
|
||||
Launcher Launcher
|
||||
}
|
||||
|
||||
// GenerateRecoveryPassword returns a fresh random one-time password for
|
||||
@@ -64,12 +69,16 @@ func Run(ctx context.Context, store *checkpoint.Store, rs *checkpoint.RunState,
|
||||
return checkpoint.StepOutcome{}, err
|
||||
}
|
||||
|
||||
proc := &Process{}
|
||||
if startErr := proc.Start(ctx, ProcessOptions{
|
||||
BinaryPath: opts.BinaryPath, ConfigPath: opts.ConfigPath,
|
||||
launcher := opts.Launcher
|
||||
if launcher == nil {
|
||||
launcher = BinaryLauncher{BinaryPath: opts.BinaryPath}
|
||||
}
|
||||
proc, startErr := launcher.Launch(ctx, LaunchOptions{
|
||||
ConfigPath: opts.ConfigPath,
|
||||
RecoveryMode: true, AdminUser: opts.AdminUser, AdminPassword: password,
|
||||
ExtraEnv: opts.ExtraEnv,
|
||||
}); startErr != nil {
|
||||
})
|
||||
if startErr != nil {
|
||||
return checkpoint.StepOutcome{}, startErr
|
||||
}
|
||||
|
||||
@@ -115,7 +124,7 @@ func Run(ctx context.Context, store *checkpoint.Store, rs *checkpoint.RunState,
|
||||
// words are usually the whole diagnosis - a bind conflict, a rejected
|
||||
// config value - and without them the caller is left guessing at a
|
||||
// timeout.
|
||||
func outputSuffix(proc *Process) string {
|
||||
func outputSuffix(proc Supervised) string {
|
||||
out := strings.TrimSpace(proc.Output())
|
||||
if out == "" {
|
||||
return " (the process produced no output)"
|
||||
|
||||
Reference in New Issue
Block a user