R4e (mesh-controller): deliver from the builder's keys, ask build-agent only for its own modules, follow the forge's protection for checks, announce the controller's states #441
@@ -73,6 +73,8 @@ func run() error {
|
||||
}
|
||||
|
||||
switch args[0] {
|
||||
case "register":
|
||||
return registerCommand(ctx, args[1:])
|
||||
case "build", "rebuild":
|
||||
// **The hand build goes with the asks** (novox/hq ADR 0501 §5): refused by name, naming the replacement.
|
||||
return removedByName(args[0])
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
sdkdelivery "git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"github.com/novox/mesh-controller/internal/builderkeys"
|
||||
)
|
||||
|
||||
// The terminal's exception becomes a registration, not a build (novox/hq ADR 0501 §4; issue 1851, A11b).
|
||||
//
|
||||
// `register <module> <owner>/<repository> <path>`, at the controller's terminal alone: the operator names the module,
|
||||
// its repository and its path, and the controller registers it from the builder's done key of the fingerprint the
|
||||
// indexer's trunk value of that module names — the registration a build asked at the terminal used to make for a
|
||||
// repository the catalogue does not build (ADR 0432 §3, ADR 0420 §7). No verb reaches it (terminalOnlyCommands), and it
|
||||
// asks no build: a key not done is waited for, and said.
|
||||
func registerCommand(ctx context.Context, args []string) error {
|
||||
if len(args) != 3 {
|
||||
return errors.New("register <module> <owner>/<repository> <path> (the path \".\" for a module at the repository's root)")
|
||||
}
|
||||
if !startedAtTheTerminal() {
|
||||
return terminalRefusal("register is run at the controller's terminal only, never through a verb: it registers " +
|
||||
"a module from a repository the catalogue does not build, and whoever may call a verb includes agents " +
|
||||
"(novox/hq ADR 0266, ADR 0501 §4). Nothing was done")
|
||||
}
|
||||
module, repository, path := args[0], strings.Trim(args[1], "/"), strings.Trim(args[2], "/")
|
||||
if path == sdkdelivery.RootPath {
|
||||
path = ""
|
||||
}
|
||||
open, err := openStores(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer open.Close()
|
||||
keys, closeKeys, err := openBuilderKeys()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closeKeys()
|
||||
trunk, err := keys.ModuleAt(ctx, module, "")
|
||||
if errors.Is(err, builderkeys.ErrNoValue) {
|
||||
return fmt.Errorf("the indexer holds no trunk value of %s: a module is registered from the builder's key of a "+
|
||||
"merged trunk, and %s has none yet. Nothing was registered", module, module)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := sameModuleSource(module, repository, path, trunk); err != nil {
|
||||
return err
|
||||
}
|
||||
manifest, kept, err := takeInKey(ctx, open.inventory, keys, keyTake{Module: module, Trunk: trunk,
|
||||
Ask: registrationAsk{Terminal: true}})
|
||||
if errors.Is(err, errKeyWaits) {
|
||||
return fmt.Errorf("%s is not registered yet, and nothing was asked: %v. Run register again once its key is done",
|
||||
module, err)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("%s %s registered from the builder's key %s (%s at %.8s), as asked at the controller's terminal; "+
|
||||
"assigned nowhere\n", manifest.Module, manifest.Version, short(kept.SourceFingerprint), repository, kept.Commit)
|
||||
return nil
|
||||
}
|
||||
|
||||
// sameModuleSource refuses a registration whose repository or path is not the one the indexer's trunk value names: the
|
||||
// operator names exactly what is registered (ADR 0501 §4).
|
||||
func sameModuleSource(module, repository, path string, trunk sdkdelivery.Module) error {
|
||||
if !strings.EqualFold(trunk.Repository, repository) || keyPath(trunk) != path {
|
||||
return fmt.Errorf("the indexer holds %s in %s at %q, not in %s at %q: the registration names exactly what it "+
|
||||
"registers. Nothing was registered", module, trunk.Repository, orRoot(keyPath(trunk)), repository, orRoot(path))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
sdkdelivery "git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
// A11b (ADR 0501 §4): the terminal's registration names exactly the module's repository and path the indexer holds,
|
||||
// and no verb reaches it.
|
||||
func TestTheTerminalsRegistrationNamesItsSourceExactlyAndNoVerbReachesIt(t *testing.T) {
|
||||
trunk := sdkdelivery.Module{Repository: "someone/app", Path: sdkdelivery.RootPath, Commit: "c0ffee"}
|
||||
if err := sameModuleSource("app", "someone/app", "", trunk); err != nil {
|
||||
t.Fatalf("the source the indexer holds is refused: %v", err)
|
||||
}
|
||||
if err := sameModuleSource("app", "someone/other", "", trunk); err == nil || !strings.Contains(err.Error(), "someone/app") {
|
||||
t.Fatalf("another repository is taken: %v", err)
|
||||
}
|
||||
if err := sameModuleSource("app", "someone/app", "modules/app", trunk); err == nil {
|
||||
t.Fatal("another path is taken")
|
||||
}
|
||||
// The generic verb only reads; and register is the terminal's only, whatever reads.
|
||||
if _, err := argvFor("command", map[string]any{"command": "register app someone/app ."}); err == nil {
|
||||
t.Fatal("a verb reaches the terminal's registration")
|
||||
}
|
||||
var refused *heldAtTheTerminal
|
||||
if err := terminalOnly([]string{"register", "app", "someone/app", "."}); !errors.As(err, &refused) {
|
||||
t.Fatalf("register is not the terminal's only: %v", err)
|
||||
}
|
||||
t.Setenv(verbVar, "command")
|
||||
var held *heldAtTheTerminal
|
||||
if err := registerCommand(context.Background(), []string{"app", "someone/app", "."}); !errors.As(err, &held) {
|
||||
t.Fatalf("register through a verb: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -1784,6 +1784,9 @@ var terminalOnlyCommands = map[string]string{
|
||||
"broker": "the bus's accounts",
|
||||
"api": "the controller's API keys",
|
||||
"licence": "the licences' secrets",
|
||||
// The terminal's exception of ADR 0501 §4: a module from a repository the catalogue does not build, registered
|
||||
// from its done key.
|
||||
"register": "a module's registration from a repository the catalogue does not build",
|
||||
}
|
||||
|
||||
// givenAtTheDesk is exactly the line the `give` and `secret-ask` verbs compose, and nothing beside it: `secret ask
|
||||
|
||||
Reference in New Issue
Block a user