From 7d12b66fb8f2246d94c72f9647faed9ce8f9b940 Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Mon, 23 May 2016 10:08:04 +0200 Subject: [PATCH] add mimetypes Signed-off-by: Antonio Murdaca --- cmd/skopeo/copy.go | 2 +- directory/directory.go | 9 +++- docker/docker_image.go | 2 +- docker/docker_image_src.go | 10 ++-- docker/utils/fixtures/v2list.manifest.json | 56 ++++++++++++++++++++++ docker/utils/manifest.go | 13 ++++- docker/utils/manifest_test.go | 1 + openshift/openshift.go | 4 +- types/types.go | 5 +- 9 files changed, 87 insertions(+), 15 deletions(-) create mode 100644 docker/utils/fixtures/v2list.manifest.json diff --git a/cmd/skopeo/copy.go b/cmd/skopeo/copy.go index ffa2c2e1..5fe78ce6 100644 --- a/cmd/skopeo/copy.go +++ b/cmd/skopeo/copy.go @@ -54,7 +54,7 @@ func copyHandler(context *cli.Context) { } signBy := context.String("sign-by") - manifest, err := src.GetManifest() + manifest, _, err := src.GetManifest() if err != nil { logrus.Fatalf("Error reading manifest: %s", err.Error()) } diff --git a/directory/directory.go b/directory/directory.go index a399ef6f..757a4b1a 100644 --- a/directory/directory.go +++ b/directory/directory.go @@ -84,8 +84,13 @@ func (s *dirImageSource) IntendedDockerReference() string { return "" } -func (s *dirImageSource) GetManifest() ([]byte, error) { - return ioutil.ReadFile(manifestPath(s.dir)) +// it's up to the caller to determine the MIME type of the returned manifest's bytes +func (s *dirImageSource) GetManifest() ([]byte, string, error) { + m, err := ioutil.ReadFile(manifestPath(s.dir)) + if err != nil { + return nil, "", err + } + return m, "", err } func (s *dirImageSource) GetLayer(digest string) (io.ReadCloser, error) { diff --git a/docker/docker_image.go b/docker/docker_image.go index 517d6d3d..37e4f479 100644 --- a/docker/docker_image.go +++ b/docker/docker_image.go @@ -44,7 +44,7 @@ func (i *dockerImage) IntendedDockerReference() string { // Manifest is like ImageSource.GetManifest, but the result is cached; it is OK to call this however often you need. func (i *dockerImage) Manifest() ([]byte, error) { if i.cachedManifest == nil { - m, err := i.src.GetManifest() + m, _, err := i.src.GetManifest() if err != nil { return nil, err } diff --git a/docker/docker_image_src.go b/docker/docker_image_src.go index cfaa81a7..777fef2f 100644 --- a/docker/docker_image_src.go +++ b/docker/docker_image_src.go @@ -55,24 +55,24 @@ func (s *dockerImageSource) IntendedDockerReference() string { return fmt.Sprintf("%s:%s", s.ref.Name(), s.tag) } -func (s *dockerImageSource) GetManifest() ([]byte, error) { +func (s *dockerImageSource) GetManifest() ([]byte, string, error) { url := fmt.Sprintf(manifestURL, s.ref.RemoteName(), s.tag) // TODO(runcom) set manifest version header! schema1 for now - then schema2 etc etc and v1 // TODO(runcom) NO, switch on the resulter manifest like Docker is doing res, err := s.c.makeRequest("GET", url, nil, nil) if err != nil { - return nil, err + return nil, "", err } defer res.Body.Close() manblob, err := ioutil.ReadAll(res.Body) if err != nil { - return nil, err + return nil, "", err } if res.StatusCode != http.StatusOK { - return nil, errFetchManifest{res.StatusCode, manblob} + return nil, "", errFetchManifest{res.StatusCode, manblob} } // We might validate manblob against the Docker-Content-Digest header here to protect against transport errors. - return manblob, nil + return manblob, res.Header.Get("Content-Type"), nil } func (s *dockerImageSource) GetLayer(digest string) (io.ReadCloser, error) { diff --git a/docker/utils/fixtures/v2list.manifest.json b/docker/utils/fixtures/v2list.manifest.json new file mode 100644 index 00000000..1bf9896e --- /dev/null +++ b/docker/utils/fixtures/v2list.manifest.json @@ -0,0 +1,56 @@ +{ + "schemaVersion": 2, + "mediaType": "application/vnd.docker.distribution.manifest.list.v2+json", + "manifests": [ + { + "mediaType": "application/vnd.docker.distribution.manifest.v1+json", + "size": 2094, + "digest": "sha256:7820f9a86d4ad15a2c4f0c0e5479298df2aa7c2f6871288e2ef8546f3e7b6783", + "platform": { + "architecture": "ppc64le", + "os": "linux" + } + }, + { + "mediaType": "application/vnd.docker.distribution.manifest.v1+json", + "size": 1922, + "digest": "sha256:ae1b0e06e8ade3a11267564a26e750585ba2259c0ecab59ab165ad1af41d1bdd", + "platform": { + "architecture": "amd64", + "os": "linux", + "features": [ + "sse" + ] + } + }, + { + "mediaType": "application/vnd.docker.distribution.manifest.v1+json", + "size": 2084, + "digest": "sha256:e4c0df75810b953d6717b8f8f28298d73870e8aa2a0d5e77b8391f16fdfbbbe2", + "platform": { + "architecture": "s390x", + "os": "linux" + } + }, + { + "mediaType": "application/vnd.docker.distribution.manifest.v1+json", + "size": 2084, + "digest": "sha256:07ebe243465ef4a667b78154ae6c3ea46fdb1582936aac3ac899ea311a701b40", + "platform": { + "architecture": "arm", + "os": "linux", + "variant": "armv7" + } + }, + { + "mediaType": "application/vnd.docker.distribution.manifest.v1+json", + "size": 2090, + "digest": "sha256:fb2fc0707b86dafa9959fe3d29e66af8787aee4d9a23581714be65db4265ad8a", + "platform": { + "architecture": "arm64", + "os": "linux", + "variant": "armv8" + } + } + ] +} diff --git a/docker/utils/manifest.go b/docker/utils/manifest.go index f583dd08..aa87a2d2 100644 --- a/docker/utils/manifest.go +++ b/docker/utils/manifest.go @@ -10,11 +10,22 @@ import ( // FIXME: Should we just use docker/distribution and docker/docker implementations directly? +// ManifestMIMETypes returns a slice of supported MIME types +func ManifestMIMETypes() []string { + return []string{ + DockerV2Schema1MIMEType, + DockerV2Schema2MIMEType, + DockerV2ListMIMEType, + } +} + const ( // DockerV2Schema1MIMEType MIME type represents Docker manifest schema 1 DockerV2Schema1MIMEType = "application/vnd.docker.distribution.manifest.v1+json" // DockerV2Schema2MIMEType MIME type represents Docker manifest schema 2 DockerV2Schema2MIMEType = "application/vnd.docker.distribution.manifest.v2+json" + // DockerV2ListMIMEType MIME type represents Docker manifest schema 2 list + DockerV2ListMIMEType = "application/vnd.docker.distribution.manifest.list.v2+json" ) // GuessManifestMIMEType guesses MIME type of a manifest and returns it _if it is recognized_, or "" if unknown or unrecognized. @@ -32,7 +43,7 @@ func GuessManifestMIMEType(manifest []byte) string { } switch meta.MediaType { - case DockerV2Schema2MIMEType: // A recognized type. + case DockerV2Schema2MIMEType, DockerV2ListMIMEType: // A recognized type. return meta.MediaType } switch meta.SchemaVersion { diff --git a/docker/utils/manifest_test.go b/docker/utils/manifest_test.go index 101365ed..ca4c7c6d 100644 --- a/docker/utils/manifest_test.go +++ b/docker/utils/manifest_test.go @@ -15,6 +15,7 @@ func TestGuessManifestMIMEType(t *testing.T) { mimeType string }{ {"v2s2.manifest.json", DockerV2Schema2MIMEType}, + {"v2list.manifest.json", DockerV2ListMIMEType}, {"v2s1.manifest.json", DockerV2Schema1MIMEType}, {"v2s1-invalid-signatures.manifest.json", DockerV2Schema1MIMEType}, {"v2s2nomime.manifest.json", DockerV2Schema2MIMEType}, // It is unclear whether this one is legal, but we should guess v2s2 if anything at all. diff --git a/openshift/openshift.go b/openshift/openshift.go index 0afe21e8..4b4dc94a 100644 --- a/openshift/openshift.go +++ b/openshift/openshift.go @@ -193,9 +193,9 @@ func (s *openshiftImageSource) IntendedDockerReference() string { return s.client.canonicalDockerReference() } -func (s *openshiftImageSource) GetManifest() ([]byte, error) { +func (s *openshiftImageSource) GetManifest() ([]byte, string, error) { if err := s.ensureImageIsResolved(); err != nil { - return nil, err + return nil, "", err } return s.docker.GetManifest() } diff --git a/types/types.go b/types/types.go index 6e828915..f3f186f8 100644 --- a/types/types.go +++ b/types/types.go @@ -27,9 +27,8 @@ type ImageSource interface { // (not as the image itself, or its underlying storage, claims). This can be used e.g. to determine which public keys are trusted for this image. // May be "" if unknown. IntendedDockerReference() string - // GetManifest returns the image's manifest. It may use a remote (= slow) service. - // FIXME? This should also return a MIME type if known, to differentiate between schema versions. - GetManifest() ([]byte, error) + // GetManifest returns the image's manifest along with its MIME type. The empty string is returned if the MIME type is unknown. It may use a remote (= slow) service. + GetManifest() ([]byte, string, error) // Note: Calling GetLayer() may have ordering dependencies WRT other methods of this type. FIXME: How does this work with (docker save) on stdin? GetLayer(digest string) (io.ReadCloser, error) // GetSignatures returns the image's signatures. It may use a remote (= slow) service.