Using tarball.ImageFromPath() fails against an image with 0-byte layer.tar files. Although empty layers seem to be typically represented by 1024-byte files with all NULs, 0-byte layers are produced in the wild and Moby considers 0-byte layer.tar files are considered valid.
A test case is at: https://github.com/briandealwis/go-cr-bug. This test case is using a windows image produced as a test by bazelbuild/rules_docker
$ tar tvf bazel-out/darwin-fastbuild/bin/tests/docker/basic_windows_image.tar
-rw-r--r-- 0 0 0 907 31 Dec 1969 378fb1f1c5d59102b2b942e7c5cd76ce913cb5a4b74e97f87041dc61ab14b21e.json
-rw-r--r-- 0 0 0 3 31 Dec 1969 0239ecd27132f0d70c7946a5365ef1f0a3edc7ecf6bdeeb61b4215598beb9828/VERSION
-rw-r--r-- 0 0 0 10240 31 Dec 1969 0239ecd27132f0d70c7946a5365ef1f0a3edc7ecf6bdeeb61b4215598beb9828/layer.tar
-rw-r--r-- 0 0 0 461 31 Dec 1969 0239ecd27132f0d70c7946a5365ef1f0a3edc7ecf6bdeeb61b4215598beb9828/json
-rw-r--r-- 0 0 0 3 31 Dec 1969 54625cd3a0909e177f99d40c983676e65cd2ba5e3231670bd3b3a060463e384c/VERSION
-rw-r--r-- 0 0 0 0 31 Dec 1969 54625cd3a0909e177f99d40c983676e65cd2ba5e3231670bd3b3a060463e384c/layer.tar
-rw-r--r-- 0 0 0 250 31 Dec 1969 54625cd3a0909e177f99d40c983676e65cd2ba5e3231670bd3b3a060463e384c/json
-rw-r--r-- 0 0 0 3 31 Dec 1969 fa2da5bcc17fb2e5c2025c1b76104bdd0f5b9ce64d6d4b29762e58aa11d97ee8/VERSION
-rw-r--r-- 0 0 0 0 31 Dec 1969 fa2da5bcc17fb2e5c2025c1b76104bdd0f5b9ce64d6d4b29762e58aa11d97ee8/layer.tar
-rw-r--r-- 0 0 0 178 31 Dec 1969 fa2da5bcc17fb2e5c2025c1b76104bdd0f5b9ce64d6d4b29762e58aa11d97ee8/json
-rw-r--r-- 0 0 0 115 31 Dec 1969 repositories
-rw-r--r-- 0 0 0 1037 31 Dec 1969 manifest.json
I stumbled across this bug trying to update bazelbuild/rules_docker to use container-structure-test from v1.4.0 to v1.7.0, which led to test failures. container-structure-test switched out to use use go-containerregistry with v1.6.0.
The issue is that tarball.ImageFromPath calls into go-containerregistry to open the Image, which checks if the image layers are compressed. This is determined by checking if the top-most layer IsGzipped.
|
// IsGzipped detects whether the input stream is compressed. |
|
func IsGzipped(r io.Reader) (bool, error) { |
|
magicHeader := make([]byte, 2) |
|
if _, err := r.Read(magicHeader); err != nil { |
|
return false, err |
|
} |
|
return bytes.Equal(magicHeader, gzipMagicHeader), nil |
|
} |
The io.Reader's Read call returns (0, io.EOF) and this error is cascaded back to the callers, even though this is a normal return.
The following fix just treats a 0-byte file as not being compressed. But given that empty layers are usually represented as 1024-byte NUL files, I suppose it's possible that there could be a 1-byte or 2-byte layer.tar file too?
diff --git a/pkg/v1/v1util/zip.go b/pkg/v1/v1util/zip.go
index 57514a4..2b0f24f 100644
--- a/pkg/v1/v1util/zip.go
+++ b/pkg/v1/v1util/zip.go
@@ -73,7 +73,11 @@ func GunzipReadCloser(r io.ReadCloser) (io.ReadCloser, error) {
// IsGzipped detects whether the input stream is compressed.
func IsGzipped(r io.Reader) (bool, error) {
magicHeader := make([]byte, 2)
- if _, err := r.Read(magicHeader); err != nil {
+ n, err := r.Read(magicHeader)
+ if n == 0 && err == io.EOF {
+ return false, nil
+ }
+ if err != nil {
return false, err
}
return bytes.Equal(magicHeader, gzipMagicHeader), nil
Using
tarball.ImageFromPath()fails against an image with 0-byte layer.tar files. Although empty layers seem to be typically represented by 1024-byte files with all NULs, 0-byte layers are produced in the wild and Moby considers 0-byte layer.tar files are considered valid.A test case is at: https://github.com/briandealwis/go-cr-bug. This test case is using a windows image produced as a test by bazelbuild/rules_docker
I stumbled across this bug trying to update bazelbuild/rules_docker to use container-structure-test from v1.4.0 to v1.7.0, which led to test failures. container-structure-test switched out to use use go-containerregistry with v1.6.0.
The issue is that
tarball.ImageFromPathcalls intogo-containerregistryto open the Image, which checks if the image layers are compressed. This is determined by checking if the top-most layerIsGzipped.go-containerregistry/pkg/v1/v1util/zip.go
Lines 73 to 80 in 70c1146
The
io.Reader'sReadcall returns(0, io.EOF)and this error is cascaded back to the callers, even though this is a normal return.The following fix just treats a 0-byte file as not being compressed. But given that empty layers are usually represented as 1024-byte NUL files, I suppose it's possible that there could be a 1-byte or 2-byte layer.tar file too?