A module that puts nothing on a machine cannot be assigned to one

The image every module in the scripted toolchain is compiled on top of is
registered as a module so the mesh can build, version and depend on it. It is
not one: nothing about it belongs on a machine. Assigning it succeeded, the
machine was sent a declaration containing nothing of it, and everything
reported success — the operator had said run this here and the mesh had agreed
to something it cannot do.

A module whose resources are worked out per node is asked about separately, so
it stays assignable, which is the point of it.
This commit is contained in:
2026-09-14 11:08:32 +02:00
parent 91db25389e
commit 95a9da8bdb
+43
View File
@@ -431,6 +431,9 @@ func (i *Inventory) Assign(ctx context.Context, nodeName, module string) error {
if err != nil { if err != nil {
return err return err
} }
if err := i.runsSomewhere(ctx, module); err != nil {
return err
}
_, err = i.store.Pool().Exec(ctx, _, err = i.store.Pool().Exec(ctx,
`insert into assignment (node, module) values ($1, $2) on conflict do nothing`, `insert into assignment (node, module) values ($1, $2) on conflict do nothing`,
node.ID, module) node.ID, module)
@@ -871,3 +874,43 @@ func (i *Inventory) Running(ctx context.Context, module string) ([]string, error
} }
return out, rows.Err() return out, rows.Err()
} }
// runsSomewhere refuses to put a module on a machine when it would put nothing there.
//
// **Not every thing the mesh builds is a thing a machine runs.** The image every module in the
// scripted toolchain is compiled on top of is built, versioned and depended upon like a module, and
// is registered as one so the mesh can track all three. It is not one: it declares no resources,
// because nothing about it belongs on a machine — its whole purpose is to be the starting point for
// other modules' builds.
//
// Without this, assigning it succeeds, the machine is sent a declaration containing nothing of it,
// and everything reports success. The operator has said "run this here" and the mesh has agreed to
// something it cannot do. Said at the assignment, which is where somebody is standing.
//
// A module whose resources are worked out per node declares none here and is still assignable —
// that is the point of it — so it is asked about separately rather than caught by the same test.
func (i *Inventory) runsSomewhere(ctx context.Context, module string) error {
var raw []byte
err := i.store.Pool().QueryRow(ctx,
`select manifest from module where name = $1`, module).Scan(&raw)
if errors.Is(err, pgx.ErrNoRows) {
// Left to the insert, which already says this and says it the same way everywhere.
return nil
}
if err != nil {
return err
}
var m catalogue.Manifest
if err := json.Unmarshal(raw, &m); err != nil {
// Unreadable is not the same as empty. A manifest the mesh cannot parse is a separate
// fault and refusing the assignment here would report it as the wrong one.
return nil
}
if m.Computed != "" || len(m.Resources) > 0 {
return nil
}
return fmt.Errorf(
"%s puts nothing on a machine, so there is nothing to assign. It is something this mesh "+
"builds and other modules are built on top of, not something a machine runs — "+
"`module list` shows what it produces", module)
}