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"
|
storagedriver "github.com/docker/distribution/registry/storage/driver"
|
||||||
"github.com/docker/distribution/registry/storage/driver/factory"
|
"github.com/docker/distribution/registry/storage/driver/factory"
|
||||||
storagemiddleware "github.com/docker/distribution/registry/storage/driver/middleware"
|
storagemiddleware "github.com/docker/distribution/registry/storage/driver/middleware"
|
||||||
|
"github.com/docker/libtrust"
|
||||||
"github.com/garyburd/redigo/redis"
|
"github.com/garyburd/redigo/redis"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
@ -67,10 +68,15 @@ type App struct {
|
||||||
|
|
||||||
redis *redis.Pool
|
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
|
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
|
readOnly bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,6 +145,13 @@ func NewApp(ctx context.Context, configuration *configuration.Configuration) *Ap
|
||||||
app.configureRedis(configuration)
|
app.configureRedis(configuration)
|
||||||
app.configureLogHook(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 != "" {
|
if configuration.HTTP.Host != "" {
|
||||||
u, err := url.Parse(configuration.HTTP.Host)
|
u, err := url.Parse(configuration.HTTP.Host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -8,6 +8,8 @@ import (
|
||||||
"github.com/docker/distribution"
|
"github.com/docker/distribution"
|
||||||
ctxu "github.com/docker/distribution/context"
|
ctxu "github.com/docker/distribution/context"
|
||||||
"github.com/docker/distribution/digest"
|
"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/errcode"
|
||||||
"github.com/docker/distribution/registry/api/v2"
|
"github.com/docker/distribution/registry/api/v2"
|
||||||
"github.com/gorilla/handlers"
|
"github.com/gorilla/handlers"
|
||||||
|
@ -51,8 +53,6 @@ type imageManifestHandler struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetImageManifest fetches the image manifest from the storage backend, if it exists.
|
// 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) {
|
func (imh *imageManifestHandler) GetImageManifest(w http.ResponseWriter, r *http.Request) {
|
||||||
ctxu.GetLogger(imh).Debug("GetImageManifest")
|
ctxu.GetLogger(imh).Debug("GetImageManifest")
|
||||||
manifests, err := imh.Repository.Manifests(imh)
|
manifests, err := imh.Repository.Manifests(imh)
|
||||||
|
@ -83,6 +83,47 @@ func (imh *imageManifestHandler) GetImageManifest(w http.ResponseWriter, r *http
|
||||||
return
|
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()
|
ct, p, err := manifest.Payload()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
|
Loading…
Reference in a new issue