mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
9d6acbee92
PR #18590 caused compatibility issues with registries such as gcr.io which support both the v1 and v2 protocols, but do not provide the same set of images over both protocols. After #18590, pulls from these registries would never use the v1 protocol, because of the Docker-Distribution-Api-Version header indicating that v2 was supported. Fix the problem by making an exception for the case where a manifest is not found. This should allow fallback to v1 in case that image is exposed over the v1 protocol but not the v2 protocol. This avoids the overly aggressive fallback behavior before #18590 which would allow protocol fallback after almost any error, but restores interoperability with mixed v1/v2 registry setups. Fixes #18832 Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
242 lines
6.8 KiB
Go
242 lines
6.8 KiB
Go
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)
|
|
}
|
|
}
|
|
|
|
// TestPullFallbackOn404 tries to pull a nonexistent manifest and confirms that
|
|
// the pull falls back to the v1 protocol.
|
|
//
|
|
// Ref: docker/docker#18832
|
|
func (s *DockerRegistrySuite) TestPullFallbackOn404(c *check.C) {
|
|
repoName := fmt.Sprintf("%v/does/not/exist", privateRegistryURL)
|
|
|
|
out, _, _ := dockerCmdWithError("pull", repoName)
|
|
|
|
c.Assert(out, checker.Contains, "v1 ping attempt")
|
|
}
|