162 lines
5.1 KiB
Go
162 lines
5.1 KiB
Go
package image
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"metis/pkg/inject"
|
|
)
|
|
|
|
type shortWriteOnly struct{}
|
|
|
|
func (shortWriteOnly) Write(p []byte) (int, error) {
|
|
if len(p) == 0 {
|
|
return 0, nil
|
|
}
|
|
return len(p) - 1, nil
|
|
}
|
|
|
|
type failAfterFirstRead struct{ done bool }
|
|
|
|
func (r *failAfterFirstRead) Read(p []byte) (int, error) {
|
|
if r.done {
|
|
return 0, io.ErrUnexpectedEOF
|
|
}
|
|
r.done = true
|
|
copy(p, []byte("ab"))
|
|
return 2, nil
|
|
}
|
|
|
|
func TestInjectRootFSWithFakesReportsReplacingByteProgress(t *testing.T) {
|
|
scripts := fakeRootfsCommands(t, true)
|
|
t.Setenv("PATH", scripts+string(os.PathListSeparator)+os.Getenv("PATH"))
|
|
|
|
imagePath := filepath.Join(t.TempDir(), "image.img")
|
|
if err := os.WriteFile(imagePath, make([]byte, 4096), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
files := []inject.FileSpec{{Path: "etc/metis/firstboot.env", Content: []byte("METIS_HOSTNAME='titan-13'\n"), Mode: 0o600, RootFS: true}}
|
|
var replacing []RootFSProgressUpdate
|
|
if err := InjectRootFSWithProgress(imagePath, files, func(update RootFSProgressUpdate) {
|
|
if update.Step == RootFSProgressReplacing && update.TotalBytes > 0 {
|
|
replacing = append(replacing, update)
|
|
}
|
|
}); err != nil {
|
|
t.Fatalf("InjectRootFSWithProgress: %v", err)
|
|
}
|
|
if len(replacing) == 0 {
|
|
t.Fatal("expected replacing progress updates")
|
|
}
|
|
last := replacing[len(replacing)-1]
|
|
if last.WrittenBytes != last.TotalBytes || last.TotalBytes != 1024 {
|
|
t.Fatalf("unexpected replacing progress: %+v", last)
|
|
}
|
|
}
|
|
|
|
func TestMkdirTempNearPathPrefersImageAdjacentScratch(t *testing.T) {
|
|
imagePath := filepath.Join(t.TempDir(), "build", "node.img")
|
|
if err := os.MkdirAll(filepath.Dir(imagePath), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
workDir, err := mkdirTempNearPath(imagePath, "metis-rootfs-")
|
|
if err != nil {
|
|
t.Fatalf("mkdirTempNearPath: %v", err)
|
|
}
|
|
defer os.RemoveAll(workDir)
|
|
if got, want := filepath.Base(filepath.Dir(workDir)), ".metis-tmp"; got != want {
|
|
t.Fatalf("temp parent = %q want %q", got, want)
|
|
}
|
|
}
|
|
|
|
func TestCopyWithProgressReportsByteCounts(t *testing.T) {
|
|
var dst bytes.Buffer
|
|
var updates []RootFSProgressUpdate
|
|
src := bytes.NewReader([]byte("hello world"))
|
|
written, err := copyWithProgress(&dst, src, int64(src.Len()), func(written, total int64) {
|
|
updates = append(updates, RootFSProgressUpdate{Step: RootFSProgressReplacing, WrittenBytes: written, TotalBytes: total})
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("copyWithProgress: %v", err)
|
|
}
|
|
if written != int64(len("hello world")) {
|
|
t.Fatalf("written = %d", written)
|
|
}
|
|
if dst.String() != "hello world" {
|
|
t.Fatalf("unexpected dst content: %q", dst.String())
|
|
}
|
|
if len(updates) == 0 || updates[len(updates)-1].WrittenBytes != updates[len(updates)-1].TotalBytes {
|
|
t.Fatalf("expected final byte-complete update, got %#v", updates)
|
|
}
|
|
}
|
|
|
|
func TestReplacePartitionWritesDataAndReportsProgress(t *testing.T) {
|
|
dir := t.TempDir()
|
|
imagePath := filepath.Join(dir, "image.img")
|
|
rootPath := filepath.Join(dir, "root.ext4")
|
|
imageBytes := make([]byte, 1024)
|
|
rootBytes := bytes.Repeat([]byte("r"), 512)
|
|
if err := os.WriteFile(imagePath, imageBytes, 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(rootPath, rootBytes, 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var updates []RootFSProgressUpdate
|
|
if err := replacePartition(imagePath, rootPath, partitionTablePart{Start: 1, Size: 1}, 512, func(written, total int64) {
|
|
updates = append(updates, RootFSProgressUpdate{Step: RootFSProgressReplacing, WrittenBytes: written, TotalBytes: total})
|
|
}); err != nil {
|
|
t.Fatalf("replacePartition: %v", err)
|
|
}
|
|
got, err := os.ReadFile(imagePath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !bytes.Equal(got[512:], rootBytes) {
|
|
t.Fatalf("partition bytes were not replaced: %q", got[512:])
|
|
}
|
|
if len(updates) == 0 || updates[len(updates)-1].WrittenBytes != 512 || updates[len(updates)-1].TotalBytes != 512 {
|
|
t.Fatalf("expected final replace progress update, got %#v", updates)
|
|
}
|
|
}
|
|
|
|
func TestMkdirTempNearPathHonorsOverrideAndFallback(t *testing.T) {
|
|
t.Run("override", func(t *testing.T) {
|
|
override := filepath.Join(t.TempDir(), "override")
|
|
t.Setenv("METIS_ROOTFS_TMP_DIR", override)
|
|
workDir, err := mkdirTempNearPath(filepath.Join(t.TempDir(), "build", "node.img"), "metis-rootfs-")
|
|
if err != nil {
|
|
t.Fatalf("mkdirTempNearPath override: %v", err)
|
|
}
|
|
defer os.RemoveAll(workDir)
|
|
if got := filepath.Dir(workDir); got != override {
|
|
t.Fatalf("override parent = %q want %q", got, override)
|
|
}
|
|
})
|
|
t.Run("fallback tempdir", func(t *testing.T) {
|
|
workDir, err := mkdirTempNearPath("", "metis-rootfs-")
|
|
if err != nil {
|
|
t.Fatalf("mkdirTempNearPath fallback: %v", err)
|
|
}
|
|
defer os.RemoveAll(workDir)
|
|
if workDir == "" {
|
|
t.Fatal("expected fallback temp dir path")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestCopyWithProgressPropagatesReaderAndWriterErrors(t *testing.T) {
|
|
t.Run("short write", func(t *testing.T) {
|
|
if _, err := copyWithProgress(shortWriteOnly{}, bytes.NewReader([]byte("abc")), 3, nil); err != io.ErrShortWrite {
|
|
t.Fatalf("expected short write, got %v", err)
|
|
}
|
|
})
|
|
t.Run("reader error", func(t *testing.T) {
|
|
if _, err := copyWithProgress(&bytes.Buffer{}, &failAfterFirstRead{}, 4, nil); err != io.ErrUnexpectedEOF {
|
|
t.Fatalf("expected reader error, got %v", err)
|
|
}
|
|
})
|
|
}
|