mesh-bootstrap: the installer, and the two things its first real run found #8
@@ -75,6 +75,14 @@ host:
|
||||
# hold, and the tag is the only name that survives the transfer. Saving by id produces an archive
|
||||
# with no tags at all, which the installer refuses; caught here instead, in front of the person who
|
||||
# can fix it.
|
||||
#
|
||||
# BOOTSTRAP_OUT is where the binary is written, and it exists because something other than a person
|
||||
# now builds this: the lab rebuilds every artifact it runs from source before a raise, into paths it
|
||||
# chose (mesh-lab's src/rebuild.ts, and novox/hq 04-ISSUES/005 for why it does that at all). A
|
||||
# caller that could not say where the output goes would have to copy it afterwards, which is one
|
||||
# more step to forget.
|
||||
BOOTSTRAP_OUT ?= mesh-bootstrap
|
||||
|
||||
bootstrap:
|
||||
@test -n "$(IMAGE)" || { echo "IMAGE= is required; an installer carrying no control-plane image cannot raise a mesh"; exit 1; }
|
||||
@case "$(IMAGE)" in sha256:*) echo "IMAGE=$(IMAGE) is an image id. The installer identifies the carried image by its tag, because an id is the digest of a configuration that a runtime rewrites as it loads. Pass a name:tag"; exit 1;; esac
|
||||
@@ -82,11 +90,11 @@ bootstrap:
|
||||
@test -n "$$(docker image inspect --format '{{len .RepoTags}}' "$(IMAGE)" | grep -v '^0$$')" || { echo "$(IMAGE) has no repository tag, so the saved archive would carry no name the installer can ask a runtime about. Tag it first: docker tag $(IMAGE) mesh-control:<version>"; exit 1; }
|
||||
@cp internal/image/control-plane.tar internal/image/control-plane.tar.placeholder
|
||||
@docker save --output internal/image/control-plane.tar "$(IMAGE)"
|
||||
@CGO_ENABLED=0 go build -ldflags="-s -w -X main.version=$(VERSION)" -o mesh-bootstrap ./cmd/mesh-bootstrap; \
|
||||
@CGO_ENABLED=0 go build -ldflags="-s -w -X main.version=$(VERSION)" -o "$(BOOTSTRAP_OUT)" ./cmd/mesh-bootstrap; \
|
||||
status=$$?; \
|
||||
mv internal/image/control-plane.tar.placeholder internal/image/control-plane.tar; \
|
||||
exit $$status
|
||||
@echo "built mesh-bootstrap carrying $(IMAGE)"
|
||||
@echo "built $(BOOTSTRAP_OUT) carrying $(IMAGE)"
|
||||
|
||||
clean:
|
||||
rm -f mesh-host mesh-bootstrap
|
||||
|
||||
@@ -104,6 +104,15 @@ func Preflight(ctx context.Context, o Options, d Deps, say func(string)) ([]byte
|
||||
// reason. Everything else is somebody else's image at somebody else's registry, and a machine
|
||||
// that cannot reach it fails inside a pull, which reports a network error where a person
|
||||
// reads a missing image.
|
||||
//
|
||||
// "The mesh's own image is skipped" used to mean "skipped if the template happened to name it
|
||||
// in a way that needs no registry", and that is not the same sentence. A template names the
|
||||
// control plane by SOMETHING — the reference is a slot, and step 3 replaces whatever is in it
|
||||
// with the id of the image this installer carries. Whatever the slot held is therefore never
|
||||
// pulled, never fetched, and never reached; requiring it to be reachable refuses a correct
|
||||
// install because of a string that is about to be thrown away. Found on the first real run: the
|
||||
// lab's template still carried `192.0.2.250:5000/mesh-control@…`, the address of a registry that
|
||||
// no longer exists, and preflight timed out dialling it.
|
||||
for _, host := range registriesIn(parsed) {
|
||||
dialing, cancel := context.WithTimeout(ctx, o.Timeout)
|
||||
err := d.Dial(dialing, host)
|
||||
@@ -171,12 +180,17 @@ func containerRuntimeDetector(run Runner) profile.Detector {
|
||||
|
||||
// registriesIn is every host the bundle's images would be fetched from, without duplicates and in
|
||||
// the order they appear.
|
||||
//
|
||||
// The control plane's own resource is excluded by identity rather than by the shape of what it
|
||||
// names. Its image reference is a slot the installer overwrites with the id of the image it
|
||||
// carries, so no registry ever serves it — and a template that filled that slot with a registry
|
||||
// this machine cannot reach is not a machine with a network problem.
|
||||
func registriesIn(d *declaration.Declaration) []string {
|
||||
var hosts []string
|
||||
seen := map[string]bool{}
|
||||
for _, r := range d.Resources {
|
||||
container, ok := r.(*declaration.Container)
|
||||
if !ok {
|
||||
if !ok || container.Identity() == ControlPlaneID {
|
||||
continue
|
||||
}
|
||||
host, served := registryOf(container.Image)
|
||||
|
||||
@@ -100,6 +100,31 @@ func TestOnlyTheRegistriesTheBundleNamesAreAskedAbout(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// And the control plane's slot is excluded whatever is in it.
|
||||
//
|
||||
// Found on the first real run of this installer. A template names the control plane by SOMETHING
|
||||
// and step 3 replaces it with the id of the carried image, so whatever was there is never pulled —
|
||||
// but preflight was reading that slot like any other and dialling it. The lab's template still
|
||||
// carried the address of a registry the lab no longer raises, so a correct install timed out in
|
||||
// preflight against a machine with a perfectly good network.
|
||||
func TestTheControlPlanesOwnRegistryIsNeverAskedAbout(t *testing.T) {
|
||||
parsed, err := declaration.ParseFileTrusted([]byte(`{"declaration":1,"resources":[
|
||||
{"id":"store","type":"container","name":"mesh-store","image":"postgres@sha256:` +
|
||||
strings.Repeat("7", 64) + `"},
|
||||
{"id":"control-plane","type":"container","name":"mesh-control","image":"192.0.2.250:5000/mesh-control@sha256:` +
|
||||
strings.Repeat("8", 64) + `"}
|
||||
]}`))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
got := registriesIn(parsed)
|
||||
if len(got) != 1 || got[0] != DefaultRegistry {
|
||||
t.Fatalf("asked about %v; the control plane's own reference is about to be replaced and "+
|
||||
"must not be reached for", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWhereAnImageWouldBeFetchedFrom(t *testing.T) {
|
||||
// The container runtime's own rule: the part before the first slash is a registry host if it
|
||||
// has a dot, a port, or is localhost. Getting this wrong means dialling a hostname that is
|
||||
|
||||
Reference in New Issue
Block a user