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 }