|
@@ -2,6 +2,8 @@ package main
|
|
|
|
|
|
import (
|
|
import (
|
|
"fmt"
|
|
"fmt"
|
|
|
|
+ "os/exec"
|
|
|
|
+ "strings"
|
|
|
|
|
|
"github.com/go-check/check"
|
|
"github.com/go-check/check"
|
|
)
|
|
)
|
|
@@ -37,3 +39,134 @@ func (s *DockerRegistrySuite) TestPullImageWithAliases(c *check.C) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+// 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)
|
|
|
|
+ if err != nil {
|
|
|
|
+ c.Fatal(err)
|
|
|
|
+ }
|
|
|
|
+ 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, check.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)
|
|
|
|
+ if strings.TrimSpace(out) != "/bin/sh -c echo "+repo {
|
|
|
|
+ c.Fatalf("CMD did not contain /bin/sh -c echo %s: %s", repo, out)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// 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
|
|
|
|
+ if err == nil {
|
|
|
|
+ c.Fatal("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)
|
|
|
|
+ if err != nil {
|
|
|
|
+ c.Fatal(err)
|
|
|
|
+ }
|
|
|
|
+ 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, check.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)
|
|
|
|
+ if strings.TrimSpace(out) != "/bin/sh -c echo "+repo {
|
|
|
|
+ c.Fatalf("CMD did not contain /bin/sh -c echo %s: %s", repo, out)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|