github.com/vincentwoo/docker@v0.7.3-0.20160116130405-82401a4b13c0/integration-cli/docker_cli_push_test.go (about) 1 package main 2 3 import ( 4 "archive/tar" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "os/exec" 9 "path/filepath" 10 "strings" 11 "time" 12 13 "github.com/docker/docker/cliconfig" 14 "github.com/docker/docker/pkg/integration/checker" 15 "github.com/go-check/check" 16 ) 17 18 // Pushing an image to a private registry. 19 func testPushBusyboxImage(c *check.C) { 20 repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL) 21 // tag the image to upload it to the private registry 22 dockerCmd(c, "tag", "busybox", repoName) 23 // push the image to the registry 24 dockerCmd(c, "push", repoName) 25 } 26 27 func (s *DockerRegistrySuite) TestPushBusyboxImage(c *check.C) { 28 testPushBusyboxImage(c) 29 } 30 31 func (s *DockerSchema1RegistrySuite) TestPushBusyboxImage(c *check.C) { 32 testPushBusyboxImage(c) 33 } 34 35 // pushing an image without a prefix should throw an error 36 func (s *DockerSuite) TestPushUnprefixedRepo(c *check.C) { 37 out, _, err := dockerCmdWithError("push", "busybox") 38 c.Assert(err, check.NotNil, check.Commentf("pushing an unprefixed repo didn't result in a non-zero exit status: %s", out)) 39 } 40 41 func testPushUntagged(c *check.C) { 42 repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL) 43 expected := "Repository does not exist" 44 45 out, _, err := dockerCmdWithError("push", repoName) 46 c.Assert(err, check.NotNil, check.Commentf("pushing the image to the private registry should have failed: output %q", out)) 47 c.Assert(out, checker.Contains, expected, check.Commentf("pushing the image failed")) 48 } 49 50 func (s *DockerRegistrySuite) TestPushUntagged(c *check.C) { 51 testPushUntagged(c) 52 } 53 54 func (s *DockerSchema1RegistrySuite) TestPushUntagged(c *check.C) { 55 testPushUntagged(c) 56 } 57 58 func testPushBadTag(c *check.C) { 59 repoName := fmt.Sprintf("%v/dockercli/busybox:latest", privateRegistryURL) 60 expected := "does not exist" 61 62 out, _, err := dockerCmdWithError("push", repoName) 63 c.Assert(err, check.NotNil, check.Commentf("pushing the image to the private registry should have failed: output %q", out)) 64 c.Assert(out, checker.Contains, expected, check.Commentf("pushing the image failed")) 65 } 66 67 func (s *DockerRegistrySuite) TestPushBadTag(c *check.C) { 68 testPushBadTag(c) 69 } 70 71 func (s *DockerSchema1RegistrySuite) TestPushBadTag(c *check.C) { 72 testPushBadTag(c) 73 } 74 75 func testPushMultipleTags(c *check.C) { 76 repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL) 77 repoTag1 := fmt.Sprintf("%v/dockercli/busybox:t1", privateRegistryURL) 78 repoTag2 := fmt.Sprintf("%v/dockercli/busybox:t2", privateRegistryURL) 79 // tag the image and upload it to the private registry 80 dockerCmd(c, "tag", "busybox", repoTag1) 81 82 dockerCmd(c, "tag", "busybox", repoTag2) 83 84 dockerCmd(c, "push", repoName) 85 86 // Ensure layer list is equivalent for repoTag1 and repoTag2 87 out1, _ := dockerCmd(c, "pull", repoTag1) 88 89 imageAlreadyExists := ": Image already exists" 90 var out1Lines []string 91 for _, outputLine := range strings.Split(out1, "\n") { 92 if strings.Contains(outputLine, imageAlreadyExists) { 93 out1Lines = append(out1Lines, outputLine) 94 } 95 } 96 97 out2, _ := dockerCmd(c, "pull", repoTag2) 98 99 var out2Lines []string 100 for _, outputLine := range strings.Split(out2, "\n") { 101 if strings.Contains(outputLine, imageAlreadyExists) { 102 out1Lines = append(out1Lines, outputLine) 103 } 104 } 105 c.Assert(out2Lines, checker.HasLen, len(out1Lines)) 106 107 for i := range out1Lines { 108 c.Assert(out1Lines[i], checker.Equals, out2Lines[i]) 109 } 110 } 111 112 func (s *DockerRegistrySuite) TestPushMultipleTags(c *check.C) { 113 testPushMultipleTags(c) 114 } 115 116 func (s *DockerSchema1RegistrySuite) TestPushMultipleTags(c *check.C) { 117 testPushMultipleTags(c) 118 } 119 120 func testPushEmptyLayer(c *check.C) { 121 repoName := fmt.Sprintf("%v/dockercli/emptylayer", privateRegistryURL) 122 emptyTarball, err := ioutil.TempFile("", "empty_tarball") 123 c.Assert(err, check.IsNil, check.Commentf("Unable to create test file")) 124 125 tw := tar.NewWriter(emptyTarball) 126 err = tw.Close() 127 c.Assert(err, check.IsNil, check.Commentf("Error creating empty tarball")) 128 129 freader, err := os.Open(emptyTarball.Name()) 130 c.Assert(err, check.IsNil, check.Commentf("Could not open test tarball")) 131 132 importCmd := exec.Command(dockerBinary, "import", "-", repoName) 133 importCmd.Stdin = freader 134 out, _, err := runCommandWithOutput(importCmd) 135 c.Assert(err, check.IsNil, check.Commentf("import failed: %q", out)) 136 137 // Now verify we can push it 138 out, _, err = dockerCmdWithError("push", repoName) 139 c.Assert(err, check.IsNil, check.Commentf("pushing the image to the private registry has failed: %s", out)) 140 } 141 142 func (s *DockerRegistrySuite) TestPushEmptyLayer(c *check.C) { 143 testPushEmptyLayer(c) 144 } 145 146 func (s *DockerSchema1RegistrySuite) TestPushEmptyLayer(c *check.C) { 147 testPushEmptyLayer(c) 148 } 149 150 func (s *DockerRegistrySuite) TestCrossRepositoryLayerPush(c *check.C) { 151 sourceRepoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL) 152 // tag the image to upload it to the private registry 153 dockerCmd(c, "tag", "busybox", sourceRepoName) 154 // push the image to the registry 155 out1, _, err := dockerCmdWithError("push", sourceRepoName) 156 c.Assert(err, check.IsNil, check.Commentf("pushing the image to the private registry has failed: %s", out1)) 157 // ensure that none of the layers were mounted from another repository during push 158 c.Assert(strings.Contains(out1, "Mounted from"), check.Equals, false) 159 160 destRepoName := fmt.Sprintf("%v/dockercli/crossrepopush", privateRegistryURL) 161 // retag the image to upload the same layers to another repo in the same registry 162 dockerCmd(c, "tag", "busybox", destRepoName) 163 // push the image to the registry 164 out2, _, err := dockerCmdWithError("push", destRepoName) 165 c.Assert(err, check.IsNil, check.Commentf("pushing the image to the private registry has failed: %s", out2)) 166 // ensure that layers were mounted from the first repo during push 167 c.Assert(strings.Contains(out2, "Mounted from dockercli/busybox"), check.Equals, true) 168 169 // ensure that we can pull and run the cross-repo-pushed repository 170 dockerCmd(c, "rmi", destRepoName) 171 dockerCmd(c, "pull", destRepoName) 172 out3, _ := dockerCmd(c, "run", destRepoName, "echo", "-n", "hello world") 173 c.Assert(out3, check.Equals, "hello world") 174 } 175 176 func (s *DockerSchema1RegistrySuite) TestCrossRepositoryLayerPushNotSupported(c *check.C) { 177 sourceRepoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL) 178 // tag the image to upload it to the private registry 179 dockerCmd(c, "tag", "busybox", sourceRepoName) 180 // push the image to the registry 181 out1, _, err := dockerCmdWithError("push", sourceRepoName) 182 c.Assert(err, check.IsNil, check.Commentf("pushing the image to the private registry has failed: %s", out1)) 183 // ensure that none of the layers were mounted from another repository during push 184 c.Assert(strings.Contains(out1, "Mounted from"), check.Equals, false) 185 186 destRepoName := fmt.Sprintf("%v/dockercli/crossrepopush", privateRegistryURL) 187 // retag the image to upload the same layers to another repo in the same registry 188 dockerCmd(c, "tag", "busybox", destRepoName) 189 // push the image to the registry 190 out2, _, err := dockerCmdWithError("push", destRepoName) 191 c.Assert(err, check.IsNil, check.Commentf("pushing the image to the private registry has failed: %s", out2)) 192 // schema1 registry should not support cross-repo layer mounts, so ensure that this does not happen 193 c.Assert(strings.Contains(out2, "Mounted from dockercli/busybox"), check.Equals, false) 194 195 // ensure that we can pull and run the second pushed repository 196 dockerCmd(c, "rmi", destRepoName) 197 dockerCmd(c, "pull", destRepoName) 198 out3, _ := dockerCmd(c, "run", destRepoName, "echo", "-n", "hello world") 199 c.Assert(out3, check.Equals, "hello world") 200 } 201 202 func (s *DockerTrustSuite) TestTrustedPush(c *check.C) { 203 repoName := fmt.Sprintf("%v/dockercli/trusted:latest", privateRegistryURL) 204 // tag the image and upload it to the private registry 205 dockerCmd(c, "tag", "busybox", repoName) 206 207 pushCmd := exec.Command(dockerBinary, "push", repoName) 208 s.trustedCmd(pushCmd) 209 out, _, err := runCommandWithOutput(pushCmd) 210 c.Assert(err, check.IsNil, check.Commentf("Error running trusted push: %s\n%s", err, out)) 211 c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push")) 212 213 // Try pull after push 214 pullCmd := exec.Command(dockerBinary, "pull", repoName) 215 s.trustedCmd(pullCmd) 216 out, _, err = runCommandWithOutput(pullCmd) 217 c.Assert(err, check.IsNil, check.Commentf(out)) 218 c.Assert(string(out), checker.Contains, "Status: Downloaded", check.Commentf(out)) 219 } 220 221 func (s *DockerTrustSuite) TestTrustedPushWithEnvPasswords(c *check.C) { 222 repoName := fmt.Sprintf("%v/dockerclienv/trusted:latest", privateRegistryURL) 223 // tag the image and upload it to the private registry 224 dockerCmd(c, "tag", "busybox", repoName) 225 226 pushCmd := exec.Command(dockerBinary, "push", repoName) 227 s.trustedCmdWithPassphrases(pushCmd, "12345678", "12345678") 228 out, _, err := runCommandWithOutput(pushCmd) 229 c.Assert(err, check.IsNil, check.Commentf("Error running trusted push: %s\n%s", err, out)) 230 c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push")) 231 232 // Try pull after push 233 pullCmd := exec.Command(dockerBinary, "pull", repoName) 234 s.trustedCmd(pullCmd) 235 out, _, err = runCommandWithOutput(pullCmd) 236 c.Assert(err, check.IsNil, check.Commentf(out)) 237 c.Assert(string(out), checker.Contains, "Status: Downloaded", check.Commentf(out)) 238 } 239 240 // This test ensures backwards compatibility with old ENV variables. Should be 241 // deprecated by 1.10 242 func (s *DockerTrustSuite) TestTrustedPushWithDeprecatedEnvPasswords(c *check.C) { 243 repoName := fmt.Sprintf("%v/dockercli/trusteddeprecated:latest", privateRegistryURL) 244 // tag the image and upload it to the private registry 245 dockerCmd(c, "tag", "busybox", repoName) 246 247 pushCmd := exec.Command(dockerBinary, "push", repoName) 248 s.trustedCmdWithDeprecatedEnvPassphrases(pushCmd, "12345678", "12345678") 249 out, _, err := runCommandWithOutput(pushCmd) 250 c.Assert(err, check.IsNil, check.Commentf("Error running trusted push: %s\n%s", err, out)) 251 c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push")) 252 } 253 254 func (s *DockerTrustSuite) TestTrustedPushWithFailingServer(c *check.C) { 255 repoName := fmt.Sprintf("%v/dockercli/trusted:latest", privateRegistryURL) 256 // tag the image and upload it to the private registry 257 dockerCmd(c, "tag", "busybox", repoName) 258 259 pushCmd := exec.Command(dockerBinary, "push", repoName) 260 s.trustedCmdWithServer(pushCmd, "https://example.com:81/") 261 out, _, err := runCommandWithOutput(pushCmd) 262 c.Assert(err, check.NotNil, check.Commentf("Missing error while running trusted push w/ no server")) 263 c.Assert(out, checker.Contains, "error contacting notary server", check.Commentf("Missing expected output on trusted push")) 264 } 265 266 func (s *DockerTrustSuite) TestTrustedPushWithoutServerAndUntrusted(c *check.C) { 267 repoName := fmt.Sprintf("%v/dockercli/trusted:latest", privateRegistryURL) 268 // tag the image and upload it to the private registry 269 dockerCmd(c, "tag", "busybox", repoName) 270 271 pushCmd := exec.Command(dockerBinary, "push", "--disable-content-trust", repoName) 272 s.trustedCmdWithServer(pushCmd, "https://example.com/") 273 out, _, err := runCommandWithOutput(pushCmd) 274 c.Assert(err, check.IsNil, check.Commentf("trusted push with no server and --disable-content-trust failed: %s\n%s", err, out)) 275 c.Assert(out, check.Not(checker.Contains), "Error establishing connection to notary repository", check.Commentf("Missing expected output on trusted push with --disable-content-trust:")) 276 } 277 278 func (s *DockerTrustSuite) TestTrustedPushWithExistingTag(c *check.C) { 279 repoName := fmt.Sprintf("%v/dockerclitag/trusted:latest", privateRegistryURL) 280 // tag the image and upload it to the private registry 281 dockerCmd(c, "tag", "busybox", repoName) 282 dockerCmd(c, "push", repoName) 283 284 pushCmd := exec.Command(dockerBinary, "push", repoName) 285 s.trustedCmd(pushCmd) 286 out, _, err := runCommandWithOutput(pushCmd) 287 c.Assert(err, check.IsNil, check.Commentf("trusted push failed: %s\n%s", err, out)) 288 c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push with existing tag")) 289 290 // Try pull after push 291 pullCmd := exec.Command(dockerBinary, "pull", repoName) 292 s.trustedCmd(pullCmd) 293 out, _, err = runCommandWithOutput(pullCmd) 294 c.Assert(err, check.IsNil, check.Commentf(out)) 295 c.Assert(string(out), checker.Contains, "Status: Downloaded", check.Commentf(out)) 296 } 297 298 func (s *DockerTrustSuite) TestTrustedPushWithExistingSignedTag(c *check.C) { 299 repoName := fmt.Sprintf("%v/dockerclipushpush/trusted:latest", privateRegistryURL) 300 // tag the image and upload it to the private registry 301 dockerCmd(c, "tag", "busybox", repoName) 302 303 // Do a trusted push 304 pushCmd := exec.Command(dockerBinary, "push", repoName) 305 s.trustedCmd(pushCmd) 306 out, _, err := runCommandWithOutput(pushCmd) 307 c.Assert(err, check.IsNil, check.Commentf("trusted push failed: %s\n%s", err, out)) 308 c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push with existing tag")) 309 310 // Do another trusted push 311 pushCmd = exec.Command(dockerBinary, "push", repoName) 312 s.trustedCmd(pushCmd) 313 out, _, err = runCommandWithOutput(pushCmd) 314 c.Assert(err, check.IsNil, check.Commentf("trusted push failed: %s\n%s", err, out)) 315 c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push with existing tag")) 316 317 dockerCmd(c, "rmi", repoName) 318 319 // Try pull to ensure the double push did not break our ability to pull 320 pullCmd := exec.Command(dockerBinary, "pull", repoName) 321 s.trustedCmd(pullCmd) 322 out, _, err = runCommandWithOutput(pullCmd) 323 c.Assert(err, check.IsNil, check.Commentf("Error running trusted pull: %s\n%s", err, out)) 324 c.Assert(out, checker.Contains, "Status: Downloaded", check.Commentf("Missing expected output on trusted pull with --disable-content-trust")) 325 326 } 327 328 func (s *DockerTrustSuite) TestTrustedPushWithIncorrectPassphraseForNonRoot(c *check.C) { 329 repoName := fmt.Sprintf("%v/dockercliincorretpwd/trusted:latest", privateRegistryURL) 330 // tag the image and upload it to the private registry 331 dockerCmd(c, "tag", "busybox", repoName) 332 333 // Push with default passphrases 334 pushCmd := exec.Command(dockerBinary, "push", repoName) 335 s.trustedCmd(pushCmd) 336 out, _, err := runCommandWithOutput(pushCmd) 337 c.Assert(err, check.IsNil, check.Commentf("trusted push failed: %s\n%s", err, out)) 338 c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push:\n%s", out)) 339 340 // Push with wrong passphrases 341 pushCmd = exec.Command(dockerBinary, "push", repoName) 342 s.trustedCmdWithPassphrases(pushCmd, "12345678", "87654321") 343 out, _, err = runCommandWithOutput(pushCmd) 344 c.Assert(err, check.NotNil, check.Commentf("Error missing from trusted push with short targets passphrase: \n%s", out)) 345 c.Assert(out, checker.Contains, "could not find necessary signing keys", check.Commentf("Missing expected output on trusted push with short targets/snapsnot passphrase")) 346 } 347 348 // This test ensures backwards compatibility with old ENV variables. Should be 349 // deprecated by 1.10 350 func (s *DockerTrustSuite) TestTrustedPushWithIncorrectDeprecatedPassphraseForNonRoot(c *check.C) { 351 repoName := fmt.Sprintf("%v/dockercliincorretdeprecatedpwd/trusted:latest", privateRegistryURL) 352 // tag the image and upload it to the private registry 353 dockerCmd(c, "tag", "busybox", repoName) 354 355 // Push with default passphrases 356 pushCmd := exec.Command(dockerBinary, "push", repoName) 357 s.trustedCmd(pushCmd) 358 out, _, err := runCommandWithOutput(pushCmd) 359 c.Assert(err, check.IsNil, check.Commentf("trusted push failed: %s\n%s", err, out)) 360 c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push")) 361 362 // Push with wrong passphrases 363 pushCmd = exec.Command(dockerBinary, "push", repoName) 364 s.trustedCmdWithDeprecatedEnvPassphrases(pushCmd, "12345678", "87654321") 365 out, _, err = runCommandWithOutput(pushCmd) 366 c.Assert(err, check.NotNil, check.Commentf("Error missing from trusted push with short targets passphrase: \n%s", out)) 367 c.Assert(out, checker.Contains, "could not find necessary signing keys", check.Commentf("Missing expected output on trusted push with short targets/snapsnot passphrase")) 368 } 369 370 func (s *DockerTrustSuite) TestTrustedPushWithExpiredSnapshot(c *check.C) { 371 c.Skip("Currently changes system time, causing instability") 372 repoName := fmt.Sprintf("%v/dockercliexpiredsnapshot/trusted:latest", privateRegistryURL) 373 // tag the image and upload it to the private registry 374 dockerCmd(c, "tag", "busybox", repoName) 375 376 // Push with default passphrases 377 pushCmd := exec.Command(dockerBinary, "push", repoName) 378 s.trustedCmd(pushCmd) 379 out, _, err := runCommandWithOutput(pushCmd) 380 c.Assert(err, check.IsNil, check.Commentf("trusted push failed: %s\n%s", err, out)) 381 c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push")) 382 383 // Snapshots last for three years. This should be expired 384 fourYearsLater := time.Now().Add(time.Hour * 24 * 365 * 4) 385 386 runAtDifferentDate(fourYearsLater, func() { 387 // Push with wrong passphrases 388 pushCmd = exec.Command(dockerBinary, "push", repoName) 389 s.trustedCmd(pushCmd) 390 out, _, err = runCommandWithOutput(pushCmd) 391 c.Assert(err, check.NotNil, check.Commentf("Error missing from trusted push with expired snapshot: \n%s", out)) 392 c.Assert(out, checker.Contains, "repository out-of-date", check.Commentf("Missing expected output on trusted push with expired snapshot")) 393 }) 394 } 395 396 func (s *DockerTrustSuite) TestTrustedPushWithExpiredTimestamp(c *check.C) { 397 c.Skip("Currently changes system time, causing instability") 398 repoName := fmt.Sprintf("%v/dockercliexpiredtimestamppush/trusted:latest", privateRegistryURL) 399 // tag the image and upload it to the private registry 400 dockerCmd(c, "tag", "busybox", repoName) 401 402 // Push with default passphrases 403 pushCmd := exec.Command(dockerBinary, "push", repoName) 404 s.trustedCmd(pushCmd) 405 out, _, err := runCommandWithOutput(pushCmd) 406 c.Assert(err, check.IsNil, check.Commentf("trusted push failed: %s\n%s", err, out)) 407 c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push")) 408 409 // The timestamps expire in two weeks. Lets check three 410 threeWeeksLater := time.Now().Add(time.Hour * 24 * 21) 411 412 // Should succeed because the server transparently re-signs one 413 runAtDifferentDate(threeWeeksLater, func() { 414 pushCmd := exec.Command(dockerBinary, "push", repoName) 415 s.trustedCmd(pushCmd) 416 out, _, err := runCommandWithOutput(pushCmd) 417 c.Assert(err, check.IsNil, check.Commentf("Error running trusted push: %s\n%s", err, out)) 418 c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push with expired timestamp")) 419 }) 420 } 421 422 func (s *DockerTrustSuite) TestTrustedPushWithReleasesDelegation(c *check.C) { 423 repoName := fmt.Sprintf("%v/dockerclireleasedelegation/trusted", privateRegistryURL) 424 targetName := fmt.Sprintf("%s:latest", repoName) 425 pwd := "12345678" 426 s.setupDelegations(c, repoName, pwd) 427 428 // tag the image and upload it to the private registry 429 dockerCmd(c, "tag", "busybox", targetName) 430 431 pushCmd := exec.Command(dockerBinary, "-D", "push", targetName) 432 s.trustedCmdWithPassphrases(pushCmd, pwd, pwd) 433 out, _, err := runCommandWithOutput(pushCmd) 434 c.Assert(err, check.IsNil, check.Commentf("trusted push failed: %s\n%s", err, out)) 435 c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push with existing tag")) 436 437 // Try pull after push 438 pullCmd := exec.Command(dockerBinary, "pull", targetName) 439 s.trustedCmd(pullCmd) 440 out, _, err = runCommandWithOutput(pullCmd) 441 c.Assert(err, check.IsNil, check.Commentf(out)) 442 c.Assert(string(out), checker.Contains, "Status: Downloaded", check.Commentf(out)) 443 444 // check to make sure that the target has been added to targets/releases and not targets 445 contents, err := ioutil.ReadFile(filepath.Join(cliconfig.ConfigDir(), "trust/tuf", repoName, "metadata/targets.json")) 446 c.Assert(err, check.IsNil, check.Commentf("Unable to read targets metadata")) 447 c.Assert(strings.Contains(string(contents), `"latest"`), checker.False, check.Commentf(string(contents))) 448 449 contents, err = ioutil.ReadFile(filepath.Join(cliconfig.ConfigDir(), "trust/tuf", repoName, "metadata/targets/releases.json")) 450 c.Assert(err, check.IsNil, check.Commentf("Unable to read targets/releases metadata")) 451 c.Assert(string(contents), checker.Contains, `"latest"`, check.Commentf(string(contents))) 452 }