From bbc8563b75f3892ab5192f418a35a28f757c9a3c Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Wed, 24 Jan 2024 15:32:47 -0500 Subject: [PATCH] package_test.go - Support OCI Image Layout (#37727) Modify TestDocker such that is can read both the original docker image layout and the OCI Image Layout. This works by reading the config and layer file names from the manifest.yml instead of assuming their names. Fixes #37726 (cherry picked from commit ebd85124f0a7b98604cc3380a52d269d59150216) --- dev-tools/packaging/package_test.go | 69 ++++++++++++++++++++++------- 1 file changed, 54 insertions(+), 15 deletions(-) diff --git a/dev-tools/packaging/package_test.go b/dev-tools/packaging/package_test.go index b094859d660..790dd759c0c 100644 --- a/dev-tools/packaging/package_test.go +++ b/dev-tools/packaging/package_test.go @@ -29,10 +29,10 @@ import ( "flag" "fmt" "io" - "io/ioutil" "os" "path/filepath" "regexp" + "slices" "strings" "testing" @@ -105,6 +105,7 @@ func TestZip(t *testing.T) { func TestDocker(t *testing.T) { dockers := getFiles(t, regexp.MustCompile(`\.docker\.tar\.gz$`)) for _, docker := range dockers { + t.Log(docker) checkDocker(t, docker) } } @@ -655,13 +656,19 @@ func readZip(zipFile string) (*packageFile, error) { } func readDocker(dockerFile string) (*packageFile, *dockerInfo, error) { + // Read the manifest file first so that the config file and layer + // names are known in advance. + manifest, err := getDockerManifest(dockerFile) + if err != nil { + return nil, nil, err + } + file, err := os.Open(dockerFile) if err != nil { return nil, nil, err } defer file.Close() - var manifest *dockerManifest var info *dockerInfo layers := make(map[string]*packageFile) @@ -682,22 +689,17 @@ func readDocker(dockerFile string) (*packageFile, *dockerInfo, error) { } switch { - case header.Name == "manifest.json": - manifest, err = readDockerManifest(tarReader) - if err != nil { - return nil, nil, err - } - case strings.HasSuffix(header.Name, ".json") && header.Name != "manifest.json": + case header.Name == manifest.Config: info, err = readDockerInfo(tarReader) if err != nil { return nil, nil, err } - case strings.HasSuffix(header.Name, "/layer.tar"): + case slices.Contains(manifest.Layers, header.Name): layer, err := readTarContents(header.Name, tarReader) if err != nil { return nil, nil, err } - layers[filepath.Dir(header.Name)] = layer + layers[header.Name] = layer } } @@ -711,10 +713,9 @@ func readDocker(dockerFile string) (*packageFile, *dockerInfo, error) { // Read layers in order and for each file keep only the entry seen in the later layer p := &packageFile{Name: filepath.Base(dockerFile), Contents: map[string]packageEntry{}} for _, layer := range manifest.Layers { - layerID := filepath.Dir(layer) - layerFile, found := layers[layerID] + layerFile, found := layers[layer] if !found { - return nil, nil, fmt.Errorf("layer not found: %s", layerID) + return nil, nil, fmt.Errorf("layer not found: %s", layer) } for name, entry := range layerFile.Contents { // Check only files in working dir and entrypoint @@ -740,6 +741,44 @@ func readDocker(dockerFile string) (*packageFile, *dockerInfo, error) { return p, info, nil } +// getDockerManifest opens a gzipped tar file to read the Docker manifest.json +// that it is expected to contain. +func getDockerManifest(file string) (*dockerManifest, error) { + f, err := os.Open(file) + if err != nil { + return nil, err + } + defer f.Close() + + gzipReader, err := gzip.NewReader(f) + if err != nil { + return nil, err + } + defer gzipReader.Close() + + var manifest *dockerManifest + tarReader := tar.NewReader(gzipReader) + for { + header, err := tarReader.Next() + if err != nil { + if errors.Is(err, io.EOF) { + break + } + return nil, err + } + + if header.Name == "manifest.json" { + manifest, err = readDockerManifest(tarReader) + if err != nil { + return nil, err + } + break + } + } + + return manifest, nil +} + type dockerManifest struct { Config string RepoTags []string @@ -747,7 +786,7 @@ type dockerManifest struct { } func readDockerManifest(r io.Reader) (*dockerManifest, error) { - data, err := ioutil.ReadAll(r) + data, err := io.ReadAll(r) if err != nil { return nil, err } @@ -776,7 +815,7 @@ type dockerInfo struct { } func readDockerInfo(r io.Reader) (*dockerInfo, error) { - data, err := ioutil.ReadAll(r) + data, err := io.ReadAll(r) if err != nil { return nil, err }