123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446 |
- package main
- import (
- "encoding/json"
- "fmt"
- "io/ioutil"
- "os"
- "os/exec"
- "path/filepath"
- "runtime"
- "strings"
- "github.com/docker/distribution"
- "github.com/docker/distribution/digest"
- "github.com/docker/distribution/manifest"
- "github.com/docker/distribution/manifest/manifestlist"
- "github.com/docker/distribution/manifest/schema2"
- "github.com/docker/docker/pkg/integration/checker"
- "github.com/go-check/check"
- )
- // testPullImageWithAliases pulls a specific image tag and verifies that any aliases (i.e., other
- // tags for the same image) are not also pulled down.
- //
- // Ref: docker/docker#8141
- func testPullImageWithAliases(c *check.C) {
- repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
- repos := []string{}
- for _, tag := range []string{"recent", "fresh"} {
- repos = append(repos, fmt.Sprintf("%v:%v", repoName, tag))
- }
- // Tag and push the same image multiple times.
- for _, repo := range repos {
- dockerCmd(c, "tag", "busybox", repo)
- dockerCmd(c, "push", repo)
- }
- // Clear local images store.
- args := append([]string{"rmi"}, repos...)
- dockerCmd(c, args...)
- // Pull a single tag and verify it doesn't bring down all aliases.
- dockerCmd(c, "pull", repos[0])
- dockerCmd(c, "inspect", repos[0])
- for _, repo := range repos[1:] {
- _, _, err := dockerCmdWithError("inspect", repo)
- c.Assert(err, checker.NotNil, check.Commentf("Image %v shouldn't have been pulled down", repo))
- }
- }
- func (s *DockerRegistrySuite) TestPullImageWithAliases(c *check.C) {
- testPullImageWithAliases(c)
- }
- func (s *DockerSchema1RegistrySuite) TestPullImageWithAliases(c *check.C) {
- testPullImageWithAliases(c)
- }
- // testConcurrentPullWholeRepo pulls the same repo concurrently.
- func testConcurrentPullWholeRepo(c *check.C) {
- repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
- repos := []string{}
- for _, tag := range []string{"recent", "fresh", "todays"} {
- repo := fmt.Sprintf("%v:%v", repoName, tag)
- _, err := buildImage(repo, fmt.Sprintf(`
- FROM busybox
- ENTRYPOINT ["/bin/echo"]
- ENV FOO foo
- ENV BAR bar
- CMD echo %s
- `, repo), true)
- c.Assert(err, checker.IsNil)
- dockerCmd(c, "push", repo)
- repos = append(repos, repo)
- }
- // Clear local images store.
- args := append([]string{"rmi"}, repos...)
- dockerCmd(c, args...)
- // Run multiple re-pulls concurrently
- results := make(chan error)
- numPulls := 3
- for i := 0; i != numPulls; i++ {
- go func() {
- _, _, err := runCommandWithOutput(exec.Command(dockerBinary, "pull", "-a", repoName))
- results <- err
- }()
- }
- // These checks are separate from the loop above because the check
- // package is not goroutine-safe.
- for i := 0; i != numPulls; i++ {
- err := <-results
- c.Assert(err, checker.IsNil, check.Commentf("concurrent pull failed with error: %v", err))
- }
- // Ensure all tags were pulled successfully
- for _, repo := range repos {
- dockerCmd(c, "inspect", repo)
- out, _ := dockerCmd(c, "run", "--rm", repo)
- c.Assert(strings.TrimSpace(out), checker.Equals, "/bin/sh -c echo "+repo)
- }
- }
- func (s *DockerRegistrySuite) testConcurrentPullWholeRepo(c *check.C) {
- testConcurrentPullWholeRepo(c)
- }
- func (s *DockerSchema1RegistrySuite) testConcurrentPullWholeRepo(c *check.C) {
- testConcurrentPullWholeRepo(c)
- }
- // testConcurrentFailingPull tries a concurrent pull that doesn't succeed.
- func testConcurrentFailingPull(c *check.C) {
- repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
- // Run multiple pulls concurrently
- results := make(chan error)
- numPulls := 3
- for i := 0; i != numPulls; i++ {
- go func() {
- _, _, err := runCommandWithOutput(exec.Command(dockerBinary, "pull", repoName+":asdfasdf"))
- results <- err
- }()
- }
- // These checks are separate from the loop above because the check
- // package is not goroutine-safe.
- for i := 0; i != numPulls; i++ {
- err := <-results
- c.Assert(err, checker.NotNil, check.Commentf("expected pull to fail"))
- }
- }
- func (s *DockerRegistrySuite) testConcurrentFailingPull(c *check.C) {
- testConcurrentFailingPull(c)
- }
- func (s *DockerSchema1RegistrySuite) testConcurrentFailingPull(c *check.C) {
- testConcurrentFailingPull(c)
- }
- // testConcurrentPullMultipleTags pulls multiple tags from the same repo
- // concurrently.
- func testConcurrentPullMultipleTags(c *check.C) {
- repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
- repos := []string{}
- for _, tag := range []string{"recent", "fresh", "todays"} {
- repo := fmt.Sprintf("%v:%v", repoName, tag)
- _, err := buildImage(repo, fmt.Sprintf(`
- FROM busybox
- ENTRYPOINT ["/bin/echo"]
- ENV FOO foo
- ENV BAR bar
- CMD echo %s
- `, repo), true)
- c.Assert(err, checker.IsNil)
- dockerCmd(c, "push", repo)
- repos = append(repos, repo)
- }
- // Clear local images store.
- args := append([]string{"rmi"}, repos...)
- dockerCmd(c, args...)
- // Re-pull individual tags, in parallel
- results := make(chan error)
- for _, repo := range repos {
- go func(repo string) {
- _, _, err := runCommandWithOutput(exec.Command(dockerBinary, "pull", repo))
- results <- err
- }(repo)
- }
- // These checks are separate from the loop above because the check
- // package is not goroutine-safe.
- for range repos {
- err := <-results
- c.Assert(err, checker.IsNil, check.Commentf("concurrent pull failed with error: %v", err))
- }
- // Ensure all tags were pulled successfully
- for _, repo := range repos {
- dockerCmd(c, "inspect", repo)
- out, _ := dockerCmd(c, "run", "--rm", repo)
- c.Assert(strings.TrimSpace(out), checker.Equals, "/bin/sh -c echo "+repo)
- }
- }
- func (s *DockerRegistrySuite) TestConcurrentPullMultipleTags(c *check.C) {
- testConcurrentPullMultipleTags(c)
- }
- func (s *DockerSchema1RegistrySuite) TestConcurrentPullMultipleTags(c *check.C) {
- testConcurrentPullMultipleTags(c)
- }
- // testPullIDStability verifies that pushing an image and pulling it back
- // preserves the image ID.
- func testPullIDStability(c *check.C) {
- derivedImage := privateRegistryURL + "/dockercli/id-stability"
- baseImage := "busybox"
- _, err := buildImage(derivedImage, fmt.Sprintf(`
- FROM %s
- ENV derived true
- ENV asdf true
- RUN dd if=/dev/zero of=/file bs=1024 count=1024
- CMD echo %s
- `, baseImage, derivedImage), true)
- if err != nil {
- c.Fatal(err)
- }
- originalID, err := getIDByName(derivedImage)
- if err != nil {
- c.Fatalf("error inspecting: %v", err)
- }
- dockerCmd(c, "push", derivedImage)
- // Pull
- out, _ := dockerCmd(c, "pull", derivedImage)
- if strings.Contains(out, "Pull complete") {
- c.Fatalf("repull redownloaded a layer: %s", out)
- }
- derivedIDAfterPull, err := getIDByName(derivedImage)
- if err != nil {
- c.Fatalf("error inspecting: %v", err)
- }
- if derivedIDAfterPull != originalID {
- c.Fatal("image's ID unexpectedly changed after a repush/repull")
- }
- // Make sure the image runs correctly
- out, _ = dockerCmd(c, "run", "--rm", derivedImage)
- if strings.TrimSpace(out) != derivedImage {
- c.Fatalf("expected %s; got %s", derivedImage, out)
- }
- // Confirm that repushing and repulling does not change the computed ID
- dockerCmd(c, "push", derivedImage)
- dockerCmd(c, "rmi", derivedImage)
- dockerCmd(c, "pull", derivedImage)
- derivedIDAfterPull, err = getIDByName(derivedImage)
- if err != nil {
- c.Fatalf("error inspecting: %v", err)
- }
- if derivedIDAfterPull != originalID {
- c.Fatal("image's ID unexpectedly changed after a repush/repull")
- }
- if err != nil {
- c.Fatalf("error inspecting: %v", err)
- }
- // Make sure the image still runs
- out, _ = dockerCmd(c, "run", "--rm", derivedImage)
- if strings.TrimSpace(out) != derivedImage {
- c.Fatalf("expected %s; got %s", derivedImage, out)
- }
- }
- func (s *DockerRegistrySuite) TestPullIDStability(c *check.C) {
- testPullIDStability(c)
- }
- func (s *DockerSchema1RegistrySuite) TestPullIDStability(c *check.C) {
- testPullIDStability(c)
- }
- // #21213
- func testPullNoLayers(c *check.C) {
- repoName := fmt.Sprintf("%v/dockercli/scratch", privateRegistryURL)
- _, err := buildImage(repoName, `
- FROM scratch
- ENV foo bar`,
- true)
- if err != nil {
- c.Fatal(err)
- }
- dockerCmd(c, "push", repoName)
- dockerCmd(c, "rmi", repoName)
- dockerCmd(c, "pull", repoName)
- }
- func (s *DockerRegistrySuite) TestPullNoLayers(c *check.C) {
- testPullNoLayers(c)
- }
- func (s *DockerSchema1RegistrySuite) TestPullNoLayers(c *check.C) {
- testPullNoLayers(c)
- }
- func (s *DockerRegistrySuite) TestPullManifestList(c *check.C) {
- testRequires(c, NotArm)
- pushDigest, err := setupImage(c)
- c.Assert(err, checker.IsNil, check.Commentf("error setting up image"))
- // Inject a manifest list into the registry
- manifestList := &manifestlist.ManifestList{
- Versioned: manifest.Versioned{
- SchemaVersion: 2,
- MediaType: manifestlist.MediaTypeManifestList,
- },
- Manifests: []manifestlist.ManifestDescriptor{
- {
- Descriptor: distribution.Descriptor{
- Digest: "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b",
- Size: 3253,
- MediaType: schema2.MediaTypeManifest,
- },
- Platform: manifestlist.PlatformSpec{
- Architecture: "bogus_arch",
- OS: "bogus_os",
- },
- },
- {
- Descriptor: distribution.Descriptor{
- Digest: pushDigest,
- Size: 3253,
- MediaType: schema2.MediaTypeManifest,
- },
- Platform: manifestlist.PlatformSpec{
- Architecture: runtime.GOARCH,
- OS: runtime.GOOS,
- },
- },
- },
- }
- manifestListJSON, err := json.MarshalIndent(manifestList, "", " ")
- c.Assert(err, checker.IsNil, check.Commentf("error marshalling manifest list"))
- manifestListDigest := digest.FromBytes(manifestListJSON)
- hexDigest := manifestListDigest.Hex()
- registryV2Path := filepath.Join(s.reg.dir, "docker", "registry", "v2")
- // Write manifest list to blob store
- blobDir := filepath.Join(registryV2Path, "blobs", "sha256", hexDigest[:2], hexDigest)
- err = os.MkdirAll(blobDir, 0755)
- c.Assert(err, checker.IsNil, check.Commentf("error creating blob dir"))
- blobPath := filepath.Join(blobDir, "data")
- err = ioutil.WriteFile(blobPath, []byte(manifestListJSON), 0644)
- c.Assert(err, checker.IsNil, check.Commentf("error writing manifest list"))
- // Add to revision store
- revisionDir := filepath.Join(registryV2Path, "repositories", remoteRepoName, "_manifests", "revisions", "sha256", hexDigest)
- err = os.Mkdir(revisionDir, 0755)
- c.Assert(err, checker.IsNil, check.Commentf("error creating revision dir"))
- revisionPath := filepath.Join(revisionDir, "link")
- err = ioutil.WriteFile(revisionPath, []byte(manifestListDigest.String()), 0644)
- c.Assert(err, checker.IsNil, check.Commentf("error writing revision link"))
- // Update tag
- tagPath := filepath.Join(registryV2Path, "repositories", remoteRepoName, "_manifests", "tags", "latest", "current", "link")
- err = ioutil.WriteFile(tagPath, []byte(manifestListDigest.String()), 0644)
- c.Assert(err, checker.IsNil, check.Commentf("error writing tag link"))
- // Verify that the image can be pulled through the manifest list.
- out, _ := dockerCmd(c, "pull", repoName)
- // The pull output includes "Digest: <digest>", so find that
- matches := digestRegex.FindStringSubmatch(out)
- c.Assert(matches, checker.HasLen, 2, check.Commentf("unable to parse digest from pull output: %s", out))
- pullDigest := matches[1]
- // Make sure the pushed and pull digests match
- c.Assert(manifestListDigest.String(), checker.Equals, pullDigest)
- // Was the image actually created?
- dockerCmd(c, "inspect", repoName)
- dockerCmd(c, "rmi", repoName)
- }
- func (s *DockerRegistryAuthHtpasswdSuite) TestPullWithExternalAuth(c *check.C) {
- osPath := os.Getenv("PATH")
- defer os.Setenv("PATH", osPath)
- workingDir, err := os.Getwd()
- c.Assert(err, checker.IsNil)
- absolute, err := filepath.Abs(filepath.Join(workingDir, "fixtures", "auth"))
- c.Assert(err, checker.IsNil)
- testPath := fmt.Sprintf("%s%c%s", osPath, filepath.ListSeparator, absolute)
- os.Setenv("PATH", testPath)
- repoName := fmt.Sprintf("%v/dockercli/busybox:authtest", privateRegistryURL)
- tmp, err := ioutil.TempDir("", "integration-cli-")
- c.Assert(err, checker.IsNil)
- externalAuthConfig := `{ "credsStore": "shell-test" }`
- configPath := filepath.Join(tmp, "config.json")
- err = ioutil.WriteFile(configPath, []byte(externalAuthConfig), 0644)
- c.Assert(err, checker.IsNil)
- dockerCmd(c, "--config", tmp, "login", "-u", s.reg.username, "-p", s.reg.password, privateRegistryURL)
- b, err := ioutil.ReadFile(configPath)
- c.Assert(err, checker.IsNil)
- c.Assert(string(b), checker.Not(checker.Contains), "\"auth\":")
- dockerCmd(c, "--config", tmp, "tag", "busybox", repoName)
- dockerCmd(c, "--config", tmp, "push", repoName)
- dockerCmd(c, "--config", tmp, "pull", repoName)
- }
- // TestRunImplicitPullWithNoTag should pull implicitly only the default tag (latest)
- func (s *DockerRegistrySuite) TestRunImplicitPullWithNoTag(c *check.C) {
- testRequires(c, DaemonIsLinux)
- repo := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
- repoTag1 := fmt.Sprintf("%v:latest", repo)
- repoTag2 := fmt.Sprintf("%v:t1", repo)
- // tag the image and upload it to the private registry
- dockerCmd(c, "tag", "busybox", repoTag1)
- dockerCmd(c, "tag", "busybox", repoTag2)
- dockerCmd(c, "push", repo)
- dockerCmd(c, "rmi", repoTag1)
- dockerCmd(c, "rmi", repoTag2)
- out, _, err := dockerCmdWithError("run", repo)
- c.Assert(err, check.IsNil)
- c.Assert(out, checker.Contains, fmt.Sprintf("Unable to find image '%s:latest' locally", repo))
- // There should be only one line for repo, the one with repo:latest
- outImageCmd, _, err := dockerCmdWithError("images", repo)
- splitOutImageCmd := strings.Split(strings.TrimSpace(outImageCmd), "\n")
- c.Assert(splitOutImageCmd, checker.HasLen, 2)
- }
|