Skip to content

ggcr: panic on getting image layers with config application/vnd.buildkit.cacheconfig.v0 from tar #2192

@bthuilot

Description

@bthuilot

Describe the bug

When calling v1.Image.Layers() from an imported 'tar' image whose config is media type application/vnd.buildkit.cacheconfig.v0, the function will panic.

This is due to the layer code attempting to read layer information from DiffIDs array which does not exist in the cacheconfig type (linked here).

To Reproduce

  1. Export a build cache image to tar using tarball.WriteToFile()
  2. Import the tar using the tarball.ImageFromPath()
  3. Call the Layers() function

Below is a script that will reproduce, if the first CLI argument is a reference to a build cache image

package main

import (
	"fmt"
	"os"
	"path"

	"github.com/google/go-containerregistry/pkg/authn"
	"github.com/google/go-containerregistry/pkg/name"
	"github.com/google/go-containerregistry/pkg/v1/remote"
	"github.com/google/go-containerregistry/pkg/v1/tarball"
)

func main() {
	if len(os.Args) < 2 {
		fmt.Println("please provide the image reference as an arugment")
		os.Exit(1)
	}

	imageRef, err := name.ParseReference(os.Args[1])
	if err != nil {
		fmt.Println("unable to parse name reference", err)
		os.Exit(1)
	}

	remoteImg, err := remote.Image(imageRef, remote.WithAuthFromKeychain(authn.DefaultKeychain))
	if err != nil {
		fmt.Println("unable to get remote image", err)
		os.Exit(1)
	}
	
	tmpTar := path.Join(os.TempDir(), "test.tar")
	defer func() {
		if err = os.Remove(tmpTar); err != nil {
			fmt.Println("error removing tar", err)
		}
	}()
	fmt.Printf("writing tar to %s\n", tmpTar)

	if err = tarball.WriteToFile(tmpTar, imageRef, remoteImg); err != nil {
		fmt.Println("unable to get write image", err)
		os.Exit(1)
	}

	fmt.Println("importing image")
	img, err := tarball.ImageFromPath(tmpTar, nil)

	if err != nil {
		fmt.Println("unable to read exported image", err)
		os.Exit(1)
	}

	fmt.Println("getting layers")
	layers, err := img.Layers() // this will panic
	if err != nil {
		fmt.Println("unable to get layers", err)
		os.Exit(1)
	}

	fmt.Printf("found %d layers\n", len(layers))
}

Expected behavior

Layers() should still return as expected, with []v1.Layer

Additional context

A Build cache image can be built & pushed to registry via the github action docker/build-push-action and setting cache-to: type=registry,ref=$REF where $REF is the image ref to push the cache to

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions