85 lines
2.4 KiB
Go
85 lines
2.4 KiB
Go
|
package storage
|
||
|
|
||
|
import (
|
||
|
"net/http"
|
||
|
|
||
|
"github.com/docker/distribution/digest"
|
||
|
"github.com/docker/distribution/storagedriver"
|
||
|
)
|
||
|
|
||
|
// delegateLayerHandler provides a simple implementation of layerHandler that
|
||
|
// simply issues HTTP Temporary Redirects to the URL provided by the
|
||
|
// storagedriver for a given Layer.
|
||
|
type delegateLayerHandler struct {
|
||
|
storageDriver storagedriver.StorageDriver
|
||
|
pathMapper *pathMapper
|
||
|
}
|
||
|
|
||
|
var _ LayerHandler = &delegateLayerHandler{}
|
||
|
|
||
|
func newDelegateLayerHandler(storageDriver storagedriver.StorageDriver, options map[string]interface{}) (LayerHandler, error) {
|
||
|
return &delegateLayerHandler{storageDriver: storageDriver, pathMapper: defaultPathMapper}, nil
|
||
|
}
|
||
|
|
||
|
// Resolve returns an http.Handler which can serve the contents of the given
|
||
|
// Layer, or an error if not supported by the storagedriver.
|
||
|
func (lh *delegateLayerHandler) Resolve(layer Layer) (http.Handler, error) {
|
||
|
layerURL, err := lh.urlFor(layer)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
|
http.Redirect(w, r, layerURL, http.StatusTemporaryRedirect)
|
||
|
}), nil
|
||
|
}
|
||
|
|
||
|
// urlFor returns a download URL for the given layer, or the empty string if
|
||
|
// unsupported.
|
||
|
func (lh *delegateLayerHandler) urlFor(layer Layer) (string, error) {
|
||
|
blobPath, err := lh.resolveBlobPath(layer.Name(), layer.Digest())
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
layerURL, err := lh.storageDriver.URLFor(blobPath)
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
return layerURL, nil
|
||
|
}
|
||
|
|
||
|
// resolveBlobPath looks up the blob location in the repositories from a
|
||
|
// layer/blob link file, returning blob path or an error on failure.
|
||
|
func (lh *delegateLayerHandler) resolveBlobPath(name string, dgst digest.Digest) (string, error) {
|
||
|
pathSpec := layerLinkPathSpec{name: name, digest: dgst}
|
||
|
layerLinkPath, err := lh.pathMapper.path(pathSpec)
|
||
|
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
layerLinkContent, err := lh.storageDriver.GetContent(layerLinkPath)
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
// NOTE(stevvooe): The content of the layer link should match the digest.
|
||
|
// This layer of indirection is for name-based content protection.
|
||
|
|
||
|
linked, err := digest.ParseDigest(string(layerLinkContent))
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
bp := blobPathSpec{digest: linked}
|
||
|
|
||
|
return lh.pathMapper.path(bp)
|
||
|
}
|
||
|
|
||
|
// init registers the delegate layerHandler backend.
|
||
|
func init() {
|
||
|
RegisterLayerHandler("delegate", LayerHandlerInitFunc(newDelegateLayerHandler))
|
||
|
}
|