diff --git a/cmd/skopeo/main.go b/cmd/skopeo/main.go index 3617eeb2df..68b5b36b48 100644 --- a/cmd/skopeo/main.go +++ b/cmd/skopeo/main.go @@ -129,6 +129,10 @@ func main() { } rootCmd, _ := createApp() if err := rootCmd.Execute(); err != nil { + if isNotFoundImageError(err) { + logrus.Error(err) + logrus.Exit(2) + } logrus.Fatal(err) } } diff --git a/cmd/skopeo/proxy.go b/cmd/skopeo/proxy.go index aab85365b1..9396f42273 100644 --- a/cmd/skopeo/proxy.go +++ b/cmd/skopeo/proxy.go @@ -73,13 +73,10 @@ import ( "github.com/containers/image/v5/image" "github.com/containers/image/v5/manifest" - ocilayout "github.com/containers/image/v5/oci/layout" "github.com/containers/image/v5/pkg/blobinfocache" "github.com/containers/image/v5/transports" "github.com/containers/image/v5/transports/alltransports" "github.com/containers/image/v5/types" - dockerdistributionerrcode "github.com/docker/distribution/registry/api/errcode" - dockerdistributionapi "github.com/docker/distribution/registry/api/v2" "github.com/opencontainers/go-digest" imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/sirupsen/logrus" @@ -219,25 +216,6 @@ func (h *proxyHandler) OpenImage(args []any) (replyBuf, error) { return h.openImageImpl(args, false) } -// isDockerManifestUnknownError is a copy of code from containers/image, -// please update there first. -func isDockerManifestUnknownError(err error) bool { - var ec dockerdistributionerrcode.ErrorCoder - if !errors.As(err, &ec) { - return false - } - return ec.ErrorCode() == dockerdistributionapi.ErrorCodeManifestUnknown -} - -// isNotFoundImageError heuristically attempts to determine whether an error -// is saying the remote source couldn't find the image (as opposed to an -// authentication error, an I/O error etc.) -// TODO drive this into containers/image properly -func isNotFoundImageError(err error) bool { - return isDockerManifestUnknownError(err) || - errors.Is(err, ocilayout.ImageNotFoundError{}) -} - func (h *proxyHandler) openImageImpl(args []any, allowNotFound bool) (retReplyBuf replyBuf, retErr error) { h.lock.Lock() defer h.lock.Unlock() diff --git a/cmd/skopeo/utils.go b/cmd/skopeo/utils.go index 3fb0fab9e9..b630ca656e 100644 --- a/cmd/skopeo/utils.go +++ b/cmd/skopeo/utils.go @@ -12,9 +12,13 @@ import ( "github.com/containers/common/pkg/retry" "github.com/containers/image/v5/directory" "github.com/containers/image/v5/manifest" + ocilayout "github.com/containers/image/v5/oci/layout" "github.com/containers/image/v5/pkg/compression" "github.com/containers/image/v5/transports/alltransports" "github.com/containers/image/v5/types" + "github.com/containers/storage" + dockerdistributionerrcode "github.com/docker/distribution/registry/api/errcode" + dockerdistributionapi "github.com/docker/distribution/registry/api/v2" imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -406,3 +410,23 @@ func promptForPassphrase(privateKeyFile string, stdin, stdout *os.File) (string, fmt.Fprintf(stdout, "\n") return string(passphrase), nil } + +// isNotFoundImageError heuristically attempts to determine whether an error +// is saying the remote source couldn't find the image (as opposed to an +// authentication error, an I/O error etc.) +// TODO drive this into containers/image properly +func isNotFoundImageError(err error) bool { + return isDockerManifestUnknownError(err) || + errors.Is(err, storage.ErrNotAnImage) || + errors.Is(err, ocilayout.ImageNotFoundError{}) +} + +// isDockerManifestUnknownError is a copy of code from containers/image, +// please update there first. +func isDockerManifestUnknownError(err error) bool { + var ec dockerdistributionerrcode.ErrorCoder + if !errors.As(err, &ec) { + return false + } + return ec.ErrorCode() == dockerdistributionapi.ErrorCodeManifestUnknown +} diff --git a/docs/skopeo.1.md b/docs/skopeo.1.md index 26583f1160..98209d497b 100644 --- a/docs/skopeo.1.md +++ b/docs/skopeo.1.md @@ -123,6 +123,13 @@ Print the version number Default directory containing registry configuration, if **--registries.d** is not specified. The contents of this directory are documented in [containers-registries.d(5)](https://github.com/containers/image/blob/main/docs/containers-registries.d.5.md). +## EXIT STATUS +`skopeo` exists with status 0 if the operation was successfully. Error result +in a non-zero exit code. If the input image cannot be found an exit status of +2 is returned. Note that this is best effort and for remote registries the +status often cannot be reliably reported. + + ## SEE ALSO skopeo-login(1), docker-login(1), containers-auth.json(5), containers-storage.conf(5), containers-policy.json(5), containers-transports(5) diff --git a/systemtest/010-inspect.bats b/systemtest/010-inspect.bats index 548174da48..6a8ad80989 100644 --- a/systemtest/010-inspect.bats +++ b/systemtest/010-inspect.bats @@ -129,4 +129,11 @@ END_EXPECT expect_output --from="$repo_tags" "" "inspect --no-tags was expected to return empty RepoTags[]" } +@test "inspect: image unknown" { + # non existing image + run_skopeo 2 inspect containers-storage:non-existing-tag + expect_output --substring "identifier is not an image" \ + "skopeo inspect containers-storage:010101010101" +} + # vim: filetype=sh diff --git a/systemtest/040-local-registry-auth.bats b/systemtest/040-local-registry-auth.bats index 34e5c68560..e2880bf74b 100644 --- a/systemtest/040-local-registry-auth.bats +++ b/systemtest/040-local-registry-auth.bats @@ -40,7 +40,7 @@ function setup() { expect_output --substring "authentication required" # Correct creds, but no such image - run_skopeo 1 inspect --tls-verify=false --creds=$testuser:$testpassword \ + run_skopeo 2 inspect --tls-verify=false --creds=$testuser:$testpassword \ docker://localhost:5000/nonesuch expect_output --substring "manifest unknown" diff --git a/systemtest/060-delete.bats b/systemtest/060-delete.bats index 09e6688a66..70b3a191b1 100644 --- a/systemtest/060-delete.bats +++ b/systemtest/060-delete.bats @@ -24,7 +24,7 @@ function setup() { run_skopeo delete --tls-verify=false $localimg # make sure image is removed from registry - expected_rc=1 + expected_rc=2 run_skopeo $expected_rc inspect --tls-verify=false $localimg }