mirror of
https://github.com/containers/skopeo.git
synced 2025-04-28 03:10:18 +00:00
main: return exit code 2
when an input is not found
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 <mvogt@redhat.com>
This commit is contained in:
parent
d5ac962b5e
commit
16b6f0ade5
@ -129,6 +129,10 @@ func main() {
|
|||||||
}
|
}
|
||||||
rootCmd, _ := createApp()
|
rootCmd, _ := createApp()
|
||||||
if err := rootCmd.Execute(); err != nil {
|
if err := rootCmd.Execute(); err != nil {
|
||||||
|
if isNotFoundImageError(err) {
|
||||||
|
logrus.StandardLogger().Log(logrus.FatalLevel, err)
|
||||||
|
logrus.Exit(2)
|
||||||
|
}
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,13 +73,10 @@ import (
|
|||||||
|
|
||||||
"github.com/containers/image/v5/image"
|
"github.com/containers/image/v5/image"
|
||||||
"github.com/containers/image/v5/manifest"
|
"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/pkg/blobinfocache"
|
||||||
"github.com/containers/image/v5/transports"
|
"github.com/containers/image/v5/transports"
|
||||||
"github.com/containers/image/v5/transports/alltransports"
|
"github.com/containers/image/v5/transports/alltransports"
|
||||||
"github.com/containers/image/v5/types"
|
"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"
|
"github.com/opencontainers/go-digest"
|
||||||
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
@ -219,25 +216,6 @@ func (h *proxyHandler) OpenImage(args []any) (replyBuf, error) {
|
|||||||
return h.openImageImpl(args, false)
|
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) {
|
func (h *proxyHandler) openImageImpl(args []any, allowNotFound bool) (retReplyBuf replyBuf, retErr error) {
|
||||||
h.lock.Lock()
|
h.lock.Lock()
|
||||||
defer h.lock.Unlock()
|
defer h.lock.Unlock()
|
||||||
|
@ -12,9 +12,13 @@ import (
|
|||||||
"github.com/containers/common/pkg/retry"
|
"github.com/containers/common/pkg/retry"
|
||||||
"github.com/containers/image/v5/directory"
|
"github.com/containers/image/v5/directory"
|
||||||
"github.com/containers/image/v5/manifest"
|
"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/pkg/compression"
|
||||||
|
"github.com/containers/image/v5/storage"
|
||||||
"github.com/containers/image/v5/transports/alltransports"
|
"github.com/containers/image/v5/transports/alltransports"
|
||||||
"github.com/containers/image/v5/types"
|
"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"
|
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@ -406,3 +410,23 @@ func promptForPassphrase(privateKeyFile string, stdin, stdout *os.File) (string,
|
|||||||
fmt.Fprintf(stdout, "\n")
|
fmt.Fprintf(stdout, "\n")
|
||||||
return string(passphrase), nil
|
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
|
||||||
|
}
|
||||||
|
@ -114,6 +114,15 @@ Print the version number
|
|||||||
| [skopeo-standalone-verify(1)](skopeo-standalone-verify.1.md)| Verify an image signature. |
|
| [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. |
|
| [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
|
## FILES
|
||||||
**/etc/containers/policy.json**
|
**/etc/containers/policy.json**
|
||||||
Default trust policy file, if **--policy** is not specified.
|
Default trust policy file, if **--policy** is not specified.
|
||||||
|
@ -129,4 +129,11 @@ END_EXPECT
|
|||||||
expect_output --from="$repo_tags" "" "inspect --no-tags was expected to return empty RepoTags[]"
|
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
|
# vim: filetype=sh
|
||||||
|
@ -40,7 +40,7 @@ function setup() {
|
|||||||
expect_output --substring "authentication required"
|
expect_output --substring "authentication required"
|
||||||
|
|
||||||
# Correct creds, but no such image
|
# 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
|
docker://localhost:5000/nonesuch
|
||||||
expect_output --substring "manifest unknown"
|
expect_output --substring "manifest unknown"
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ function setup() {
|
|||||||
run_skopeo delete --tls-verify=false $localimg
|
run_skopeo delete --tls-verify=false $localimg
|
||||||
|
|
||||||
# make sure image is removed from registry
|
# make sure image is removed from registry
|
||||||
expected_rc=1
|
expected_rc=2
|
||||||
run_skopeo $expected_rc inspect --tls-verify=false $localimg
|
run_skopeo $expected_rc inspect --tls-verify=false $localimg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user