package bootstrap import ( "context" "encoding/json" "strings" "testing" "time" ) // Defends novox/hq issue 616: genesis's base step builds the toolchain of every language a module // genesis installs is compiled in. It built only mesh-tools, and the store's module, with Go code, // stopped genesis at step 17: "this mesh holds no copy of it. Build mesh-tools-go first". // genesisModules is every catalogue module genesis installs on its own, read from the steps that // install them: the registry, the build agent, the store, the bus, the vault, the catalogue and the // filter (extras are the operator's choice and come after the base too, but are not genesis's own). var genesisModules = []string{RegistryModule, BuilderModule, "postgres", BusModule, "mesh-vault", "mesh-catalog", "nftables"} // Every genesis module's bundles are compiled in a toolchain the base step builds, read from the real // catalogue's manifests: a module that gains a language genesis does not build fails here, not at a // genesis. func TestTheBaseStepBuildsEveryToolchainAGenesisModuleIsCompiledIn(t *testing.T) { built := map[string]bool{} for _, tc := range genesisToolchains { built[tc.Language] = true } for _, module := range genesisModules { var m struct { Build struct { Artifacts []struct { Name, Kind, Language string } `json:"artifacts"` } `json:"build"` } if err := json.Unmarshal(theCataloguesModule(t, module), &m); err != nil { t.Fatalf("%s: %v", module, err) } for _, a := range m.Build.Artifacts { if a.Kind != "bundle" { continue // an image builds on the bases its Dockerfile names, pinned upstream } if !built[a.Language] { t.Errorf("%s's %q is a %s bundle, and genesis's base step builds no %s toolchain", module, a.Name, a.Language, a.Language) } } } } // And the base step asks for each toolchain's build: the base, then the Go toolchain beside it. func TestTheBaseStepBuildsTheGoToolchainBesideTheBase(t *testing.T) { runtime := &asked{answer: aMeshThatAgrees(nil)} control := controlPlane{container: "mesh-controller", run: runtime.run, timeout: time.Second} o := Options{ToolsSource: Source{Repository: "git://192.0.2.30/mesh-tools.git"}} if err := BuildBase(context.Background(), o, control, quietly); err != nil { t.Fatal(err) } var builds []string for _, c := range runtime.commands { if strings.Contains(c, "/mesh-controller build ") { builds = append(builds, c[strings.Index(c, "build "):]) } } want := []string{ "build git://192.0.2.30/mesh-tools.git --ref main --wait 1200s", "build git://192.0.2.30/mesh-tools-go.git --ref main --wait 1200s", } if strings.Join(builds, "\n") != strings.Join(want, "\n") { t.Fatalf("the base step built\n%s\nand not\n%s", strings.Join(builds, "\n"), strings.Join(want, "\n")) } } func TestTheGoToolchainIsAsGivenOrBesideTheBase(t *testing.T) { for _, c := range []struct{ tools, given, want string }{ {"http://forge/novox/mesh-tools.git", "", "http://forge/novox/mesh-tools-go.git"}, {"http://forge/novox/mesh-tools", "", "http://forge/novox/mesh-tools-go"}, {"http://forge/other/base.git", "http://forge/other/go.git", "http://forge/other/go.git"}, } { got, err := toolsGoSource(Options{ToolsSource: Source{Repository: c.tools}, ToolsGoSource: Source{Repository: c.given}}) if err != nil || got.Repository != c.want { t.Errorf("tools %s, given %q: %q %v; want %s", c.tools, c.given, got.Repository, err, c.want) } } if _, err := toolsGoSource(Options{ToolsSource: Source{Repository: "http://forge/other/base.git"}}); err == nil || !strings.Contains(err.Error(), "--tools-go-source") { t.Errorf("a base not named mesh-tools, and no Go toolchain given, was not refused naming the flag: %v", err) } }