distribution/registry/storage/cloudfrontlayerhandler.go
Stephen J Day 65b0d73cb7 Move storagedriver package to registry/storage/driver
This change is slightly more complex than previous package maves in that the
package name changed. To address this, we simply always reference the package
driver as storagedriver to avoid compatbility issues with existing code. While
unfortunate, this can be cleaned up over time.

Signed-off-by: Stephen J Day <stephen.day@docker.com>
2015-02-11 12:43:04 -08:00

122 lines
3.2 KiB
Go

package storage
import (
"crypto/x509"
"encoding/pem"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"time"
"github.com/AdRoll/goamz/cloudfront"
storagedriver "github.com/docker/distribution/registry/storage/driver"
)
// cloudFrontLayerHandler provides an simple implementation of layerHandler that
// constructs temporary signed CloudFront URLs from the storagedriver layer URL,
// then issues HTTP Temporary Redirects to this CloudFront content URL.
type cloudFrontLayerHandler struct {
cloudfront *cloudfront.CloudFront
delegateLayerHandler *delegateLayerHandler
duration time.Duration
}
var _ LayerHandler = &cloudFrontLayerHandler{}
// newCloudFrontLayerHandler constructs and returns a new CloudFront
// LayerHandler implementation.
// Required options: baseurl, privatekey, keypairid
func newCloudFrontLayerHandler(storageDriver storagedriver.StorageDriver, options map[string]interface{}) (LayerHandler, error) {
base, ok := options["baseurl"]
if !ok {
return nil, fmt.Errorf("No baseurl provided")
}
baseURL, ok := base.(string)
if !ok {
return nil, fmt.Errorf("baseurl must be a string")
}
pk, ok := options["privatekey"]
if !ok {
return nil, fmt.Errorf("No privatekey provided")
}
pkPath, ok := pk.(string)
if !ok {
return nil, fmt.Errorf("privatekey must be a string")
}
kpid, ok := options["keypairid"]
if !ok {
return nil, fmt.Errorf("No keypairid provided")
}
keypairID, ok := kpid.(string)
if !ok {
return nil, fmt.Errorf("keypairid must be a string")
}
pkBytes, err := ioutil.ReadFile(pkPath)
if err != nil {
return nil, fmt.Errorf("Failed to read privatekey file: %s", err)
}
block, _ := pem.Decode([]byte(pkBytes))
if block == nil {
return nil, fmt.Errorf("Failed to decode private key as an rsa private key")
}
privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
lh, err := newDelegateLayerHandler(storageDriver, options)
if err != nil {
return nil, err
}
dlh := lh.(*delegateLayerHandler)
cf := cloudfront.New(baseURL, privateKey, keypairID)
duration := 20 * time.Minute
d, ok := options["duration"]
if ok {
switch d := d.(type) {
case time.Duration:
duration = d
case string:
dur, err := time.ParseDuration(d)
if err != nil {
return nil, fmt.Errorf("Invalid duration: %s", err)
}
duration = dur
}
}
return &cloudFrontLayerHandler{cloudfront: cf, delegateLayerHandler: dlh, duration: duration}, 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 *cloudFrontLayerHandler) Resolve(layer Layer) (http.Handler, error) {
layerURLStr, err := lh.delegateLayerHandler.urlFor(layer, nil)
if err != nil {
return nil, err
}
layerURL, err := url.Parse(layerURLStr)
if err != nil {
return nil, err
}
cfURL, err := lh.cloudfront.CannedSignedURL(layerURL.Path, "", time.Now().Add(lh.duration))
if err != nil {
return nil, err
}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, cfURL, http.StatusTemporaryRedirect)
}), nil
}
// init registers the cloudfront layerHandler backend.
func init() {
RegisterLayerHandler("cloudfront", LayerHandlerInitFunc(newCloudFrontLayerHandler))
}