package builder import ( "archive/tar" "bytes" "context" "fmt" "os" "strings" "testing" "time" "github.com/novox/mesh-controller/internal/catalogue" ) // aFilesystem is a filesystem tarball of files, each written at the time given. func aFilesystem(t *testing.T, at time.Time, files map[string]string) []byte { t.Helper() var buf bytes.Buffer tw := tar.NewWriter(&buf) for _, name := range []string{"usr/local/bin/mesh-builder", "etc/apk/world"} { body, ok := files[name] if !ok { continue } if err := tw.WriteHeader(&tar.Header{Name: name, Mode: 0o755, Size: int64(len(body)), ModTime: at, Typeflag: tar.TypeReg}); err != nil { t.Fatal(err) } if _, err := tw.Write([]byte(body)); err != nil { t.Fatal(err) } } if err := tw.Close(); err != nil { t.Fatal(err) } return buf.Bytes() } // anEngine answers the container engine's commands for images by reference: each image's filesystem and labels. func anEngine(images map[string][]byte, labels map[string]string, failing string) Runner { created := map[string]string{} return func(_ context.Context, _ string, name string, args ...string) (string, error) { joined := strings.Join(args, " ") ref := args[len(args)-1] if failing != "" && strings.Contains(joined, failing) { return "", fmt.Errorf("the engine refused %s", joined) } switch { case args[0] == "pull", args[0] == "rm": return "", nil case args[0] == "image": return fmt.Sprintf(`linux/amd64/ {"Entrypoint":["/usr/local/bin/mesh-builder"],"Labels":{"commit":%q}}`, labels[ref]), nil case args[0] == "create": id := fmt.Sprintf("container%04d", len(created)) created[id] = ref return id + "\n", nil case args[0] == "export": return "", os.WriteFile(args[2], images[created[ref]], 0o600) } return "", fmt.Errorf("unexpected %s %s", name, joined) } } // TestAModuleWhoseProgramDidNotChangeIsSaidUnchanged is novox/hq issue 721: mesh-controller #335 changed one sentence // in a package mesh-builder imports and never reaches, and build-agent moved. Built again, its image differs only in // the files' times and its commit label, and the check says it is the same as the build the mesh holds. func TestAModuleWhoseProgramDidNotChangeIsSaidUnchanged(t *testing.T) { held := map[string]string{"build-agent/agent": "store/build-agent@sha256:aaaa"} made := []catalogue.Built{{Name: "agent", Kind: "image", Reference: "scratch/build-agent@sha256:bbbb"}} files := map[string]string{"usr/local/bin/mesh-builder": "the same program", "etc/apk/world": "docker-cli\ngit\n"} images := map[string][]byte{ "store/build-agent@sha256:aaaa": aFilesystem(t, time.Unix(1000, 0), files), "scratch/build-agent@sha256:bbbb": aFilesystem(t, time.Unix(2000, 0), files), } labels := map[string]string{"store/build-agent@sha256:aaaa": "a5500f73", "scratch/build-agent@sha256:bbbb": "c2ec0a60"} same, against, err := sameAsHeld(context.Background(), anEngine(images, labels, ""), t.TempDir(), "build-agent", made, held) if err != nil { t.Fatal(err) } if !strings.HasPrefix(same, "unchanged: its program is the same as running build agent") || against["agent"] != held["build-agent/agent"] { t.Fatalf("an image the same but for its times and labels is not said unchanged: %q %v", same, against) } // **Any file differing moves it**, not only its program. changed := map[string]string{"usr/local/bin/mesh-builder": "the same program", "etc/apk/world": "docker-cli\ngit\ncurl\n"} images["scratch/build-agent@sha256:bbbb"] = aFilesystem(t, time.Unix(2000, 0), changed) if same, _, err := sameAsHeld(context.Background(), anEngine(images, labels, ""), t.TempDir(), "build-agent", made, held); err != nil || same != "" { t.Fatalf("an image with another file is said unchanged: %q %v", same, err) } // **A comparison that fails says nothing**, and the module moves. images["scratch/build-agent@sha256:bbbb"] = aFilesystem(t, time.Unix(2000, 0), files) if same, _, err := sameAsHeld(context.Background(), anEngine(images, labels, "export"), t.TempDir(), "build-agent", made, held); err == nil || same != "" { t.Fatalf("a comparison that failed said %q, %v", same, err) } // No held build, or an artifact that is not an image: nothing compared. if same, _, _ := sameAsHeld(context.Background(), anEngine(images, labels, ""), t.TempDir(), "build-agent", made, nil); same != "" { t.Fatalf("a module the mesh holds no build of is said unchanged: %q", same) } archive := []catalogue.Built{{Name: "agent", Kind: "archive", Reference: "scratch/build-agent@sha256:bbbb"}} if same, _, _ := sameAsHeld(context.Background(), anEngine(images, labels, ""), t.TempDir(), "build-agent", archive, held); same != "" { t.Fatalf("an archive is said unchanged without being compared: %q", same) } } // TestAnImageWithAnotherEntrypointIsNotTheSame: the configuration counts, but for its labels. func TestAnImageWithAnotherEntrypointIsNotTheSame(t *testing.T) { a, err := configContent(`linux/amd64/ {"Entrypoint":["/a"],"Labels":{"commit":"1"}}`) if err != nil { t.Fatal(err) } b, _ := configContent(`linux/amd64/ {"Labels":{"commit":"2"},"Entrypoint":["/a"]}`) c, _ := configContent(`linux/amd64/ {"Entrypoint":["/b"],"Labels":{"commit":"1"}}`) if a != b || a == c { t.Fatalf("labels count, or the entrypoint does not: %q %q %q", a, b, c) } }