github.com/lalkh/containerd@v1.4.3/metadata/leases_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  	"testing"
    21  
    22  	"github.com/containerd/containerd/errdefs"
    23  	"github.com/containerd/containerd/leases"
    24  	"github.com/pkg/errors"
    25  	bolt "go.etcd.io/bbolt"
    26  )
    27  
    28  func TestLeases(t *testing.T) {
    29  	ctx, db, cancel := testEnv(t)
    30  	defer cancel()
    31  
    32  	lm := NewLeaseManager(NewDB(db, nil, nil))
    33  
    34  	testCases := []struct {
    35  		ID        string
    36  		CreateErr error
    37  		DeleteErr error
    38  	}{
    39  		{
    40  			ID: "tx1",
    41  		},
    42  		{
    43  			ID:        "tx1",
    44  			CreateErr: errdefs.ErrAlreadyExists,
    45  			DeleteErr: errdefs.ErrNotFound,
    46  		},
    47  		{
    48  			ID: "tx2",
    49  		},
    50  	}
    51  
    52  	var ll []leases.Lease
    53  
    54  	for _, tc := range testCases {
    55  		if err := db.Update(func(tx *bolt.Tx) error {
    56  			lease, err := lm.Create(WithTransactionContext(ctx, tx), leases.WithID(tc.ID))
    57  			if err != nil {
    58  				if tc.CreateErr != nil && errors.Is(err, tc.CreateErr) {
    59  					return nil
    60  				}
    61  				return err
    62  			}
    63  			ll = append(ll, lease)
    64  			return nil
    65  		}); err != nil {
    66  			t.Fatal(err)
    67  		}
    68  	}
    69  
    70  	listed, err := lm.List(ctx)
    71  	if err != nil {
    72  		t.Fatal(err)
    73  	}
    74  
    75  	if len(listed) != len(ll) {
    76  		t.Fatalf("Expected %d lease, got %d", len(ll), len(listed))
    77  	}
    78  	for i := range listed {
    79  		if listed[i].ID != ll[i].ID {
    80  			t.Fatalf("Expected lease ID %s, got %s", ll[i].ID, listed[i].ID)
    81  		}
    82  		if listed[i].CreatedAt != ll[i].CreatedAt {
    83  			t.Fatalf("Expected lease created at time %s, got %s", ll[i].CreatedAt, listed[i].CreatedAt)
    84  		}
    85  	}
    86  
    87  	for _, tc := range testCases {
    88  		if err := lm.Delete(ctx, leases.Lease{
    89  			ID: tc.ID,
    90  		}); err != nil {
    91  			if tc.DeleteErr == nil && !errors.Is(err, tc.DeleteErr) {
    92  				t.Fatal(err)
    93  			}
    94  
    95  		}
    96  	}
    97  
    98  	listed, err = lm.List(ctx)
    99  	if err != nil {
   100  		t.Fatal(err)
   101  	}
   102  
   103  	if len(listed) > 0 {
   104  		t.Fatalf("Expected no leases, found %d: %v", len(listed), listed)
   105  	}
   106  }
   107  
   108  func TestLeasesList(t *testing.T) {
   109  	ctx, db, cancel := testEnv(t)
   110  	defer cancel()
   111  
   112  	lm := NewLeaseManager(NewDB(db, nil, nil))
   113  
   114  	testset := [][]leases.Opt{
   115  		{
   116  			leases.WithID("lease1"),
   117  			leases.WithLabels(map[string]string{
   118  				"label1": "value1",
   119  				"label3": "other",
   120  			}),
   121  		},
   122  		{
   123  			leases.WithID("lease2"),
   124  			leases.WithLabels(map[string]string{
   125  				"label1": "value1",
   126  				"label2": "",
   127  				"label3": "other",
   128  			}),
   129  		},
   130  		{
   131  			leases.WithID("lease3"),
   132  			leases.WithLabels(map[string]string{
   133  				"label1": "value2",
   134  				"label2": "something",
   135  			}),
   136  		},
   137  	}
   138  
   139  	// Insert all
   140  	if err := db.Update(func(tx *bolt.Tx) error {
   141  		for _, opts := range testset {
   142  			_, err := lm.Create(WithTransactionContext(ctx, tx), opts...)
   143  			if err != nil {
   144  				return err
   145  			}
   146  		}
   147  		return nil
   148  	}); err != nil {
   149  		t.Fatal(err)
   150  	}
   151  
   152  	for _, testcase := range []struct {
   153  		name     string
   154  		filters  []string
   155  		expected []string
   156  	}{
   157  		{
   158  			name:     "All",
   159  			filters:  []string{},
   160  			expected: []string{"lease1", "lease2", "lease3"},
   161  		},
   162  		{
   163  			name:     "ID",
   164  			filters:  []string{"id==lease1"},
   165  			expected: []string{"lease1"},
   166  		},
   167  		{
   168  			name:     "IDx2",
   169  			filters:  []string{"id==lease1", "id==lease2"},
   170  			expected: []string{"lease1", "lease2"},
   171  		},
   172  		{
   173  			name:     "Label1",
   174  			filters:  []string{"labels.label1"},
   175  			expected: []string{"lease1", "lease2", "lease3"},
   176  		},
   177  
   178  		{
   179  			name:     "Label1value1",
   180  			filters:  []string{"labels.label1==value1"},
   181  			expected: []string{"lease1", "lease2"},
   182  		},
   183  		{
   184  			name:     "Label1value2",
   185  			filters:  []string{"labels.label1==value2"},
   186  			expected: []string{"lease3"},
   187  		},
   188  		{
   189  			name:     "Label2",
   190  			filters:  []string{"labels.label2"},
   191  			expected: []string{"lease3"},
   192  		},
   193  		{
   194  			name:     "Label3",
   195  			filters:  []string{"labels.label2", "labels.label3"},
   196  			expected: []string{"lease1", "lease2", "lease3"},
   197  		},
   198  	} {
   199  		t.Run(testcase.name, func(t *testing.T) {
   200  			results, err := lm.List(ctx, testcase.filters...)
   201  			if err != nil {
   202  				t.Fatal(err)
   203  			}
   204  
   205  			if len(results) != len(testcase.expected) {
   206  				t.Errorf("length of result does not match expected: %v != %v", len(results), len(testcase.expected))
   207  			}
   208  
   209  			expectedMap := map[string]struct{}{}
   210  			for _, expected := range testcase.expected {
   211  				expectedMap[expected] = struct{}{}
   212  			}
   213  
   214  			for _, result := range results {
   215  				if _, ok := expectedMap[result.ID]; !ok {
   216  					t.Errorf("unexpected match: %v", result.ID)
   217  				} else {
   218  					delete(expectedMap, result.ID)
   219  				}
   220  			}
   221  			if len(expectedMap) > 0 {
   222  				for match := range expectedMap {
   223  					t.Errorf("missing match: %v", match)
   224  				}
   225  			}
   226  
   227  		})
   228  	}
   229  
   230  	// delete everything to test it
   231  	for _, opts := range testset {
   232  		var lease leases.Lease
   233  		for _, opt := range opts {
   234  			if err := opt(&lease); err != nil {
   235  				t.Fatal(err)
   236  			}
   237  		}
   238  
   239  		if err := lm.Delete(ctx, lease); err != nil {
   240  			t.Fatal(err)
   241  		}
   242  
   243  		// try it again, get not found
   244  		if err := lm.Delete(ctx, lease); err == nil {
   245  			t.Fatalf("expected error deleting non-existent lease")
   246  		} else if !errdefs.IsNotFound(err) {
   247  			t.Fatalf("unexpected error: %s", err)
   248  		}
   249  	}
   250  }
   251  
   252  func TestLeaseResource(t *testing.T) {
   253  	ctx, db, cancel := testEnv(t)
   254  	defer cancel()
   255  
   256  	lm := NewLeaseManager(NewDB(db, nil, nil))
   257  
   258  	var (
   259  		leaseID = "l1"
   260  
   261  		lease = leases.Lease{
   262  			ID: leaseID,
   263  		}
   264  
   265  		snapshotterKey = "RstMI3X8vguKoPFkmIStZ5fQFI7F1L0o"
   266  	)
   267  
   268  	// prepare lease
   269  	if _, err := lm.Create(ctx, leases.WithID(leaseID)); err != nil {
   270  		t.Fatal(err)
   271  	}
   272  
   273  	testCases := []struct {
   274  		lease    leases.Lease
   275  		resource leases.Resource
   276  		err      error
   277  	}{
   278  		{
   279  			lease: lease,
   280  			resource: leases.Resource{
   281  				ID:   "sha256:29f5d56d12684887bdfa50dcd29fc31eea4aaf4ad3bec43daf19026a7ce69912",
   282  				Type: "content",
   283  			},
   284  		},
   285  		{
   286  			lease: lease,
   287  			resource: leases.Resource{
   288  				ID:   "d2UdcINOwrBTQG9kS8rySAM3eMNBSojH",
   289  				Type: "ingests",
   290  			},
   291  		},
   292  		{
   293  			// allow to add resource which exists
   294  			lease: lease,
   295  			resource: leases.Resource{
   296  				ID:   "d2UdcINOwrBTQG9kS8rySAM3eMNBSojH",
   297  				Type: "ingests",
   298  			},
   299  		},
   300  		{
   301  			// not allow to reference to lease
   302  			lease: lease,
   303  			resource: leases.Resource{
   304  				ID:   "xCAV3F6PddlXitbtby0Vo23Qof6RTWpG",
   305  				Type: "leases",
   306  			},
   307  			err: errdefs.ErrNotImplemented,
   308  		},
   309  		{
   310  			// not allow to reference to container
   311  			lease: lease,
   312  			resource: leases.Resource{
   313  				ID:   "05O9ljptPu5Qq9kZGOacEfymBwQFM8ZH",
   314  				Type: "containers",
   315  			},
   316  			err: errdefs.ErrNotImplemented,
   317  		},
   318  		{
   319  			// not allow to reference to image
   320  			lease: lease,
   321  			resource: leases.Resource{
   322  				ID:   "qBUHpWBn03YaCt9cL3PPGKWoxBqTlLfu",
   323  				Type: "image",
   324  			},
   325  			err: errdefs.ErrNotImplemented,
   326  		},
   327  		{
   328  			lease: lease,
   329  			resource: leases.Resource{
   330  				ID:   "HMemOhlygombYhkhHhAZj5aRbDy2a3z2",
   331  				Type: "snapshots",
   332  			},
   333  			err: errdefs.ErrInvalidArgument,
   334  		},
   335  		{
   336  			lease: lease,
   337  			resource: leases.Resource{
   338  				ID:   snapshotterKey,
   339  				Type: "snapshots/overlayfs",
   340  			},
   341  		},
   342  		{
   343  			lease: lease,
   344  			resource: leases.Resource{
   345  				ID:   "HMemOhlygombYhkhHhAZj5aRbDy2a3z2",
   346  				Type: "snapshots/overlayfs/type1",
   347  			},
   348  			err: errdefs.ErrInvalidArgument,
   349  		},
   350  		{
   351  			lease: leases.Lease{
   352  				ID: "non-found",
   353  			},
   354  			resource: leases.Resource{
   355  				ID:   "HMemOhlygombYhkhHhAZj5aRbDy2a3z2",
   356  				Type: "snapshots/overlayfs",
   357  			},
   358  			err: errdefs.ErrNotFound,
   359  		},
   360  	}
   361  
   362  	idxList := make(map[leases.Resource]bool)
   363  	for i, tc := range testCases {
   364  		if err := db.Update(func(tx *bolt.Tx) error {
   365  			err0 := lm.AddResource(WithTransactionContext(ctx, tx), tc.lease, tc.resource)
   366  			if !errors.Is(err0, tc.err) {
   367  				return errors.Errorf("expect error (%v), but got (%v)", tc.err, err0)
   368  			}
   369  
   370  			if err0 == nil {
   371  				// not visited yet
   372  				idxList[tc.resource] = false
   373  			}
   374  			return nil
   375  		}); err != nil {
   376  			t.Fatalf("failed to run case %d with resource: %v", i, err)
   377  		}
   378  	}
   379  
   380  	// check list function
   381  	var gotList []leases.Resource
   382  	gotList, err := lm.ListResources(ctx, lease)
   383  	if err != nil {
   384  		t.Fatal(err)
   385  	}
   386  
   387  	if len(gotList) != len(idxList) {
   388  		t.Fatalf("expected (%d) resources, but got (%d)", len(idxList), len(gotList))
   389  	}
   390  
   391  	for _, r := range gotList {
   392  		visited, ok := idxList[r]
   393  		if !ok {
   394  			t.Fatalf("unexpected resource(%v)", r)
   395  		}
   396  		if visited {
   397  			t.Fatalf("duplicate resource(%v)", r)
   398  		}
   399  		idxList[r] = true
   400  	}
   401  
   402  	// remove snapshots
   403  	if err := lm.DeleteResource(ctx, lease, leases.Resource{
   404  		ID:   snapshotterKey,
   405  		Type: "snapshots/overlayfs",
   406  	}); err != nil {
   407  		t.Fatal(err)
   408  	}
   409  
   410  	// check list number
   411  	gotList, err = lm.ListResources(ctx, lease)
   412  	if err != nil {
   413  		t.Fatal(err)
   414  	}
   415  
   416  	if len(gotList)+1 != len(idxList) {
   417  		t.Fatalf("expected (%d) resources, but got (%d)", len(idxList)-1, len(gotList))
   418  	}
   419  }