package vmsup import ( "bytes" "compress/gzip" "crypto/sha256" "encoding/hex" "errors" "os" "path/filepath" "testing" "time" ) func gz(t *testing.T, data []byte) []byte { t.Helper() var b bytes.Buffer w := gzip.NewWriter(&b) _, _ = w.Write(data) _ = w.Close() return b.Bytes() } func sum(b []byte) string { h := sha256.Sum256(b); return hex.EncodeToString(h[:]) } func TestExtract(t *testing.T) { data := []byte("#!/bin/true\nfake binary\n") spec := binarySpec{path: filepath.Join(t.TempDir(), "bin", "victoria-metrics-prod"), want: sum(data)} if err := extract(gz(t, data), spec); err != nil { t.Fatal(err) } fi, err := os.Stat(spec.path) if err != nil { t.Fatal(err) } if fi.Mode().Perm() != 0o755 { t.Fatalf("mode %v", fi.Mode()) } if err := spec.verify(); err != nil { t.Fatal(err) } // Idempotent: a file with the right hash is left alone. old := time.Now().Add(-time.Hour) _ = os.Chtimes(spec.path, old, old) if err := extract(gz(t, data), spec); err != nil { t.Fatal(err) } if fi2, _ := os.Stat(spec.path); fi2.ModTime().After(old.Add(time.Minute)) { t.Fatal("existing good binary was rewritten") } // A tampered file on disk is replaced from the verified embedded copy. if err := os.WriteFile(spec.path, []byte("evil"), 0o755); err != nil { t.Fatal(err) } if err := extract(gz(t, data), spec); err != nil { t.Fatal(err) } if err := spec.verify(); err != nil { t.Fatal(err) } } func TestExtractRefusesBadChecksum(t *testing.T) { dir := t.TempDir() spec := binarySpec{path: filepath.Join(dir, "victoria-metrics-prod"), want: sum([]byte("expected"))} err := extract(gz(t, []byte("something else")), spec) if !errors.Is(err, ErrChecksum) { t.Fatalf("err = %v, want ErrChecksum", err) } if _, err := os.Stat(spec.path); err == nil { t.Fatal("bad binary was installed") } if ents, _ := os.ReadDir(dir); len(ents) != 0 { t.Fatalf("temp files left behind: %v", ents) } // corrupt gzip data is also a checksum failure if err := extract([]byte("not gzip"), spec); !errors.Is(err, ErrChecksum) { t.Fatalf("corrupt archive: %v", err) } } func TestVerifyDetectsChange(t *testing.T) { p := filepath.Join(t.TempDir(), "b") if err := os.WriteFile(p, []byte("one"), 0o755); err != nil { t.Fatal(err) } spec := binarySpec{path: p, want: sum([]byte("one"))} if err := spec.verify(); err != nil { t.Fatal(err) } if err := os.WriteFile(p, []byte("two"), 0o755); err != nil { t.Fatal(err) } if err := spec.verify(); !errors.Is(err, ErrChecksum) { t.Fatalf("err = %v", err) } if err := (binarySpec{path: p}).verify(); err != nil { t.Fatal("unverified spec must pass") } } func TestRetentionArg(t *testing.T) { day := 24 * time.Hour for d, want := range map[time.Duration]string{ 30 * day: "30d", 2 * 365 * day: "2y", 10 * 365 * day: "10y", 120 * day: "120d", time.Hour: "1d", 36 * time.Hour: "2d", } { if got := RetentionArg(d); got != want { t.Errorf("RetentionArg(%s) = %s, want %s", d, got, want) } } }