// Command mesh-control 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-substrate and the step the first node cannot get past without. package main import ( "context" "flag" "fmt" "os" "os/signal" "syscall" "github.com/novox/mesh-control/internal/identity" "github.com/novox/mesh-control/internal/inventory" "github.com/novox/mesh-control/internal/licences" "github.com/novox/mesh-control/internal/link" "github.com/novox/mesh-control/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-control: %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 "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-control — the control plane migrate bring each context's schema up to date node add create a node record node list the nodes this mesh knows about node show what one machine reported it can do, and why token issue --node a one-time right to join, for an existing record token issue --new 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 send a node a signed declaration overlay place [flags] say where a node is and how it is reached overlay show the private network, as the mesh computes it module add register a module from its manifest module list what modules this mesh knows about module moved the source has a newer commit than the mesh built module forget remove one, unless a node is running it module issue --node a broker account for a module, scoped to its emits and consumes 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 put a module on a node unassign take it off settings set what a module's config should say, for the whole mesh settings set --node ...or for one machine settings clear [--node ] take a layer away secret accept carry a value the mesh did not make and cannot invent secret accept ... --from ...read it from a file rather than being asked build [--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 [] what has been built lately, and what came of it builder issue 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 rotate [--consumer ] a new credential for every holder, both ends at once pin which node this one gets a provision from unpin put that question back plan [--files|--json] what that node would run, and why push [] [--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("")+`. 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)) }