package catalogue import ( "strings" "testing" ) // A module with nothing that publishes binds what it binds, and the mesh may not move it. // // This is the case that made novox/hq 04-ISSUES/028's fix wrong on its first pass: a port was // assigned to every module that declared one, so a service listening directly had the rule set // opened on a number nothing was listening on, and its real port shut. The firewall reported // success and blocked the service, which is the exact failure the mechanism exists to prevent. func TestAPortNothingPublishesIsNotTheMeshsToMove(t *testing.T) { m := Manifest{Module: "talker", Listens: []Listening{{Port: 9101, From: FromMesh}}} at, mayAssign := m.MachineSide(9101) if mayAssign { t.Fatal("the mesh took a port it cannot move: nothing translates it, so assigning one " + "opens the wrong number and leaves the service unreachable") } if at != 9101 { t.Fatalf("a port nothing publishes reaches the machine where it binds, not at %d", at) } } // A container publishing in short form is exactly the case the mesh may choose. func TestAContainerPublishingShortIsTheMeshsToChoose(t *testing.T) { m := Manifest{Module: "store", Resources: []map[string]any{ {"type": "container", "id": "server", "ports": []any{"5432"}}, }} if _, mayAssign := m.MachineSide(5432); !mayAssign { t.Fatal("a container's mapping is what translates a port, so this one is the mesh's to " + "choose; refusing it puts every module back on a number it guessed") } } // A manifest that wrote its own mapping already chose, and the machine side is the outer one. func TestAMappingTheManifestWroteIsNotReassigned(t *testing.T) { m := Manifest{Module: "mail", Resources: []map[string]any{ {"type": "container", "id": "front", "ports": []any{"7080:80"}}, }} for _, named := range []int{7080, 80} { at, mayAssign := m.MachineSide(named) if mayAssign { t.Fatalf("%d was reassigned though the manifest published it explicitly, which "+ "would open a rule on a port the container does not publish", named) } if at != 7080 { t.Fatalf("naming %d gave %d; the machine side of 7080:80 is 7080", named, at) } } } // A port some other container publishes is not this port. func TestAPortNotInTheMappingIsNotFound(t *testing.T) { m := Manifest{Module: "mail", Resources: []map[string]any{ {"type": "container", "id": "front", "ports": []any{"25", "7080:80"}}, }} if at, mayAssign := m.MachineSide(993); mayAssign || at != 993 { t.Fatalf("993 is published by nothing here, so it binds where it binds: got %d, %v", at, mayAssign) } } // The module that provides the artifact store may not be delivered through it. // // Building publishes to the store and the builder will not start without one, so a module that // provides the store and also builds something asks the mesh to put an artifact into the thing // that artifact is needed to create. On a mesh new enough to have no registry, that is a build // that never returns (novox/hq 04-ISSUES/029). func TestTheArtifactStoreCannotBeDeliveredThroughItself(t *testing.T) { _, err := ParseManifest([]byte(`{"module":"registry","version":"1",` + `"provides":[{"name":"artifact-store","scope":"mesh"}],` + `"build":{"artifacts":[{"name":"registry","kind":"upstream","from":"registry:2"}]},` + `"resources":[{"id":"store","type":"container","name":"mesh-registry",` + `"artifact":"registry","ports":["5000:5000"]}]}`)) if err == nil { t.Fatal("a registry module that builds its own image was accepted; the build has " + "nowhere to publish until the module it belongs to is already running") } if !strings.Contains(err.Error(), "artifact-store") { t.Fatalf("refused without naming the provision the cycle turns on: %v", err) } } // Naming the image directly is the way out, and must stay accepted. func TestAnArtifactStoreThatNamesItsImageIsAccepted(t *testing.T) { _, err := ParseManifest([]byte(`{"module":"registry","version":"1",` + `"provides":[{"name":"artifact-store","scope":"mesh"}],` + `"resources":[{"id":"store","type":"container","name":"mesh-registry",` + `"image":"registry@sha256:` + `266f282fabd7cd3df053ee7c658c77b42380d1a2f0d8e5a1c0d7a6d5b5c4a3b2",` + `"ports":["5000:5000"]}]}`)) if err != nil { t.Fatalf("the one way an artifact store can be delivered was refused: %v", err) } } // And an ordinary module still builds whatever it likes. func TestAModuleThatDoesNotProvideTheStoreStillBuilds(t *testing.T) { _, err := ParseManifest([]byte(`{"module":"forge","version":"1",` + `"build":{"artifacts":[{"name":"forge","kind":"upstream","from":"gitea/gitea:1.22"}]},` + `"resources":[{"id":"run","type":"container","name":"forge","artifact":"forge"}]}`)) if err != nil { t.Fatalf("an ordinary module was caught by a rule about the artifact store: %v", err) } }