summaryrefslogtreecommitdiff
path: root/vendor/github.com/docker/distribution/context
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/docker/distribution/context')
-rw-r--r--vendor/github.com/docker/distribution/context/context.go85
-rw-r--r--vendor/github.com/docker/distribution/context/doc.go89
-rw-r--r--vendor/github.com/docker/distribution/context/http.go366
-rw-r--r--vendor/github.com/docker/distribution/context/logger.go116
-rw-r--r--vendor/github.com/docker/distribution/context/trace.go104
-rw-r--r--vendor/github.com/docker/distribution/context/util.go24
-rw-r--r--vendor/github.com/docker/distribution/context/version.go16
7 files changed, 800 insertions, 0 deletions
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..9b623074e
--- /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 variables.
+//
+// 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..7d65c8524
--- /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"
+
+ "github.com/docker/distribution/uuid"
+ "github.com/gorilla/mux"
+ log "github.com/sirupsen/logrus"
+)
+
+// 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.<component>". For example, r.RequestURI
+func (ctx *httpRequestContext) Value(key interface{}) interface{} {
+ if keyStr, ok := key.(string); ok {
+ if keyStr == "http.request" {
+ return ctx.r
+ }
+
+ if !strings.HasPrefix(keyStr, "http.request.") {
+ goto fallback
+ }
+
+ parts := strings.Split(keyStr, ".")
+
+ if len(parts) != 3 {
+ goto fallback
+ }
+
+ switch parts[2] {
+ case "uri":
+ return ctx.r.RequestURI
+ case "remoteaddr":
+ return RemoteAddr(ctx.r)
+ case "method":
+ return ctx.r.Method
+ case "host":
+ return ctx.r.Host
+ case "referer":
+ referer := ctx.r.Referer()
+ if referer != "" {
+ return referer
+ }
+ case "useragent":
+ return ctx.r.UserAgent()
+ case "id":
+ return ctx.id
+ case "startedat":
+ return ctx.startedAt
+ case "contenttype":
+ ct := ctx.r.Header.Get("Content-Type")
+ if ct != "" {
+ return ct
+ }
+ }
+ }
+
+fallback:
+ return ctx.Context.Value(key)
+}
+
+type muxVarsContext struct {
+ Context
+ vars map[string]string
+}
+
+func (ctx *muxVarsContext) Value(key interface{}) interface{} {
+ if keyStr, ok := key.(string); ok {
+ if keyStr == "vars" {
+ return ctx.vars
+ }
+
+ if strings.HasPrefix(keyStr, "vars.") {
+ keyStr = strings.TrimPrefix(keyStr, "vars.")
+ }
+
+ if v, ok := ctx.vars[keyStr]; ok {
+ return v
+ }
+ }
+
+ return ctx.Context.Value(key)
+}
+
+// instrumentedResponseWriterCN provides response writer information in a
+// context. It implements http.CloseNotifier so that users can detect
+// early disconnects.
+type instrumentedResponseWriterCN struct {
+ instrumentedResponseWriter
+ http.CloseNotifier
+}
+
+// instrumentedResponseWriter provides response writer information in a
+// context. This variant is only used in the case where CloseNotifier is not
+// implemented by the parent ResponseWriter.
+type instrumentedResponseWriter struct {
+ http.ResponseWriter
+ Context
+
+ mu sync.Mutex
+ status int
+ written int64
+}
+
+func (irw *instrumentedResponseWriter) Write(p []byte) (n int, err error) {
+ n, err = irw.ResponseWriter.Write(p)
+
+ irw.mu.Lock()
+ irw.written += int64(n)
+
+ // Guess the likely status if not set.
+ if irw.status == 0 {
+ irw.status = http.StatusOK
+ }
+
+ irw.mu.Unlock()
+
+ return
+}
+
+func (irw *instrumentedResponseWriter) WriteHeader(status int) {
+ irw.ResponseWriter.WriteHeader(status)
+
+ irw.mu.Lock()
+ irw.status = status
+ irw.mu.Unlock()
+}
+
+func (irw *instrumentedResponseWriter) Flush() {
+ if flusher, ok := irw.ResponseWriter.(http.Flusher); ok {
+ flusher.Flush()
+ }
+}
+
+func (irw *instrumentedResponseWriter) Value(key interface{}) interface{} {
+ if keyStr, ok := key.(string); ok {
+ if keyStr == "http.response" {
+ return irw
+ }
+
+ if !strings.HasPrefix(keyStr, "http.response.") {
+ goto fallback
+ }
+
+ parts := strings.Split(keyStr, ".")
+
+ if len(parts) != 3 {
+ goto fallback
+ }
+
+ irw.mu.Lock()
+ defer irw.mu.Unlock()
+
+ switch parts[2] {
+ case "written":
+ return irw.written
+ case "status":
+ return irw.status
+ case "contenttype":
+ contentType := irw.Header().Get("Content-Type")
+ if contentType != "" {
+ return contentType
+ }
+ }
+ }
+
+fallback:
+ return irw.Context.Value(key)
+}
+
+func (irw *instrumentedResponseWriterCN) Value(key interface{}) interface{} {
+ if keyStr, ok := key.(string); ok {
+ if keyStr == "http.response" {
+ return irw
+ }
+ }
+
+ return irw.instrumentedResponseWriter.Value(key)
+}
diff --git a/vendor/github.com/docker/distribution/context/logger.go b/vendor/github.com/docker/distribution/context/logger.go
new file mode 100644
index 000000000..86c5964e4
--- /dev/null
+++ b/vendor/github.com/docker/distribution/context/logger.go
@@ -0,0 +1,116 @@
+package context
+
+import (
+ "fmt"
+
+ "github.com/sirupsen/logrus"
+ "runtime"
+)
+
+// Logger provides a leveled-logging interface.
+type Logger interface {
+ // standard logger methods
+ Print(args ...interface{})
+ Printf(format string, args ...interface{})
+ Println(args ...interface{})
+
+ Fatal(args ...interface{})
+ Fatalf(format string, args ...interface{})
+ Fatalln(args ...interface{})
+
+ Panic(args ...interface{})
+ Panicf(format string, args ...interface{})
+ Panicln(args ...interface{})
+
+ // Leveled methods, from logrus
+ Debug(args ...interface{})
+ Debugf(format string, args ...interface{})
+ Debugln(args ...interface{})
+
+ Error(args ...interface{})
+ Errorf(format string, args ...interface{})
+ Errorln(args ...interface{})
+
+ Info(args ...interface{})
+ Infof(format string, args ...interface{})
+ Infoln(args ...interface{})
+
+ Warn(args ...interface{})
+ Warnf(format string, args ...interface{})
+ Warnln(args ...interface{})
+}
+
+// WithLogger creates a new context with provided logger.
+func WithLogger(ctx Context, logger Logger) Context {
+ return WithValue(ctx, "logger", logger)
+}
+
+// GetLoggerWithField returns a logger instance with the specified field key
+// and value without affecting the context. Extra specified keys will be
+// resolved from the context.
+func GetLoggerWithField(ctx Context, key, value interface{}, keys ...interface{}) Logger {
+ return getLogrusLogger(ctx, keys...).WithField(fmt.Sprint(key), value)
+}
+
+// GetLoggerWithFields returns a logger instance with the specified fields
+// without affecting the context. Extra specified keys will be resolved from
+// the context.
+func GetLoggerWithFields(ctx Context, fields map[interface{}]interface{}, keys ...interface{}) Logger {
+ // must convert from interface{} -> interface{} to string -> interface{} for logrus.
+ lfields := make(logrus.Fields, len(fields))
+ for key, value := range fields {
+ lfields[fmt.Sprint(key)] = value
+ }
+
+ return getLogrusLogger(ctx, keys...).WithFields(lfields)
+}
+
+// GetLogger returns the logger from the current context, if present. If one
+// or more keys are provided, they will be resolved on the context and
+// included in the logger. While context.Value takes an interface, any key
+// argument passed to GetLogger will be passed to fmt.Sprint when expanded as
+// a logging key field. If context keys are integer constants, for example,
+// its recommended that a String method is implemented.
+func GetLogger(ctx Context, keys ...interface{}) Logger {
+ return getLogrusLogger(ctx, keys...)
+}
+
+// GetLogrusLogger returns the logrus logger for the context. If one more keys
+// are provided, they will be resolved on the context and included in the
+// logger. Only use this function if specific logrus functionality is
+// required.
+func getLogrusLogger(ctx Context, keys ...interface{}) *logrus.Entry {
+ var logger *logrus.Entry
+
+ // Get a logger, if it is present.
+ loggerInterface := ctx.Value("logger")
+ if loggerInterface != nil {
+ if lgr, ok := loggerInterface.(*logrus.Entry); ok {
+ logger = lgr
+ }
+ }
+
+ if logger == nil {
+ fields := logrus.Fields{}
+
+ // Fill in the instance id, if we have it.
+ instanceID := ctx.Value("instance.id")
+ if instanceID != nil {
+ fields["instance.id"] = instanceID
+ }
+
+ fields["go.version"] = runtime.Version()
+ // If no logger is found, just return the standard logger.
+ logger = logrus.StandardLogger().WithFields(fields)
+ }
+
+ fields := logrus.Fields{}
+ for _, key := range keys {
+ v := ctx.Value(key)
+ if v != nil {
+ fields[fmt.Sprint(key)] = v
+ }
+ }
+
+ return logger.WithFields(fields)
+}
diff --git a/vendor/github.com/docker/distribution/context/trace.go b/vendor/github.com/docker/distribution/context/trace.go
new file mode 100644
index 000000000..721964a84
--- /dev/null
+++ b/vendor/github.com/docker/distribution/context/trace.go
@@ -0,0 +1,104 @@
+package context
+
+import (
+ "runtime"
+ "time"
+
+ "github.com/docker/distribution/uuid"
+)
+
+// WithTrace allocates a traced timing span in a new context. This allows a
+// caller to track the time between calling WithTrace and the returned done
+// function. When the done function is called, a log message is emitted with a
+// "trace.duration" field, corresponding to the elapsed time and a
+// "trace.func" field, corresponding to the function that called WithTrace.
+//
+// The logging keys "trace.id" and "trace.parent.id" are provided to implement
+// dapper-like tracing. This function should be complemented with a WithSpan
+// method that could be used for tracing distributed RPC calls.
+//
+// The main benefit of this function is to post-process log messages or
+// intercept them in a hook to provide timing data. Trace ids and parent ids
+// can also be linked to provide call tracing, if so required.
+//
+// Here is an example of the usage:
+//
+// func timedOperation(ctx Context) {
+// ctx, done := WithTrace(ctx)
+// defer done("this will be the log message")
+// // ... function body ...
+// }
+//
+// If the function ran for roughly 1s, such a usage would emit a log message
+// as follows:
+//
+// INFO[0001] this will be the log message trace.duration=1.004575763s trace.func=github.com/docker/distribution/context.traceOperation trace.id=<id> ...
+//
+// Notice that the function name is automatically resolved, along with the
+// package and a trace id is emitted that can be linked with parent ids.
+func WithTrace(ctx Context) (Context, func(format string, a ...interface{})) {
+ if ctx == nil {
+ ctx = Background()
+ }
+
+ pc, file, line, _ := runtime.Caller(1)
+ f := runtime.FuncForPC(pc)
+ ctx = &traced{
+ Context: ctx,
+ id: uuid.Generate().String(),
+ start: time.Now(),
+ parent: GetStringValue(ctx, "trace.id"),
+ fnname: f.Name(),
+ file: file,
+ line: line,
+ }
+
+ return ctx, func(format string, a ...interface{}) {
+ GetLogger(ctx,
+ "trace.duration",
+ "trace.id",
+ "trace.parent.id",
+ "trace.func",
+ "trace.file",
+ "trace.line").
+ Debugf(format, a...)
+ }
+}
+
+// traced represents a context that is traced for function call timing. It
+// also provides fast lookup for the various attributes that are available on
+// the trace.
+type traced struct {
+ Context
+ id string
+ parent string
+ start time.Time
+ fnname string
+ file string
+ line int
+}
+
+func (ts *traced) Value(key interface{}) interface{} {
+ switch key {
+ case "trace.start":
+ return ts.start
+ case "trace.duration":
+ return time.Since(ts.start)
+ case "trace.id":
+ return ts.id
+ case "trace.parent.id":
+ if ts.parent == "" {
+ return nil // must return nil to signal no parent.
+ }
+
+ return ts.parent
+ case "trace.func":
+ return ts.fnname
+ case "trace.file":
+ return ts.file
+ case "trace.line":
+ return ts.line
+ }
+
+ return ts.Context.Value(key)
+}
diff --git a/vendor/github.com/docker/distribution/context/util.go b/vendor/github.com/docker/distribution/context/util.go
new file mode 100644
index 000000000..cb9ef52e3
--- /dev/null
+++ b/vendor/github.com/docker/distribution/context/util.go
@@ -0,0 +1,24 @@
+package context
+
+import (
+ "time"
+)
+
+// Since looks up key, which should be a time.Time, and returns the duration
+// since that time. If the key is not found, the value returned will be zero.
+// This is helpful when inferring metrics related to context execution times.
+func Since(ctx Context, key interface{}) time.Duration {
+ if startedAt, ok := ctx.Value(key).(time.Time); ok {
+ return time.Since(startedAt)
+ }
+ return 0
+}
+
+// GetStringValue returns a string value from the context. The empty string
+// will be returned if not found.
+func GetStringValue(ctx Context, key interface{}) (value string) {
+ if valuev, ok := ctx.Value(key).(string); ok {
+ value = valuev
+ }
+ return value
+}
diff --git a/vendor/github.com/docker/distribution/context/version.go b/vendor/github.com/docker/distribution/context/version.go
new file mode 100644
index 000000000..746cda02e
--- /dev/null
+++ b/vendor/github.com/docker/distribution/context/version.go
@@ -0,0 +1,16 @@
+package context
+
+// WithVersion stores the application version in the context. The new context
+// gets a logger to ensure log messages are marked with the application
+// version.
+func WithVersion(ctx Context, version string) Context {
+ ctx = WithValue(ctx, "version", version)
+ // push a new logger onto the stack
+ return WithLogger(ctx, GetLogger(ctx, "version"))
+}
+
+// GetVersion returns the application version from the context. An empty
+// string may returned if the version was not set on the context.
+func GetVersion(ctx Context) string {
+ return GetStringValue(ctx, "version")
+}