integration: Adds test for multilayer image import

This test will make sure there aren't any issues with multilayered
images during import. Keep in mind that in the case of multilayered
images, they have to be unpacked first in order to be usable.

Signed-off-by: Claudiu Belu <cbelu@cloudbasesolutions.com>
This commit is contained in:
Claudiu Belu 2021-08-30 06:42:09 -07:00
parent 45e0e5a77e
commit 7a7a9a282c
3 changed files with 66 additions and 10 deletions

View File

@ -32,9 +32,10 @@ const (
) )
var ( var (
testImage = "ghcr.io/containerd/busybox:1.32" testImage = "ghcr.io/containerd/busybox:1.32"
shortCommand = withProcessArgs("true") testMultiLayeredImage = "gcr.io/k8s-cri-containerd/volume-copy-up:2.1"
longCommand = withProcessArgs("/bin/sh", "-c", "while true; do sleep 1; done") shortCommand = withProcessArgs("true")
longCommand = withProcessArgs("/bin/sh", "-c", "while true; do sleep 1; done")
) )
func TestImagePullSchema1WithEmptyLayers(t *testing.T) { func TestImagePullSchema1WithEmptyLayers(t *testing.T) {

View File

@ -30,11 +30,12 @@ const (
) )
var ( var (
defaultRoot = filepath.Join(os.Getenv("programfiles"), "containerd", "root-test") defaultRoot = filepath.Join(os.Getenv("programfiles"), "containerd", "root-test")
defaultState = filepath.Join(os.Getenv("programfiles"), "containerd", "state-test") defaultState = filepath.Join(os.Getenv("programfiles"), "containerd", "state-test")
testImage string testImage string
shortCommand = withTrue() testMultiLayeredImage = "gcr.io/k8s-cri-containerd/volume-copy-up:2.1"
longCommand = withProcessArgs("ping", "-t", "localhost") shortCommand = withTrue()
longCommand = withProcessArgs("ping", "-t", "localhost")
) )
func init() { func init() {

View File

@ -27,12 +27,17 @@ import (
"reflect" "reflect"
"runtime" "runtime"
"testing" "testing"
"time"
. "github.com/containerd/containerd" . "github.com/containerd/containerd"
"github.com/containerd/containerd/archive/compression" "github.com/containerd/containerd/archive/compression"
"github.com/containerd/containerd/archive/tartest" "github.com/containerd/containerd/archive/tartest"
"github.com/containerd/containerd/images" "github.com/containerd/containerd/images"
"github.com/containerd/containerd/images/archive" "github.com/containerd/containerd/images/archive"
"github.com/containerd/containerd/leases"
"github.com/containerd/containerd/oci"
"github.com/containerd/containerd/platforms"
digest "github.com/opencontainers/go-digest" digest "github.com/opencontainers/go-digest"
specs "github.com/opencontainers/image-spec/specs-go" specs "github.com/opencontainers/image-spec/specs-go"
ocispec "github.com/opencontainers/image-spec/specs-go/v1" ocispec "github.com/opencontainers/image-spec/specs-go/v1"
@ -41,6 +46,18 @@ import (
// TestExportAndImport exports testImage as a tar stream, // TestExportAndImport exports testImage as a tar stream,
// and import the tar stream as a new image. // and import the tar stream as a new image.
func TestExportAndImport(t *testing.T) { func TestExportAndImport(t *testing.T) {
testExportImport(t, testImage)
}
// TestExportAndImportMultiLayer exports testMultiLayeredImage as a tar stream,
// and import the tar stream as a new image. This should ensure that imported
// images remain sane, and that the Garbage Collector won't delete part of its
// content.
func TestExportAndImportMultiLayer(t *testing.T) {
testExportImport(t, testMultiLayeredImage)
}
func testExportImport(t *testing.T, imageName string) {
if testing.Short() { if testing.Short() {
t.Skip() t.Skip()
} }
@ -53,17 +70,19 @@ func TestExportAndImport(t *testing.T) {
} }
defer client.Close() defer client.Close()
_, err = client.Fetch(ctx, testImage) _, err = client.Fetch(ctx, imageName)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
wb := bytes.NewBuffer(nil) wb := bytes.NewBuffer(nil)
err = client.Export(ctx, wb, archive.WithAllPlatforms(), archive.WithImage(client.ImageService(), testImage)) err = client.Export(ctx, wb, archive.WithPlatform(platforms.Default()), archive.WithImage(client.ImageService(), imageName))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
client.ImageService().Delete(ctx, imageName)
opts := []ImportOpt{ opts := []ImportOpt{
WithImageRefTranslator(archive.AddRefPrefix("foo/bar")), WithImageRefTranslator(archive.AddRefPrefix("foo/bar")),
} }
@ -72,6 +91,41 @@ func TestExportAndImport(t *testing.T) {
t.Fatalf("Import failed: %+v", err) t.Fatalf("Import failed: %+v", err)
} }
// We need to unpack the image, especially if it's multilayered.
for _, img := range imgrecs {
image := NewImage(client, img)
// TODO: Show unpack status
t.Logf("unpacking %s (%s)...", img.Name, img.Target.Digest)
err = image.Unpack(ctx, "")
if err != nil {
t.Fatalf("Error while unpacking image: %+v", err)
}
t.Log("done")
}
// we're triggering the Garbage Collector to do its job.
ls := client.LeasesService()
l, err := ls.Create(ctx, leases.WithRandomID(), leases.WithExpiration(time.Hour))
if err != nil {
t.Fatalf("Error while creating lease: %+v", err)
}
if err = ls.Delete(ctx, l, leases.SynchronousDelete); err != nil {
t.Fatalf("Error while deleting lease: %+v", err)
}
image, err := client.GetImage(ctx, imageName)
if err != nil {
t.Fatal(err)
}
id := t.Name()
container, err := client.NewContainer(ctx, id, WithNewSnapshot(id, image), WithNewSpec(oci.WithImageConfig(image)))
if err != nil {
t.Fatalf("Error while creating container: %+v", err)
}
container.Delete(ctx, WithSnapshotCleanup)
for _, imgrec := range imgrecs { for _, imgrec := range imgrecs {
if imgrec.Name == testImage { if imgrec.Name == testImage {
continue continue