-
-
- IRC
- |
-
- #docker-distribution on FreeNode
- |
-
-
-
- Issue Tracker
- |
-
- github.com/docker/distribution/issues
- |
-
-
-
- Google Groups
- |
-
- https://groups.google.com/a/dockerproject.org/forum/#!forum/distribution
- |
-
-
-
- Mailing List
- |
-
- docker@dockerproject.org
- |
-
-
-
-
-## License
-
-This project is distributed under [Apache License, Version 2.0](LICENSE).
diff --git a/vendor/github.com/docker/distribution/blobs.go b/vendor/github.com/docker/distribution/blobs.go
new file mode 100644
index 000000000..79c5fb33b
--- /dev/null
+++ b/vendor/github.com/docker/distribution/blobs.go
@@ -0,0 +1,257 @@
+package distribution
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "net/http"
+ "time"
+
+ "github.com/docker/distribution/context"
+ "github.com/docker/distribution/reference"
+ "github.com/opencontainers/go-digest"
+)
+
+var (
+ // ErrBlobExists returned when blob already exists
+ ErrBlobExists = errors.New("blob exists")
+
+ // ErrBlobDigestUnsupported when blob digest is an unsupported version.
+ ErrBlobDigestUnsupported = errors.New("unsupported blob digest")
+
+ // ErrBlobUnknown when blob is not found.
+ ErrBlobUnknown = errors.New("unknown blob")
+
+ // ErrBlobUploadUnknown returned when upload is not found.
+ ErrBlobUploadUnknown = errors.New("blob upload unknown")
+
+ // ErrBlobInvalidLength returned when the blob has an expected length on
+ // commit, meaning mismatched with the descriptor or an invalid value.
+ ErrBlobInvalidLength = errors.New("blob invalid length")
+)
+
+// ErrBlobInvalidDigest returned when digest check fails.
+type ErrBlobInvalidDigest struct {
+ Digest digest.Digest
+ Reason error
+}
+
+func (err ErrBlobInvalidDigest) Error() string {
+ return fmt.Sprintf("invalid digest for referenced layer: %v, %v",
+ err.Digest, err.Reason)
+}
+
+// ErrBlobMounted returned when a blob is mounted from another repository
+// instead of initiating an upload session.
+type ErrBlobMounted struct {
+ From reference.Canonical
+ Descriptor Descriptor
+}
+
+func (err ErrBlobMounted) Error() string {
+ return fmt.Sprintf("blob mounted from: %v to: %v",
+ err.From, err.Descriptor)
+}
+
+// Descriptor describes targeted content. Used in conjunction with a blob
+// store, a descriptor can be used to fetch, store and target any kind of
+// blob. The struct also describes the wire protocol format. Fields should
+// only be added but never changed.
+type Descriptor struct {
+ // MediaType describe the type of the content. All text based formats are
+ // encoded as utf-8.
+ MediaType string `json:"mediaType,omitempty"`
+
+ // Size in bytes of content.
+ Size int64 `json:"size,omitempty"`
+
+ // Digest uniquely identifies the content. A byte stream can be verified
+ // against against this digest.
+ Digest digest.Digest `json:"digest,omitempty"`
+
+ // URLs contains the source URLs of this content.
+ URLs []string `json:"urls,omitempty"`
+
+ // NOTE: Before adding a field here, please ensure that all
+ // other options have been exhausted. Much of the type relationships
+ // depend on the simplicity of this type.
+}
+
+// Descriptor returns the descriptor, to make it satisfy the Describable
+// interface. Note that implementations of Describable are generally objects
+// which can be described, not simply descriptors; this exception is in place
+// to make it more convenient to pass actual descriptors to functions that
+// expect Describable objects.
+func (d Descriptor) Descriptor() Descriptor {
+ return d
+}
+
+// BlobStatter makes blob descriptors available by digest. The service may
+// provide a descriptor of a different digest if the provided digest is not
+// canonical.
+type BlobStatter interface {
+ // Stat provides metadata about a blob identified by the digest. If the
+ // blob is unknown to the describer, ErrBlobUnknown will be returned.
+ Stat(ctx context.Context, dgst digest.Digest) (Descriptor, error)
+}
+
+// BlobDeleter enables deleting blobs from storage.
+type BlobDeleter interface {
+ Delete(ctx context.Context, dgst digest.Digest) error
+}
+
+// BlobEnumerator enables iterating over blobs from storage
+type BlobEnumerator interface {
+ Enumerate(ctx context.Context, ingester func(dgst digest.Digest) error) error
+}
+
+// BlobDescriptorService manages metadata about a blob by digest. Most
+// implementations will not expose such an interface explicitly. Such mappings
+// should be maintained by interacting with the BlobIngester. Hence, this is
+// left off of BlobService and BlobStore.
+type BlobDescriptorService interface {
+ BlobStatter
+
+ // SetDescriptor assigns the descriptor to the digest. The provided digest and
+ // the digest in the descriptor must map to identical content but they may
+ // differ on their algorithm. The descriptor must have the canonical
+ // digest of the content and the digest algorithm must match the
+ // annotators canonical algorithm.
+ //
+ // Such a facility can be used to map blobs between digest domains, with
+ // the restriction that the algorithm of the descriptor must match the
+ // canonical algorithm (ie sha256) of the annotator.
+ SetDescriptor(ctx context.Context, dgst digest.Digest, desc Descriptor) error
+
+ // Clear enables descriptors to be unlinked
+ Clear(ctx context.Context, dgst digest.Digest) error
+}
+
+// BlobDescriptorServiceFactory creates middleware for BlobDescriptorService.
+type BlobDescriptorServiceFactory interface {
+ BlobAccessController(svc BlobDescriptorService) BlobDescriptorService
+}
+
+// ReadSeekCloser is the primary reader type for blob data, combining
+// io.ReadSeeker with io.Closer.
+type ReadSeekCloser interface {
+ io.ReadSeeker
+ io.Closer
+}
+
+// BlobProvider describes operations for getting blob data.
+type BlobProvider interface {
+ // Get returns the entire blob identified by digest along with the descriptor.
+ Get(ctx context.Context, dgst digest.Digest) ([]byte, error)
+
+ // Open provides a ReadSeekCloser to the blob identified by the provided
+ // descriptor. If the blob is not known to the service, an error will be
+ // returned.
+ Open(ctx context.Context, dgst digest.Digest) (ReadSeekCloser, error)
+}
+
+// BlobServer can serve blobs via http.
+type BlobServer interface {
+ // ServeBlob attempts to serve the blob, identifed by dgst, via http. The
+ // service may decide to redirect the client elsewhere or serve the data
+ // directly.
+ //
+ // This handler only issues successful responses, such as 2xx or 3xx,
+ // meaning it serves data or issues a redirect. If the blob is not
+ // available, an error will be returned and the caller may still issue a
+ // response.
+ //
+ // The implementation may serve the same blob from a different digest
+ // domain. The appropriate headers will be set for the blob, unless they
+ // have already been set by the caller.
+ ServeBlob(ctx context.Context, w http.ResponseWriter, r *http.Request, dgst digest.Digest) error
+}
+
+// BlobIngester ingests blob data.
+type BlobIngester interface {
+ // Put inserts the content p into the blob service, returning a descriptor
+ // or an error.
+ Put(ctx context.Context, mediaType string, p []byte) (Descriptor, error)
+
+ // Create allocates a new blob writer to add a blob to this service. The
+ // returned handle can be written to and later resumed using an opaque
+ // identifier. With this approach, one can Close and Resume a BlobWriter
+ // multiple times until the BlobWriter is committed or cancelled.
+ Create(ctx context.Context, options ...BlobCreateOption) (BlobWriter, error)
+
+ // Resume attempts to resume a write to a blob, identified by an id.
+ Resume(ctx context.Context, id string) (BlobWriter, error)
+}
+
+// BlobCreateOption is a general extensible function argument for blob creation
+// methods. A BlobIngester may choose to honor any or none of the given
+// BlobCreateOptions, which can be specific to the implementation of the
+// BlobIngester receiving them.
+// TODO (brianbland): unify this with ManifestServiceOption in the future
+type BlobCreateOption interface {
+ Apply(interface{}) error
+}
+
+// CreateOptions is a collection of blob creation modifiers relevant to general
+// blob storage intended to be configured by the BlobCreateOption.Apply method.
+type CreateOptions struct {
+ Mount struct {
+ ShouldMount bool
+ From reference.Canonical
+ // Stat allows to pass precalculated descriptor to link and return.
+ // Blob access check will be skipped if set.
+ Stat *Descriptor
+ }
+}
+
+// BlobWriter provides a handle for inserting data into a blob store.
+// Instances should be obtained from BlobWriteService.Writer and
+// BlobWriteService.Resume. If supported by the store, a writer can be
+// recovered with the id.
+type BlobWriter interface {
+ io.WriteCloser
+ io.ReaderFrom
+
+ // Size returns the number of bytes written to this blob.
+ Size() int64
+
+ // ID returns the identifier for this writer. The ID can be used with the
+ // Blob service to later resume the write.
+ ID() string
+
+ // StartedAt returns the time this blob write was started.
+ StartedAt() time.Time
+
+ // Commit completes the blob writer process. The content is verified
+ // against the provided provisional descriptor, which may result in an
+ // error. Depending on the implementation, written data may be validated
+ // against the provisional descriptor fields. If MediaType is not present,
+ // the implementation may reject the commit or assign "application/octet-
+ // stream" to the blob. The returned descriptor may have a different
+ // digest depending on the blob store, referred to as the canonical
+ // descriptor.
+ Commit(ctx context.Context, provisional Descriptor) (canonical Descriptor, err error)
+
+ // Cancel ends the blob write without storing any data and frees any
+ // associated resources. Any data written thus far will be lost. Cancel
+ // implementations should allow multiple calls even after a commit that
+ // result in a no-op. This allows use of Cancel in a defer statement,
+ // increasing the assurance that it is correctly called.
+ Cancel(ctx context.Context) error
+}
+
+// BlobService combines the operations to access, read and write blobs. This
+// can be used to describe remote blob services.
+type BlobService interface {
+ BlobStatter
+ BlobProvider
+ BlobIngester
+}
+
+// BlobStore represent the entire suite of blob related operations. Such an
+// implementation can access, read, write, delete and serve blobs.
+type BlobStore interface {
+ BlobService
+ BlobServer
+ BlobDeleter
+}
diff --git a/vendor/github.com/docker/distribution/context/context.go b/vendor/github.com/docker/distribution/context/context.go
new file mode 100644
index 000000000..23cbf5b54
--- /dev/null
+++ b/vendor/github.com/docker/distribution/context/context.go
@@ -0,0 +1,85 @@
+package context
+
+import (
+ "sync"
+
+ "github.com/docker/distribution/uuid"
+ "golang.org/x/net/context"
+)
+
+// Context is a copy of Context from the golang.org/x/net/context package.
+type Context interface {
+ context.Context
+}
+
+// instanceContext is a context that provides only an instance id. It is
+// provided as the main background context.
+type instanceContext struct {
+ Context
+ id string // id of context, logged as "instance.id"
+ once sync.Once // once protect generation of the id
+}
+
+func (ic *instanceContext) Value(key interface{}) interface{} {
+ if key == "instance.id" {
+ ic.once.Do(func() {
+ // We want to lazy initialize the UUID such that we don't
+ // call a random generator from the package initialization
+ // code. For various reasons random could not be available
+ // https://github.com/docker/distribution/issues/782
+ ic.id = uuid.Generate().String()
+ })
+ return ic.id
+ }
+
+ return ic.Context.Value(key)
+}
+
+var background = &instanceContext{
+ Context: context.Background(),
+}
+
+// Background returns a non-nil, empty Context. The background context
+// provides a single key, "instance.id" that is globally unique to the
+// process.
+func Background() Context {
+ return background
+}
+
+// WithValue returns a copy of parent in which the value associated with key is
+// val. Use context Values only for request-scoped data that transits processes
+// and APIs, not for passing optional parameters to functions.
+func WithValue(parent Context, key, val interface{}) Context {
+ return context.WithValue(parent, key, val)
+}
+
+// stringMapContext is a simple context implementation that checks a map for a
+// key, falling back to a parent if not present.
+type stringMapContext struct {
+ context.Context
+ m map[string]interface{}
+}
+
+// WithValues returns a context that proxies lookups through a map. Only
+// supports string keys.
+func WithValues(ctx context.Context, m map[string]interface{}) context.Context {
+ mo := make(map[string]interface{}, len(m)) // make our own copy.
+ for k, v := range m {
+ mo[k] = v
+ }
+
+ return stringMapContext{
+ Context: ctx,
+ m: mo,
+ }
+}
+
+func (smc stringMapContext) Value(key interface{}) interface{} {
+ if ks, ok := key.(string); ok {
+ if v, ok := smc.m[ks]; ok {
+ return v
+ }
+ }
+
+ return smc.Context.Value(key)
+}
diff --git a/vendor/github.com/docker/distribution/context/doc.go b/vendor/github.com/docker/distribution/context/doc.go
new file mode 100644
index 000000000..3b4ab8882
--- /dev/null
+++ b/vendor/github.com/docker/distribution/context/doc.go
@@ -0,0 +1,89 @@
+// Package context provides several utilities for working with
+// golang.org/x/net/context in http requests. Primarily, the focus is on
+// logging relevant request information but this package is not limited to
+// that purpose.
+//
+// The easiest way to get started is to get the background context:
+//
+// ctx := context.Background()
+//
+// The returned context should be passed around your application and be the
+// root of all other context instances. If the application has a version, this
+// line should be called before anything else:
+//
+// ctx := context.WithVersion(context.Background(), version)
+//
+// The above will store the version in the context and will be available to
+// the logger.
+//
+// Logging
+//
+// The most useful aspect of this package is GetLogger. This function takes
+// any context.Context interface and returns the current logger from the
+// context. Canonical usage looks like this:
+//
+// GetLogger(ctx).Infof("something interesting happened")
+//
+// GetLogger also takes optional key arguments. The keys will be looked up in
+// the context and reported with the logger. The following example would
+// return a logger that prints the version with each log message:
+//
+// ctx := context.Context(context.Background(), "version", version)
+// GetLogger(ctx, "version").Infof("this log message has a version field")
+//
+// The above would print out a log message like this:
+//
+// INFO[0000] this log message has a version field version=v2.0.0-alpha.2.m
+//
+// When used with WithLogger, we gain the ability to decorate the context with
+// loggers that have information from disparate parts of the call stack.
+// Following from the version example, we can build a new context with the
+// configured logger such that we always print the version field:
+//
+// ctx = WithLogger(ctx, GetLogger(ctx, "version"))
+//
+// Since the logger has been pushed to the context, we can now get the version
+// field for free with our log messages. Future calls to GetLogger on the new
+// context will have the version field:
+//
+// GetLogger(ctx).Infof("this log message has a version field")
+//
+// This becomes more powerful when we start stacking loggers. Let's say we
+// have the version logger from above but also want a request id. Using the
+// context above, in our request scoped function, we place another logger in
+// the context:
+//
+// ctx = context.WithValue(ctx, "http.request.id", "unique id") // called when building request context
+// ctx = WithLogger(ctx, GetLogger(ctx, "http.request.id"))
+//
+// When GetLogger is called on the new context, "http.request.id" will be
+// included as a logger field, along with the original "version" field:
+//
+// INFO[0000] this log message has a version field http.request.id=unique id version=v2.0.0-alpha.2.m
+//
+// Note that this only affects the new context, the previous context, with the
+// version field, can be used independently. Put another way, the new logger,
+// added to the request context, is unique to that context and can have
+// request scoped varaibles.
+//
+// HTTP Requests
+//
+// This package also contains several methods for working with http requests.
+// The concepts are very similar to those described above. We simply place the
+// request in the context using WithRequest. This makes the request variables
+// available. GetRequestLogger can then be called to get request specific
+// variables in a log line:
+//
+// ctx = WithRequest(ctx, req)
+// GetRequestLogger(ctx).Infof("request variables")
+//
+// Like above, if we want to include the request data in all log messages in
+// the context, we push the logger to a new context and use that one:
+//
+// ctx = WithLogger(ctx, GetRequestLogger(ctx))
+//
+// The concept is fairly powerful and ensures that calls throughout the stack
+// can be traced in log messages. Using the fields like "http.request.id", one
+// can analyze call flow for a particular request with a simple grep of the
+// logs.
+package context
diff --git a/vendor/github.com/docker/distribution/context/http.go b/vendor/github.com/docker/distribution/context/http.go
new file mode 100644
index 000000000..7fe9b8ab0
--- /dev/null
+++ b/vendor/github.com/docker/distribution/context/http.go
@@ -0,0 +1,366 @@
+package context
+
+import (
+ "errors"
+ "net"
+ "net/http"
+ "strings"
+ "sync"
+ "time"
+
+ log "github.com/Sirupsen/logrus"
+ "github.com/docker/distribution/uuid"
+ "github.com/gorilla/mux"
+)
+
+// Common errors used with this package.
+var (
+ ErrNoRequestContext = errors.New("no http request in context")
+ ErrNoResponseWriterContext = errors.New("no http response in context")
+)
+
+func parseIP(ipStr string) net.IP {
+ ip := net.ParseIP(ipStr)
+ if ip == nil {
+ log.Warnf("invalid remote IP address: %q", ipStr)
+ }
+ return ip
+}
+
+// RemoteAddr extracts the remote address of the request, taking into
+// account proxy headers.
+func RemoteAddr(r *http.Request) string {
+ if prior := r.Header.Get("X-Forwarded-For"); prior != "" {
+ proxies := strings.Split(prior, ",")
+ if len(proxies) > 0 {
+ remoteAddr := strings.Trim(proxies[0], " ")
+ if parseIP(remoteAddr) != nil {
+ return remoteAddr
+ }
+ }
+ }
+ // X-Real-Ip is less supported, but worth checking in the
+ // absence of X-Forwarded-For
+ if realIP := r.Header.Get("X-Real-Ip"); realIP != "" {
+ if parseIP(realIP) != nil {
+ return realIP
+ }
+ }
+
+ return r.RemoteAddr
+}
+
+// RemoteIP extracts the remote IP of the request, taking into
+// account proxy headers.
+func RemoteIP(r *http.Request) string {
+ addr := RemoteAddr(r)
+
+ // Try parsing it as "IP:port"
+ if ip, _, err := net.SplitHostPort(addr); err == nil {
+ return ip
+ }
+
+ return addr
+}
+
+// WithRequest places the request on the context. The context of the request
+// is assigned a unique id, available at "http.request.id". The request itself
+// is available at "http.request". Other common attributes are available under
+// the prefix "http.request.". If a request is already present on the context,
+// this method will panic.
+func WithRequest(ctx Context, r *http.Request) Context {
+ if ctx.Value("http.request") != nil {
+ // NOTE(stevvooe): This needs to be considered a programming error. It
+ // is unlikely that we'd want to have more than one request in
+ // context.
+ panic("only one request per context")
+ }
+
+ return &httpRequestContext{
+ Context: ctx,
+ startedAt: time.Now(),
+ id: uuid.Generate().String(),
+ r: r,
+ }
+}
+
+// GetRequest returns the http request in the given context. Returns
+// ErrNoRequestContext if the context does not have an http request associated
+// with it.
+func GetRequest(ctx Context) (*http.Request, error) {
+ if r, ok := ctx.Value("http.request").(*http.Request); r != nil && ok {
+ return r, nil
+ }
+ return nil, ErrNoRequestContext
+}
+
+// GetRequestID attempts to resolve the current request id, if possible. An
+// error is return if it is not available on the context.
+func GetRequestID(ctx Context) string {
+ return GetStringValue(ctx, "http.request.id")
+}
+
+// WithResponseWriter returns a new context and response writer that makes
+// interesting response statistics available within the context.
+func WithResponseWriter(ctx Context, w http.ResponseWriter) (Context, http.ResponseWriter) {
+ if closeNotifier, ok := w.(http.CloseNotifier); ok {
+ irwCN := &instrumentedResponseWriterCN{
+ instrumentedResponseWriter: instrumentedResponseWriter{
+ ResponseWriter: w,
+ Context: ctx,
+ },
+ CloseNotifier: closeNotifier,
+ }
+
+ return irwCN, irwCN
+ }
+
+ irw := instrumentedResponseWriter{
+ ResponseWriter: w,
+ Context: ctx,
+ }
+ return &irw, &irw
+}
+
+// GetResponseWriter returns the http.ResponseWriter from the provided
+// context. If not present, ErrNoResponseWriterContext is returned. The
+// returned instance provides instrumentation in the context.
+func GetResponseWriter(ctx Context) (http.ResponseWriter, error) {
+ v := ctx.Value("http.response")
+
+ rw, ok := v.(http.ResponseWriter)
+ if !ok || rw == nil {
+ return nil, ErrNoResponseWriterContext
+ }
+
+ return rw, nil
+}
+
+// getVarsFromRequest let's us change request vars implementation for testing
+// and maybe future changes.
+var getVarsFromRequest = mux.Vars
+
+// WithVars extracts gorilla/mux vars and makes them available on the returned
+// context. Variables are available at keys with the prefix "vars.". For
+// example, if looking for the variable "name", it can be accessed as
+// "vars.name". Implementations that are accessing values need not know that
+// the underlying context is implemented with gorilla/mux vars.
+func WithVars(ctx Context, r *http.Request) Context {
+ return &muxVarsContext{
+ Context: ctx,
+ vars: getVarsFromRequest(r),
+ }
+}
+
+// GetRequestLogger returns a logger that contains fields from the request in
+// the current context. If the request is not available in the context, no
+// fields will display. Request loggers can safely be pushed onto the context.
+func GetRequestLogger(ctx Context) Logger {
+ return GetLogger(ctx,
+ "http.request.id",
+ "http.request.method",
+ "http.request.host",
+ "http.request.uri",
+ "http.request.referer",
+ "http.request.useragent",
+ "http.request.remoteaddr",
+ "http.request.contenttype")
+}
+
+// GetResponseLogger reads the current response stats and builds a logger.
+// Because the values are read at call time, pushing a logger returned from
+// this function on the context will lead to missing or invalid data. Only
+// call this at the end of a request, after the response has been written.
+func GetResponseLogger(ctx Context) Logger {
+ l := getLogrusLogger(ctx,
+ "http.response.written",
+ "http.response.status",
+ "http.response.contenttype")
+
+ duration := Since(ctx, "http.request.startedat")
+
+ if duration > 0 {
+ l = l.WithField("http.response.duration", duration.String())
+ }
+
+ return l
+}
+
+// httpRequestContext makes information about a request available to context.
+type httpRequestContext struct {
+ Context
+
+ startedAt time.Time
+ id string
+ r *http.Request
+}
+
+// Value returns a keyed element of the request for use in the context. To get
+// the request itself, query "request". For other components, access them as
+// "request.