push --behind, and a builder told where to publish
`status` says which machines are not doing what they were told, and nothing acted on it: a machine that refused or failed stayed wrong until somebody ran push again naming it. `push --behind` sends only to machines whose last report was not a clean apply. A command rather than a timer, deliberately: a scheduler is then a scheduler over this, where building the scheduler first would have meant two paths to one act with nothing to compare them against. Naming a machine and asking which machines need one are different requests, so `push <node> --behind` is refused rather than guessed. With nothing behind it says so, because "nothing needed one" and "this did not run" must never look the same. A machine failing the same way for six hours is pushed to anyway and said about — refusing would leave no way to retry after fixing the cause, and this is a command somebody ran. Proven in the lab: a machine is broken with a package that does not exist, `push --behind` names it and not the machine that is fine, the module is corrected, and the machine recovers without anybody naming it. And the builder can be told where to publish rather than configured. A builder that is a module requires an artifact store, and the mesh writes it the same binding any consumer of any provision gets. A binding with no address is refused rather than falling back to anything — that would publish to a store on the wrong machine and be found out much later. The variable remains for a builder run by a person, which is how it is still run while being developed.
This commit is contained in:
@@ -145,7 +145,7 @@ func usage() {
|
||||
pin <node> <provision> <from> which node this one gets a provision from
|
||||
unpin <node> <provision> put that question back
|
||||
plan <node> [--files|--json] what that node would run, and why
|
||||
push [<node>] send a node everything it should be
|
||||
push [<node>] [--behind] send a node everything it should be, or only those that need it
|
||||
version what this binary is
|
||||
|
||||
Each context reaches its own store through its own credential (novox/hq ADR 0008), named
|
||||
@@ -1406,8 +1406,28 @@ func planCommand(ctx context.Context, args []string) error {
|
||||
// half-configured for as long as that lasts — and the two are computed from the same picture of
|
||||
// the mesh, so sending them apart would let them disagree.
|
||||
func pushCommand(ctx context.Context, args []string) error {
|
||||
set := flag.NewFlagSet("push", flag.ContinueOnError)
|
||||
// Only the machines that need it.
|
||||
//
|
||||
// **A command rather than a timer, to begin with.** Something that re-pushes on a schedule is
|
||||
// a scheduler over this, and building the scheduler first would mean two paths to one act
|
||||
// with nothing to compare them against. A person can run this; so can cron; so can whatever
|
||||
// eventually watches.
|
||||
behind := set.Bool("behind", false,
|
||||
"only machines whose last declaration was refused or partly failed")
|
||||
positionals, err := parseAround(set, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
args = positionals
|
||||
if len(args) > 1 {
|
||||
return errors.New("push [<node>] — one node, or all of them")
|
||||
return errors.New("push [<node>] [--behind] — one node, or all of them")
|
||||
}
|
||||
if len(args) == 1 && *behind {
|
||||
// Naming a machine and asking for the ones that need it are two different requests, and
|
||||
// guessing which was meant would sometimes push to a machine somebody did not name.
|
||||
return errors.New("push <node> or push --behind, not both: one names a machine and the " +
|
||||
"other asks which machines need one")
|
||||
}
|
||||
inv, err := openInventory(ctx)
|
||||
if err != nil {
|
||||
@@ -1428,6 +1448,25 @@ func pushCommand(ctx context.Context, args []string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Which machines are not in the state they were sent, when that is what was asked for.
|
||||
var needsOne map[string]inventory.Doing
|
||||
if *behind {
|
||||
wrong, err := inv.NotDoingWhatTheyWereTold(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
needsOne = map[string]inventory.Doing{}
|
||||
for _, d := range wrong {
|
||||
needsOne[d.Node] = d
|
||||
}
|
||||
if len(needsOne) == 0 {
|
||||
// Said rather than doing nothing quietly. "Nothing needed one" and "this did not run"
|
||||
// must never look the same.
|
||||
fmt.Println("every machine is doing what it was told")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
gens, err := generators(ctx, inv)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -1453,6 +1492,20 @@ func pushCommand(ctx context.Context, args []string) error {
|
||||
if len(args) == 1 && n.Name != args[0] {
|
||||
continue
|
||||
}
|
||||
if *behind {
|
||||
doing, needs := needsOne[n.Name]
|
||||
if !needs {
|
||||
continue
|
||||
}
|
||||
// A machine that has been failing the same way for a long time is not going to stop
|
||||
// because it was asked again. Said, and pushed to anyway — refusing would leave no
|
||||
// way to retry after fixing the cause, and this is a command somebody ran.
|
||||
if since := time.Since(doing.At); since > 6*time.Hour {
|
||||
fmt.Printf("%s has been %s since %s; pushing again anyway, but the cause is "+
|
||||
"unlikely to be timing\n",
|
||||
n.Name, doing.Outcome, doing.At.Local().Format("2006-01-02 15:04"))
|
||||
}
|
||||
}
|
||||
plan, settings, err := planFor(ctx, inv, n.Name)
|
||||
if err != nil {
|
||||
refusals = append(refusals, fmt.Sprintf("%s:\n%v", n.Name, err))
|
||||
|
||||
Reference in New Issue
Block a user