Rename mesh-control -> mesh-controller, substrate -> foundation
One name per thing, per the HQ glossary: the module/container/image/binary/repo becomes mesh-controller, the seat the-controller, and the store+broker pair the foundation (embedded base bundles, default template and example lock renamed with their go:embed directives). No behaviour change — a pure vocabulary rename. Claude-Session: https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
This commit is contained in:
@@ -0,0 +1,208 @@
|
||||
// Command mesh-controller is the control plane: everything that needs to know about more than one
|
||||
// node (novox/hq ADR 0006).
|
||||
//
|
||||
// It runs as one process holding several contexts, each owning its own store. Today it holds one,
|
||||
// `inventory`, and does one thing with it — brings its schema up to date, which is step 3 of the
|
||||
// bootstrap in novox/hq 07-the-foundation and the step the first node cannot get past without.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/novox/mesh-controller/internal/identity"
|
||||
"github.com/novox/mesh-controller/internal/inventory"
|
||||
"github.com/novox/mesh-controller/internal/licences"
|
||||
"github.com/novox/mesh-controller/internal/link"
|
||||
"github.com/novox/mesh-controller/internal/store"
|
||||
)
|
||||
|
||||
// version is stamped at link time. Unset in a development build, and it says so rather than
|
||||
// claiming a number.
|
||||
var version = "development build"
|
||||
|
||||
// held is a context this process was granted, and the schema it carries.
|
||||
//
|
||||
// novox/hq ADR 0006 names seven. One is built. The list is short because the others do not exist
|
||||
// yet, not because they are optional.
|
||||
var held = []struct {
|
||||
name string
|
||||
migrations func() ([]store.Migration, error)
|
||||
}{
|
||||
{inventory.Name, inventory.Migrations},
|
||||
{identity.Name, identity.Migrations},
|
||||
{licences.Name, licences.Migrations},
|
||||
}
|
||||
|
||||
func main() {
|
||||
if err := run(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "mesh-controller: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func run() error {
|
||||
args := os.Args[1:]
|
||||
if len(args) == 0 {
|
||||
usage()
|
||||
return fmt.Errorf("no command given")
|
||||
}
|
||||
|
||||
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||
defer stop()
|
||||
|
||||
switch args[0] {
|
||||
case "build":
|
||||
return buildCommand(ctx, args[1:])
|
||||
case "builder":
|
||||
return builderCommand(ctx, args[1:])
|
||||
case "board":
|
||||
return boardCommand(ctx, args[1:])
|
||||
case "api":
|
||||
return apiCommand(ctx, args[1:])
|
||||
case "licence":
|
||||
return licenceCommand(ctx, args[1:])
|
||||
case "rotate":
|
||||
return rotateCommand(ctx, args[1:])
|
||||
case "builds":
|
||||
return buildsCommand(ctx, args[1:])
|
||||
case "pin":
|
||||
return pinCommand(ctx, args[1:], true)
|
||||
case "unpin":
|
||||
return pinCommand(ctx, args[1:], false)
|
||||
case "migrate":
|
||||
return migrate(ctx)
|
||||
case "node":
|
||||
return nodeCommand(ctx, args[1:])
|
||||
case "token":
|
||||
return tokenCommand(ctx, args[1:])
|
||||
case "identity":
|
||||
return identityCommand(ctx, args[1:])
|
||||
case "broker":
|
||||
return brokerCommand(args[1:])
|
||||
case "serve":
|
||||
return serve(ctx)
|
||||
case "upgrade":
|
||||
return upgradeCommand(ctx, args[1:])
|
||||
case "declare":
|
||||
return declare(ctx, args[1:])
|
||||
case "overlay":
|
||||
return overlayCommand(ctx, args[1:])
|
||||
case "module":
|
||||
return moduleCommand(ctx, args[1:])
|
||||
case "assign", "unassign":
|
||||
return assignCommand(ctx, args[0], args[1:])
|
||||
case "settings":
|
||||
return settingsCommand(ctx, args[1:])
|
||||
case "secret":
|
||||
return secretCommand(ctx, args[1:])
|
||||
case "plan":
|
||||
return planCommand(ctx, args[1:])
|
||||
case "push":
|
||||
return pushCommand(ctx, args[1:])
|
||||
case "status":
|
||||
return statusCommand(ctx, args[1:])
|
||||
case "version":
|
||||
fmt.Println(version)
|
||||
return nil
|
||||
case "help", "-h", "--help":
|
||||
usage()
|
||||
return nil
|
||||
default:
|
||||
usage()
|
||||
return fmt.Errorf("%q is not a command", args[0])
|
||||
}
|
||||
}
|
||||
|
||||
func usage() {
|
||||
fmt.Fprint(os.Stderr, `mesh-controller — the control plane
|
||||
|
||||
migrate bring each context's schema up to date
|
||||
node add <name> create a node record
|
||||
node list the nodes this mesh knows about
|
||||
node show <name> what one machine reported it can do, and why
|
||||
node public-domain <name> the domain it composes its routed names under
|
||||
node public-domain <name> <d> ...set it to d
|
||||
node public-domain <name> --clear ...it faces the outside no longer
|
||||
token issue --node <name> a one-time right to join, for an existing record
|
||||
token issue --new <name> create the record and issue for it
|
||||
identity show this control plane's signing key
|
||||
broker show where the broker is, and what to expect there
|
||||
serve consume what nodes say, and answer
|
||||
declare <node> <file> send a node a signed declaration
|
||||
overlay place <node> [flags] say where a node is and how it is reached
|
||||
overlay show the private network, as the mesh computes it
|
||||
module add <file> register a module from its manifest
|
||||
module list what modules this mesh knows about
|
||||
module moved <name> <commit> the source has a newer commit than the mesh built
|
||||
module forget <name> remove one, unless a node runs it or the mesh holds things for it
|
||||
module forget <name> --and-what-it-holds ...and discard its settings, secrets and ports too
|
||||
module issue <name> --node <m> a broker account for a module, scoped to its emits and consumes
|
||||
upgrade <name> what happens when this module's current version moves
|
||||
upgrade <name> roll-out [--together] ...send it to the machines running it
|
||||
upgrade <name> record ...record that they are behind, and send nothing
|
||||
status [--json] what is wrong, what is quiet, and what is out of date
|
||||
board [--listen ADDR] the same three questions, as a page that holds nothing
|
||||
api --issuer URL [--listen A] assign and unassign over http, for a surface that is not here
|
||||
assign <node> <module> put a module on a node
|
||||
unassign <node> <module> take it off
|
||||
settings set <module> <file> what a module's config should say, for the whole mesh
|
||||
settings set <module> <file> --node <n> ...or for one machine
|
||||
settings clear <module> [--node <n>] take a layer away
|
||||
secret accept <node> <module> <name> carry a value the mesh did not make and cannot invent
|
||||
secret accept ... --from <file> ...read it from a file rather than being asked
|
||||
build <repository> [--ref R] have a build machine build it, and record what came out
|
||||
build --behind build every module the mesh holds older than its source
|
||||
builds [<module>] what has been built lately, and what came of it
|
||||
builder issue <name> a broker account for a build machine, scoped to build work
|
||||
licence add|list|use|key model access, under the name a person calls it
|
||||
licence manager <name> <node> the node that holds a refreshable licence's refresh token
|
||||
licence refresh <name> mint a new access token and seal it to every holder
|
||||
rotate <provision> [--consumer <n>] a new credential for every holder, both ends at once
|
||||
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>] [--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
|
||||
`+store.Variable("<context>")+` — or `+store.FileVariable("<context>")+`, naming a file that holds
|
||||
the same thing and keeps the password out of the environment. This process holds:
|
||||
|
||||
`)
|
||||
for _, c := range held {
|
||||
fmt.Fprintf(os.Stderr, " %-12s database %-12s from %s\n",
|
||||
c.name, store.Database(c.name), store.Variable(c.name))
|
||||
}
|
||||
fmt.Fprintln(os.Stderr)
|
||||
}
|
||||
|
||||
// parseAround reads flags that may sit before, after or between positional arguments.
|
||||
//
|
||||
// The standard library stops at the first non-flag argument, so `module add thing.json --source x`
|
||||
// parses no flags at all and silently ignores every one of them. The host learned this the same
|
||||
// way and says so in its own parser: a flag that is quietly dropped is the fault this project
|
||||
// keeps naming, and it looks exactly like success.
|
||||
func parseAround(set *flag.FlagSet, args []string) ([]string, error) {
|
||||
var positionals []string
|
||||
rest := args
|
||||
for {
|
||||
if err := set.Parse(rest); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rest = set.Args()
|
||||
if len(rest) == 0 {
|
||||
return positionals, nil
|
||||
}
|
||||
positionals = append(positionals, rest[0])
|
||||
rest = rest[1:]
|
||||
}
|
||||
}
|
||||
|
||||
func (b builds) Built(ctx context.Context, result link.BuildResult) error {
|
||||
return b.inv.RecordBuild(ctx, buildFrom(result))
|
||||
}
|
||||
Reference in New Issue
Block a user