From 45e795ef46d189b4db9f758fad8aeb99e26f5370 Mon Sep 17 00:00:00 2001 From: Hamza El-Saawy Date: Fri, 8 Sep 2023 12:50:09 -0400 Subject: [PATCH] Skip shim tests if shim binary is not found Rather than failing tests when attempting to exec the shim executable, look up its path first and skip if it is not found. Most testing binaries require that other binaries be located in the same directory as them (see `require.Binary`), but since the CI runs the shim tests directly, add `require.BinaryInPath`, which looks for the binary in the path or current working directory first. Signed-off-by: Hamza El-Saawy --- .../global_command_test.go | 17 ++++--- test/containerd-shim-runhcs-v1/start_test.go | 15 +++--- test/pkg/require/requires.go | 51 ++++++++++++++++--- 3 files changed, 61 insertions(+), 22 deletions(-) diff --git a/test/containerd-shim-runhcs-v1/global_command_test.go b/test/containerd-shim-runhcs-v1/global_command_test.go index 0de82e503a..15fef752be 100644 --- a/test/containerd-shim-runhcs-v1/global_command_test.go +++ b/test/containerd-shim-runhcs-v1/global_command_test.go @@ -5,29 +5,30 @@ package main import ( "bytes" - "os" "os/exec" - "path/filepath" "strings" "testing" + + "github.com/Microsoft/hcsshim/test/pkg/require" ) +const shimExe = "containerd-shim-runhcs-v1.exe" + func runGlobalCommand(t *testing.T, args []string) (string, string, error) { t.Helper() - wd, err := os.Getwd() - if err != nil { - t.Fatalf("failed os.Getwd() with: %v", err) - } + + shim := require.BinaryInPath(t, shimExe) cmd := exec.Command( - filepath.Join(wd, "containerd-shim-runhcs-v1.exe"), + shim, args..., ) + t.Logf("execing global command: %s", cmd.String()) outb := bytes.Buffer{} errb := bytes.Buffer{} cmd.Stdout = &outb cmd.Stderr = &errb - err = cmd.Run() + err := cmd.Run() return outb.String(), errb.String(), err } diff --git a/test/containerd-shim-runhcs-v1/start_test.go b/test/containerd-shim-runhcs-v1/start_test.go index b93d43ee20..be03cf0a2c 100644 --- a/test/containerd-shim-runhcs-v1/start_test.go +++ b/test/containerd-shim-runhcs-v1/start_test.go @@ -16,11 +16,13 @@ import ( "time" "github.com/Microsoft/go-winio" - "github.com/Microsoft/hcsshim/pkg/annotations" task "github.com/containerd/containerd/api/runtime/task/v2" "github.com/containerd/ttrpc" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" + + "github.com/Microsoft/hcsshim/pkg/annotations" + "github.com/Microsoft/hcsshim/test/pkg/require" ) func createStartCommand(t *testing.T) (*exec.Cmd, *bytes.Buffer, *bytes.Buffer) { @@ -31,12 +33,10 @@ func createStartCommand(t *testing.T) (*exec.Cmd, *bytes.Buffer, *bytes.Buffer) func createStartCommandWithID(t *testing.T, id string) (*exec.Cmd, *bytes.Buffer, *bytes.Buffer) { t.Helper() bundleDir := t.TempDir() - wd, err := os.Getwd() - if err != nil { - t.Fatalf("failed os.Getwd() with: %v", err) - } + + shim := require.BinaryInPath(t, shimExe) cmd := exec.Command( - filepath.Join(wd, "containerd-shim-runhcs-v1.exe"), + shim, "--namespace", t.Name(), "--address", "need-a-real-one", "--publish-binary", "need-a-real-one", @@ -44,6 +44,9 @@ func createStartCommandWithID(t *testing.T, id string) (*exec.Cmd, *bytes.Buffer "start", ) cmd.Dir = bundleDir + + t.Logf("execing start command: %s", cmd.String()) + outb := bytes.Buffer{} errb := bytes.Buffer{} cmd.Stdout = &outb diff --git a/test/pkg/require/requires.go b/test/pkg/require/requires.go index 877ddb70e6..4ad499f4d6 100644 --- a/test/pkg/require/requires.go +++ b/test/pkg/require/requires.go @@ -1,7 +1,10 @@ package require import ( + "errors" + "fmt" "os" + "os/exec" "path/filepath" "testing" @@ -42,14 +45,35 @@ func AnyFeature(tb testing.TB, given *flag.IncludeExcludeStringSet, want ...stri tb.Skipf("skipping test due to missing features: %s", want) } +// Binary tries to locate `binary` in the PATH (or the current working directory), +// or the same the same directory as the currently-executing binary. +// +// Returns full binary path if it exists, otherwise, skips the test. +func BinaryInPath(tb testing.TB, binary string) string { + tb.Helper() + + if path, err := exec.LookPath(binary); err == nil || errors.Is(err, exec.ErrDot) { + p, found, err := fileExists(path) + if found { + return p + } + tb.Logf("did not find binary %q at path %q: %v", binary, path, err) + } else { + tb.Logf("could not look up binary %q: %v", binary, err) + } + + return Binary(tb, binary) +} + // Binary checks if `binary` exists in the same directory as the test // binary. // Returns full binary path if it exists, otherwise, skips the test. func Binary(tb testing.TB, binary string) string { tb.Helper() + executable, err := os.Executable() if err != nil { - tb.Skipf("error locating executable: %s", err) + tb.Skipf("error locating executable: %v", err) return "" } @@ -61,22 +85,33 @@ func Binary(tb testing.TB, binary string) string { // if it exists. Otherwise, it skips the test. func File(tb testing.TB, path, file string) string { tb.Helper() + + p, found, err := fileExists(filepath.Join(path, file)) + if err != nil { + tb.Fatal(err) + } else if !found { + tb.Skipf("file %q not found in %q", file, path) + } + return p +} + +// fileExists checks if path points to a valid file. +func fileExists(path string) (string, bool, error) { path, err := filepath.Abs(path) if err != nil { - tb.Fatalf("could not resolve path %q: %v", path, err) + return "", false, fmt.Errorf("could not resolve path %q: %w", path, err) } - p := filepath.Join(path, file) - fi, err := os.Stat(p) + fi, err := os.Stat(path) switch { case os.IsNotExist(err): - tb.Skipf("file %q not found", p) + return path, false, nil case err != nil: - tb.Fatalf("could not stat file %q: %v", p, err) + return "", false, fmt.Errorf("could not stat file %q: %w", path, err) case fi.IsDir(): - tb.Fatalf("path %q is a directory", p) + return "", false, fmt.Errorf("path %q is a directory", path) default: } - return p + return path, true, nil }