github.com/khulnasoft/cli@v0.0.0-20240402070845-01bcad7beefa/e2e/image/push_test.go (about) 1 package image 2 3 import ( 4 "fmt" 5 "os" 6 "strings" 7 "testing" 8 9 "github.com/khulnasoft/cli/e2e/internal/fixtures" 10 "github.com/khulnasoft/cli/internal/test/environment" 11 "github.com/khulnasoft/cli/internal/test/output" 12 "gotest.tools/v3/assert" 13 "gotest.tools/v3/fs" 14 "gotest.tools/v3/golden" 15 "gotest.tools/v3/icmd" 16 "gotest.tools/v3/skip" 17 ) 18 19 const ( 20 notary = "/usr/local/bin/notary" 21 22 pubkey1 = "./testdata/notary/delgkey1.crt" 23 privkey1 = "./testdata/notary/delgkey1.key" 24 pubkey2 = "./testdata/notary/delgkey2.crt" 25 privkey2 = "./testdata/notary/delgkey2.key" 26 pubkey3 = "./testdata/notary/delgkey3.crt" 27 privkey3 = "./testdata/notary/delgkey3.key" 28 pubkey4 = "./testdata/notary/delgkey4.crt" 29 privkey4 = "./testdata/notary/delgkey4.key" 30 ) 31 32 func TestPushAllTags(t *testing.T) { 33 skip.If(t, environment.RemoteDaemon()) 34 35 // Compared digests are linux/amd64 specific. 36 // TODO: Fix this test and make it work on all platforms. 37 environment.SkipIfNotPlatform(t, "linux/amd64") 38 39 _ = createImage(t, "push-all-tags", "latest", "v1", "v1.0", "v1.0.1") 40 result := icmd.RunCmd(icmd.Command("docker", "push", "--all-tags", registryPrefix+"/push-all-tags")) 41 42 result.Assert(t, icmd.Success) 43 golden.Assert(t, result.Stderr(), "push-with-content-trust-err.golden") 44 output.Assert(t, result.Stdout(), map[int]func(string) error{ 45 0: output.Equals("The push refers to repository [registry:5000/push-all-tags]"), 46 1: output.Equals("7cd52847ad77: Preparing"), 47 3: output.Equals("latest: digest: sha256:e2e16842c9b54d985bf1ef9242a313f36b856181f188de21313820e177002501 size: 528"), 48 6: output.Equals("v1: digest: sha256:e2e16842c9b54d985bf1ef9242a313f36b856181f188de21313820e177002501 size: 528"), 49 9: output.Equals("v1.0: digest: sha256:e2e16842c9b54d985bf1ef9242a313f36b856181f188de21313820e177002501 size: 528"), 50 12: output.Equals("v1.0.1: digest: sha256:e2e16842c9b54d985bf1ef9242a313f36b856181f188de21313820e177002501 size: 528"), 51 }) 52 } 53 54 func TestPushWithContentTrust(t *testing.T) { 55 skip.If(t, environment.RemoteDaemon()) 56 57 // Compared digests are linux/amd64 specific. 58 // TODO: Fix this test and make it work on all platforms. 59 environment.SkipIfNotPlatform(t, "linux/amd64") 60 61 dir := fixtures.SetupConfigFile(t) 62 defer dir.Remove() 63 image := createImage(t, "trust-push", "latest") 64 65 result := icmd.RunCmd(icmd.Command("docker", "push", image), 66 fixtures.WithConfig(dir.Path()), 67 fixtures.WithTrust, 68 fixtures.WithNotary, 69 fixtures.WithPassphrase("foo", "bar"), 70 ) 71 result.Assert(t, icmd.Success) 72 golden.Assert(t, result.Stderr(), "push-with-content-trust-err.golden") 73 output.Assert(t, result.Stdout(), map[int]func(string) error{ 74 0: output.Equals("The push refers to repository [registry:5000/trust-push]"), 75 1: output.Equals("7cd52847ad77: Preparing"), 76 3: output.Equals("latest: digest: sha256:e2e16842c9b54d985bf1ef9242a313f36b856181f188de21313820e177002501 size: 528"), 77 4: output.Equals("Signing and pushing trust metadata"), 78 5: output.Equals(`Finished initializing "registry:5000/trust-push"`), 79 6: output.Equals("Successfully signed registry:5000/trust-push:latest"), 80 }) 81 } 82 83 func TestPushQuietErrors(t *testing.T) { 84 result := icmd.RunCmd(icmd.Command("docker", "push", "--quiet", "nosuchimage")) 85 result.Assert(t, icmd.Expected{ 86 ExitCode: 1, 87 Err: "An image does not exist locally with the tag: nosuchimage", 88 }) 89 } 90 91 func TestPushWithContentTrustUnreachableServer(t *testing.T) { 92 skip.If(t, environment.RemoteDaemon()) 93 94 dir := fixtures.SetupConfigFile(t) 95 defer dir.Remove() 96 image := createImage(t, "trust-push-unreachable", "latest") 97 98 result := icmd.RunCmd(icmd.Command("docker", "push", image), 99 fixtures.WithConfig(dir.Path()), 100 fixtures.WithTrust, 101 fixtures.WithNotaryServer("https://invalidnotaryserver"), 102 ) 103 result.Assert(t, icmd.Expected{ 104 ExitCode: 1, 105 Err: "error contacting notary server", 106 }) 107 } 108 109 func TestPushWithContentTrustExistingTag(t *testing.T) { 110 skip.If(t, environment.RemoteDaemon()) 111 112 dir := fixtures.SetupConfigFile(t) 113 defer dir.Remove() 114 image := createImage(t, "trust-push-existing", "latest") 115 116 result := icmd.RunCmd(icmd.Command("docker", "push", image)) 117 result.Assert(t, icmd.Success) 118 119 result = icmd.RunCmd(icmd.Command("docker", "push", image), 120 fixtures.WithConfig(dir.Path()), 121 fixtures.WithTrust, 122 fixtures.WithNotary, 123 fixtures.WithPassphrase("foo", "bar"), 124 ) 125 result.Assert(t, icmd.Expected{ 126 Out: "Signing and pushing trust metadata", 127 }) 128 129 // Re-push 130 result = icmd.RunCmd(icmd.Command("docker", "push", image), 131 fixtures.WithConfig(dir.Path()), 132 fixtures.WithTrust, 133 fixtures.WithNotary, 134 fixtures.WithPassphrase("foo", "bar"), 135 ) 136 result.Assert(t, icmd.Expected{ 137 Out: "Signing and pushing trust metadata", 138 }) 139 } 140 141 func TestPushWithContentTrustReleasesDelegationOnly(t *testing.T) { 142 skip.If(t, environment.RemoteDaemon()) 143 144 role := "targets/releases" 145 146 dir := fixtures.SetupConfigFile(t) 147 defer dir.Remove() 148 copyPrivateKey(t, dir.Join("trust", "private"), privkey1) 149 notaryDir := setupNotaryConfig(t, dir) 150 defer notaryDir.Remove() 151 homeDir := fs.NewDir(t, "push_test_home") 152 defer notaryDir.Remove() 153 154 baseRef := fmt.Sprintf("%s/%s", registryPrefix, "trust-push-releases-delegation") 155 targetRef := fmt.Sprintf("%s:%s", baseRef, "latest") 156 157 // Init repository 158 notaryInit(t, notaryDir, homeDir, baseRef) 159 // Add delegation key (public key) 160 notaryAddDelegation(t, notaryDir, homeDir, baseRef, role, pubkey1) 161 // Publish it 162 notaryPublish(t, notaryDir, homeDir, baseRef) 163 // Import private key 164 notaryImportPrivateKey(t, notaryDir, homeDir, baseRef, role, privkey1) 165 166 // Tag & push with content trust 167 icmd.RunCommand("docker", "pull", fixtures.AlpineImage).Assert(t, icmd.Success) 168 icmd.RunCommand("docker", "tag", fixtures.AlpineImage, targetRef).Assert(t, icmd.Success) 169 result := icmd.RunCmd(icmd.Command("docker", "push", targetRef), 170 fixtures.WithConfig(dir.Path()), 171 fixtures.WithTrust, 172 fixtures.WithNotary, 173 fixtures.WithPassphrase("foo", "foo"), 174 ) 175 result.Assert(t, icmd.Expected{ 176 Out: "Signing and pushing trust metadata", 177 }) 178 179 targetsInRole := notaryListTargetsInRole(t, notaryDir, homeDir, baseRef, role) 180 assert.Assert(t, targetsInRole["latest"] == role, "%v", targetsInRole) 181 targetsInRole = notaryListTargetsInRole(t, notaryDir, homeDir, baseRef, "targets") 182 assert.Assert(t, targetsInRole["latest"] != "targets", "%v", targetsInRole) 183 184 result = icmd.RunCmd(icmd.Command("docker", "pull", targetRef), 185 fixtures.WithConfig(dir.Path()), 186 fixtures.WithTrust, 187 fixtures.WithNotary, 188 ) 189 result.Assert(t, icmd.Success) 190 } 191 192 func TestPushWithContentTrustSignsAllFirstLevelRolesWeHaveKeysFor(t *testing.T) { 193 skip.If(t, environment.RemoteDaemon()) 194 195 dir := fixtures.SetupConfigFile(t) 196 defer dir.Remove() 197 copyPrivateKey(t, dir.Join("trust", "private"), privkey1) 198 copyPrivateKey(t, dir.Join("trust", "private"), privkey2) 199 copyPrivateKey(t, dir.Join("trust", "private"), privkey3) 200 notaryDir := setupNotaryConfig(t, dir) 201 defer notaryDir.Remove() 202 homeDir := fs.NewDir(t, "push_test_home") 203 defer notaryDir.Remove() 204 205 baseRef := fmt.Sprintf("%s/%s", registryPrefix, "trust-push-releases-first-roles") 206 targetRef := fmt.Sprintf("%s:%s", baseRef, "latest") 207 208 // Init repository 209 notaryInit(t, notaryDir, homeDir, baseRef) 210 // Add delegation key (public key) 211 notaryAddDelegation(t, notaryDir, homeDir, baseRef, "targets/role1", pubkey1) 212 notaryAddDelegation(t, notaryDir, homeDir, baseRef, "targets/role2", pubkey2) 213 notaryAddDelegation(t, notaryDir, homeDir, baseRef, "targets/role3", pubkey3) 214 notaryAddDelegation(t, notaryDir, homeDir, baseRef, "targets/role1/subrole", pubkey3) 215 // Import private key 216 notaryImportPrivateKey(t, notaryDir, homeDir, baseRef, "targets/role1", privkey1) 217 notaryImportPrivateKey(t, notaryDir, homeDir, baseRef, "targets/role2", privkey2) 218 notaryImportPrivateKey(t, notaryDir, homeDir, baseRef, "targets/role1/subrole", privkey3) 219 // Publish it 220 notaryPublish(t, notaryDir, homeDir, baseRef) 221 222 // Tag & push with content trust 223 icmd.RunCommand("docker", "pull", fixtures.AlpineImage).Assert(t, icmd.Success) 224 icmd.RunCommand("docker", "tag", fixtures.AlpineImage, targetRef).Assert(t, icmd.Success) 225 result := icmd.RunCmd(icmd.Command("docker", "push", targetRef), 226 fixtures.WithConfig(dir.Path()), 227 fixtures.WithTrust, 228 fixtures.WithNotary, 229 fixtures.WithPassphrase("foo", "foo"), 230 ) 231 result.Assert(t, icmd.Expected{ 232 Out: "Signing and pushing trust metadata", 233 }) 234 235 // check to make sure that the target has been added to targets/role1 and targets/role2, and 236 // not targets (because there are delegations) or targets/role3 (due to missing key) or 237 // targets/role1/subrole (due to it being a second level delegation) 238 targetsInRole := notaryListTargetsInRole(t, notaryDir, homeDir, baseRef, "targets/role1") 239 assert.Assert(t, targetsInRole["latest"] == "targets/role1", "%v", targetsInRole) 240 targetsInRole = notaryListTargetsInRole(t, notaryDir, homeDir, baseRef, "targets/role2") 241 assert.Assert(t, targetsInRole["latest"] == "targets/role2", "%v", targetsInRole) 242 targetsInRole = notaryListTargetsInRole(t, notaryDir, homeDir, baseRef, "targets") 243 assert.Assert(t, targetsInRole["latest"] != "targets", "%v", targetsInRole) 244 245 assert.NilError(t, os.RemoveAll(dir.Join("trust"))) 246 // Try to pull, should fail because non of these are the release role 247 // FIXME(vdemeester) should be unit test 248 result = icmd.RunCmd(icmd.Command("docker", "pull", targetRef), 249 fixtures.WithConfig(dir.Path()), 250 fixtures.WithTrust, 251 fixtures.WithNotary, 252 ) 253 result.Assert(t, icmd.Expected{ 254 ExitCode: 1, 255 }) 256 } 257 258 func TestPushWithContentTrustSignsForRolesWithKeysAndValidPaths(t *testing.T) { 259 skip.If(t, environment.RemoteDaemon()) 260 261 dir := fixtures.SetupConfigFile(t) 262 defer dir.Remove() 263 copyPrivateKey(t, dir.Join("trust", "private"), privkey1) 264 copyPrivateKey(t, dir.Join("trust", "private"), privkey2) 265 copyPrivateKey(t, dir.Join("trust", "private"), privkey3) 266 copyPrivateKey(t, dir.Join("trust", "private"), privkey4) 267 notaryDir := setupNotaryConfig(t, dir) 268 defer notaryDir.Remove() 269 homeDir := fs.NewDir(t, "push_test_home") 270 defer notaryDir.Remove() 271 272 baseRef := fmt.Sprintf("%s/%s", registryPrefix, "trust-push-releases-keys-valid-paths") 273 targetRef := fmt.Sprintf("%s:%s", baseRef, "latest") 274 275 // Init repository 276 notaryInit(t, notaryDir, homeDir, baseRef) 277 // Add delegation key (public key) 278 notaryAddDelegation(t, notaryDir, homeDir, baseRef, "targets/role1", pubkey1, "l", "z") 279 notaryAddDelegation(t, notaryDir, homeDir, baseRef, "targets/role2", pubkey2, "x", "y") 280 notaryAddDelegation(t, notaryDir, homeDir, baseRef, "targets/role3", pubkey3, "latest") 281 notaryAddDelegation(t, notaryDir, homeDir, baseRef, "targets/role4", pubkey4, "latest") 282 // Import private keys (except 3rd key) 283 notaryImportPrivateKey(t, notaryDir, homeDir, baseRef, "targets/role1", privkey1) 284 notaryImportPrivateKey(t, notaryDir, homeDir, baseRef, "targets/role2", privkey2) 285 notaryImportPrivateKey(t, notaryDir, homeDir, baseRef, "targets/role4", privkey4) 286 // Publish it 287 notaryPublish(t, notaryDir, homeDir, baseRef) 288 289 // Tag & push with content trust 290 icmd.RunCommand("docker", "pull", fixtures.AlpineImage).Assert(t, icmd.Success) 291 icmd.RunCommand("docker", "tag", fixtures.AlpineImage, targetRef).Assert(t, icmd.Success) 292 result := icmd.RunCmd(icmd.Command("docker", "push", targetRef), 293 fixtures.WithConfig(dir.Path()), 294 fixtures.WithTrust, 295 fixtures.WithNotary, 296 fixtures.WithPassphrase("foo", "foo"), 297 ) 298 result.Assert(t, icmd.Expected{ 299 Out: "Signing and pushing trust metadata", 300 }) 301 302 // check to make sure that the target has been added to targets/role1 and targets/role4, and 303 // not targets (because there are delegations) or targets/role2 (due to path restrictions) or 304 // targets/role3 (due to missing key) 305 targetsInRole := notaryListTargetsInRole(t, notaryDir, homeDir, baseRef, "targets/role1") 306 assert.Assert(t, targetsInRole["latest"] == "targets/role1", "%v", targetsInRole) 307 targetsInRole = notaryListTargetsInRole(t, notaryDir, homeDir, baseRef, "targets/role4") 308 assert.Assert(t, targetsInRole["latest"] == "targets/role4", "%v", targetsInRole) 309 targetsInRole = notaryListTargetsInRole(t, notaryDir, homeDir, baseRef, "targets") 310 assert.Assert(t, targetsInRole["latest"] != "targets", "%v", targetsInRole) 311 312 assert.NilError(t, os.RemoveAll(dir.Join("trust"))) 313 // Try to pull, should fail because non of these are the release role 314 // FIXME(vdemeester) should be unit test 315 result = icmd.RunCmd(icmd.Command("docker", "pull", targetRef), 316 fixtures.WithConfig(dir.Path()), 317 fixtures.WithTrust, 318 fixtures.WithNotary, 319 ) 320 result.Assert(t, icmd.Expected{ 321 ExitCode: 1, 322 }) 323 } 324 325 func createImage(t *testing.T, repo string, tags ...string) string { 326 t.Helper() 327 icmd.RunCommand("docker", "pull", fixtures.AlpineImage).Assert(t, icmd.Success) 328 329 for _, tag := range tags { 330 image := fmt.Sprintf("%s/%s:%s", registryPrefix, repo, tag) 331 icmd.RunCommand("docker", "tag", fixtures.AlpineImage, image).Assert(t, icmd.Success) 332 } 333 return fmt.Sprintf("%s/%s:%s", registryPrefix, repo, tags[0]) 334 } 335 336 //nolint:unparam 337 func withNotaryPassphrase(pwd string) func(*icmd.Cmd) { 338 return func(c *icmd.Cmd) { 339 c.Env = append(c.Env, []string{ 340 fmt.Sprintf("NOTARY_ROOT_PASSPHRASE=%s", pwd), 341 fmt.Sprintf("NOTARY_TARGETS_PASSPHRASE=%s", pwd), 342 fmt.Sprintf("NOTARY_SNAPSHOT_PASSPHRASE=%s", pwd), 343 fmt.Sprintf("NOTARY_DELEGATION_PASSPHRASE=%s", pwd), 344 }...) 345 } 346 } 347 348 func notaryImportPrivateKey(t *testing.T, notaryDir, homeDir *fs.Dir, baseRef, role, privkey string) { 349 t.Helper() 350 icmd.RunCmd( 351 icmd.Command(notary, "-c", notaryDir.Join("client-config.json"), "key", "import", privkey, "-g", baseRef, "-r", role), 352 withNotaryPassphrase("foo"), 353 fixtures.WithHome(homeDir.Path()), 354 ).Assert(t, icmd.Success) 355 } 356 357 func notaryPublish(t *testing.T, notaryDir, homeDir *fs.Dir, baseRef string) { 358 t.Helper() 359 icmd.RunCmd( 360 icmd.Command(notary, "-c", notaryDir.Join("client-config.json"), "publish", baseRef), 361 withNotaryPassphrase("foo"), 362 fixtures.WithHome(homeDir.Path()), 363 ).Assert(t, icmd.Success) 364 } 365 366 func notaryAddDelegation(t *testing.T, notaryDir, homeDir *fs.Dir, baseRef, role, pubkey string, paths ...string) { 367 t.Helper() 368 pathsArg := "--all-paths" 369 if len(paths) > 0 { 370 pathsArg = "--paths=" + strings.Join(paths, ",") 371 } 372 icmd.RunCmd( 373 icmd.Command(notary, "-c", notaryDir.Join("client-config.json"), "delegation", "add", baseRef, role, pubkey, pathsArg), 374 withNotaryPassphrase("foo"), 375 fixtures.WithHome(homeDir.Path()), 376 ).Assert(t, icmd.Success) 377 } 378 379 func notaryInit(t *testing.T, notaryDir, homeDir *fs.Dir, baseRef string) { 380 t.Helper() 381 icmd.RunCmd( 382 icmd.Command(notary, "-c", notaryDir.Join("client-config.json"), "init", baseRef), 383 withNotaryPassphrase("foo"), 384 fixtures.WithHome(homeDir.Path()), 385 ).Assert(t, icmd.Success) 386 } 387 388 func notaryListTargetsInRole(t *testing.T, notaryDir, homeDir *fs.Dir, baseRef, role string) map[string]string { 389 t.Helper() 390 result := icmd.RunCmd( 391 icmd.Command(notary, "-c", notaryDir.Join("client-config.json"), "list", baseRef, "-r", role), 392 fixtures.WithHome(homeDir.Path()), 393 ) 394 out := result.Combined() 395 396 // should look something like: 397 // NAME DIGEST SIZE (BYTES) ROLE 398 // ------------------------------------------------------------------------------------------------------ 399 // latest 24a36bbc059b1345b7e8be0df20f1b23caa3602e85d42fff7ecd9d0bd255de56 1377 targets 400 401 targets := make(map[string]string) 402 403 // no target 404 lines := strings.Split(strings.TrimSpace(out), "\n") 405 if len(lines) == 1 && strings.Contains(out, "No targets present in this repository.") { 406 return targets 407 } 408 409 // otherwise, there is at least one target 410 assert.Assert(t, len(lines) >= 3, "output is %s", out) 411 412 for _, line := range lines[2:] { 413 tokens := strings.Fields(line) 414 assert.Assert(t, len(tokens) == 4) 415 targets[tokens[0]] = tokens[3] 416 } 417 418 return targets 419 } 420 421 func setupNotaryConfig(t *testing.T, dockerConfigDir fs.Dir) *fs.Dir { 422 t.Helper() 423 return fs.NewDir(t, "notary_test", fs.WithMode(0o700), 424 fs.WithFile("client-config.json", fmt.Sprintf(` 425 { 426 "trust_dir": "%s", 427 "remote_server": { 428 "url": "%s" 429 } 430 }`, dockerConfigDir.Join("trust"), fixtures.NotaryURL)), 431 ) 432 } 433 434 func copyPrivateKey(t *testing.T, dir, source string) { 435 t.Helper() 436 icmd.RunCommand("/bin/cp", source, dir).Assert(t, icmd.Success) 437 }