inventory: forgetting a module says what goes with it, and refuses until told
`module forget` cascaded. The settings, the module's own secrets and the ports the mesh chose all name the module by a foreign key that cascades, so removing the row took all three and reported "forgotten" — an action succeeding into a state its own verify would reject (novox/hq 04-ISSUES/017). A sealed secret is not recoverable afterwards, because the mesh discarded the plaintext when it made it. It now reads what it would destroy, names each thing one at a time, and refuses. `--and-what-it-holds` is how somebody says they mean it, and the removal then reports what went — this being the only record that any of it ever existed. Reported as "operator settings do not persist, because re-registering a module cascade-deletes them". Half of that is wrong, and the test now says so out loud: the upsert is on the name, so `module add` at a new version leaves the settings, the secrets and the ports exactly where they were. The command that destroyed them was `forget`, and a wrong belief about which command destroys data is expensive in both directions — it sends people looking for a fault that is not there, and leaves the real one unexamined. Checked by internal/inventory/forget_test.go, which writes all three, re-registers the module at a new version, reads them back, and only then tries to forget it. Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
This commit is contained in:
@@ -188,13 +188,37 @@ func moduleCommand(ctx context.Context, args []string) error {
|
||||
return nil
|
||||
|
||||
case "forget":
|
||||
if len(args) != 2 {
|
||||
return errors.New("module forget <name>")
|
||||
}
|
||||
if err := inv.ForgetModule(ctx, args[1]); err != nil {
|
||||
// **What goes with it is said before it goes** (novox/hq 04-ISSUES/017). The settings, the
|
||||
// module's own secrets and the ports the mesh chose all cascade off the module row, so
|
||||
// `forget` used to destroy them and report "forgotten" — an action succeeding into a state
|
||||
// its own verify would reject, and a sealed secret is not recoverable afterwards.
|
||||
set := flag.NewFlagSet("module forget", flag.ContinueOnError)
|
||||
andHeld := set.Bool("and-what-it-holds", false,
|
||||
"discard its settings, its own secrets and its ports along with it")
|
||||
positionals, err := parseAround(set, args[1:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("%s forgotten\n", args[1])
|
||||
if len(positionals) != 1 {
|
||||
return errors.New("module forget <name> [--and-what-it-holds]")
|
||||
}
|
||||
if !*andHeld {
|
||||
if err := inv.ForgetModule(ctx, positionals[0]); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("%s forgotten\n", positionals[0])
|
||||
return nil
|
||||
}
|
||||
held, err := inv.DiscardModule(ctx, positionals[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("%s forgotten\n", positionals[0])
|
||||
for _, line := range held.Lines() {
|
||||
// Said after the fact as well as before it: this is the only record that these
|
||||
// existed, and the next person to ask why the module came back empty reads it here.
|
||||
fmt.Printf(" discarded%s\n", strings.TrimPrefix(line, " "))
|
||||
}
|
||||
return nil
|
||||
|
||||
case "issue":
|
||||
|
||||
Reference in New Issue
Block a user