The packages port given at genesis is a node setting, not manifest text (hq issue 085) #21
@@ -98,8 +98,8 @@ const usage = `mesh-bootstrap — make a bare machine into a mesh
|
|||||||
everything this mesh will ever run
|
everything this mesh will ever run
|
||||||
--source-path the module's directory inside that repository, if not its root
|
--source-path the module's directory inside that repository, if not its root
|
||||||
--catalog a checkout of the mesh's catalogue, holding the registry's, the
|
--catalog a checkout of the mesh's catalogue, holding the registry's, the
|
||||||
control plane's and the builder's manifests. Without it this stops
|
control plane's, the builder's and — when a packages port is given
|
||||||
after step 6
|
— the forge's manifests. Without it this stops after step 6
|
||||||
--node the name this machine is known by (default: its hostname)
|
--node the name this machine is known by (default: its hostname)
|
||||||
--registry where this mesh keeps its own images (default ` + defaultRegistry + `)
|
--registry where this mesh keeps its own images (default ` + defaultRegistry + `)
|
||||||
every node pulls the control plane from this, so on a mesh of more
|
every node pulls the control plane from this, so on a mesh of more
|
||||||
@@ -279,7 +279,7 @@ func newFlagSet(opts *bootstrap.Options, jsonOut *bool) *flag.FlagSet {
|
|||||||
set.StringVar(&opts.Out, "out", opts.Out, "where the produced bundle is written")
|
set.StringVar(&opts.Out, "out", opts.Out, "where the produced bundle is written")
|
||||||
set.StringVar(&opts.State, "state", opts.State, "where this node records what it has applied")
|
set.StringVar(&opts.State, "state", opts.State, "where this node records what it has applied")
|
||||||
set.StringVar(&opts.Catalogue, "catalog", opts.Catalogue,
|
set.StringVar(&opts.Catalogue, "catalog", opts.Catalogue,
|
||||||
"a checkout of the mesh's catalogue, for the registry's and the builder's manifests and what phase two installs; without it this stops after the foundation")
|
"a checkout of the mesh's catalogue, for the registry's, the builder's and the forge's manifests and what phase two installs; without it this stops after the foundation")
|
||||||
set.StringVar(&opts.Source.Repository, "source", opts.Source.Repository,
|
set.StringVar(&opts.Source.Repository, "source", opts.Source.Repository,
|
||||||
"the repository the control plane is built from, on a mesh that already exists")
|
"the repository the control plane is built from, on a mesh that already exists")
|
||||||
set.StringVar(&opts.Source.Ref, "source-ref", opts.Source.Ref,
|
set.StringVar(&opts.Source.Ref, "source-ref", opts.Source.Ref,
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
package bootstrap
|
package bootstrap
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -55,9 +53,6 @@ func InstallBuilder(ctx context.Context, o Options, d Deps, control controlPlane
|
|||||||
"This is the manifest that makes the builder an ordinary module. Without it the mesh "+
|
"This is the manifest that makes the builder an ordinary module. Without it the mesh "+
|
||||||
"has the image and no way to run it, so nothing can be built here", err)
|
"has the image and no way to run it, so nothing can be built here", err)
|
||||||
}
|
}
|
||||||
if manifest, err = followPackagesPort(manifest, o.Ports.orDefaults().Packages); err != nil {
|
|
||||||
return out, err
|
|
||||||
}
|
|
||||||
pinned, places, err := pinPlaceholder(manifest, published.Reference, BuilderModule)
|
pinned, places, err := pinPlaceholder(manifest, published.Reference, BuilderModule)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return out, err
|
return out, err
|
||||||
@@ -89,25 +84,3 @@ func InstallBuilder(ctx context.Context, o Options, d Deps, control controlPlane
|
|||||||
out.Installed.Pushed, err = pushNode(ctx, o, control, say)
|
out.Installed.Pushed, err = pushNode(ctx, o, control, say)
|
||||||
return out, err
|
return out, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// packagesPortInBinding is the package registry's port as the builder's manifest names it, in the
|
|
||||||
// binding file it carries — JSON inside a JSON string, so its quotes are escaped.
|
|
||||||
const packagesPortInBinding = `\"port\": 3000`
|
|
||||||
|
|
||||||
// followPackagesPort points the builder's package binding at the port the node gave the package
|
|
||||||
// registry (novox/hq ADR 0100). Genesis raises the registry by hand before gitea is a module, so no
|
|
||||||
// binding the controller resolves can say where it is; the builder carries the address in its own
|
|
||||||
// manifest, and a port given at genesis must reach it there or the base build dials a port nothing
|
|
||||||
// answers on. At the default it is left byte for byte as the catalogue has it.
|
|
||||||
func followPackagesPort(manifest []byte, port int) ([]byte, error) {
|
|
||||||
if port == defaultGiteaPort {
|
|
||||||
return manifest, nil
|
|
||||||
}
|
|
||||||
if n := bytes.Count(manifest, []byte(packagesPortInBinding)); n != 1 {
|
|
||||||
return nil, fmt.Errorf("the builder's manifest names the package registry's port %d time(s) where "+
|
|
||||||
"this installer looks for it once (%s), so the port given with --packages-port cannot reach "+
|
|
||||||
"it; nothing was changed", n, packagesPortInBinding)
|
|
||||||
}
|
|
||||||
return bytes.Replace(manifest, []byte(packagesPortInBinding),
|
|
||||||
[]byte(`\"port\": `+strconv.Itoa(port)), 1), nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,16 +1,21 @@
|
|||||||
package bootstrap
|
package bootstrap
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"context"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Defends novox/hq ADR 0100: a port given for the package registry at genesis reaches the one
|
// Defends novox/hq 04-ISSUES/085: the port given for the package registry at genesis is a setting
|
||||||
// thing that dials it by a fixed number, the builder's package binding.
|
// of a module and not text in a manifest, so registering that manifest again does not put the
|
||||||
|
// catalogue's number back.
|
||||||
|
|
||||||
// The builder's package binding exactly as the catalogue's manifest carries it.
|
// theBuildersBinding is the resource the builder carries in place of a binding nothing can resolve
|
||||||
const builderManifest = `{
|
// yet, exactly as the catalogue's manifest has it — settable, with the port as its only default.
|
||||||
|
const theBuildersBinding = `{
|
||||||
"module": "builder",
|
"module": "builder",
|
||||||
"resources": [
|
"resources": [
|
||||||
{
|
{
|
||||||
@@ -18,52 +23,147 @@ const builderManifest = `{
|
|||||||
"type": "file",
|
"type": "file",
|
||||||
"path": "/var/lib/mesh/builder/package-registry.json",
|
"path": "/var/lib/mesh/builder/package-registry.json",
|
||||||
"mode": "0600",
|
"mode": "0600",
|
||||||
|
"merge": "json",
|
||||||
|
"protected": ["provision", "from", "at", "as"],
|
||||||
"content": "{\"provision\": \"package-registry\", \"from\": \"gitea\", \"at\": \"127.0.0.1\", \"as\": \"mesh-builder\", \"serves\": {\"scheme\": \"http\", \"port\": 3000, \"npm-path\": \"/api/packages/novox/npm/\"}}\n"
|
"content": "{\"provision\": \"package-registry\", \"from\": \"gitea\", \"at\": \"127.0.0.1\", \"as\": \"mesh-builder\", \"serves\": {\"scheme\": \"http\", \"port\": 3000, \"npm-path\": \"/api/packages/novox/npm/\"}}\n"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}`
|
}`
|
||||||
|
|
||||||
func bindingPort(t *testing.T, manifest []byte) float64 {
|
func recording(t *testing.T) (*controlRecorder, controlPlane) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
var m struct {
|
t.Setenv("TMPDIR", t.TempDir())
|
||||||
Resources []struct {
|
c := &controlRecorder{settings: map[string]string{}}
|
||||||
Content string `json:"content"`
|
return c, controlPlane{container: "temp-mesh-controller", run: c.run, timeout: time.Second}
|
||||||
} `json:"resources"`
|
|
||||||
}
|
|
||||||
if err := json.Unmarshal(manifest, &m); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
var binding struct {
|
|
||||||
Serves struct {
|
|
||||||
Port float64 `json:"port"`
|
|
||||||
} `json:"serves"`
|
|
||||||
}
|
|
||||||
if err := json.Unmarshal([]byte(m.Resources[0].Content), &binding); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
return binding.Serves.Port
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTheBuilderFollowsThePackageRegistrysGivenPort(t *testing.T) {
|
func TestTheBuildersPackageRegistryPortIsASettingAndNotTheManifest(t *testing.T) {
|
||||||
got, err := followPackagesPort([]byte(builderManifest), 3100)
|
c, control := recording(t)
|
||||||
if err != nil {
|
o := Options{Node: "anchor", Ports: FoundationPorts{Packages: 3100}, Wait: time.Second}
|
||||||
|
if _, err := installModule(context.Background(), o, control, BuilderModule,
|
||||||
|
[]byte(theBuildersBinding), quietly); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if p := bindingPort(t, got); p != 3100 {
|
if got := c.settings["builder-settings.json"]; got != `{"serves":{"port":3100}}` {
|
||||||
t.Errorf("the builder's binding dials %v, not the port given", p)
|
t.Errorf("the builder was told %q about the package registry's port", got)
|
||||||
|
}
|
||||||
|
// The manifest reaches the mesh as the catalogue wrote it. A port rewritten into it here is a
|
||||||
|
// port the next registration from the catalogue silently takes back.
|
||||||
|
if got := c.settings["builder-module.json"]; got != theBuildersBinding {
|
||||||
|
t.Errorf("the installer changed the builder's manifest:\n%s", got)
|
||||||
|
}
|
||||||
|
set, push := c.index("settings set builder"), c.index("push anchor")
|
||||||
|
if set < 0 || push < 0 || set > push {
|
||||||
|
t.Fatalf("the port was not set before the push that raises the builder: %v", c.told)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTheBuilderOnTheDefaultPortIsUnchanged(t *testing.T) {
|
func TestTheBuilderOnTheDefaultPackagesPortIsToldNothing(t *testing.T) {
|
||||||
got, err := followPackagesPort([]byte(builderManifest), 3000)
|
c, control := recording(t)
|
||||||
if err != nil || string(got) != builderManifest {
|
o := Options{Node: "anchor", Wait: time.Second}
|
||||||
t.Errorf("the default port changed the manifest: %v", err)
|
if _, err := installModule(context.Background(), o, control, BuilderModule,
|
||||||
|
[]byte(theBuildersBinding), quietly); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if c.index("settings") >= 0 {
|
||||||
|
t.Errorf("a genesis on the catalogue's packages port set a setting: %v", c.told)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestABuilderManifestThatNoLongerNamesThePortIsRefused(t *testing.T) {
|
// aCatalogueWith writes a checkout holding one module's manifest, which is all the installer reads
|
||||||
moved := strings.Replace(builderManifest, `\"port\": 3000`, `\"port\": 3001`, 1)
|
// a catalogue for.
|
||||||
if _, err := followPackagesPort([]byte(moved), 3100); err == nil || !strings.Contains(err.Error(), "--packages-port") {
|
func aCatalogueWith(t *testing.T, module, manifest string) string {
|
||||||
t.Errorf("a manifest the port cannot reach was accepted: %v", err)
|
t.Helper()
|
||||||
|
dir := t.TempDir()
|
||||||
|
at := filepath.Join(dir, catalogueDir, module)
|
||||||
|
if err := os.MkdirAll(at, 0o700); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(filepath.Join(at, "module.json"), []byte(manifest), 0o600); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return dir
|
||||||
|
}
|
||||||
|
|
||||||
|
const theForgesManifest = `{"module": "gitea", "version": "1"}`
|
||||||
|
|
||||||
|
func TestThePackagesPortIsTheForgeModulesSettingOnThisNode(t *testing.T) {
|
||||||
|
c, control := recording(t)
|
||||||
|
o := Options{Node: "anchor", Catalogue: aCatalogueWith(t, ForgeModule, theForgesManifest),
|
||||||
|
Ports: FoundationPorts{Packages: 3100}, Wait: time.Second}
|
||||||
|
if err := recordTheForgesPort(context.Background(), o, control, quietly); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if got := c.settings["gitea-settings.json"]; got != `{"ports":{"3000":3100}}` {
|
||||||
|
t.Errorf("the forge module was told %q about its port", got)
|
||||||
|
}
|
||||||
|
// Registered so there is something to hold the setting against, and never assigned: the forge
|
||||||
|
// module cannot run until the base it stands on has been built.
|
||||||
|
add, set := c.index("module add"), c.index("settings set gitea")
|
||||||
|
if add < 0 || set < 0 || add > set {
|
||||||
|
t.Fatalf("the forge's setting was recorded against a module the mesh does not know: %v", c.told)
|
||||||
|
}
|
||||||
|
if c.index("assign") >= 0 {
|
||||||
|
t.Errorf("genesis assigned the forge module: %v", c.told)
|
||||||
|
}
|
||||||
|
if !strings.Contains(c.told[set], "--node anchor") {
|
||||||
|
t.Errorf("the forge's port was set for the whole mesh, not this machine: %s", c.told[set])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// knowingControl is a control plane that already has the forge in its catalogue, as a mesh that
|
||||||
|
// has been running has.
|
||||||
|
type knowingControl struct{ controlRecorder }
|
||||||
|
|
||||||
|
func (k *knowingControl) run(ctx context.Context, name string, args ...string) (string, error) {
|
||||||
|
out, err := k.controlRecorder.run(ctx, name, args...)
|
||||||
|
if name == "docker" && args[0] == "exec" && len(args) > 4 &&
|
||||||
|
args[3] == "module" && args[4] == "list" {
|
||||||
|
return ForgeModule + " 1 assigned to anchor\n", nil
|
||||||
|
}
|
||||||
|
return out, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestARerunDoesNotReplaceAForgeTheMeshAlreadyHas(t *testing.T) {
|
||||||
|
t.Setenv("TMPDIR", t.TempDir())
|
||||||
|
k := &knowingControl{controlRecorder{settings: map[string]string{}}}
|
||||||
|
control := controlPlane{container: "mesh-controller", run: k.run, timeout: time.Second}
|
||||||
|
// An older checkout than the mesh is running, which is what makes an overwrite here damage.
|
||||||
|
o := Options{Node: "anchor", Catalogue: aCatalogueWith(t, ForgeModule, `{"module": "gitea"}`),
|
||||||
|
Ports: FoundationPorts{Packages: 3100}, Wait: time.Second}
|
||||||
|
if err := recordTheForgesPort(context.Background(), o, control, quietly); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// Registering is an overwrite, and the manifest of a forge that is built and assigned is not
|
||||||
|
// this installer's to replace with whatever checkout it was pointed at.
|
||||||
|
if k.index("module add") >= 0 {
|
||||||
|
t.Errorf("a re-run replaced the registered forge's manifest: %v", k.told)
|
||||||
|
}
|
||||||
|
// The port is still recorded: a settings row needs the module row, and it is already there.
|
||||||
|
if got := k.settings["gitea-settings.json"]; got != `{"ports":{"3000":3100}}` {
|
||||||
|
t.Errorf("the forge's port was not recorded on a mesh that knows it: %q", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAGenesisOnTheCataloguesPackagesPortRegistersNoForge(t *testing.T) {
|
||||||
|
c, control := recording(t)
|
||||||
|
o := Options{Node: "anchor", Catalogue: aCatalogueWith(t, ForgeModule, theForgesManifest),
|
||||||
|
Wait: time.Second}
|
||||||
|
if err := recordTheForgesPort(context.Background(), o, control, quietly); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(c.told) != 0 {
|
||||||
|
t.Errorf("a genesis on the defaults told the mesh something new: %v", c.told)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAForgeManifestTheCatalogueDoesNotHaveIsNamed(t *testing.T) {
|
||||||
|
_, control := recording(t)
|
||||||
|
o := Options{Node: "anchor", Catalogue: t.TempDir(),
|
||||||
|
Ports: FoundationPorts{Packages: 3100}, Wait: time.Second}
|
||||||
|
err := recordTheForgesPort(context.Background(), o, control, quietly)
|
||||||
|
if err == nil || !strings.Contains(err.Error(), ForgeModule) ||
|
||||||
|
!strings.Contains(err.Error(), "package registry") {
|
||||||
|
t.Errorf("a missing forge manifest was not explained: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,21 @@ const (
|
|||||||
defaultGiteaPort = 3000
|
defaultGiteaPort = 3000
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ForgeModule is the module that owns the package registry — the one the bootstrap forge above is
|
||||||
|
// a stand-in for, and which takes it over once the base exists.
|
||||||
|
//
|
||||||
|
// Named here because the port given for the package registry is that module's setting on this node
|
||||||
|
// and not this installer's private number (novox/hq 04-ISSUES/085). What follows that setting is
|
||||||
|
// what the mesh derives from a module's ports: the forge's container mapping, its rule in the
|
||||||
|
// filter, its opening on an adopted node, and what it says it serves — so a consumer the mesh binds
|
||||||
|
// is told where the machine actually put the registry.
|
||||||
|
//
|
||||||
|
// What does NOT follow it, and is not this change's to fix: the address of itself the forge's
|
||||||
|
// runtime is given, which the catalogue writes as a literal (novox/hq 04-ISSUES/088), and the port
|
||||||
|
// in its route contribution. Both are already wrong for any machine port the mesh assigned, with a
|
||||||
|
// given port or without one.
|
||||||
|
const ForgeModule = "gitea"
|
||||||
|
|
||||||
// RaisePackageRegistry puts a working npm registry in front of the base build. It is idempotent:
|
// RaisePackageRegistry puts a working npm registry in front of the base build. It is idempotent:
|
||||||
// every step tolerates having been done, because genesis is safe to run again.
|
// every step tolerates having been done, because genesis is safe to run again.
|
||||||
func RaisePackageRegistry(ctx context.Context, o Options, d Deps, control controlPlane,
|
func RaisePackageRegistry(ctx context.Context, o Options, d Deps, control controlPlane,
|
||||||
@@ -100,6 +115,11 @@ func RaisePackageRegistry(ctx context.Context, o Options, d Deps, control contro
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
say(" recording the port it was given as the forge module's own")
|
||||||
|
if err := recordTheForgesPort(ctx, o, control, say); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
say(" delivering the builder its registry credential")
|
say(" delivering the builder its registry credential")
|
||||||
if err := deliverBuilderNpm(ctx, o, control, builderPassword, say); err != nil {
|
if err := deliverBuilderNpm(ctx, o, control, builderPassword, say); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -108,6 +128,59 @@ func RaisePackageRegistry(ctx context.Context, o Options, d Deps, control contro
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// recordTheForgesPort makes the port given for the package registry a setting of the module that
|
||||||
|
// serves it, on this node (novox/hq 04-ISSUES/085, ADR 0100).
|
||||||
|
//
|
||||||
|
// **The forge is the one foundation port that was not a setting.** The store's, the bus's, the
|
||||||
|
// broker's management port and the registry's each become a `ports` setting of the module that
|
||||||
|
// binds them, set where that module is registered and assigned; the forge is raised by hand here,
|
||||||
|
// long before its module can be built, so there was no registration to hang it on and the number
|
||||||
|
// lived in rewritten manifest text instead. So the manifest is registered here — not assigned, and
|
||||||
|
// nothing of it runs — for the one thing registering buys: a settings row needs the module row to
|
||||||
|
// exist. Whenever somebody later assigns the forge on this node, it comes up on the port this
|
||||||
|
// machine was given rather than on the catalogue's.
|
||||||
|
//
|
||||||
|
// **Registered only when the mesh does not already know it, which is the opposite of every other
|
||||||
|
// `module add` here.** The rest of the installer registers every run on purpose: the manifest is
|
||||||
|
// what changes between runs, and skipping it would pin the mesh to a previous image. This one
|
||||||
|
// changes nothing about the forge and wants nothing of the checkout's manifest — and registering
|
||||||
|
// is an overwrite, so a re-run of genesis pointed at an older catalogue would replace the manifest
|
||||||
|
// of a forge that is built and assigned, with a push a few lines later. The port is the only thing
|
||||||
|
// this is here to record, and the setting does not need the manifest to be this checkout's.
|
||||||
|
//
|
||||||
|
// A node given the catalogue's own port records nothing and registers nothing, so a genesis on the
|
||||||
|
// defaults does exactly what it did before.
|
||||||
|
func recordTheForgesPort(ctx context.Context, o Options, control controlPlane,
|
||||||
|
say func(string)) error {
|
||||||
|
if o.Ports.orDefaults().Packages == DefaultPorts().Packages {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
known, err := control.tell(ctx, "module", "list")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if mentions(known, ForgeModule) {
|
||||||
|
say(" known " + ForgeModule + " — left as the mesh has it; only its port is set here")
|
||||||
|
} else {
|
||||||
|
manifest, err := readManifest(o.Catalogue, ForgeModule)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%w\n"+
|
||||||
|
"This is the module that owns the forge genesis just raised. Without it the port this "+
|
||||||
|
"machine was given for the package registry is nobody's setting, and taking the forge "+
|
||||||
|
"over would put it back on the catalogue's port", err)
|
||||||
|
}
|
||||||
|
remote := "/" + ForgeModule + "-module.json"
|
||||||
|
if err := control.carrying(ctx, ForgeModule+"-module.json", manifest, remote); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := control.tell(ctx, "module", "add", remote); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
say(" registered " + ForgeModule + " — registered, not assigned: nothing of it runs yet")
|
||||||
|
}
|
||||||
|
return setFoundationSettings(ctx, o, control, ForgeModule, say)
|
||||||
|
}
|
||||||
|
|
||||||
// seedGiteaDatabase creates gitea's role and database inside the foundation postgres, the same way
|
// seedGiteaDatabase creates gitea's role and database inside the foundation postgres, the same way
|
||||||
// the foundation creates its own — psql run through the store container (the map's Route B). The role
|
// the foundation creates its own — psql run through the store container (the map's Route B). The role
|
||||||
// is created before the database because the database is owned by it. Both are tolerant of already
|
// is created before the database because the database is owned by it. Both are tolerant of already
|
||||||
|
|||||||
@@ -105,12 +105,31 @@ func (p FoundationPorts) moduleSettings() map[string]map[string]int {
|
|||||||
add("lavinmq", d.AMQP, p.AMQP)
|
add("lavinmq", d.AMQP, p.AMQP)
|
||||||
add("lavinmq", d.Management, p.Management)
|
add("lavinmq", d.Management, p.Management)
|
||||||
add(RegistryModule, d.Registry, p.Registry)
|
add(RegistryModule, d.Registry, p.Registry)
|
||||||
|
add(ForgeModule, d.Packages, p.Packages)
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// PortsSetting is the controller's settings key for a module's given ports.
|
// PortsSetting is the controller's settings key for a module's given ports.
|
||||||
const PortsSetting = "ports"
|
const PortsSetting = "ports"
|
||||||
|
|
||||||
|
// ServesSetting is the settings key that carries what a provider serves, inside the one binding a
|
||||||
|
// module carries rather than resolves — the builder's package binding (novox/hq 04-ISSUES/085).
|
||||||
|
//
|
||||||
|
// **A port given at genesis has to be a setting wherever it is read, or something puts it back.**
|
||||||
|
// The builder reaches the package registry through a binding written in its own manifest, because
|
||||||
|
// at genesis no module yet provides `package-registry` and so there is nothing for the controller
|
||||||
|
// to resolve it from. That binding used to be rewritten, as text, when the installer registered the
|
||||||
|
// builder — which a later registration from the catalogue undid silently. Set as a node's setting
|
||||||
|
// instead, it is held by the controller rather than by the manifest, so re-registering the builder
|
||||||
|
// leaves it where it was.
|
||||||
|
//
|
||||||
|
// **A settings layer is the module's, not one resource's.** The controller lays it over every file
|
||||||
|
// of that module which merges as JSON, so `serves` lands in the package binding only because that
|
||||||
|
// binding is the builder's one mergeable file. A second mergeable file added to the builder would
|
||||||
|
// be given a `serves` key too, meaning nothing to whatever reads it. Worth knowing before adding
|
||||||
|
// one.
|
||||||
|
const ServesSetting = "serves"
|
||||||
|
|
||||||
// setFoundationSettings tells the controller the ports this node gave a foundation module — and,
|
// setFoundationSettings tells the controller the ports this node gave a foundation module — and,
|
||||||
// on an adopted node, that the registry is reached from anywhere, as a node pulls from it before it
|
// on an adopted node, that the registry is reached from anywhere, as a node pulls from it before it
|
||||||
// has a private-network address (novox/hq ADR 0100). Done after the module is registered and before
|
// has a private-network address (novox/hq ADR 0100). Done after the module is registered and before
|
||||||
@@ -124,6 +143,13 @@ func setFoundationSettings(ctx context.Context, o Options, control controlPlane,
|
|||||||
if o.Adopted && module == RegistryModule {
|
if o.Adopted && module == RegistryModule {
|
||||||
values["expose"] = map[string]string{strconv.Itoa(DefaultPorts().Registry): "anywhere"}
|
values["expose"] = map[string]string{strconv.Itoa(DefaultPorts().Registry): "anywhere"}
|
||||||
}
|
}
|
||||||
|
if packages := o.Ports.orDefaults().Packages; module == BuilderModule &&
|
||||||
|
packages != DefaultPorts().Packages {
|
||||||
|
// The one binding nothing resolves: the builder dials the forge by a number it carries.
|
||||||
|
// The rest of the binding — the provision, the forge it is from, the account it presents —
|
||||||
|
// is the manifest's and stays there.
|
||||||
|
values[ServesSetting] = map[string]int{"port": packages}
|
||||||
|
}
|
||||||
if len(values) == 0 {
|
if len(values) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -220,7 +220,7 @@ func TestAPredecessorsContainerUnderTheMeshsNameIsRefused(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// controlRecorder is a control plane that answers everything and writes down what it was told,
|
// controlRecorder is a control plane that answers everything and writes down what it was told,
|
||||||
// with the content of every settings file carried to it.
|
// with the content of every file carried to it, by the name it was carried under.
|
||||||
type controlRecorder struct {
|
type controlRecorder struct {
|
||||||
told []string
|
told []string
|
||||||
settings map[string]string
|
settings map[string]string
|
||||||
@@ -229,9 +229,7 @@ type controlRecorder struct {
|
|||||||
func (c *controlRecorder) run(_ context.Context, name string, args ...string) (string, error) {
|
func (c *controlRecorder) run(_ context.Context, name string, args ...string) (string, error) {
|
||||||
if name == "docker" && args[0] == "cp" {
|
if name == "docker" && args[0] == "cp" {
|
||||||
raw, _ := os.ReadFile(args[1])
|
raw, _ := os.ReadFile(args[1])
|
||||||
if strings.HasSuffix(args[2], "-settings.json") {
|
|
||||||
c.settings[filepath.Base(args[2])] = string(raw)
|
c.settings[filepath.Base(args[2])] = string(raw)
|
||||||
}
|
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
if name == "docker" && args[0] == "exec" {
|
if name == "docker" && args[0] == "exec" {
|
||||||
|
|||||||
Reference in New Issue
Block a user