diff --git a/oci/archive/oci_src.go b/oci/archive/oci_src.go index 6c9ee33402..ee8409896c 100644 --- a/oci/archive/oci_src.go +++ b/oci/archive/oci_src.go @@ -28,6 +28,18 @@ func (e ImageNotFoundError) Error() string { return fmt.Sprintf("no descriptor found for reference %q", e.ref.image) } +// ArchiveFileNotFoundError occurs when the archive file does not exist. +type ArchiveFileNotFoundError struct { + // ref is the image reference + ref ociArchiveReference + // path is the file path that was not present + path string +} + +func (e ArchiveFileNotFoundError) Error() string { + return fmt.Sprintf("archive file not found: %q", e.path) +} + type ociArchiveImageSource struct { impl.Compat diff --git a/oci/archive/oci_src_test.go b/oci/archive/oci_src_test.go index 6f00afc0cc..61ec0ed86b 100644 --- a/oci/archive/oci_src_test.go +++ b/oci/archive/oci_src_test.go @@ -1,5 +1,26 @@ package archive -import "github.com/containers/image/v5/internal/private" +import ( + "path/filepath" + "testing" + + "github.com/containers/image/v5/internal/private" + "github.com/containers/image/v5/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) var _ private.ImageSource = (*ociArchiveImageSource)(nil) + +func TestNewImageSourceNotFound(t *testing.T) { + emptyDir := t.TempDir() + sysctx := types.SystemContext{BlobInfoCacheDir: emptyDir} + archivePath := filepath.Join(emptyDir, "foo.ociarchive") + imgref, err := ParseReference(archivePath) + require.NoError(t, err) + _, err = LoadManifestDescriptorWithContext(&sysctx, imgref) + assert.NotNil(t, err) + var aerr ArchiveFileNotFoundError + assert.ErrorAs(t, err, &aerr) + assert.Equal(t, aerr.path, archivePath) +} diff --git a/oci/archive/oci_transport.go b/oci/archive/oci_transport.go index 2a03feeeac..d5fee36310 100644 --- a/oci/archive/oci_transport.go +++ b/oci/archive/oci_transport.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "io/fs" "os" "strings" @@ -171,18 +172,24 @@ func createOCIRef(sys *types.SystemContext, image string) (tempDirOCIRef, error) // creates the temporary directory and copies the tarred content to it func createUntarTempDir(sys *types.SystemContext, ref ociArchiveReference) (tempDirOCIRef, error) { + src := ref.resolvedFile + arch, err := os.Open(src) + if err != nil { + if errors.Is(err, fs.ErrNotExist) { + return tempDirOCIRef{}, ArchiveFileNotFoundError{ref: ref, path: src} + } else { + return tempDirOCIRef{}, err + } + } + defer arch.Close() + tempDirRef, err := createOCIRef(sys, ref.image) if err != nil { return tempDirOCIRef{}, fmt.Errorf("creating oci reference: %w", err) } - src := ref.resolvedFile dst := tempDirRef.tempDirectory + // TODO: This can take quite some time, and should ideally be cancellable using a context.Context. - arch, err := os.Open(src) - if err != nil { - return tempDirOCIRef{}, err - } - defer arch.Close() if err := archive.NewDefaultArchiver().Untar(arch, dst, &archive.TarOptions{NoLchown: true}); err != nil { if err := tempDirRef.deleteTempDir(); err != nil { return tempDirOCIRef{}, fmt.Errorf("deleting temp directory %q: %w", tempDirRef.tempDirectory, err)