Build a pre-schema2 registry to test schema1 push/pull

Add DockerSchema1RegistrySuite which uses this registry, and make
applicable integration tests run as part of this suite.

Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
This commit is contained in:
Aaron Lehmann 2015-12-18 15:06:23 -08:00
parent 2bb8c85bc5
commit 1fa2e31151
7 changed files with 259 additions and 27 deletions

View File

@ -142,14 +142,21 @@ RUN set -x \
) \
&& rm -rf "$SECCOMP_PATH"
# Install registry
ENV REGISTRY_COMMIT ec87e9b6971d831f0eff752ddb54fb64693e51cd
# Install two versions of the registry. The first is an older version that
# only supports schema1 manifests. The second is a newer version that supports
# both. This allows integration-cli tests to cover push/pull with both schema1
# and schema2 manifests.
ENV REGISTRY_COMMIT_SCHEMA1 ec87e9b6971d831f0eff752ddb54fb64693e51cd
ENV REGISTRY_COMMIT a7ae88da459b98b481a245e5b1750134724ac67d
RUN set -x \
&& export GOPATH="$(mktemp -d)" \
&& git clone https://github.com/docker/distribution.git "$GOPATH/src/github.com/docker/distribution" \
&& (cd "$GOPATH/src/github.com/docker/distribution" && git checkout -q "$REGISTRY_COMMIT") \
&& GOPATH="$GOPATH/src/github.com/docker/distribution/Godeps/_workspace:$GOPATH" \
go build -o /usr/local/bin/registry-v2 github.com/docker/distribution/cmd/registry \
&& (cd "$GOPATH/src/github.com/docker/distribution" && git checkout -q "$REGISTRY_COMMIT_SCHEMA1") \
&& GOPATH="$GOPATH/src/github.com/docker/distribution/Godeps/_workspace:$GOPATH" \
go build -o /usr/local/bin/registry-v2-schema1 github.com/docker/distribution/cmd/registry \
&& rm -rf "$GOPATH"
# Install notary server

View File

@ -48,7 +48,7 @@ type DockerRegistrySuite struct {
func (s *DockerRegistrySuite) SetUpTest(c *check.C) {
testRequires(c, DaemonIsLinux)
s.reg = setupRegistry(c)
s.reg = setupRegistry(c, false)
s.d = NewDaemon(c)
}
@ -62,6 +62,34 @@ func (s *DockerRegistrySuite) TearDownTest(c *check.C) {
s.d.Stop()
}
func init() {
check.Suite(&DockerSchema1RegistrySuite{
ds: &DockerSuite{},
})
}
type DockerSchema1RegistrySuite struct {
ds *DockerSuite
reg *testRegistryV2
d *Daemon
}
func (s *DockerSchema1RegistrySuite) SetUpTest(c *check.C) {
testRequires(c, DaemonIsLinux)
s.reg = setupRegistry(c, true)
s.d = NewDaemon(c)
}
func (s *DockerSchema1RegistrySuite) TearDownTest(c *check.C) {
if s.reg != nil {
s.reg.Close()
}
if s.ds != nil {
s.ds.TearDownTest(c)
}
s.d.Stop()
}
func init() {
check.Suite(&DockerDaemonSuite{
ds: &DockerSuite{},
@ -97,7 +125,7 @@ type DockerTrustSuite struct {
}
func (s *DockerTrustSuite) SetUpTest(c *check.C) {
s.reg = setupRegistry(c)
s.reg = setupRegistry(c, false)
s.not = setupNotary(c)
}

View File

@ -10,6 +10,7 @@ import (
"github.com/docker/distribution/digest"
"github.com/docker/distribution/manifest/schema1"
"github.com/docker/distribution/manifest/schema2"
"github.com/docker/docker/pkg/integration/checker"
"github.com/docker/docker/pkg/stringutils"
"github.com/docker/engine-api/types"
@ -56,7 +57,7 @@ func setupImageWithTag(c *check.C, tag string) (digest.Digest, error) {
return digest.Digest(pushDigest), nil
}
func (s *DockerRegistrySuite) TestPullByTagDisplaysDigest(c *check.C) {
func testPullByTagDisplaysDigest(c *check.C) {
testRequires(c, DaemonIsLinux)
pushDigest, err := setupImage(c)
c.Assert(err, checker.IsNil, check.Commentf("error setting up image"))
@ -73,7 +74,15 @@ func (s *DockerRegistrySuite) TestPullByTagDisplaysDigest(c *check.C) {
c.Assert(pushDigest.String(), checker.Equals, pullDigest)
}
func (s *DockerRegistrySuite) TestPullByDigest(c *check.C) {
func (s *DockerRegistrySuite) TestPullByTagDisplaysDigest(c *check.C) {
testPullByTagDisplaysDigest(c)
}
func (s *DockerSchema1RegistrySuite) TestPullByTagDisplaysDigest(c *check.C) {
testPullByTagDisplaysDigest(c)
}
func testPullByDigest(c *check.C) {
testRequires(c, DaemonIsLinux)
pushDigest, err := setupImage(c)
c.Assert(err, checker.IsNil, check.Commentf("error setting up image"))
@ -91,7 +100,15 @@ func (s *DockerRegistrySuite) TestPullByDigest(c *check.C) {
c.Assert(pushDigest.String(), checker.Equals, pullDigest)
}
func (s *DockerRegistrySuite) TestPullByDigestNoFallback(c *check.C) {
func (s *DockerRegistrySuite) TestPullByDigest(c *check.C) {
testPullByDigest(c)
}
func (s *DockerSchema1RegistrySuite) TestPullByDigest(c *check.C) {
testPullByDigest(c)
}
func testPullByDigestNoFallback(c *check.C) {
testRequires(c, DaemonIsLinux)
// pull from the registry using the <name>@<digest> reference
imageReference := fmt.Sprintf("%s@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", repoName)
@ -100,6 +117,14 @@ func (s *DockerRegistrySuite) TestPullByDigestNoFallback(c *check.C) {
c.Assert(out, checker.Contains, "manifest unknown", check.Commentf("expected non-zero exit status and correct error message when pulling non-existing image"))
}
func (s *DockerRegistrySuite) TestPullByDigestNoFallback(c *check.C) {
testPullByDigestNoFallback(c)
}
func (s *DockerSchema1RegistrySuite) TestPullByDigestNoFallback(c *check.C) {
testPullByDigestNoFallback(c)
}
func (s *DockerRegistrySuite) TestCreateByDigest(c *check.C) {
pushDigest, err := setupImage(c)
c.Assert(err, checker.IsNil, check.Commentf("error setting up image"))
@ -372,6 +397,7 @@ func (s *DockerRegistrySuite) TestDeleteImageByIDOnlyPulledByDigest(c *check.C)
// TestPullFailsWithAlteredManifest tests that a `docker pull` fails when
// we have modified a manifest blob and its digest cannot be verified.
// This is the schema2 version of the test.
func (s *DockerRegistrySuite) TestPullFailsWithAlteredManifest(c *check.C) {
testRequires(c, DaemonIsLinux)
manifestDigest, err := setupImage(c)
@ -380,6 +406,46 @@ func (s *DockerRegistrySuite) TestPullFailsWithAlteredManifest(c *check.C) {
// Load the target manifest blob.
manifestBlob := s.reg.readBlobContents(c, manifestDigest)
var imgManifest schema2.Manifest
err = json.Unmarshal(manifestBlob, &imgManifest)
c.Assert(err, checker.IsNil, check.Commentf("unable to decode image manifest from blob"))
// Change a layer in the manifest.
imgManifest.Layers[0].Digest = digest.Digest("sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
// Move the existing data file aside, so that we can replace it with a
// malicious blob of data. NOTE: we defer the returned undo func.
undo := s.reg.tempMoveBlobData(c, manifestDigest)
defer undo()
alteredManifestBlob, err := json.MarshalIndent(imgManifest, "", " ")
c.Assert(err, checker.IsNil, check.Commentf("unable to encode altered image manifest to JSON"))
s.reg.writeBlobContents(c, manifestDigest, alteredManifestBlob)
// Now try pulling that image by digest. We should get an error about
// digest verification for the manifest digest.
// Pull from the registry using the <name>@<digest> reference.
imageReference := fmt.Sprintf("%s@%s", repoName, manifestDigest)
out, exitStatus, _ := dockerCmdWithError("pull", imageReference)
c.Assert(exitStatus, checker.Not(check.Equals), 0)
expectedErrorMsg := fmt.Sprintf("manifest verification failed for digest %s", manifestDigest)
c.Assert(out, checker.Contains, expectedErrorMsg)
}
// TestPullFailsWithAlteredManifest tests that a `docker pull` fails when
// we have modified a manifest blob and its digest cannot be verified.
// This is the schema1 version of the test.
func (s *DockerSchema1RegistrySuite) TestPullFailsWithAlteredManifest(c *check.C) {
testRequires(c, DaemonIsLinux)
manifestDigest, err := setupImage(c)
c.Assert(err, checker.IsNil, check.Commentf("error setting up image"))
// Load the target manifest blob.
manifestBlob := s.reg.readBlobContents(c, manifestDigest)
var imgManifest schema1.Manifest
err = json.Unmarshal(manifestBlob, &imgManifest)
c.Assert(err, checker.IsNil, check.Commentf("unable to decode image manifest from blob"))
@ -413,6 +479,7 @@ func (s *DockerRegistrySuite) TestPullFailsWithAlteredManifest(c *check.C) {
// TestPullFailsWithAlteredLayer tests that a `docker pull` fails when
// we have modified a layer blob and its digest cannot be verified.
// This is the schema2 version of the test.
func (s *DockerRegistrySuite) TestPullFailsWithAlteredLayer(c *check.C) {
testRequires(c, DaemonIsLinux)
manifestDigest, err := setupImage(c)
@ -421,6 +488,49 @@ func (s *DockerRegistrySuite) TestPullFailsWithAlteredLayer(c *check.C) {
// Load the target manifest blob.
manifestBlob := s.reg.readBlobContents(c, manifestDigest)
var imgManifest schema2.Manifest
err = json.Unmarshal(manifestBlob, &imgManifest)
c.Assert(err, checker.IsNil)
// Next, get the digest of one of the layers from the manifest.
targetLayerDigest := imgManifest.Layers[0].Digest
// Move the existing data file aside, so that we can replace it with a
// malicious blob of data. NOTE: we defer the returned undo func.
undo := s.reg.tempMoveBlobData(c, targetLayerDigest)
defer undo()
// Now make a fake data blob in this directory.
s.reg.writeBlobContents(c, targetLayerDigest, []byte("This is not the data you are looking for."))
// Now try pulling that image by digest. We should get an error about
// digest verification for the target layer digest.
// Remove distribution cache to force a re-pull of the blobs
if err := os.RemoveAll(filepath.Join(dockerBasePath, "image", s.d.storageDriver, "distribution")); err != nil {
c.Fatalf("error clearing distribution cache: %v", err)
}
// Pull from the registry using the <name>@<digest> reference.
imageReference := fmt.Sprintf("%s@%s", repoName, manifestDigest)
out, exitStatus, _ := dockerCmdWithError("pull", imageReference)
c.Assert(exitStatus, checker.Not(check.Equals), 0, check.Commentf("expected a zero exit status"))
expectedErrorMsg := fmt.Sprintf("filesystem layer verification failed for digest %s", targetLayerDigest)
c.Assert(out, checker.Contains, expectedErrorMsg, check.Commentf("expected error message in output: %s", out))
}
// TestPullFailsWithAlteredLayer tests that a `docker pull` fails when
// we have modified a layer blob and its digest cannot be verified.
// This is the schema1 version of the test.
func (s *DockerSchema1RegistrySuite) TestPullFailsWithAlteredLayer(c *check.C) {
testRequires(c, DaemonIsLinux)
manifestDigest, err := setupImage(c)
c.Assert(err, checker.IsNil)
// Load the target manifest blob.
manifestBlob := s.reg.readBlobContents(c, manifestDigest)
var imgManifest schema1.Manifest
err = json.Unmarshal(manifestBlob, &imgManifest)
c.Assert(err, checker.IsNil)

View File

@ -9,11 +9,11 @@ import (
"github.com/go-check/check"
)
// TestPullImageWithAliases pulls a specific image tag and verifies that any aliases (i.e., other
// 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) {
func testPullImageWithAliases(c *check.C) {
repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
repos := []string{}
@ -40,8 +40,16 @@ func (s *DockerRegistrySuite) TestPullImageWithAliases(c *check.C) {
}
}
// TestConcurrentPullWholeRepo pulls the same repo concurrently.
func (s *DockerRegistrySuite) TestConcurrentPullWholeRepo(c *check.C) {
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{}
@ -89,8 +97,16 @@ func (s *DockerRegistrySuite) TestConcurrentPullWholeRepo(c *check.C) {
}
}
// TestConcurrentFailingPull tries a concurrent pull that doesn't succeed.
func (s *DockerRegistrySuite) TestConcurrentFailingPull(c *check.C) {
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
@ -112,9 +128,17 @@ func (s *DockerRegistrySuite) TestConcurrentFailingPull(c *check.C) {
}
}
// TestConcurrentPullMultipleTags pulls multiple tags from the same repo
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 (s *DockerRegistrySuite) TestConcurrentPullMultipleTags(c *check.C) {
func testConcurrentPullMultipleTags(c *check.C) {
repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
repos := []string{}
@ -161,9 +185,17 @@ func (s *DockerRegistrySuite) TestConcurrentPullMultipleTags(c *check.C) {
}
}
// TestPullIDStability verifies that pushing an image and pulling it back
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 (s *DockerRegistrySuite) TestPullIDStability(c *check.C) {
func testPullIDStability(c *check.C) {
derivedImage := privateRegistryURL + "/dockercli/id-stability"
baseImage := "busybox"
@ -229,6 +261,14 @@ func (s *DockerRegistrySuite) TestPullIDStability(c *check.C) {
}
}
func (s *DockerRegistrySuite) TestPullIDStability(c *check.C) {
testPullIDStability(c)
}
func (s *DockerSchema1RegistrySuite) TestPullIDStability(c *check.C) {
testPullIDStability(c)
}
// TestPullFallbackOn404 tries to pull a nonexistent manifest and confirms that
// the pull falls back to the v1 protocol.
//

View File

@ -16,7 +16,7 @@ import (
)
// Pushing an image to a private registry.
func (s *DockerRegistrySuite) TestPushBusyboxImage(c *check.C) {
func testPushBusyboxImage(c *check.C) {
repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
// tag the image to upload it to the private registry
dockerCmd(c, "tag", "busybox", repoName)
@ -24,13 +24,21 @@ func (s *DockerRegistrySuite) TestPushBusyboxImage(c *check.C) {
dockerCmd(c, "push", repoName)
}
func (s *DockerRegistrySuite) TestPushBusyboxImage(c *check.C) {
testPushBusyboxImage(c)
}
func (s *DockerSchema1RegistrySuite) TestPushBusyboxImage(c *check.C) {
testPushBusyboxImage(c)
}
// pushing an image without a prefix should throw an error
func (s *DockerSuite) TestPushUnprefixedRepo(c *check.C) {
out, _, err := dockerCmdWithError("push", "busybox")
c.Assert(err, check.NotNil, check.Commentf("pushing an unprefixed repo didn't result in a non-zero exit status: %s", out))
}
func (s *DockerRegistrySuite) TestPushUntagged(c *check.C) {
func testPushUntagged(c *check.C) {
repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
expected := "Repository does not exist"
@ -39,7 +47,15 @@ func (s *DockerRegistrySuite) TestPushUntagged(c *check.C) {
c.Assert(out, checker.Contains, expected, check.Commentf("pushing the image failed"))
}
func (s *DockerRegistrySuite) TestPushBadTag(c *check.C) {
func (s *DockerRegistrySuite) TestPushUntagged(c *check.C) {
testPushUntagged(c)
}
func (s *DockerSchema1RegistrySuite) TestPushUntagged(c *check.C) {
testPushUntagged(c)
}
func testPushBadTag(c *check.C) {
repoName := fmt.Sprintf("%v/dockercli/busybox:latest", privateRegistryURL)
expected := "does not exist"
@ -48,7 +64,15 @@ func (s *DockerRegistrySuite) TestPushBadTag(c *check.C) {
c.Assert(out, checker.Contains, expected, check.Commentf("pushing the image failed"))
}
func (s *DockerRegistrySuite) TestPushMultipleTags(c *check.C) {
func (s *DockerRegistrySuite) TestPushBadTag(c *check.C) {
testPushBadTag(c)
}
func (s *DockerSchema1RegistrySuite) TestPushBadTag(c *check.C) {
testPushBadTag(c)
}
func testPushMultipleTags(c *check.C) {
repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
repoTag1 := fmt.Sprintf("%v/dockercli/busybox:t1", privateRegistryURL)
repoTag2 := fmt.Sprintf("%v/dockercli/busybox:t2", privateRegistryURL)
@ -85,7 +109,15 @@ func (s *DockerRegistrySuite) TestPushMultipleTags(c *check.C) {
}
}
func (s *DockerRegistrySuite) TestPushEmptyLayer(c *check.C) {
func (s *DockerRegistrySuite) TestPushMultipleTags(c *check.C) {
testPushMultipleTags(c)
}
func (s *DockerSchema1RegistrySuite) TestPushMultipleTags(c *check.C) {
testPushMultipleTags(c)
}
func testPushEmptyLayer(c *check.C) {
repoName := fmt.Sprintf("%v/dockercli/emptylayer", privateRegistryURL)
emptyTarball, err := ioutil.TempFile("", "empty_tarball")
c.Assert(err, check.IsNil, check.Commentf("Unable to create test file"))
@ -107,6 +139,14 @@ func (s *DockerRegistrySuite) TestPushEmptyLayer(c *check.C) {
c.Assert(err, check.IsNil, check.Commentf("pushing the image to the private registry has failed: %s", out))
}
func (s *DockerRegistrySuite) TestPushEmptyLayer(c *check.C) {
testPushEmptyLayer(c)
}
func (s *DockerSchema1RegistrySuite) TestPushEmptyLayer(c *check.C) {
testPushEmptyLayer(c)
}
func (s *DockerTrustSuite) TestTrustedPush(c *check.C) {
repoName := fmt.Sprintf("%v/dockercli/trusted:latest", privateRegistryURL)
// tag the image and upload it to the private registry

View File

@ -1554,9 +1554,9 @@ func daemonTime(c *check.C) time.Time {
return dt
}
func setupRegistry(c *check.C) *testRegistryV2 {
func setupRegistry(c *check.C, schema1 bool) *testRegistryV2 {
testRequires(c, RegistryHosting)
reg, err := newTestRegistryV2(c)
reg, err := newTestRegistryV2(c, schema1)
c.Assert(err, check.IsNil)
// Wait for registry to be ready to serve requests.

View File

@ -12,14 +12,17 @@ import (
"github.com/go-check/check"
)
const v2binary = "registry-v2"
const (
v2binary = "registry-v2"
v2binarySchema1 = "registry-v2-schema1"
)
type testRegistryV2 struct {
cmd *exec.Cmd
dir string
}
func newTestRegistryV2(c *check.C) (*testRegistryV2, error) {
func newTestRegistryV2(c *check.C, schema1 bool) (*testRegistryV2, error) {
template := `version: 0.1
loglevel: debug
storage:
@ -41,7 +44,11 @@ http:
return nil, err
}
cmd := exec.Command(v2binary, confPath)
binary := v2binary
if schema1 {
binary = v2binarySchema1
}
cmd := exec.Command(binary, confPath)
if err := cmd.Start(); err != nil {
os.RemoveAll(tmp)
if os.IsNotExist(err) {