Recognize clients that don't support schema2, and convert manifests to schema1 on the fly
Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>master
							parent
							
								
									befd4d6e3c
								
							
						
					
					
						commit
						3f746a8207
					
				|  | @ -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 New Issue