package main import ( "fmt" "os/exec" "strings" "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 (s *DockerRegistrySuite) 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)) } } // TestConcurrentPullWholeRepo pulls the same repo concurrently. func (s *DockerRegistrySuite) 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) } } // TestConcurrentFailingPull tries a concurrent pull that doesn't succeed. func (s *DockerRegistrySuite) 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")) } } // TestConcurrentPullMultipleTags pulls multiple tags from the same repo // concurrently. func (s *DockerRegistrySuite) 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) } } // TestPullIDStability verifies that pushing an image and pulling it back // preserves the image ID. func (s *DockerRegistrySuite) 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) } }