Recognize clients that don't support schema2, and convert manifests to schema1 on the fly
Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
This commit is contained in:
parent
befd4d6e3c
commit
3f746a8207
2 changed files with 58 additions and 4 deletions
|
@ -30,6 +30,7 @@ import (
|
|||
storagedriver "github.com/docker/distribution/registry/storage/driver"
|
||||
"github.com/docker/distribution/registry/storage/driver/factory"
|
||||
storagemiddleware "github.com/docker/distribution/registry/storage/driver/middleware"
|
||||
"github.com/docker/libtrust"
|
||||
"github.com/garyburd/redigo/redis"
|
||||
"github.com/gorilla/mux"
|
||||
"golang.org/x/net/context"
|
||||
|
@ -67,10 +68,15 @@ type App struct {
|
|||
|
||||
redis *redis.Pool
|
||||
|
||||
// true if this registry is configured as a pull through cache
|
||||
// trustKey is a deprecated key used to sign manifests converted to
|
||||
// schema1 for backward compatibility. It should not be used for any
|
||||
// other purposes.
|
||||
trustKey libtrust.PrivateKey
|
||||
|
||||
// isCache is true if this registry is configured as a pull through cache
|
||||
isCache bool
|
||||
|
||||
// true if the registry is in a read-only maintenance mode
|
||||
// readOnly is true if the registry is in a read-only maintenance mode
|
||||
readOnly bool
|
||||
}
|
||||
|
||||
|
@ -139,6 +145,13 @@ func NewApp(ctx context.Context, configuration *configuration.Configuration) *Ap
|
|||
app.configureRedis(configuration)
|
||||
app.configureLogHook(configuration)
|
||||
|
||||
// Generate an ephemeral key to be used for signing converted manifests
|
||||
// for clients that don't support schema2.
|
||||
app.trustKey, err = libtrust.GenerateECP256PrivateKey()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if configuration.HTTP.Host != "" {
|
||||
u, err := url.Parse(configuration.HTTP.Host)
|
||||
if err != nil {
|
||||
|
|
|
@ -8,6 +8,8 @@ import (
|
|||
"github.com/docker/distribution"
|
||||
ctxu "github.com/docker/distribution/context"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/manifest/schema1"
|
||||
"github.com/docker/distribution/manifest/schema2"
|
||||
"github.com/docker/distribution/registry/api/errcode"
|
||||
"github.com/docker/distribution/registry/api/v2"
|
||||
"github.com/gorilla/handlers"
|
||||
|
@ -51,8 +53,6 @@ type imageManifestHandler struct {
|
|||
}
|
||||
|
||||
// GetImageManifest fetches the image manifest from the storage backend, if it exists.
|
||||
// todo(richardscothern): this assumes v2 schema 1 manifests for now but in the future
|
||||
// get the version from the Accept HTTP header
|
||||
func (imh *imageManifestHandler) GetImageManifest(w http.ResponseWriter, r *http.Request) {
|
||||
ctxu.GetLogger(imh).Debug("GetImageManifest")
|
||||
manifests, err := imh.Repository.Manifests(imh)
|
||||
|
@ -83,6 +83,47 @@ func (imh *imageManifestHandler) GetImageManifest(w http.ResponseWriter, r *http
|
|||
return
|
||||
}
|
||||
|
||||
// Only rewrite schema2 manifests when they are being fetched by tag.
|
||||
// If they are being fetched by digest, we can't return something not
|
||||
// matching the digest.
|
||||
if _, isSchema2 := manifest.(*schema2.DeserializedManifest); imh.Tag != "" && isSchema2 {
|
||||
supportsSchema2 := false
|
||||
if acceptHeaders, ok := r.Header["Accept"]; ok {
|
||||
for _, mediaType := range acceptHeaders {
|
||||
if mediaType == schema2.MediaTypeManifest {
|
||||
supportsSchema2 = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !supportsSchema2 {
|
||||
// Rewrite manifest in schema1 format
|
||||
ctxu.GetLogger(imh).Infof("rewriting manifest %s in schema1 format to support old client", imh.Digest.String())
|
||||
|
||||
targetDescriptor := manifest.Target()
|
||||
blobs := imh.Repository.Blobs(imh)
|
||||
configJSON, err := blobs.Get(imh, targetDescriptor.Digest)
|
||||
if err != nil {
|
||||
imh.Errors = append(imh.Errors, v2.ErrorCodeManifestInvalid.WithDetail(err))
|
||||
return
|
||||
}
|
||||
|
||||
builder := schema1.NewConfigManifestBuilder(imh.Repository.Blobs(imh), imh.Context.App.trustKey, imh.Repository.Name(), imh.Tag, configJSON)
|
||||
for _, d := range manifest.References() {
|
||||
if err := builder.AppendReference(d); err != nil {
|
||||
imh.Errors = append(imh.Errors, v2.ErrorCodeManifestInvalid.WithDetail(err))
|
||||
return
|
||||
}
|
||||
}
|
||||
manifest, err = builder.Build(imh)
|
||||
if err != nil {
|
||||
imh.Errors = append(imh.Errors, v2.ErrorCodeManifestInvalid.WithDetail(err))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ct, p, err := manifest.Payload()
|
||||
if err != nil {
|
||||
return
|
||||
|
|
Loading…
Reference in a new issue