128 lines
4.3 KiB
Go
128 lines
4.3 KiB
Go
package cli
|
|
|
|
import (
|
|
"archive/tar"
|
|
"bytes"
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// TestPullThroughFakeCrane runs pull against a crane stand-in that
|
|
// records its arguments and writes a one-layer docker save tarball.
|
|
func TestPullThroughFakeCrane(t *testing.T) {
|
|
home := t.TempDir()
|
|
t.Setenv("WERKDOCK_HOME", home)
|
|
tarball := filepath.Join(home, "fixture.tar")
|
|
if err := os.WriteFile(tarball, dockerSaveTarball(t), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
argsFile := filepath.Join(home, "crane.args")
|
|
fake := filepath.Join(home, "crane")
|
|
script := "#!/bin/sh\nprintf '%s\\n' \"$@\" > " + argsFile + "\ncp " + tarball + " \"$5\"\n"
|
|
if err := os.WriteFile(fake, []byte(script), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Setenv("WERKDOCK_CRANE", fake)
|
|
|
|
if code := pullCmd([]string{"--platform", "linux/arm64", "example.org:5000/team/tool:1.0"}); code != 0 {
|
|
t.Fatalf("pull exited %d", code)
|
|
}
|
|
args, _ := os.ReadFile(argsFile)
|
|
want := "pull\n--platform\nlinux/arm64\nexample.org:5000/team/tool:1.0\n"
|
|
if !strings.HasPrefix(string(args), want) {
|
|
t.Errorf("crane args:\n%s", args)
|
|
}
|
|
if _, err := os.Stat(filepath.Join(home, "images", "example.org:5000", "team", "tool", "1.0", "rootfs", "etc", "hello")); err != nil {
|
|
t.Errorf("rootfs not placed: %v", err)
|
|
}
|
|
if leftovers, _ := filepath.Glob(filepath.Join(home, ".pull-*")); len(leftovers) != 0 {
|
|
t.Errorf("staging tarball left behind: %v", leftovers)
|
|
}
|
|
if code := pullCmd([]string{"example.org:5000/team/tool:1.0"}); code != exitCLIError {
|
|
t.Errorf("second pull of an existing image: exited %d, want %d", code, exitCLIError)
|
|
}
|
|
}
|
|
|
|
func TestPullReportsCraneFailure(t *testing.T) {
|
|
home := t.TempDir()
|
|
t.Setenv("WERKDOCK_HOME", home)
|
|
fake := filepath.Join(home, "crane")
|
|
if err := os.WriteFile(fake, []byte("#!/bin/sh\nexit 1\n"), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Setenv("WERKDOCK_CRANE", fake)
|
|
if code := pullCmd([]string{"busybox"}); code != exitCLIError {
|
|
t.Errorf("exited %d, want %d", code, exitCLIError)
|
|
}
|
|
if leftovers, _ := filepath.Glob(filepath.Join(home, ".pull-*")); len(leftovers) != 0 {
|
|
t.Errorf("staging tarball left behind: %v", leftovers)
|
|
}
|
|
}
|
|
|
|
func TestPullArguments(t *testing.T) {
|
|
for _, args := range [][]string{{}, {"a", "b"}, {"Upper:1"}, {"busybox@sha256:0000"}} {
|
|
if code := pullCmd(args); code != exitCLIError {
|
|
t.Errorf("pull %q: exited %d, want %d", args, code, exitCLIError)
|
|
}
|
|
}
|
|
}
|
|
|
|
// dockerSaveTarball builds the archive `crane pull` (format tarball)
|
|
// writes: manifest.json, a config blob, one gzip-less layer.
|
|
func dockerSaveTarball(t *testing.T) []byte {
|
|
t.Helper()
|
|
var layer bytes.Buffer
|
|
lw := tar.NewWriter(&layer)
|
|
for _, h := range []tar.Header{
|
|
{Name: "etc/", Typeflag: tar.TypeDir, Mode: 0o755},
|
|
{Name: "etc/hello", Typeflag: tar.TypeReg, Mode: 0o644, Size: 6},
|
|
} {
|
|
if err := lw.WriteHeader(&h); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if h.Typeflag == tar.TypeReg {
|
|
lw.Write([]byte("hello\n"))
|
|
}
|
|
}
|
|
lw.Close()
|
|
config, _ := json.Marshal(map[string]any{
|
|
"architecture": "arm64", "os": "linux",
|
|
"config": map[string]any{"Cmd": []string{"/bin/sh"}},
|
|
})
|
|
manifest, _ := json.Marshal([]map[string]any{{
|
|
"Config": "config.json", "RepoTags": []string{"example.org:5000/team/tool:1.0"}, "Layers": []string{"layer.tar"},
|
|
}})
|
|
var out bytes.Buffer
|
|
tw := tar.NewWriter(&out)
|
|
for name, body := range map[string][]byte{"manifest.json": manifest, "config.json": config, "layer.tar": layer.Bytes()} {
|
|
if err := tw.WriteHeader(&tar.Header{Name: name, Mode: 0o644, Size: int64(len(body))}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
tw.Write(body)
|
|
}
|
|
tw.Close()
|
|
return out.Bytes()
|
|
}
|
|
|
|
func TestRmiContinuesPastMissingImages(t *testing.T) {
|
|
home := t.TempDir()
|
|
t.Setenv("WERKDOCK_HOME", home)
|
|
for _, rel := range []string{"a/latest", "b/1"} {
|
|
dir := filepath.Join(home, "images", rel)
|
|
os.MkdirAll(filepath.Join(dir, "rootfs"), 0o755)
|
|
os.WriteFile(filepath.Join(dir, "image.json"), []byte("{}\n"), 0o644)
|
|
}
|
|
if code := rmiCmd([]string{"a", "nosuch", "b:1"}); code != exitCLIError {
|
|
t.Errorf("exited %d, want %d (one of three failed)", code, exitCLIError)
|
|
}
|
|
if entries, _ := os.ReadDir(filepath.Join(home, "images")); len(entries) != 0 {
|
|
t.Errorf("images left: %v", entries)
|
|
}
|
|
if code := rmiCmd(nil); code != exitCLIError {
|
|
t.Errorf("no argument: exited %d", code)
|
|
}
|
|
}
|