f324f485 |
package main
import ( |
ad6c1b76 |
"encoding/json" |
f324f485 |
"fmt" |
ad6c1b76 |
"io/ioutil"
"os"
"path/filepath"
"runtime" |
23e68679 |
"strings" |
e25352a4 |
"testing" |
f324f485 |
|
ad6c1b76 |
"github.com/docker/distribution"
"github.com/docker/distribution/manifest"
"github.com/docker/distribution/manifest/manifestlist"
"github.com/docker/distribution/manifest/schema2" |
50c4475d |
"github.com/docker/docker/integration-cli/cli/build" |
7a855799 |
"github.com/opencontainers/go-digest" |
6345208b |
"gotest.tools/assert" |
38457285 |
"gotest.tools/icmd" |
f324f485 |
)
|
1fa2e311 |
// testPullImageWithAliases pulls a specific image tag and verifies that any aliases (i.e., other |
f324f485 |
// tags for the same image) are not also pulled down.
//
// Ref: docker/docker#8141 |
64a928a3 |
func testPullImageWithAliases(c *testing.T) { |
f324f485 |
repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
|
f23c00d8 |
var repos []string |
f324f485 |
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:] { |
dc9dd188 |
_, _, err := dockerCmdWithError("inspect", repo) |
6345208b |
assert.ErrorContains(c, err, "", "Image %v shouldn't have been pulled down", repo) |
f324f485 |
}
} |
23e68679 |
|
64a928a3 |
func (s *DockerRegistrySuite) TestPullImageWithAliases(c *testing.T) { |
1fa2e311 |
testPullImageWithAliases(c)
}
|
64a928a3 |
func (s *DockerSchema1RegistrySuite) TestPullImageWithAliases(c *testing.T) { |
21ae66c6 |
testPullImageWithAliases(c)
}
|
1fa2e311 |
// testConcurrentPullWholeRepo pulls the same repo concurrently. |
64a928a3 |
func testConcurrentPullWholeRepo(c *testing.T) { |
23e68679 |
repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
|
f23c00d8 |
var repos []string |
23e68679 |
for _, tag := range []string{"recent", "fresh", "todays"} {
repo := fmt.Sprintf("%v:%v", repoName, tag) |
50c4475d |
buildImageSuccessfully(c, repo, build.WithDockerfile(fmt.Sprintf(` |
23e68679 |
FROM busybox
ENTRYPOINT ["/bin/echo"]
ENV FOO foo
ENV BAR bar
CMD echo %s |
c10f6ef4 |
`, repo))) |
23e68679 |
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() { |
87e3fcfe |
result := icmd.RunCommand(dockerBinary, "pull", "-a", repoName)
results <- result.Error |
23e68679 |
}()
}
// These checks are separate from the loop above because the check
// package is not goroutine-safe.
for i := 0; i != numPulls; i++ {
err := <-results |
6345208b |
assert.NilError(c, err, "concurrent pull failed with error: %v", err) |
23e68679 |
}
// Ensure all tags were pulled successfully
for _, repo := range repos {
dockerCmd(c, "inspect", repo)
out, _ := dockerCmd(c, "run", "--rm", repo) |
6345208b |
assert.Equal(c, strings.TrimSpace(out), "/bin/sh -c echo "+repo) |
23e68679 |
}
}
|
64a928a3 |
func (s *DockerRegistrySuite) testConcurrentPullWholeRepo(c *testing.T) { |
1fa2e311 |
testConcurrentPullWholeRepo(c)
}
|
64a928a3 |
func (s *DockerSchema1RegistrySuite) testConcurrentPullWholeRepo(c *testing.T) { |
21ae66c6 |
testConcurrentPullWholeRepo(c)
}
|
1fa2e311 |
// testConcurrentFailingPull tries a concurrent pull that doesn't succeed. |
64a928a3 |
func testConcurrentFailingPull(c *testing.T) { |
23e68679 |
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() { |
87e3fcfe |
result := icmd.RunCommand(dockerBinary, "pull", repoName+":asdfasdf")
results <- result.Error |
23e68679 |
}()
}
// These checks are separate from the loop above because the check
// package is not goroutine-safe.
for i := 0; i != numPulls; i++ {
err := <-results |
6345208b |
assert.ErrorContains(c, err, "", "expected pull to fail") |
23e68679 |
}
}
|
64a928a3 |
func (s *DockerRegistrySuite) testConcurrentFailingPull(c *testing.T) { |
1fa2e311 |
testConcurrentFailingPull(c)
}
|
64a928a3 |
func (s *DockerSchema1RegistrySuite) testConcurrentFailingPull(c *testing.T) { |
21ae66c6 |
testConcurrentFailingPull(c)
}
|
1fa2e311 |
// testConcurrentPullMultipleTags pulls multiple tags from the same repo |
23e68679 |
// concurrently. |
64a928a3 |
func testConcurrentPullMultipleTags(c *testing.T) { |
23e68679 |
repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
|
f23c00d8 |
var repos []string |
23e68679 |
for _, tag := range []string{"recent", "fresh", "todays"} {
repo := fmt.Sprintf("%v:%v", repoName, tag) |
50c4475d |
buildImageSuccessfully(c, repo, build.WithDockerfile(fmt.Sprintf(` |
23e68679 |
FROM busybox
ENTRYPOINT ["/bin/echo"]
ENV FOO foo
ENV BAR bar
CMD echo %s |
c10f6ef4 |
`, repo))) |
23e68679 |
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) { |
87e3fcfe |
result := icmd.RunCommand(dockerBinary, "pull", repo)
results <- result.Error |
23e68679 |
}(repo)
}
// These checks are separate from the loop above because the check
// package is not goroutine-safe.
for range repos {
err := <-results |
6345208b |
assert.NilError(c, err, "concurrent pull failed with error: %v", err) |
23e68679 |
}
// Ensure all tags were pulled successfully
for _, repo := range repos {
dockerCmd(c, "inspect", repo)
out, _ := dockerCmd(c, "run", "--rm", repo) |
6345208b |
assert.Equal(c, strings.TrimSpace(out), "/bin/sh -c echo "+repo) |
23e68679 |
}
} |
4352da78 |
|
64a928a3 |
func (s *DockerRegistrySuite) TestConcurrentPullMultipleTags(c *testing.T) { |
1fa2e311 |
testConcurrentPullMultipleTags(c)
}
|
64a928a3 |
func (s *DockerSchema1RegistrySuite) TestConcurrentPullMultipleTags(c *testing.T) { |
21ae66c6 |
testConcurrentPullMultipleTags(c)
}
|
1fa2e311 |
// testPullIDStability verifies that pushing an image and pulling it back |
4352da78 |
// preserves the image ID. |
64a928a3 |
func testPullIDStability(c *testing.T) { |
4352da78 |
derivedImage := privateRegistryURL + "/dockercli/id-stability"
baseImage := "busybox"
|
50c4475d |
buildImageSuccessfully(c, derivedImage, build.WithDockerfile(fmt.Sprintf(` |
4352da78 |
FROM %s
ENV derived true
ENV asdf true
RUN dd if=/dev/zero of=/file bs=1024 count=1024
CMD echo %s |
c10f6ef4 |
`, baseImage, derivedImage))) |
4352da78 |
|
c10f6ef4 |
originalID := getIDByName(c, derivedImage) |
4352da78 |
dockerCmd(c, "push", derivedImage)
// Pull
out, _ := dockerCmd(c, "pull", derivedImage)
if strings.Contains(out, "Pull complete") {
c.Fatalf("repull redownloaded a layer: %s", out)
}
|
c10f6ef4 |
derivedIDAfterPull := getIDByName(c, derivedImage) |
4352da78 |
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)
|
c10f6ef4 |
derivedIDAfterPull = getIDByName(c, derivedImage) |
4352da78 |
if derivedIDAfterPull != originalID {
c.Fatal("image's ID unexpectedly changed after a repush/repull")
}
// 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)
}
} |
9d6acbee |
|
64a928a3 |
func (s *DockerRegistrySuite) TestPullIDStability(c *testing.T) { |
1fa2e311 |
testPullIDStability(c)
}
|
64a928a3 |
func (s *DockerSchema1RegistrySuite) TestPullIDStability(c *testing.T) { |
21ae66c6 |
testPullIDStability(c)
}
|
7cf894ce |
// #21213 |
64a928a3 |
func testPullNoLayers(c *testing.T) { |
7cf894ce |
repoName := fmt.Sprintf("%v/dockercli/scratch", privateRegistryURL)
|
50c4475d |
buildImageSuccessfully(c, repoName, build.WithDockerfile(` |
7cf894ce |
FROM scratch |
c10f6ef4 |
ENV foo bar`)) |
7cf894ce |
dockerCmd(c, "push", repoName)
dockerCmd(c, "rmi", repoName)
dockerCmd(c, "pull", repoName)
}
|
64a928a3 |
func (s *DockerRegistrySuite) TestPullNoLayers(c *testing.T) { |
7cf894ce |
testPullNoLayers(c)
}
|
64a928a3 |
func (s *DockerSchema1RegistrySuite) TestPullNoLayers(c *testing.T) { |
21ae66c6 |
testPullNoLayers(c)
}
|
64a928a3 |
func (s *DockerRegistrySuite) TestPullManifestList(c *testing.T) { |
4f339570 |
testRequires(c, NotArm) |
ad6c1b76 |
pushDigest, err := setupImage(c) |
6345208b |
assert.NilError(c, err, "error setting up image") |
ad6c1b76 |
// 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, "", " ") |
6345208b |
assert.NilError(c, err, "error marshalling manifest list") |
ad6c1b76 |
manifestListDigest := digest.FromBytes(manifestListJSON)
hexDigest := manifestListDigest.Hex()
|
4300e5e8 |
registryV2Path := s.reg.Path() |
ad6c1b76 |
// Write manifest list to blob store
blobDir := filepath.Join(registryV2Path, "blobs", "sha256", hexDigest[:2], hexDigest)
err = os.MkdirAll(blobDir, 0755) |
6345208b |
assert.NilError(c, err, "error creating blob dir") |
ad6c1b76 |
blobPath := filepath.Join(blobDir, "data")
err = ioutil.WriteFile(blobPath, []byte(manifestListJSON), 0644) |
6345208b |
assert.NilError(c, err, "error writing manifest list") |
ad6c1b76 |
// Add to revision store
revisionDir := filepath.Join(registryV2Path, "repositories", remoteRepoName, "_manifests", "revisions", "sha256", hexDigest)
err = os.Mkdir(revisionDir, 0755) |
2f069fa3 |
assert.Assert(c, err == nil, "error creating revision dir") |
ad6c1b76 |
revisionPath := filepath.Join(revisionDir, "link")
err = ioutil.WriteFile(revisionPath, []byte(manifestListDigest.String()), 0644) |
2f069fa3 |
assert.Assert(c, err == nil, "error writing revision link") |
ad6c1b76 |
// Update tag
tagPath := filepath.Join(registryV2Path, "repositories", remoteRepoName, "_manifests", "tags", "latest", "current", "link")
err = ioutil.WriteFile(tagPath, []byte(manifestListDigest.String()), 0644) |
6345208b |
assert.NilError(c, err, "error writing tag link") |
ad6c1b76 |
// 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) |
673cf751 |
assert.Equal(c, len(matches), 2, fmt.Sprintf("unable to parse digest from pull output: %s", out)) |
ad6c1b76 |
pullDigest := matches[1]
// Make sure the pushed and pull digests match |
6345208b |
assert.Equal(c, manifestListDigest.String(), pullDigest) |
ad6c1b76 |
// Was the image actually created?
dockerCmd(c, "inspect", repoName)
dockerCmd(c, "rmi", repoName)
} |
cf721c23 |
|
67d752ac |
// #23100 |
64a928a3 |
func (s *DockerRegistryAuthHtpasswdSuite) TestPullWithExternalAuthLoginWithScheme(c *testing.T) { |
67d752ac |
osPath := os.Getenv("PATH")
defer os.Setenv("PATH", osPath)
workingDir, err := os.Getwd() |
6345208b |
assert.NilError(c, err) |
67d752ac |
absolute, err := filepath.Abs(filepath.Join(workingDir, "fixtures", "auth")) |
6345208b |
assert.NilError(c, err) |
67d752ac |
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-") |
6345208b |
assert.NilError(c, err) |
67d752ac |
externalAuthConfig := `{ "credsStore": "shell-test" }`
configPath := filepath.Join(tmp, "config.json")
err = ioutil.WriteFile(configPath, []byte(externalAuthConfig), 0644) |
6345208b |
assert.NilError(c, err) |
67d752ac |
|
4300e5e8 |
dockerCmd(c, "--config", tmp, "login", "-u", s.reg.Username(), "-p", s.reg.Password(), privateRegistryURL) |
67d752ac |
b, err := ioutil.ReadFile(configPath) |
6345208b |
assert.NilError(c, err) |
07b24365 |
assert.Assert(c, !strings.Contains(string(b), "\"auth\":")) |
67d752ac |
dockerCmd(c, "--config", tmp, "tag", "busybox", repoName)
dockerCmd(c, "--config", tmp, "push", repoName)
dockerCmd(c, "--config", tmp, "logout", privateRegistryURL) |
4300e5e8 |
dockerCmd(c, "--config", tmp, "login", "-u", s.reg.Username(), "-p", s.reg.Password(), "https://"+privateRegistryURL) |
67d752ac |
dockerCmd(c, "--config", tmp, "pull", repoName)
// likewise push should work
repoName2 := fmt.Sprintf("%v/dockercli/busybox:nocreds", privateRegistryURL)
dockerCmd(c, "tag", repoName, repoName2)
dockerCmd(c, "--config", tmp, "push", repoName2)
// logout should work w scheme also because it will be stripped
dockerCmd(c, "--config", tmp, "logout", "https://"+privateRegistryURL)
}
|
64a928a3 |
func (s *DockerRegistryAuthHtpasswdSuite) TestPullWithExternalAuth(c *testing.T) { |
cf721c23 |
osPath := os.Getenv("PATH")
defer os.Setenv("PATH", osPath)
workingDir, err := os.Getwd() |
6345208b |
assert.NilError(c, err) |
cf721c23 |
absolute, err := filepath.Abs(filepath.Join(workingDir, "fixtures", "auth")) |
6345208b |
assert.NilError(c, err) |
cf721c23 |
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-") |
6345208b |
assert.NilError(c, err) |
cf721c23 |
externalAuthConfig := `{ "credsStore": "shell-test" }`
configPath := filepath.Join(tmp, "config.json")
err = ioutil.WriteFile(configPath, []byte(externalAuthConfig), 0644) |
6345208b |
assert.NilError(c, err) |
cf721c23 |
|
4300e5e8 |
dockerCmd(c, "--config", tmp, "login", "-u", s.reg.Username(), "-p", s.reg.Password(), privateRegistryURL) |
cf721c23 |
b, err := ioutil.ReadFile(configPath) |
6345208b |
assert.NilError(c, err) |
07b24365 |
assert.Assert(c, !strings.Contains(string(b), "\"auth\":")) |
cf721c23 |
dockerCmd(c, "--config", tmp, "tag", "busybox", repoName)
dockerCmd(c, "--config", tmp, "push", repoName)
dockerCmd(c, "--config", tmp, "pull", repoName)
} |
641c1808 |
|
f66f5d4b |
// TestRunImplicitPullWithNoTag should pull implicitly only the default tag (latest) |
64a928a3 |
func (s *DockerRegistrySuite) TestRunImplicitPullWithNoTag(c *testing.T) { |
641c1808 |
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)
|
ba0afd70 |
out, _ := dockerCmd(c, "run", repo) |
ed9449a4 |
assert.Assert(c, strings.Contains(out, fmt.Sprintf("Unable to find image '%s:latest' locally", repo))) |
641c1808 |
// There should be only one line for repo, the one with repo:latest |
ba0afd70 |
outImageCmd, _ := dockerCmd(c, "images", repo) |
641c1808 |
splitOutImageCmd := strings.Split(strings.TrimSpace(outImageCmd), "\n") |
7b91af80 |
assert.Equal(c, len(splitOutImageCmd), 2) |
641c1808 |
} |