github.com/containerd/nerdctl@v1.7.7/pkg/imgutil/snapshotter_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 imgutil
    18  
    19  import (
    20  	"context"
    21  	"reflect"
    22  	"testing"
    23  
    24  	"github.com/containerd/containerd"
    25  	ctdsnapshotters "github.com/containerd/containerd/pkg/snapshotters"
    26  	"github.com/containerd/nerdctl/pkg/imgutil/pull"
    27  	digest "github.com/opencontainers/go-digest"
    28  	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
    29  	"gotest.tools/v3/assert"
    30  )
    31  
    32  const (
    33  	targetRefLabel = "containerd.io/snapshot/remote/stargz.reference"
    34  	testRef        = "test:latest"
    35  )
    36  
    37  func TestGetSnapshotterOpts(t *testing.T) {
    38  	type testCase struct {
    39  		sns   []string
    40  		check func(t *testing.T, o snapshotterOpts)
    41  	}
    42  	testCases := []testCase{
    43  		{
    44  			sns:   []string{"overlayfs"},
    45  			check: sameOpts(&defaultSnapshotterOpts{snapshotter: "overlayfs"}),
    46  		},
    47  		{
    48  			sns:   []string{"overlayfs2"},
    49  			check: sameOpts(&defaultSnapshotterOpts{snapshotter: "overlayfs2"}),
    50  		},
    51  		{
    52  			sns:   []string{"stargz", "stargz-v1"},
    53  			check: remoteSnOpts("stargz", true),
    54  		},
    55  		{
    56  			sns:   []string{"soci"},
    57  			check: remoteSnOpts("soci", true),
    58  		},
    59  		{
    60  			sns:   []string{"overlaybd", "overlaybd-v2"},
    61  			check: sameOpts(&remoteSnapshotterOpts{snapshotter: "overlaybd"}),
    62  		},
    63  		{
    64  			sns:   []string{"nydus", "nydus-v3"},
    65  			check: sameOpts(&remoteSnapshotterOpts{snapshotter: "nydus"}),
    66  		},
    67  	}
    68  	for _, tc := range testCases {
    69  		for i := range tc.sns {
    70  			got := getSnapshotterOpts(tc.sns[i])
    71  			tc.check(t, got)
    72  		}
    73  	}
    74  }
    75  
    76  func remoteSnOpts(name string, withExtra bool) func(*testing.T, snapshotterOpts) {
    77  	return func(t *testing.T, got snapshotterOpts) {
    78  		opts, ok := got.(*remoteSnapshotterOpts)
    79  		assert.Equal(t, ok, true)
    80  		assert.Equal(t, opts.snapshotter, name)
    81  		assert.Equal(t, opts.extraLabels != nil, withExtra)
    82  	}
    83  }
    84  
    85  func sameOpts(want snapshotterOpts) func(*testing.T, snapshotterOpts) {
    86  	return func(t *testing.T, got snapshotterOpts) {
    87  		if !reflect.DeepEqual(got, want) {
    88  			t.Errorf("getSnapshotterOpts() got = %v, want %v", got, want)
    89  		}
    90  	}
    91  }
    92  
    93  func getAndApplyRemoteOpts(t *testing.T, sn string) *containerd.RemoteContext {
    94  	config := &pull.Config{}
    95  	snOpts := getSnapshotterOpts(sn)
    96  	rFlags := RemoteSnapshotterFlags{}
    97  	snOpts.apply(config, testRef, rFlags)
    98  
    99  	rc := &containerd.RemoteContext{}
   100  	for _, o := range config.RemoteOpts {
   101  		// here passing a nil client is safe
   102  		// because the remote opts will not use client
   103  		if err := o(nil, rc); err != nil {
   104  			t.Errorf("failed to apply remote opts: %s", err)
   105  		}
   106  	}
   107  
   108  	return rc
   109  }
   110  
   111  func TestDefaultSnapshotterOpts(t *testing.T) {
   112  	rc := getAndApplyRemoteOpts(t, "overlayfs")
   113  	assert.Equal(t, rc.Snapshotter, "overlayfs")
   114  }
   115  
   116  // dummyImageHandler will return a dummy layer
   117  // see https://github.com/containerd/containerd/blob/77d53d2d230c3bcd3f02e6f493019a72905c875b/images/mediatypes.go#L115
   118  type dummyImageHandler struct{}
   119  
   120  func (dih *dummyImageHandler) Handle(_ctx context.Context, _desc ocispec.Descriptor) (subdescs []ocispec.Descriptor, err error) {
   121  	return []ocispec.Descriptor{
   122  		{
   123  			MediaType: "application/vnd.oci.image.layer.dummy",
   124  			Digest:    digest.FromString("dummy"),
   125  		},
   126  	}, nil
   127  }
   128  
   129  func TestRemoteSnapshotterOpts(t *testing.T) {
   130  	tests := []struct {
   131  		name  string
   132  		check []func(t *testing.T, a map[string]string)
   133  	}{
   134  		{
   135  			name: "stargz",
   136  			check: []func(t *testing.T, a map[string]string){
   137  				checkRemoteSnapshotterAnnotataions, checkStargzSnapshotterAnnotataions,
   138  			},
   139  		},
   140  		{
   141  			name: "soci",
   142  			check: []func(t *testing.T, a map[string]string){
   143  				checkRemoteSnapshotterAnnotataions, checkSociSnapshotterAnnotataions,
   144  			},
   145  		},
   146  		{
   147  			name:  "nydus",
   148  			check: []func(t *testing.T, a map[string]string){checkRemoteSnapshotterAnnotataions},
   149  		},
   150  		{
   151  			name:  "overlaybd",
   152  			check: []func(t *testing.T, a map[string]string){checkRemoteSnapshotterAnnotataions},
   153  		},
   154  	}
   155  
   156  	for _, tt := range tests {
   157  		tt := tt
   158  		sn := tt.name
   159  		t.Run(sn, func(t *testing.T) {
   160  			rc := getAndApplyRemoteOpts(t, sn)
   161  			assert.Equal(t, rc.Snapshotter, sn)
   162  
   163  			desc := ocispec.Descriptor{
   164  				MediaType: ocispec.MediaTypeImageManifest,
   165  			}
   166  
   167  			h := &dummyImageHandler{}
   168  			got, err := rc.HandlerWrapper(h).Handle(context.Background(), desc)
   169  
   170  			assert.NilError(t, err)
   171  			assert.Check(t, len(got) == 1)
   172  			for _, f := range tt.check {
   173  				f(t, got[0].Annotations)
   174  			}
   175  		})
   176  	}
   177  }
   178  
   179  func checkRemoteSnapshotterAnnotataions(t *testing.T, a map[string]string) {
   180  	assert.Check(t, a != nil)
   181  	assert.Equal(t, a[ctdsnapshotters.TargetRefLabel], testRef)
   182  }
   183  
   184  func checkStargzSnapshotterAnnotataions(t *testing.T, a map[string]string) {
   185  	assert.Check(t, a != nil)
   186  	_, ok := a["containerd.io/snapshot/remote/urls"]
   187  	assert.Equal(t, ok, true)
   188  }
   189  
   190  // using values from soci source to check for annotations (
   191  // see https://github.com/awslabs/soci-snapshotter/blob/b05ba712d246ecc5146469f87e5e9305702fd72b/fs/source/source.go#L80C1-L80C6
   192  func checkSociSnapshotterAnnotataions(t *testing.T, a map[string]string) {
   193  	assert.Check(t, a != nil)
   194  	_, ok := a["containerd.io/snapshot/remote/soci.size"]
   195  	assert.Equal(t, ok, true)
   196  	_, ok = a["containerd.io/snapshot/remote/image.layers.size"]
   197  	assert.Equal(t, ok, true)
   198  	_, ok = a["containerd.io/snapshot/remote/soci.index.digest"]
   199  	assert.Equal(t, ok, true)
   200  
   201  }