update: roll back what the restart actually deploys
On the deployment deploy/README.md documents, source_dir and install_dir are
the same tree and the restart command rebuilds the image from it. The
Dockerfile builds from cmd/ and internal/ and .dockerignore keeps the host
binaries out, so restoring the snapshotted binaries restored bytes nothing
reads. A bad commit therefore cost two health timeouts and two image builds
and ended in ErrRollbackFailed with an instruction to copy files back by hand,
which would not have helped either.
A deployment that rebuilds from source now has to say how the source is put
back. source_rollback "git" records the commit before the update and checks it
back out before the rollback restart. It refuses a dirty tree, because the
recorded commit does not describe one and a forced checkout would delete his
work. A build-from-source config that says nothing is refused by Validate, at
startup, rather than at the one rollback that mattered.
Also in this change, all from the same review:
- MethodPing, the one method a locked daemon answers. Preflight passed on an
unlocked daemon and the post-restart Presence read failed on a locked one,
so a good update read as SHE IS PROBABLY DOWN once the env key is gone.
- A dial failure is reported apart from a read failure. The documented
socket is under /var/lib/docker, which a non-root operator cannot
traverse, and "she is not answering" was the wrong diagnosis.
- Verify refuses to run as root over a tree owned by someone else. It runs
make build and make test in place, and root-owned artifacts break his next
ordinary make.
- A rollback no longer reverts config_files. That undid every config edit
since the last apply, phraser.model_path among them.
- The verify-failure path no longer reports rolled_back for a compile error.
- waitHealthy caps each attempt at the remaining budget, so a 90s timeout
cannot run to 99s.
- tail cuts on a rune boundary. Russian test names showed the seam.
- The claim that mavend does not import internal/update is replaced with
what is enforced: mavend constructs no Updater and nothing can call Apply.
- snapshot_dir inside source_dir is refused. It landed in the build context.
Found in review of #69.
This commit is contained in:
@@ -486,6 +486,11 @@ type Server struct {
|
||||
ListSpeakersFn ListSpeakersFunc
|
||||
ForgetSpeakerFn ForgetSpeakerFunc
|
||||
|
||||
// LockedFn — reports whether the daemon is in locked (pre-unlock) mode.
|
||||
// Read by MethodPing only. Nil ⇒ not locked, which is what an embedded or
|
||||
// test Server without the unlock dance is.
|
||||
LockedFn func() bool
|
||||
|
||||
// UnlockFn — unwraps the store encryption key from the wrapped blob using
|
||||
// the passkey PRF secret, opens the encrypted store, and wires
|
||||
// the rest of the daemon (voice, loop, delivery). Set by the daemon when
|
||||
@@ -933,6 +938,17 @@ func (s *Server) dispatch(ctx context.Context, req Request) (json.RawMessage, er
|
||||
// directly by the daemon (StepUp / WrapKeyFn / UnlockFn), not store
|
||||
// state, so they can never be table entries keyed on a CoreAPI method.
|
||||
switch req.Method {
|
||||
case MethodPing:
|
||||
// Deliberately reaches nothing: no store, no CoreAPI, no daemon
|
||||
// component. That is what makes it answerable in locked mode, and it is
|
||||
// the whole point — an update that restarts her into locked mode has to
|
||||
// be able to tell that apart from a daemon that did not come up.
|
||||
locked := false
|
||||
if s.LockedFn != nil {
|
||||
locked = s.LockedFn()
|
||||
}
|
||||
return marshalResult(PingResp{Alive: true, Locked: locked}), nil
|
||||
|
||||
case MethodAssertStepUp:
|
||||
if s.StepUp != nil {
|
||||
return marshalResult(nil), s.StepUp(ctx)
|
||||
|
||||
Reference in New Issue
Block a user