diff --git a/api/client/image/pull.go b/api/client/image/pull.go index e5968db269..5753c14bae 100644 --- a/api/client/image/pull.go +++ b/api/client/image/pull.go @@ -3,6 +3,7 @@ package image import ( "errors" "fmt" + "strings" "golang.org/x/net/context" @@ -77,9 +78,16 @@ func runPull(dockerCli *client.DockerCli, opts pullOptions) error { if client.IsTrusted() && !registryRef.HasDigest() { // Check if tag is digest - return dockerCli.TrustedPull(ctx, repoInfo, registryRef, authConfig, requestPrivilege) + err = dockerCli.TrustedPull(ctx, repoInfo, registryRef, authConfig, requestPrivilege) + } else { + err = dockerCli.ImagePullPrivileged(ctx, authConfig, distributionRef.String(), requestPrivilege, opts.all) + } + if err != nil { + if strings.Contains(err.Error(), "target is a plugin") { + return errors.New(err.Error() + " - Use `docker plugin install`") + } + return err } - return dockerCli.ImagePullPrivileged(ctx, authConfig, distributionRef.String(), requestPrivilege, opts.all) - + return nil } diff --git a/distribution/pull_v2.go b/distribution/pull_v2.go index 8ee6ed8aa3..958f1bf3fc 100644 --- a/distribution/pull_v2.go +++ b/distribution/pull_v2.go @@ -32,7 +32,11 @@ import ( "golang.org/x/net/context" ) -var errRootFSMismatch = errors.New("layers from manifest don't match image configuration") +var ( + errRootFSMismatch = errors.New("layers from manifest don't match image configuration") + errMediaTypePlugin = errors.New("target is a plugin") + errRootFSInvalid = errors.New("invalid rootfs in image configuration") +) // ImageConfigPullError is an error pulling the image config blob // (only applies to schema2). @@ -356,6 +360,12 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named) (tagUpdat return false, fmt.Errorf("image manifest does not exist for tag or digest %q", tagOrDigest) } + if m, ok := manifest.(*schema2.DeserializedManifest); ok { + if m.Manifest.Config.MediaType == schema2.MediaTypePluginConfig { + return false, errMediaTypePlugin + } + } + // If manSvc.Get succeeded, we can be confident that the registry on // the other side speaks the v2 protocol. p.confirmedV2 = true @@ -574,6 +584,10 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s } } + if unmarshalledConfig.RootFS == nil { + return "", "", errRootFSInvalid + } + // The DiffIDs returned in rootFS MUST match those in the config. // Otherwise the image config could be referencing layers that aren't // included in the manifest. diff --git a/plugin/distribution/pull.go b/plugin/distribution/pull.go index 1bae8d4bb7..c60865340c 100644 --- a/plugin/distribution/pull.go +++ b/plugin/distribution/pull.go @@ -143,7 +143,7 @@ func Pull(name string, rs registry.Service, metaheader http.Header, authConfig * logrus.Debugf("pull.go: error in json.Unmarshal(): %v", err) return nil, err } - if m.Config.MediaType != MediaTypeConfig { + if m.Config.MediaType != schema2.MediaTypePluginConfig { return nil, ErrUnsupportedMediaType } diff --git a/plugin/distribution/push.go b/plugin/distribution/push.go index 27e717af4c..5a9f689492 100644 --- a/plugin/distribution/push.go +++ b/plugin/distribution/push.go @@ -79,9 +79,9 @@ func Push(name string, rs registry.Service, metaHeader http.Header, authConfig * return "", err } f.Close() - mt := MediaTypeLayer + mt := schema2.MediaTypeLayer if i == 0 { - mt = MediaTypeConfig + mt = schema2.MediaTypePluginConfig } // Commit completes the write process to the BlobService. // The descriptor arg to Commit is called the "provisional" descriptor and diff --git a/plugin/distribution/types.go b/plugin/distribution/types.go index 0b1fd7aea1..8a327d1042 100644 --- a/plugin/distribution/types.go +++ b/plugin/distribution/types.go @@ -10,10 +10,5 @@ var ErrUnsupportedRegistry = errors.New("only V2 repositories are supported for // ErrUnsupportedMediaType indicates we are pulling content that's not a plugin var ErrUnsupportedMediaType = errors.New("content is not a plugin") -// Plugin related media types -const ( - MediaTypeManifest = "application/vnd.docker.distribution.manifest.v2+json" - MediaTypeConfig = "application/vnd.docker.plugin.v0+json" - MediaTypeLayer = "application/vnd.docker.image.rootfs.diff.tar.gzip" - DefaultTag = "latest" -) +// DefaultTag is the default tag for plugins +const DefaultTag = "latest"