From 8556dd1aa1d8174afcb81eba28a262e9b142231d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miloslav=20Trma=C4=8D?= Date: Thu, 20 Apr 2017 20:56:42 +0200 Subject: [PATCH] Add tests for automatic schema conversion depending on registry In addition to the default registry in the OpenShift cluster, start two more (one known to support s1 only, one known to support s1+s2), and also a docker/distribution s1-only registry. Then test that copying images around works as expected. NOTE: The docker/distribution s1-only tests currently fail and are disabled. See the added comment for details. --- integration/copy_test.go | 76 ++++++++++++++++++++++++++++++++++++---- integration/openshift.go | 34 ++++++++++++++---- 2 files changed, 98 insertions(+), 12 deletions(-) diff --git a/integration/copy_test.go b/integration/copy_test.go index 26ad09d0..bb946937 100644 --- a/integration/copy_test.go +++ b/integration/copy_test.go @@ -21,12 +21,16 @@ func init() { check.Suite(&CopySuite{}) } -const v2DockerRegistryURL = "localhost:5555" // Update also policy.json +const ( + v2DockerRegistryURL = "localhost:5555" // Update also policy.json + v2s1DockerRegistryURL = "localhost:5556" +) type CopySuite struct { - cluster *openshiftCluster - registry *testRegistryV2 - gpgHome string + cluster *openshiftCluster + registry *testRegistryV2 + s1Registry *testRegistryV2 + gpgHome string } func (s *CopySuite) SetUpSuite(c *check.C) { @@ -36,7 +40,7 @@ func (s *CopySuite) SetUpSuite(c *check.C) { s.cluster = startOpenshiftCluster(c) // FIXME: Set up TLS for the docker registry port instead of using "--tls-verify=false" all over the place. - for _, stream := range []string{"unsigned", "personal", "official", "naming", "cosigned", "compression"} { + for _, stream := range []string{"unsigned", "personal", "official", "naming", "cosigned", "compression", "schema1", "schema2"} { isJSON := fmt.Sprintf(`{ "kind": "ImageStream", "apiVersion": "v1", @@ -48,7 +52,9 @@ func (s *CopySuite) SetUpSuite(c *check.C) { runCommandWithInput(c, isJSON, "oc", "create", "-f", "-") } - s.registry = setupRegistryV2At(c, v2DockerRegistryURL, false, false) // FIXME: Set up TLS for the docker registry port instead of using "--tls-verify=false" all over the place. + // FIXME: Set up TLS for the docker registry port instead of using "--tls-verify=false" all over the place. + s.registry = setupRegistryV2At(c, v2DockerRegistryURL, false, false) + s.s1Registry = setupRegistryV2At(c, v2s1DockerRegistryURL, false, true) gpgHome, err := ioutil.TempDir("", "skopeo-gpg") c.Assert(err, check.IsNil) @@ -74,6 +80,9 @@ func (s *CopySuite) TearDownSuite(c *check.C) { if s.registry != nil { s.registry.Close() } + if s.s1Registry != nil { + s.s1Registry.Close() + } if s.cluster != nil { s.cluster.tearDown(c) } @@ -535,3 +544,58 @@ func (s *CopySuite) TestCopyNoPanicOnHTTPResponseWOTLSVerifyFalse(c *check.C) { assertSkopeoFails(c, ".*server gave HTTP response to HTTPS client.*", "copy", ourRegistry+"foobar", "dir:test") } + +func (s *CopySuite) TestCopySchemaConversion(c *check.C) { + // Test conversion / schema autodetection both for the OpenShift embedded registry… + s.testCopySchemaConversionRegistries(c, "docker://localhost:5005/myns/schema1", "docker://localhost:5006/myns/schema2") + // … and for various docker/distribution registry versions. + if false { + // FIXME: This does not currently work, because the schema1-only docker/distribution registry we have (unlike newer versions) + // enforces that a schema1 manifest contains a matching tag field. Our _s2→s1 conversion_ code does set the tag correctly, + // but a mere copy of schema1→schema1 changing the tag does not update the manifest. + // So, enabling this test results in a successful schema2→schema1 conversion, followed by a schema1→schema1 copy failure. + s.testCopySchemaConversionRegistries(c, "docker://"+v2s1DockerRegistryURL+"/schema1", "docker://"+v2DockerRegistryURL+"/schema2") + } +} + +func (s *CopySuite) testCopySchemaConversionRegistries(c *check.C, schema1Registry, schema2Registry string) { + topDir, err := ioutil.TempDir("", "schema-conversion") + c.Assert(err, check.IsNil) + defer os.RemoveAll(topDir) + for _, subdir := range []string{"input1", "input2", "dest2"} { + err := os.MkdirAll(filepath.Join(topDir, subdir), 0755) + c.Assert(err, check.IsNil) + } + input1Dir := filepath.Join(topDir, "input1") + input2Dir := filepath.Join(topDir, "input2") + destDir := filepath.Join(topDir, "dest2") + + // Ensure we are working with a schema2 image. + // dir: accepts any manifest format, i.e. this makes …/input2 a schema2 source which cannot be asked to produce schema1 like ordinary docker: registries can. + assertSkopeoSucceeds(c, "", "copy", "docker://busybox", "dir:"+input2Dir) + verifyManifestMIMEType(c, input2Dir, manifest.DockerV2Schema2MediaType) + // 2→2 (the "f2t2" in tag means "from 2 to 2") + assertSkopeoSucceeds(c, "", "copy", "--dest-tls-verify=false", "dir:"+input2Dir, schema2Registry+":f2t2") + assertSkopeoSucceeds(c, "", "copy", "--src-tls-verify=false", schema2Registry+":f2t2", "dir:"+destDir) + verifyManifestMIMEType(c, destDir, manifest.DockerV2Schema2MediaType) + // 2→1; we will use the result as a schema1 image for further tests. + assertSkopeoSucceeds(c, "", "copy", "--dest-tls-verify=false", "dir:"+input2Dir, schema1Registry+":f2t1") + assertSkopeoSucceeds(c, "", "copy", "--src-tls-verify=false", schema1Registry+":f2t1", "dir:"+input1Dir) + verifyManifestMIMEType(c, input1Dir, manifest.DockerV2Schema1SignedMediaType) + // 1→1 + assertSkopeoSucceeds(c, "", "copy", "--dest-tls-verify=false", "dir:"+input1Dir, schema1Registry+":f1t1") + assertSkopeoSucceeds(c, "", "copy", "--src-tls-verify=false", schema1Registry+":f1t1", "dir:"+destDir) + verifyManifestMIMEType(c, destDir, manifest.DockerV2Schema1SignedMediaType) + // 1→2: image stays unmodified schema1 + assertSkopeoSucceeds(c, "", "copy", "--dest-tls-verify=false", "dir:"+input1Dir, schema2Registry+":f1t2") + assertSkopeoSucceeds(c, "", "copy", "--src-tls-verify=false", schema2Registry+":f1t2", "dir:"+destDir) + verifyManifestMIMEType(c, destDir, manifest.DockerV2Schema1SignedMediaType) +} + +// Verify manifest in a dir: image at dir is expectedMIMEType. +func verifyManifestMIMEType(c *check.C, dir string, expectedMIMEType string) { + manifestBlob, err := ioutil.ReadFile(filepath.Join(dir, "manifest.json")) + c.Assert(err, check.IsNil) + mimeType := manifest.GuessMIMEType(manifestBlob) + c.Assert(mimeType, check.Equals, expectedMIMEType) +} diff --git a/integration/openshift.go b/integration/openshift.go index 48da88f3..8e15aff5 100644 --- a/integration/openshift.go +++ b/integration/openshift.go @@ -172,7 +172,28 @@ func (cluster *openshiftCluster) startRegistryProcess(c *check.C, port int, conf // startRegistry starts the OpenShift registry and waits for it to be ready, or terminates on failure. func (cluster *openshiftCluster) startRegistry(c *check.C) { + // Our “primary” registry cluster.processes = append(cluster.processes, cluster.startRegistryProcess(c, 5000, "/atomic-registry-config.yml")) + + // A registry configured with acceptschema2:false + schema1Config := fileFromFixture(c, "/atomic-registry-config.yml", map[string]string{ + "addr: :5000": "addr: :5005", + "rootdirectory: /registry": "rootdirectory: /registry-schema1", + // The default configuration currently already contains acceptschema2: false + }) + // Make sure the configuration contains "acceptschema2: false", because eventually it will be enabled upstream and this function will need to be updated. + configContents, err := ioutil.ReadFile(schema1Config) + c.Assert(err, check.IsNil) + c.Assert(string(configContents), check.Matches, "(?s).*acceptschema2: false.*") + cluster.processes = append(cluster.processes, cluster.startRegistryProcess(c, 5005, schema1Config)) + + // A registry configured with acceptschema2:true + schema2Config := fileFromFixture(c, "/atomic-registry-config.yml", map[string]string{ + "addr: :5000": "addr: :5006", + "rootdirectory: /registry": "rootdirectory: /registry-schema2", + "acceptschema2: false": "acceptschema2: true", + }) + cluster.processes = append(cluster.processes, cluster.startRegistryProcess(c, 5006, schema2Config)) } // ocLogin runs (oc login) and (oc new-project) on the cluster, or terminates on failure. @@ -196,14 +217,15 @@ func (cluster *openshiftCluster) dockerLogin(c *check.C) { out := combinedOutputOfCommand(c, "oc", "config", "view", "-o", "json", "-o", "jsonpath={.users[*].user.token}") c.Logf("oc config value: %s", out) - configJSON := fmt.Sprintf(`{ - "auths": { - "localhost:5000": { + authValue := base64.StdEncoding.EncodeToString([]byte("unused:" + out)) + auths := []string{} + for _, port := range []int{5000, 5005, 5006} { + auths = append(auths, fmt.Sprintf(`"localhost:%d": { "auth": "%s", "email": "unused" - } - } - }`, base64.StdEncoding.EncodeToString([]byte("unused:"+out))) + }`, port, authValue)) + } + configJSON := `{"auths": {` + strings.Join(auths, ",") + `}}` err = ioutil.WriteFile(filepath.Join(dockerDir, "config.json"), []byte(configJSON), 0600) c.Assert(err, check.IsNil) }