diff --git a/cmd/skopeo/utils.go b/cmd/skopeo/utils.go index bf277e3a..77ce7704 100644 --- a/cmd/skopeo/utils.go +++ b/cmd/skopeo/utils.go @@ -13,6 +13,7 @@ import ( "github.com/containers/common/pkg/retry" "github.com/containers/image/v5/directory" "github.com/containers/image/v5/manifest" + ociarchive "github.com/containers/image/v5/oci/archive" ocilayout "github.com/containers/image/v5/oci/layout" "github.com/containers/image/v5/pkg/compression" "github.com/containers/image/v5/storage" @@ -419,9 +420,12 @@ func promptForPassphrase(privateKeyFile string, stdin, stdout *os.File) (string, // authentication error, an I/O error etc.) // TODO drive this into containers/image properly func isNotFoundImageError(err error) bool { + var layoutImageNotFoundError ocilayout.ImageNotFoundError + var archiveImageNotFoundError ociarchive.ImageNotFoundError return isDockerManifestUnknownError(err) || errors.Is(err, storage.ErrNoSuchImage) || - errors.Is(err, ocilayout.ImageNotFoundError{}) + errors.As(err, &layoutImageNotFoundError) || + errors.As(err, &archiveImageNotFoundError) } // isDockerManifestUnknownError is a copy of code from containers/image, diff --git a/integration/copy_test.go b/integration/copy_test.go index 8f7667c4..56f30c5f 100644 --- a/integration/copy_test.go +++ b/integration/copy_test.go @@ -497,6 +497,8 @@ func (s *copySuite) TestCopySimple() { assertSkopeoSucceeds(t, "", "copy", "docker://registry.k8s.io/pause:latest", "oci:"+ociDest+":"+ociImgName) _, err := os.Stat(ociDest) require.NoError(t, err) + // copy exits with status 2 if the image is not found within the container, in some transports. + assertSkopeoFailsWithStatus(t, 2, "copy", "oci:"+ociDest+":thisdoesnotexist", "dir:"+t.TempDir()) // docker v2s2 -> OCI image layout without image name ociDest = "pause-latest-noimage" diff --git a/integration/utils_test.go b/integration/utils_test.go index ecd89c5e..1d8dfc55 100644 --- a/integration/utils_test.go +++ b/integration/utils_test.go @@ -55,7 +55,7 @@ func consumeAndLogOutputs(t *testing.T, id string, cmd *exec.Cmd) { } // combinedOutputOfCommand runs a command as if exec.Command().CombinedOutput(), verifies that the exit status is 0, and returns the output, -// or terminates c on failure. +// or terminates t on failure. func combinedOutputOfCommand(t *testing.T, name string, args ...string) string { t.Logf("Running %s %s", name, strings.Join(args, " ")) out, err := exec.Command(name, args...).CombinedOutput() @@ -64,8 +64,7 @@ func combinedOutputOfCommand(t *testing.T, name string, args ...string) string { } // assertSkopeoSucceeds runs a skopeo command as if exec.Command().CombinedOutput, verifies that the exit status is 0, -// and optionally that the output matches a multi-line regexp if it is nonempty; -// or terminates c on failure +// and optionally that the output matches a multi-line regexp if it is nonempty func assertSkopeoSucceeds(t *testing.T, regexp string, args ...string) { t.Logf("Running %s %s", skopeoBinary, strings.Join(args, " ")) out, err := exec.Command(skopeoBinary, args...).CombinedOutput() @@ -75,9 +74,8 @@ func assertSkopeoSucceeds(t *testing.T, regexp string, args ...string) { } } -// assertSkopeoFails runs a skopeo command as if exec.Command().CombinedOutput, verifies that the exit status is 0, -// and that the output matches a multi-line regexp; -// or terminates c on failure +// assertSkopeoFails runs a skopeo command as if exec.Command().CombinedOutput, verifies that the exit status is not 0, +// and that the output matches a multi-line regexp func assertSkopeoFails(t *testing.T, regexp string, args ...string) { t.Logf("Running %s %s", skopeoBinary, strings.Join(args, " ")) out, err := exec.Command(skopeoBinary, args...).CombinedOutput() @@ -85,15 +83,25 @@ func assertSkopeoFails(t *testing.T, regexp string, args ...string) { assert.Regexp(t, "(?s)"+regexp, string(out)) // (?s) : '.' will also match newlines } +// assertSkopeoFailsWithStatus runs a skopeo command as if exec.Command().CombinedOutput, +// and verifies that it fails with a specific exit status. +func assertSkopeoFailsWithStatus(t *testing.T, status int, args ...string) { + t.Logf("Running %s %s", skopeoBinary, strings.Join(args, " ")) + _, err := exec.Command(skopeoBinary, args...).CombinedOutput() + var exitErr *exec.ExitError + require.ErrorAs(t, err, &exitErr) + assert.Equal(t, status, exitErr.ExitCode()) +} + // runCommandWithInput runs a command as if exec.Command(), sending it the input to stdin, -// and verifies that the exit status is 0, or terminates c on failure. +// and verifies that the exit status is 0, or terminates t on failure. func runCommandWithInput(t *testing.T, input string, name string, args ...string) { cmd := exec.Command(name, args...) runExecCmdWithInput(t, cmd, input) } // runExecCmdWithInput runs an exec.Cmd, sending it the input to stdin, -// and verifies that the exit status is 0, or terminates c on failure. +// and verifies that the exit status is 0, or terminates t on failure. func runExecCmdWithInput(t *testing.T, cmd *exec.Cmd, input string) { t.Logf("Running %s %s", cmd.Path, strings.Join(cmd.Args, " ")) consumeAndLogOutputs(t, cmd.Path+" "+strings.Join(cmd.Args, " "), cmd)