100 lines
3.0 KiB
Go
100 lines
3.0 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/novox/mesh-controller/internal/inventory"
|
|
)
|
|
|
|
// movePlacementAnswer is what the move of placement says it did, or would do (issue 736).
|
|
type movePlacementAnswer struct {
|
|
// DryRun is true when nothing was moved: asked without --confirm.
|
|
DryRun bool `json:"dry-run"`
|
|
// Moved is each layer that held a placement key among its settings, and which keys.
|
|
Moved []inventory.MovedLayer `json:"moved"`
|
|
}
|
|
|
|
// movePlacementCommand is `move-placement`: the move of placement out of the settings layers (novox/hq ADR 0348
|
|
// clause 11, issue 736), one of the moves of data a release runs once after its delivery (design 57).
|
|
func movePlacementCommand(ctx context.Context, args []string) error {
|
|
set := flag.NewFlagSet("move-placement", flag.ContinueOnError)
|
|
asJSON := set.Bool("json", false, "answer as JSON")
|
|
confirm := set.Bool("confirm", false, "move the keys, rather than only say which layers hold them")
|
|
f := addHandActFlags(set)
|
|
positionals, err := parseAround(set, args)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(positionals) != 0 {
|
|
return errors.New("move-placement [--json] [--confirm --why <text>]")
|
|
}
|
|
if !*confirm && f.given() {
|
|
return errors.New("move-placement takes --why only with --confirm: without it nothing is moved. Nothing was done")
|
|
}
|
|
if *confirm {
|
|
// A move a person asks for says why, before anything is asked of the store.
|
|
if err := f.require("move-placement"); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
open, err := openStores(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer open.Close()
|
|
if *confirm {
|
|
f.record(ctx, "move-placement", nil)
|
|
}
|
|
answer, err := runMovePlacement(ctx, open.inventory, *confirm)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if *asJSON {
|
|
encoder := json.NewEncoder(os.Stdout)
|
|
encoder.SetIndent("", " ")
|
|
return encoder.Encode(answer)
|
|
}
|
|
fmt.Print(describeMovePlacement(answer))
|
|
return nil
|
|
}
|
|
|
|
// runMovePlacement is the move against a store: a dry run says what it would move.
|
|
func runMovePlacement(ctx context.Context, inv *inventory.Inventory, apply bool) (movePlacementAnswer, error) {
|
|
moved, err := inv.MovePlacement(ctx, apply)
|
|
if err != nil {
|
|
return movePlacementAnswer{}, err
|
|
}
|
|
return movePlacementAnswer{DryRun: !apply, Moved: moved}, nil
|
|
}
|
|
|
|
// describeMovePlacement says the answer as a person reads it.
|
|
func describeMovePlacement(a movePlacementAnswer) string {
|
|
if len(a.Moved) == 0 {
|
|
return "no settings layer holds a placement key: nothing to move\n"
|
|
}
|
|
var b strings.Builder
|
|
verb := "moved"
|
|
if a.DryRun {
|
|
verb = "would move"
|
|
}
|
|
for _, m := range a.Moved {
|
|
where := m.Node
|
|
if where == "" {
|
|
where = "the whole mesh"
|
|
}
|
|
fmt.Fprintf(&b, "%s on %s: %s %s\n", m.Module, where, verb, strings.Join(m.Keys, ", "))
|
|
}
|
|
if a.DryRun {
|
|
fmt.Fprintf(&b, "%d layer(s); nothing was moved — with --confirm --why <text> they are\n", len(a.Moved))
|
|
} else {
|
|
fmt.Fprintf(&b, "%d layer(s) moved beside their assignments, each kept whole in its history first\n", len(a.Moved))
|
|
}
|
|
return b.String()
|
|
}
|