github.com/containerd/Containerd@v1.4.13/metadata/snapshot_test.go (about)

     1  /*
     2     Copyright The containerd Authors.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package metadata
    18  
    19  import (
    20  	"context"
    21  	"os"
    22  	"path/filepath"
    23  	"reflect"
    24  	"runtime"
    25  	"sync"
    26  	"testing"
    27  	"time"
    28  
    29  	"github.com/containerd/containerd/errdefs"
    30  	"github.com/containerd/containerd/filters"
    31  	"github.com/containerd/containerd/mount"
    32  	"github.com/containerd/containerd/namespaces"
    33  	"github.com/containerd/containerd/pkg/testutil"
    34  	"github.com/containerd/containerd/snapshots"
    35  	"github.com/containerd/containerd/snapshots/native"
    36  	"github.com/containerd/containerd/snapshots/testsuite"
    37  	"github.com/pkg/errors"
    38  	bolt "go.etcd.io/bbolt"
    39  )
    40  
    41  func newTestSnapshotter(ctx context.Context, root string) (snapshots.Snapshotter, func() error, error) {
    42  	nativeRoot := filepath.Join(root, "native")
    43  	if err := os.Mkdir(nativeRoot, 0770); err != nil {
    44  		return nil, nil, err
    45  	}
    46  	snapshotter, err := native.NewSnapshotter(nativeRoot)
    47  	if err != nil {
    48  		return nil, nil, err
    49  	}
    50  
    51  	db, err := bolt.Open(filepath.Join(root, "metadata.db"), 0660, nil)
    52  	if err != nil {
    53  		return nil, nil, err
    54  	}
    55  
    56  	sn := NewDB(db, nil, map[string]snapshots.Snapshotter{"native": snapshotter}).Snapshotter("native")
    57  
    58  	return sn, func() error {
    59  		if err := sn.Close(); err != nil {
    60  			return err
    61  		}
    62  		return db.Close()
    63  	}, nil
    64  }
    65  
    66  func TestMetadata(t *testing.T) {
    67  	if runtime.GOOS == "windows" {
    68  		t.Skip("snapshotter not implemented on windows")
    69  	}
    70  	// Snapshot tests require mounting, still requires root
    71  	testutil.RequiresRoot(t)
    72  	testsuite.SnapshotterSuite(t, "Metadata", newTestSnapshotter)
    73  }
    74  
    75  func TestSnapshotterWithRef(t *testing.T) {
    76  	ctx, db, done := testDB(t, withSnapshotter("tmp", func(string) (snapshots.Snapshotter, error) {
    77  		return NewTmpSnapshotter(), nil
    78  	}))
    79  	defer done()
    80  
    81  	sn := db.Snapshotter("tmp")
    82  
    83  	test1opt := snapshots.WithLabels(
    84  		map[string]string{
    85  			labelSnapshotRef: "test1",
    86  		},
    87  	)
    88  
    89  	_, err := sn.Prepare(ctx, "test1-tmp", "", test1opt)
    90  	if err != nil {
    91  		t.Fatal(err)
    92  	}
    93  
    94  	err = sn.Commit(ctx, "test1", "test1-tmp", test1opt)
    95  	if err != nil {
    96  		t.Fatal(err)
    97  	}
    98  
    99  	ctx2 := namespaces.WithNamespace(ctx, "testing2")
   100  
   101  	_, err = sn.Prepare(ctx2, "test1-tmp", "", test1opt)
   102  	if err == nil {
   103  		t.Fatal("expected already exists error")
   104  	} else if !errdefs.IsAlreadyExists(err) {
   105  		t.Fatal(err)
   106  	}
   107  
   108  	// test1 should now be in the namespace
   109  	_, err = sn.Stat(ctx2, "test1")
   110  	if err != nil {
   111  		t.Fatal(err)
   112  	}
   113  
   114  	test2opt := snapshots.WithLabels(
   115  		map[string]string{
   116  			labelSnapshotRef: "test2",
   117  		},
   118  	)
   119  
   120  	_, err = sn.Prepare(ctx2, "test2-tmp", "test1", test2opt)
   121  	if err != nil {
   122  		t.Fatal(err)
   123  	}
   124  
   125  	// In original namespace, but not committed
   126  	_, err = sn.Prepare(ctx, "test2-tmp", "test1", test2opt)
   127  	if err != nil {
   128  		t.Fatal(err)
   129  	}
   130  
   131  	err = sn.Commit(ctx2, "test2", "test2-tmp", test2opt)
   132  	if err != nil {
   133  		t.Fatal(err)
   134  	}
   135  
   136  	// See note in Commit function for why
   137  	// this does not return ErrAlreadyExists
   138  	err = sn.Commit(ctx, "test2", "test2-tmp", test2opt)
   139  	if err != nil {
   140  		t.Fatal(err)
   141  	}
   142  
   143  	// This should error out, already exists in namespace
   144  	// despite mismatched parent
   145  	_, err = sn.Prepare(ctx2, "test2-tmp-again", "", test2opt)
   146  	if err == nil {
   147  		t.Fatal("expected already exists error")
   148  	} else if !errdefs.IsAlreadyExists(err) {
   149  		t.Fatal(err)
   150  	}
   151  
   152  	// In original namespace, but already exists
   153  	_, err = sn.Prepare(ctx, "test2-tmp-again", "test1", test2opt)
   154  	if err == nil {
   155  		t.Fatal("expected already exists error")
   156  	} else if !errdefs.IsAlreadyExists(err) {
   157  		t.Fatal(err)
   158  	}
   159  
   160  	// Now try a third namespace
   161  
   162  	ctx3 := namespaces.WithNamespace(ctx, "testing3")
   163  
   164  	// This should error out, matching parent not found
   165  	_, err = sn.Prepare(ctx3, "test2-tmp", "", test2opt)
   166  	if err != nil {
   167  		t.Fatal(err)
   168  	}
   169  
   170  	// Remove, not going to use yet
   171  	err = sn.Remove(ctx3, "test2-tmp")
   172  	if err != nil {
   173  		t.Fatal(err)
   174  	}
   175  
   176  	_, err = sn.Prepare(ctx3, "test2-tmp", "test1", test2opt)
   177  	if err == nil {
   178  		t.Fatal("expected not error")
   179  	} else if !errdefs.IsNotFound(err) {
   180  		t.Fatal(err)
   181  	}
   182  
   183  	_, err = sn.Prepare(ctx3, "test1-tmp", "", test1opt)
   184  	if err == nil {
   185  		t.Fatal("expected already exists error")
   186  	} else if !errdefs.IsAlreadyExists(err) {
   187  		t.Fatal(err)
   188  	}
   189  
   190  	_, err = sn.Prepare(ctx3, "test2-tmp", "test1", test2opt)
   191  	if err == nil {
   192  		t.Fatal("expected already exists error")
   193  	} else if !errdefs.IsAlreadyExists(err) {
   194  		t.Fatal(err)
   195  	}
   196  }
   197  
   198  func TestFilterInheritedLabels(t *testing.T) {
   199  	tests := []struct {
   200  		labels   map[string]string
   201  		expected map[string]string
   202  	}{
   203  		{
   204  			nil,
   205  			nil,
   206  		},
   207  		{
   208  			map[string]string{},
   209  			map[string]string{},
   210  		},
   211  		{
   212  			map[string]string{"": ""},
   213  			map[string]string{},
   214  		},
   215  		{
   216  			map[string]string{"foo": "bar"},
   217  			map[string]string{},
   218  		},
   219  		{
   220  			map[string]string{inheritedLabelsPrefix + "foo": "bar"},
   221  			map[string]string{inheritedLabelsPrefix + "foo": "bar"},
   222  		},
   223  		{
   224  			map[string]string{inheritedLabelsPrefix + "foo": "bar", "qux": "qaz"},
   225  			map[string]string{inheritedLabelsPrefix + "foo": "bar"},
   226  		},
   227  	}
   228  
   229  	for _, test := range tests {
   230  		if actual := snapshots.FilterInheritedLabels(test.labels); !reflect.DeepEqual(actual, test.expected) {
   231  			t.Fatalf("expected %v but got %v", test.expected, actual)
   232  		}
   233  	}
   234  }
   235  
   236  type tmpSnapshotter struct {
   237  	l         sync.Mutex
   238  	snapshots map[string]snapshots.Info
   239  	targets   map[string][]string
   240  }
   241  
   242  func NewTmpSnapshotter() snapshots.Snapshotter {
   243  	return &tmpSnapshotter{
   244  		snapshots: map[string]snapshots.Info{},
   245  		targets:   map[string][]string{},
   246  	}
   247  }
   248  
   249  func (s *tmpSnapshotter) Stat(ctx context.Context, key string) (snapshots.Info, error) {
   250  	s.l.Lock()
   251  	defer s.l.Unlock()
   252  	i, ok := s.snapshots[key]
   253  	if !ok {
   254  		return snapshots.Info{}, errdefs.ErrNotFound
   255  	}
   256  	return i, nil
   257  }
   258  
   259  func (s *tmpSnapshotter) Update(ctx context.Context, info snapshots.Info, fieldpaths ...string) (snapshots.Info, error) {
   260  	s.l.Lock()
   261  	defer s.l.Unlock()
   262  
   263  	i, ok := s.snapshots[info.Name]
   264  	if !ok {
   265  		return snapshots.Info{}, errdefs.ErrNotFound
   266  	}
   267  
   268  	for k, v := range info.Labels {
   269  		i.Labels[k] = v
   270  	}
   271  
   272  	s.snapshots[i.Name] = i
   273  
   274  	return i, nil
   275  }
   276  
   277  func (s *tmpSnapshotter) Usage(ctx context.Context, key string) (snapshots.Usage, error) {
   278  	s.l.Lock()
   279  	defer s.l.Unlock()
   280  	_, ok := s.snapshots[key]
   281  	if !ok {
   282  		return snapshots.Usage{}, errdefs.ErrNotFound
   283  	}
   284  	return snapshots.Usage{}, nil
   285  }
   286  
   287  func (s *tmpSnapshotter) Mounts(ctx context.Context, key string) ([]mount.Mount, error) {
   288  	s.l.Lock()
   289  	defer s.l.Unlock()
   290  	_, ok := s.snapshots[key]
   291  	if !ok {
   292  		return nil, errdefs.ErrNotFound
   293  	}
   294  	return []mount.Mount{}, nil
   295  }
   296  
   297  func (s *tmpSnapshotter) Prepare(ctx context.Context, key, parent string, opts ...snapshots.Opt) ([]mount.Mount, error) {
   298  	return s.create(ctx, key, parent, snapshots.KindActive, opts...)
   299  }
   300  
   301  func (s *tmpSnapshotter) View(ctx context.Context, key, parent string, opts ...snapshots.Opt) ([]mount.Mount, error) {
   302  	return s.create(ctx, key, parent, snapshots.KindView, opts...)
   303  }
   304  
   305  func (s *tmpSnapshotter) create(ctx context.Context, key, parent string, kind snapshots.Kind, opts ...snapshots.Opt) ([]mount.Mount, error) {
   306  	s.l.Lock()
   307  	defer s.l.Unlock()
   308  
   309  	var base snapshots.Info
   310  	for _, opt := range opts {
   311  		if err := opt(&base); err != nil {
   312  			return nil, err
   313  		}
   314  	}
   315  	base.Name = key
   316  	base.Kind = kind
   317  
   318  	target := base.Labels[labelSnapshotRef]
   319  	if target != "" {
   320  		for _, name := range s.targets[target] {
   321  			if s.snapshots[name].Parent == parent {
   322  				return nil, errors.Wrap(errdefs.ErrAlreadyExists, "found target")
   323  			}
   324  		}
   325  	}
   326  
   327  	if parent != "" {
   328  		_, ok := s.snapshots[parent]
   329  		if !ok {
   330  			return nil, errdefs.ErrNotFound
   331  		}
   332  		base.Parent = parent
   333  	}
   334  
   335  	ts := time.Now().UTC()
   336  	base.Created = ts
   337  	base.Updated = ts
   338  
   339  	s.snapshots[base.Name] = base
   340  
   341  	return []mount.Mount{}, nil
   342  }
   343  
   344  func (s *tmpSnapshotter) Commit(ctx context.Context, name, key string, opts ...snapshots.Opt) error {
   345  	s.l.Lock()
   346  	defer s.l.Unlock()
   347  
   348  	var base snapshots.Info
   349  	for _, opt := range opts {
   350  		if err := opt(&base); err != nil {
   351  			return err
   352  		}
   353  	}
   354  	base.Name = name
   355  	base.Kind = snapshots.KindCommitted
   356  
   357  	if _, ok := s.snapshots[name]; ok {
   358  		return errors.Wrap(errdefs.ErrAlreadyExists, "found name")
   359  	}
   360  
   361  	src, ok := s.snapshots[key]
   362  	if !ok {
   363  		return errdefs.ErrNotFound
   364  	}
   365  	if src.Kind == snapshots.KindCommitted {
   366  		return errdefs.ErrInvalidArgument
   367  	}
   368  	base.Parent = src.Parent
   369  
   370  	ts := time.Now().UTC()
   371  	base.Created = ts
   372  	base.Updated = ts
   373  
   374  	s.snapshots[name] = base
   375  	delete(s.snapshots, key)
   376  
   377  	if target := base.Labels[labelSnapshotRef]; target != "" {
   378  		s.targets[target] = append(s.targets[target], name)
   379  	}
   380  
   381  	return nil
   382  }
   383  
   384  func (s *tmpSnapshotter) Remove(ctx context.Context, key string) error {
   385  	s.l.Lock()
   386  	defer s.l.Unlock()
   387  
   388  	sn, ok := s.snapshots[key]
   389  	if !ok {
   390  		return errdefs.ErrNotFound
   391  	}
   392  	delete(s.snapshots, key)
   393  
   394  	// scan and remove all instances of name as a target
   395  	for ref, names := range s.targets {
   396  		for i := range names {
   397  			if names[i] == sn.Name {
   398  				if len(names) == 1 {
   399  					delete(s.targets, ref)
   400  				} else {
   401  					copy(names[i:], names[i+1:])
   402  					s.targets[ref] = names[:len(names)-1]
   403  				}
   404  				break
   405  			}
   406  		}
   407  	}
   408  
   409  	return nil
   410  }
   411  
   412  func (s *tmpSnapshotter) Walk(ctx context.Context, fn snapshots.WalkFunc, fs ...string) error {
   413  	s.l.Lock()
   414  	defer s.l.Unlock()
   415  
   416  	filter, err := filters.ParseAll(fs...)
   417  	if err != nil {
   418  		return err
   419  	}
   420  
   421  	// call func for each
   422  	for _, i := range s.snapshots {
   423  		if filter.Match(adaptSnapshot(i)) {
   424  			if err := fn(ctx, i); err != nil {
   425  				return err
   426  			}
   427  		}
   428  	}
   429  	return nil
   430  }
   431  
   432  func (s *tmpSnapshotter) Close() error {
   433  	return nil
   434  }