package bootstrap import ( "context" "fmt" "os" "path/filepath" "strings" "time" ) // controlPlane is the running control plane, asked things. // // **Through `docker exec`, not over a network.** The control plane listens on nothing — `serve` is // a broker consumer, and every administrative verb is a subcommand of the same binary that opens // the stores directly (mesh-control's own usage). So the way to tell a mesh anything, from the // machine the mesh is on, is to run its binary inside its own container. That is also what the lab // does, and having the installer and the lab drive the mesh identically is the point: the lab is // meant to exercise the installer, not a second procedure that resembles it. // // It carries which container, because the whole pivot turns on there being two of them: the // substrate's `temp-mesh-control` for steps 6 to 9, and the module's `mesh-control` afterwards. type controlPlane struct { container string run Runner timeout time.Duration } // within is the same control plane, asked with a different patience. // // A copy rather than a field somebody sets, so a slow command cannot leave every command after it // slow: the caller that needs the long wait says so on the call. func (c controlPlane) within(timeout time.Duration) controlPlane { c.timeout = timeout return c } // tell runs a mesh-control subcommand and gives back what it said. // // The failure carries the command AND the output. A mesh-control refusal is a paragraph explaining // what is wrong — "nothing provides route, wanted by registry" — and an installer that reported // only "exit status 1" would throw away the one thing a person needs. func (c controlPlane) tell(ctx context.Context, args ...string) (string, error) { asking, cancel := context.WithTimeout(ctx, c.timeout) defer cancel() out, err := c.run(asking, "docker", append( []string{"exec", c.container, controlPlaneBinary}, args...)...) if err != nil { return out, fmt.Errorf("`%s %s` was refused: %w\n%s", c.container, strings.Join(args, " "), err, indent(strings.TrimSpace(out))) } return out, nil } // carry puts a file inside the control plane's container. // // **Because every command that takes a file reads it from its own filesystem.** `module add // ` and `secret accept --from ` open a path, and the process doing the opening is // inside the container. The installer is not. So the file is copied in first, exactly as the lab // does it. // // The image is `FROM scratch` and has no shell, so nothing inside can move a file, change its mode // or clean up after itself. What is copied in stays until the container is replaced — which, for // the temporary control plane, is a container that gets removed at step 10 and takes its contents // with it. func (c controlPlane) carry(ctx context.Context, local, remote string) error { asking, cancel := context.WithTimeout(ctx, c.timeout) defer cancel() if _, err := c.run(asking, "docker", "cp", local, c.container+":"+remote); err != nil { return fmt.Errorf("cannot put %s into %s at %s: %w", local, c.container, remote, err) } return nil } // carrying writes bytes to a temporary file on the machine and copies them into the container. // // **0644, and that is not carelessness — 0600 would break it.** `docker cp` keeps the ownership and // mode a file had outside, the control plane's image runs as 65534, and the process that reads // these files is that one. The lab paid for this exactly once: a 0600 root-owned key copied in // landed unreadable, `secret accept` failed with `permission denied`, and what depended on it // crash-looped on material it never received. There is no shell in the image to chown it with. // // What goes through here is a module manifest and a store connection string. The connection is the // same value the produced bundle already holds in the clear — a substrate names its own bootstrap // credentials, and at genesis there is nowhere else for them to be — so this widens nothing. The // file on the machine is removed at once, and the copy inside the container goes when the // container does, which for the temporary control plane is step 10. func (c controlPlane) carrying(ctx context.Context, name string, content []byte, remote string) error { local := filepath.Join(os.TempDir(), name) if err := os.WriteFile(local, content, 0o644); err != nil { return fmt.Errorf("nowhere to stage %s before copying it into %s: %w", name, c.container, err) } defer os.Remove(local) return c.carry(ctx, local, remote) } func indent(s string) string { if s == "" { return "" } return " " + strings.ReplaceAll(s, "\n", "\n ") } // mentions reports whether one of a listing's lines starts with this exact word. // // Line-and-word rather than a substring search, because these listings are columns and a // substring match would find `registry` inside `registry-mirror` and report a module installed // that is not. Every one of mesh-control's `list` verbs prints the name first on the line. func mentions(listing, name string) bool { for _, line := range strings.Split(listing, "\n") { first, _, _ := strings.Cut(strings.TrimSpace(line), " ") if first == name { return true } } return false }