Skip to content

Commit

Permalink
main: return exit code 2 when an input is not found
Browse files Browse the repository at this point in the history
This commit makes skopeo return a different exit code when an
input is not found. The use case is `osbuild` which uses skopeo
to inspect images and it would be nice to differenciate between
an image that is not found and general skopeo errors (or errors
like network issues etc).

I picked exit code `2` for `not found` because it is also the value
of `ENOENT`.

Man page and a test are added.

Signed-off-by: Michael Vogt <[email protected]>
  • Loading branch information
mvo5 committed Mar 25, 2024
1 parent d5ac962 commit 16b6f0a
Show file tree
Hide file tree
Showing 7 changed files with 46 additions and 24 deletions.
4 changes: 4 additions & 0 deletions cmd/skopeo/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,10 @@ func main() {
}
rootCmd, _ := createApp()
if err := rootCmd.Execute(); err != nil {
if isNotFoundImageError(err) {
logrus.StandardLogger().Log(logrus.FatalLevel, err)
logrus.Exit(2)
}
logrus.Fatal(err)
}
}
Expand Down
22 changes: 0 additions & 22 deletions cmd/skopeo/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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()
Expand Down
24 changes: 24 additions & 0 deletions cmd/skopeo/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -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/storage"
"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"
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -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.ErrNoSuchImage) ||
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
}
9 changes: 9 additions & 0 deletions docs/skopeo.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,15 @@ Print the version number
| [skopeo-standalone-verify(1)](skopeo-standalone-verify.1.md)| Verify an image signature. |
| [skopeo-sync(1)](skopeo-sync.1.md)| Synchronize images between registry repositories and local directories. |

## EXIT STATUS
`skopeo` exits with status 0 on success, non-zero on error.

Details about the exit statuses:

**1** Generic error, details can be found in the error message.

**2** The input image cannot be found. Note that this is best effort and for remote registries the status often cannot be reliably reported.

## FILES
**/etc/containers/policy.json**
Default trust policy file, if **--policy** is not specified.
Expand Down
7 changes: 7 additions & 0 deletions systemtest/010-inspect.bats
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion systemtest/040-local-registry-auth.bats
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
2 changes: 1 addition & 1 deletion systemtest/060-delete.bats
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down

0 comments on commit 16b6f0a

Please sign in to comment.