github.com/containerd/nerdctl@v1.7.7/cmd/nerdctl/image_pull_linux_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 main
    18  
    19  import (
    20  	"fmt"
    21  	"os"
    22  	"os/exec"
    23  	"path/filepath"
    24  	"strings"
    25  	"testing"
    26  
    27  	"github.com/containerd/nerdctl/pkg/testutil"
    28  	"github.com/containerd/nerdctl/pkg/testutil/testregistry"
    29  	"gotest.tools/v3/assert"
    30  )
    31  
    32  type cosignKeyPair struct {
    33  	publicKey  string
    34  	privateKey string
    35  	cleanup    func()
    36  }
    37  
    38  func newCosignKeyPair(t testing.TB, path string) *cosignKeyPair {
    39  	td, err := os.MkdirTemp(t.TempDir(), path)
    40  	assert.NilError(t, err)
    41  
    42  	cmd := exec.Command("cosign", "generate-key-pair")
    43  	cmd.Dir = td
    44  	if out, err := cmd.CombinedOutput(); err != nil {
    45  		t.Fatalf("failed to run %v: %v (%q)", cmd.Args, err, string(out))
    46  	}
    47  
    48  	publicKey := filepath.Join(td, "cosign.pub")
    49  	privateKey := filepath.Join(td, "cosign.key")
    50  
    51  	return &cosignKeyPair{
    52  		publicKey:  publicKey,
    53  		privateKey: privateKey,
    54  		cleanup: func() {
    55  			_ = os.RemoveAll(td)
    56  		},
    57  	}
    58  }
    59  
    60  func TestImageVerifyWithCosign(t *testing.T) {
    61  	testutil.RequireExecutable(t, "cosign")
    62  	testutil.DockerIncompatible(t)
    63  	testutil.RequiresBuild(t)
    64  	t.Setenv("COSIGN_PASSWORD", "1")
    65  	keyPair := newCosignKeyPair(t, "cosign-key-pair")
    66  	defer keyPair.cleanup()
    67  	base := testutil.NewBase(t)
    68  	defer base.Cmd("builder", "prune").Run()
    69  	tID := testutil.Identifier(t)
    70  	reg := testregistry.NewPlainHTTP(base, 5000)
    71  	defer reg.Cleanup()
    72  	localhostIP := "127.0.0.1"
    73  	t.Logf("localhost IP=%q", localhostIP)
    74  	testImageRef := fmt.Sprintf("%s:%d/%s",
    75  		localhostIP, reg.ListenPort, tID)
    76  	t.Logf("testImageRef=%q", testImageRef)
    77  
    78  	dockerfile := fmt.Sprintf(`FROM %s
    79  CMD ["echo", "nerdctl-build-test-string"]
    80  	`, testutil.CommonImage)
    81  
    82  	buildCtx, err := createBuildContext(dockerfile)
    83  	assert.NilError(t, err)
    84  	defer os.RemoveAll(buildCtx)
    85  
    86  	base.Cmd("build", "-t", testImageRef, buildCtx).AssertOK()
    87  	base.Cmd("push", testImageRef, "--sign=cosign", "--cosign-key="+keyPair.privateKey).AssertOK()
    88  	base.Cmd("pull", testImageRef, "--verify=cosign", "--cosign-key="+keyPair.publicKey).AssertOK()
    89  }
    90  
    91  func TestImagePullPlainHttpWithDefaultPort(t *testing.T) {
    92  	testutil.DockerIncompatible(t)
    93  	testutil.RequiresBuild(t)
    94  	base := testutil.NewBase(t)
    95  	defer base.Cmd("builder", "prune").Run()
    96  	reg := testregistry.NewPlainHTTP(base, 80)
    97  	defer reg.Cleanup()
    98  	testImageRef := fmt.Sprintf("%s/%s:%s",
    99  		reg.IP.String(), testutil.Identifier(t), strings.Split(testutil.CommonImage, ":")[1])
   100  	t.Logf("testImageRef=%q", testImageRef)
   101  	t.Logf("testImageRef=%q", testImageRef)
   102  	dockerfile := fmt.Sprintf(`FROM %s
   103  CMD ["echo", "nerdctl-build-test-string"]
   104  	`, testutil.CommonImage)
   105  
   106  	buildCtx, err := createBuildContext(dockerfile)
   107  	assert.NilError(t, err)
   108  	defer os.RemoveAll(buildCtx)
   109  	base.Cmd("build", "-t", testImageRef, buildCtx).AssertOK()
   110  	base.Cmd("--insecure-registry", "push", testImageRef).AssertOK()
   111  	base.Cmd("--insecure-registry", "pull", testImageRef).AssertOK()
   112  }
   113  
   114  func TestImageVerifyWithCosignShouldFailWhenKeyIsNotCorrect(t *testing.T) {
   115  	testutil.RequireExecutable(t, "cosign")
   116  	testutil.DockerIncompatible(t)
   117  	testutil.RequiresBuild(t)
   118  	t.Setenv("COSIGN_PASSWORD", "1")
   119  	keyPair := newCosignKeyPair(t, "cosign-key-pair")
   120  	defer keyPair.cleanup()
   121  	base := testutil.NewBase(t)
   122  	defer base.Cmd("builder", "prune").Run()
   123  	tID := testutil.Identifier(t)
   124  	reg := testregistry.NewPlainHTTP(base, 5000)
   125  	defer reg.Cleanup()
   126  	localhostIP := "127.0.0.1"
   127  	t.Logf("localhost IP=%q", localhostIP)
   128  	testImageRef := fmt.Sprintf("%s:%d/%s",
   129  		localhostIP, reg.ListenPort, tID)
   130  	t.Logf("testImageRef=%q", testImageRef)
   131  
   132  	dockerfile := fmt.Sprintf(`FROM %s
   133  CMD ["echo", "nerdctl-build-test-string"]
   134  	`, testutil.CommonImage)
   135  
   136  	buildCtx, err := createBuildContext(dockerfile)
   137  	assert.NilError(t, err)
   138  	defer os.RemoveAll(buildCtx)
   139  
   140  	base.Cmd("build", "-t", testImageRef, buildCtx).AssertOK()
   141  	base.Cmd("push", testImageRef, "--sign=cosign", "--cosign-key="+keyPair.privateKey).AssertOK()
   142  	base.Cmd("pull", testImageRef, "--verify=cosign", "--cosign-key="+keyPair.publicKey).AssertOK()
   143  
   144  	t.Setenv("COSIGN_PASSWORD", "2")
   145  	newKeyPair := newCosignKeyPair(t, "cosign-key-pair-test")
   146  	base.Cmd("pull", testImageRef, "--verify=cosign", "--cosign-key="+newKeyPair.publicKey).AssertFail()
   147  }
   148  
   149  func TestPullSoci(t *testing.T) {
   150  	testutil.DockerIncompatible(t)
   151  	tests := []struct {
   152  		name                         string
   153  		sociIndexDigest              string
   154  		image                        string
   155  		remoteSnapshotsExpectedCount int
   156  	}{
   157  		{
   158  			name:                         "Run without specifying SOCI index",
   159  			sociIndexDigest:              "",
   160  			image:                        testutil.FfmpegSociImage,
   161  			remoteSnapshotsExpectedCount: 11,
   162  		},
   163  		{
   164  			name:                         "Run with bad SOCI index",
   165  			sociIndexDigest:              "sha256:thisisabadindex0000000000000000000000000000000000000000000000000",
   166  			image:                        testutil.FfmpegSociImage,
   167  			remoteSnapshotsExpectedCount: 11,
   168  		},
   169  	}
   170  
   171  	for _, tt := range tests {
   172  		t.Run(tt.name, func(t *testing.T) {
   173  			base := testutil.NewBase(t)
   174  			requiresSoci(base)
   175  
   176  			//counting initial snapshot mounts
   177  			initialMounts, err := exec.Command("mount").Output()
   178  			if err != nil {
   179  				t.Fatal(err)
   180  			}
   181  
   182  			remoteSnapshotsInitialCount := strings.Count(string(initialMounts), "fuse.rawBridge")
   183  
   184  			pullOutput := base.Cmd("--snapshotter=soci", "pull", tt.image).Out()
   185  			base.T.Logf("pull output: %s", pullOutput)
   186  
   187  			actualMounts, err := exec.Command("mount").Output()
   188  			if err != nil {
   189  				t.Fatal(err)
   190  			}
   191  			remoteSnapshotsActualCount := strings.Count(string(actualMounts), "fuse.rawBridge")
   192  			base.T.Logf("number of actual mounts: %v", remoteSnapshotsActualCount-remoteSnapshotsInitialCount)
   193  
   194  			rmiOutput := base.Cmd("rmi", testutil.FfmpegSociImage).Out()
   195  			base.T.Logf("rmi output: %s", rmiOutput)
   196  
   197  			base.T.Logf("number of expected mounts: %v", tt.remoteSnapshotsExpectedCount)
   198  
   199  			if tt.remoteSnapshotsExpectedCount != (remoteSnapshotsActualCount - remoteSnapshotsInitialCount) {
   200  				t.Fatalf("incorrect number of remote snapshots; expected=%d, actual=%d",
   201  					tt.remoteSnapshotsExpectedCount, remoteSnapshotsActualCount-remoteSnapshotsInitialCount)
   202  			}
   203  		})
   204  	}
   205  
   206  }