github.com/lalkh/containerd@v1.4.3/snapshots/overlay/overlay_test.go (about)

     1  // +build linux
     2  
     3  /*
     4     Copyright The containerd Authors.
     5  
     6     Licensed under the Apache License, Version 2.0 (the "License");
     7     you may not use this file except in compliance with the License.
     8     You may obtain a copy of the License at
     9  
    10         http://www.apache.org/licenses/LICENSE-2.0
    11  
    12     Unless required by applicable law or agreed to in writing, software
    13     distributed under the License is distributed on an "AS IS" BASIS,
    14     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15     See the License for the specific language governing permissions and
    16     limitations under the License.
    17  */
    18  
    19  package overlay
    20  
    21  import (
    22  	"context"
    23  	"fmt"
    24  	"io/ioutil"
    25  	"os"
    26  	"path/filepath"
    27  	"syscall"
    28  	"testing"
    29  
    30  	"github.com/containerd/containerd/mount"
    31  	"github.com/containerd/containerd/pkg/testutil"
    32  	"github.com/containerd/containerd/snapshots"
    33  	"github.com/containerd/containerd/snapshots/storage"
    34  	"github.com/containerd/containerd/snapshots/testsuite"
    35  )
    36  
    37  func newSnapshotterWithOpts(opts ...Opt) testsuite.SnapshotterFunc {
    38  	return func(ctx context.Context, root string) (snapshots.Snapshotter, func() error, error) {
    39  		snapshotter, err := NewSnapshotter(root, opts...)
    40  		if err != nil {
    41  			return nil, nil, err
    42  		}
    43  
    44  		return snapshotter, func() error { return snapshotter.Close() }, nil
    45  	}
    46  }
    47  
    48  func TestOverlay(t *testing.T) {
    49  	testutil.RequiresRoot(t)
    50  	optTestCases := map[string][]Opt{
    51  		"no opt": nil,
    52  		// default in init()
    53  		"AsynchronousRemove": {AsynchronousRemove},
    54  	}
    55  
    56  	for optsName, opts := range optTestCases {
    57  		t.Run(optsName, func(t *testing.T) {
    58  			newSnapshotter := newSnapshotterWithOpts(opts...)
    59  			testsuite.SnapshotterSuite(t, "Overlay", newSnapshotter)
    60  			t.Run("TestOverlayMounts", func(t *testing.T) {
    61  				testOverlayMounts(t, newSnapshotter)
    62  			})
    63  			t.Run("TestOverlayCommit", func(t *testing.T) {
    64  				testOverlayCommit(t, newSnapshotter)
    65  			})
    66  			t.Run("TestOverlayOverlayMount", func(t *testing.T) {
    67  				testOverlayOverlayMount(t, newSnapshotter)
    68  			})
    69  			t.Run("TestOverlayOverlayRead", func(t *testing.T) {
    70  				testOverlayOverlayRead(t, newSnapshotter)
    71  			})
    72  			t.Run("TestOverlayView", func(t *testing.T) {
    73  				testOverlayView(t, newSnapshotter)
    74  			})
    75  		})
    76  	}
    77  }
    78  
    79  func testOverlayMounts(t *testing.T, newSnapshotter testsuite.SnapshotterFunc) {
    80  	ctx := context.TODO()
    81  	root, err := ioutil.TempDir("", "overlay")
    82  	if err != nil {
    83  		t.Fatal(err)
    84  	}
    85  	defer os.RemoveAll(root)
    86  	o, _, err := newSnapshotter(ctx, root)
    87  	if err != nil {
    88  		t.Fatal(err)
    89  	}
    90  	mounts, err := o.Prepare(ctx, "/tmp/test", "")
    91  	if err != nil {
    92  		t.Fatal(err)
    93  	}
    94  	if len(mounts) != 1 {
    95  		t.Errorf("should only have 1 mount but received %d", len(mounts))
    96  	}
    97  	m := mounts[0]
    98  	if m.Type != "bind" {
    99  		t.Errorf("mount type should be bind but received %q", m.Type)
   100  	}
   101  	expected := filepath.Join(root, "snapshots", "1", "fs")
   102  	if m.Source != expected {
   103  		t.Errorf("expected source %q but received %q", expected, m.Source)
   104  	}
   105  	if m.Options[0] != "rw" {
   106  		t.Errorf("expected mount option rw but received %q", m.Options[0])
   107  	}
   108  	if m.Options[1] != "rbind" {
   109  		t.Errorf("expected mount option rbind but received %q", m.Options[1])
   110  	}
   111  }
   112  
   113  func testOverlayCommit(t *testing.T, newSnapshotter testsuite.SnapshotterFunc) {
   114  	ctx := context.TODO()
   115  	root, err := ioutil.TempDir("", "overlay")
   116  	if err != nil {
   117  		t.Fatal(err)
   118  	}
   119  	defer os.RemoveAll(root)
   120  	o, _, err := newSnapshotter(ctx, root)
   121  	if err != nil {
   122  		t.Fatal(err)
   123  	}
   124  	key := "/tmp/test"
   125  	mounts, err := o.Prepare(ctx, key, "")
   126  	if err != nil {
   127  		t.Fatal(err)
   128  	}
   129  	m := mounts[0]
   130  	if err := ioutil.WriteFile(filepath.Join(m.Source, "foo"), []byte("hi"), 0660); err != nil {
   131  		t.Fatal(err)
   132  	}
   133  	if err := o.Commit(ctx, "base", key); err != nil {
   134  		t.Fatal(err)
   135  	}
   136  }
   137  
   138  func testOverlayOverlayMount(t *testing.T, newSnapshotter testsuite.SnapshotterFunc) {
   139  	ctx := context.TODO()
   140  	root, err := ioutil.TempDir("", "overlay")
   141  	if err != nil {
   142  		t.Fatal(err)
   143  	}
   144  	defer os.RemoveAll(root)
   145  	o, _, err := newSnapshotter(ctx, root)
   146  	if err != nil {
   147  		t.Fatal(err)
   148  	}
   149  	key := "/tmp/test"
   150  	if _, err = o.Prepare(ctx, key, ""); err != nil {
   151  		t.Fatal(err)
   152  	}
   153  	if err := o.Commit(ctx, "base", key); err != nil {
   154  		t.Fatal(err)
   155  	}
   156  	var mounts []mount.Mount
   157  	if mounts, err = o.Prepare(ctx, "/tmp/layer2", "base"); err != nil {
   158  		t.Fatal(err)
   159  	}
   160  	if len(mounts) != 1 {
   161  		t.Errorf("should only have 1 mount but received %d", len(mounts))
   162  	}
   163  	m := mounts[0]
   164  	if m.Type != "overlay" {
   165  		t.Errorf("mount type should be overlay but received %q", m.Type)
   166  	}
   167  	if m.Source != "overlay" {
   168  		t.Errorf("expected source %q but received %q", "overlay", m.Source)
   169  	}
   170  	var (
   171  		bp    = getBasePath(ctx, o, root, "/tmp/layer2")
   172  		work  = "workdir=" + filepath.Join(bp, "work")
   173  		upper = "upperdir=" + filepath.Join(bp, "fs")
   174  		lower = "lowerdir=" + getParents(ctx, o, root, "/tmp/layer2")[0]
   175  	)
   176  	for i, v := range []string{
   177  		"index=off",
   178  		work,
   179  		upper,
   180  		lower,
   181  	} {
   182  		if m.Options[i] != v {
   183  			t.Errorf("expected %q but received %q", v, m.Options[i])
   184  		}
   185  	}
   186  }
   187  
   188  func getBasePath(ctx context.Context, sn snapshots.Snapshotter, root, key string) string {
   189  	o := sn.(*snapshotter)
   190  	ctx, t, err := o.ms.TransactionContext(ctx, false)
   191  	if err != nil {
   192  		panic(err)
   193  	}
   194  	defer t.Rollback()
   195  
   196  	s, err := storage.GetSnapshot(ctx, key)
   197  	if err != nil {
   198  		panic(err)
   199  	}
   200  
   201  	return filepath.Join(root, "snapshots", s.ID)
   202  }
   203  
   204  func getParents(ctx context.Context, sn snapshots.Snapshotter, root, key string) []string {
   205  	o := sn.(*snapshotter)
   206  	ctx, t, err := o.ms.TransactionContext(ctx, false)
   207  	if err != nil {
   208  		panic(err)
   209  	}
   210  	defer t.Rollback()
   211  	s, err := storage.GetSnapshot(ctx, key)
   212  	if err != nil {
   213  		panic(err)
   214  	}
   215  	parents := make([]string, len(s.ParentIDs))
   216  	for i := range s.ParentIDs {
   217  		parents[i] = filepath.Join(root, "snapshots", s.ParentIDs[i], "fs")
   218  	}
   219  	return parents
   220  }
   221  
   222  func testOverlayOverlayRead(t *testing.T, newSnapshotter testsuite.SnapshotterFunc) {
   223  	testutil.RequiresRoot(t)
   224  	ctx := context.TODO()
   225  	root, err := ioutil.TempDir("", "overlay")
   226  	if err != nil {
   227  		t.Fatal(err)
   228  	}
   229  	defer os.RemoveAll(root)
   230  	o, _, err := newSnapshotter(ctx, root)
   231  	if err != nil {
   232  		t.Fatal(err)
   233  	}
   234  	key := "/tmp/test"
   235  	mounts, err := o.Prepare(ctx, key, "")
   236  	if err != nil {
   237  		t.Fatal(err)
   238  	}
   239  	m := mounts[0]
   240  	if err := ioutil.WriteFile(filepath.Join(m.Source, "foo"), []byte("hi"), 0660); err != nil {
   241  		t.Fatal(err)
   242  	}
   243  	if err := o.Commit(ctx, "base", key); err != nil {
   244  		t.Fatal(err)
   245  	}
   246  	if mounts, err = o.Prepare(ctx, "/tmp/layer2", "base"); err != nil {
   247  		t.Fatal(err)
   248  	}
   249  	dest := filepath.Join(root, "dest")
   250  	if err := os.Mkdir(dest, 0700); err != nil {
   251  		t.Fatal(err)
   252  	}
   253  	if err := mount.All(mounts, dest); err != nil {
   254  		t.Fatal(err)
   255  	}
   256  	defer syscall.Unmount(dest, 0)
   257  	data, err := ioutil.ReadFile(filepath.Join(dest, "foo"))
   258  	if err != nil {
   259  		t.Fatal(err)
   260  	}
   261  	if e := string(data); e != "hi" {
   262  		t.Fatalf("expected file contents hi but got %q", e)
   263  	}
   264  }
   265  
   266  func testOverlayView(t *testing.T, newSnapshotter testsuite.SnapshotterFunc) {
   267  	ctx := context.TODO()
   268  	root, err := ioutil.TempDir("", "overlay")
   269  	if err != nil {
   270  		t.Fatal(err)
   271  	}
   272  	defer os.RemoveAll(root)
   273  	o, _, err := newSnapshotter(ctx, root)
   274  	if err != nil {
   275  		t.Fatal(err)
   276  	}
   277  	key := "/tmp/base"
   278  	mounts, err := o.Prepare(ctx, key, "")
   279  	if err != nil {
   280  		t.Fatal(err)
   281  	}
   282  	m := mounts[0]
   283  	if err := ioutil.WriteFile(filepath.Join(m.Source, "foo"), []byte("hi"), 0660); err != nil {
   284  		t.Fatal(err)
   285  	}
   286  	if err := o.Commit(ctx, "base", key); err != nil {
   287  		t.Fatal(err)
   288  	}
   289  
   290  	key = "/tmp/top"
   291  	_, err = o.Prepare(ctx, key, "base")
   292  	if err != nil {
   293  		t.Fatal(err)
   294  	}
   295  	if err := ioutil.WriteFile(filepath.Join(getParents(ctx, o, root, "/tmp/top")[0], "foo"), []byte("hi, again"), 0660); err != nil {
   296  		t.Fatal(err)
   297  	}
   298  	if err := o.Commit(ctx, "top", key); err != nil {
   299  		t.Fatal(err)
   300  	}
   301  
   302  	mounts, err = o.View(ctx, "/tmp/view1", "base")
   303  	if err != nil {
   304  		t.Fatal(err)
   305  	}
   306  	if len(mounts) != 1 {
   307  		t.Fatalf("should only have 1 mount but received %d", len(mounts))
   308  	}
   309  	m = mounts[0]
   310  	if m.Type != "bind" {
   311  		t.Errorf("mount type should be bind but received %q", m.Type)
   312  	}
   313  	expected := getParents(ctx, o, root, "/tmp/view1")[0]
   314  	if m.Source != expected {
   315  		t.Errorf("expected source %q but received %q", expected, m.Source)
   316  	}
   317  	if m.Options[0] != "ro" {
   318  		t.Errorf("expected mount option ro but received %q", m.Options[0])
   319  	}
   320  	if m.Options[1] != "rbind" {
   321  		t.Errorf("expected mount option rbind but received %q", m.Options[1])
   322  	}
   323  
   324  	mounts, err = o.View(ctx, "/tmp/view2", "top")
   325  	if err != nil {
   326  		t.Fatal(err)
   327  	}
   328  	if len(mounts) != 1 {
   329  		t.Fatalf("should only have 1 mount but received %d", len(mounts))
   330  	}
   331  	m = mounts[0]
   332  	if m.Type != "overlay" {
   333  		t.Errorf("mount type should be overlay but received %q", m.Type)
   334  	}
   335  	if m.Source != "overlay" {
   336  		t.Errorf("mount source should be overlay but received %q", m.Source)
   337  	}
   338  	if len(m.Options) != 2 {
   339  		t.Errorf("expected 1 additional mount option but got %d", len(m.Options))
   340  	}
   341  	lowers := getParents(ctx, o, root, "/tmp/view2")
   342  	expected = fmt.Sprintf("lowerdir=%s:%s", lowers[0], lowers[1])
   343  	if m.Options[1] != expected {
   344  		t.Errorf("expected option %q but received %q", expected, m.Options[0])
   345  	}
   346  }