github.com/rish1988/moby@v25.0.2+incompatible/integration/image/remove_unix_test.go (about) 1 //go:build !windows 2 3 package image // import "github.com/docker/docker/integration/image" 4 5 import ( 6 "io" 7 "os" 8 "path/filepath" 9 "strconv" 10 "strings" 11 "syscall" 12 "testing" 13 "unsafe" 14 15 "github.com/docker/docker/api/types" 16 "github.com/docker/docker/api/types/image" 17 _ "github.com/docker/docker/daemon/graphdriver/register" // register graph drivers 18 "github.com/docker/docker/daemon/images" 19 "github.com/docker/docker/layer" 20 "github.com/docker/docker/pkg/idtools" 21 "github.com/docker/docker/testutil" 22 "github.com/docker/docker/testutil/daemon" 23 "github.com/docker/docker/testutil/fakecontext" 24 "gotest.tools/v3/assert" 25 "gotest.tools/v3/skip" 26 ) 27 28 // This is a regression test for #38488 29 // It ensures that orphan layers can be found and cleaned up 30 // after unsuccessful image removal 31 func TestRemoveImageGarbageCollector(t *testing.T) { 32 // This test uses very platform specific way to prevent 33 // daemon for remove image layer. 34 skip.If(t, testEnv.DaemonInfo.OSType != "linux") 35 skip.If(t, testEnv.NotAmd64) 36 skip.If(t, testEnv.IsRootless, "rootless mode doesn't support overlay2 on most distros") 37 skip.If(t, testEnv.UsingSnapshotter, "tests the graph driver layer store that's not used with the containerd image store") 38 39 ctx := testutil.StartSpan(baseContext, t) 40 41 // Create daemon with overlay2 graphdriver because vfs uses disk differently 42 // and this test case would not work with it. 43 d := daemon.New(t, daemon.WithStorageDriver("overlay2")) 44 d.Start(t) 45 defer d.Stop(t) 46 47 client := d.NewClientT(t) 48 49 layerStore, _ := layer.NewStoreFromOptions(layer.StoreOptions{ 50 Root: d.Root, 51 MetadataStorePathTemplate: filepath.Join(d.RootDir(), "image", "%s", "layerdb"), 52 GraphDriver: d.StorageDriver(), 53 GraphDriverOptions: nil, 54 IDMapping: idtools.IdentityMapping{}, 55 PluginGetter: nil, 56 ExperimentalEnabled: false, 57 }) 58 i := images.NewImageService(images.ImageServiceConfig{ 59 LayerStore: layerStore, 60 }) 61 62 const imgName = "test-garbage-collector" 63 64 // Build a image with multiple layers 65 dockerfile := `FROM busybox 66 RUN echo echo Running... > /run.sh` 67 source := fakecontext.New(t, "", fakecontext.WithDockerfile(dockerfile)) 68 defer source.Close() 69 resp, err := client.ImageBuild(ctx, 70 source.AsTarReader(t), 71 types.ImageBuildOptions{ 72 Remove: true, 73 ForceRemove: true, 74 Tags: []string{imgName}, 75 }) 76 assert.NilError(t, err) 77 _, err = io.Copy(io.Discard, resp.Body) 78 resp.Body.Close() 79 assert.NilError(t, err) 80 img, _, err := client.ImageInspectWithRaw(ctx, imgName) 81 assert.NilError(t, err) 82 83 // Mark latest image layer to immutable 84 data := img.GraphDriver.Data 85 file, _ := os.Open(data["UpperDir"]) 86 attr := 0x00000010 87 fsflags := uintptr(0x40086602) 88 argp := uintptr(unsafe.Pointer(&attr)) 89 _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, file.Fd(), fsflags, argp) 90 assert.Equal(t, "errno 0", errno.Error()) 91 92 // Try to remove the image, it should generate error 93 // but marking layer back to mutable before checking errors (so we don't break CI server) 94 _, err = client.ImageRemove(ctx, imgName, image.RemoveOptions{}) 95 attr = 0x00000000 96 argp = uintptr(unsafe.Pointer(&attr)) 97 _, _, errno = syscall.Syscall(syscall.SYS_IOCTL, file.Fd(), fsflags, argp) 98 assert.Equal(t, "errno 0", errno.Error()) 99 assert.Assert(t, err != nil) 100 errStr := err.Error() 101 if !(strings.Contains(errStr, "permission denied") || strings.Contains(errStr, "operation not permitted")) { 102 t.Errorf("ImageRemove error not an permission error %s", errStr) 103 } 104 105 // Verify that layer remaining on disk 106 dir, _ := os.Stat(data["UpperDir"]) 107 assert.Equal(t, "true", strconv.FormatBool(dir.IsDir())) 108 109 // Run imageService.Cleanup() and make sure that layer was removed from disk 110 i.Cleanup() 111 _, err = os.Stat(data["UpperDir"]) 112 assert.Assert(t, os.IsNotExist(err)) 113 114 // Make sure that removal pending layers does not exist on layerdb either 115 layerdbItems, _ := os.ReadDir(filepath.Join(d.RootDir(), "/image/overlay2/layerdb/sha256")) 116 for _, folder := range layerdbItems { 117 assert.Equal(t, false, strings.HasSuffix(folder.Name(), "-removing")) 118 } 119 }