Merge pull request #343 from stevvooe/tracing-driver
context, storagedriver: trace function calls to Base storage drivermaster
						commit
						90af0f9b7a
					
				| 
						 | 
					@ -16,7 +16,7 @@ import (
 | 
				
			||||||
	"github.com/Sirupsen/logrus/formatters/logstash"
 | 
						"github.com/Sirupsen/logrus/formatters/logstash"
 | 
				
			||||||
	"github.com/bugsnag/bugsnag-go"
 | 
						"github.com/bugsnag/bugsnag-go"
 | 
				
			||||||
	"github.com/docker/distribution/configuration"
 | 
						"github.com/docker/distribution/configuration"
 | 
				
			||||||
	ctxu "github.com/docker/distribution/context"
 | 
						"github.com/docker/distribution/context"
 | 
				
			||||||
	_ "github.com/docker/distribution/health"
 | 
						_ "github.com/docker/distribution/health"
 | 
				
			||||||
	_ "github.com/docker/distribution/registry/auth/silly"
 | 
						_ "github.com/docker/distribution/registry/auth/silly"
 | 
				
			||||||
	_ "github.com/docker/distribution/registry/auth/token"
 | 
						_ "github.com/docker/distribution/registry/auth/token"
 | 
				
			||||||
| 
						 | 
					@ -29,7 +29,6 @@ import (
 | 
				
			||||||
	"github.com/docker/distribution/version"
 | 
						"github.com/docker/distribution/version"
 | 
				
			||||||
	gorhandlers "github.com/gorilla/handlers"
 | 
						gorhandlers "github.com/gorilla/handlers"
 | 
				
			||||||
	"github.com/yvasiyarov/gorelic"
 | 
						"github.com/yvasiyarov/gorelic"
 | 
				
			||||||
	"golang.org/x/net/context"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var showVersion bool
 | 
					var showVersion bool
 | 
				
			||||||
| 
						 | 
					@ -68,9 +67,9 @@ func main() {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if config.HTTP.TLS.Certificate == "" {
 | 
						if config.HTTP.TLS.Certificate == "" {
 | 
				
			||||||
		ctxu.GetLogger(app).Infof("listening on %v", config.HTTP.Addr)
 | 
							context.GetLogger(app).Infof("listening on %v", config.HTTP.Addr)
 | 
				
			||||||
		if err := http.ListenAndServe(config.HTTP.Addr, handler); err != nil {
 | 
							if err := http.ListenAndServe(config.HTTP.Addr, handler); err != nil {
 | 
				
			||||||
			ctxu.GetLogger(app).Fatalln(err)
 | 
								context.GetLogger(app).Fatalln(err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		tlsConf := &tls.Config{
 | 
							tlsConf := &tls.Config{
 | 
				
			||||||
| 
						 | 
					@ -83,23 +82,23 @@ func main() {
 | 
				
			||||||
			for _, ca := range config.HTTP.TLS.ClientCAs {
 | 
								for _, ca := range config.HTTP.TLS.ClientCAs {
 | 
				
			||||||
				caPem, err := ioutil.ReadFile(ca)
 | 
									caPem, err := ioutil.ReadFile(ca)
 | 
				
			||||||
				if err != nil {
 | 
									if err != nil {
 | 
				
			||||||
					ctxu.GetLogger(app).Fatalln(err)
 | 
										context.GetLogger(app).Fatalln(err)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if ok := pool.AppendCertsFromPEM(caPem); !ok {
 | 
									if ok := pool.AppendCertsFromPEM(caPem); !ok {
 | 
				
			||||||
					ctxu.GetLogger(app).Fatalln(fmt.Errorf("Could not add CA to pool"))
 | 
										context.GetLogger(app).Fatalln(fmt.Errorf("Could not add CA to pool"))
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			for _, subj := range pool.Subjects() {
 | 
								for _, subj := range pool.Subjects() {
 | 
				
			||||||
				ctxu.GetLogger(app).Debugf("CA Subject: %s", string(subj))
 | 
									context.GetLogger(app).Debugf("CA Subject: %s", string(subj))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			tlsConf.ClientAuth = tls.RequireAndVerifyClientCert
 | 
								tlsConf.ClientAuth = tls.RequireAndVerifyClientCert
 | 
				
			||||||
			tlsConf.ClientCAs = pool
 | 
								tlsConf.ClientCAs = pool
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ctxu.GetLogger(app).Infof("listening on %v, tls", config.HTTP.Addr)
 | 
							context.GetLogger(app).Infof("listening on %v, tls", config.HTTP.Addr)
 | 
				
			||||||
		server := &http.Server{
 | 
							server := &http.Server{
 | 
				
			||||||
			Addr:      config.HTTP.Addr,
 | 
								Addr:      config.HTTP.Addr,
 | 
				
			||||||
			Handler:   handler,
 | 
								Handler:   handler,
 | 
				
			||||||
| 
						 | 
					@ -107,7 +106,7 @@ func main() {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if err := server.ListenAndServeTLS(config.HTTP.TLS.Certificate, config.HTTP.TLS.Key); err != nil {
 | 
							if err := server.ListenAndServeTLS(config.HTTP.TLS.Certificate, config.HTTP.TLS.Key); err != nil {
 | 
				
			||||||
			ctxu.GetLogger(app).Fatalln(err)
 | 
								context.GetLogger(app).Fatalln(err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -191,7 +190,7 @@ func configureLogging(ctx context.Context, config *configuration.Configuration)
 | 
				
			||||||
	if config.Log.Level == "" && config.Log.Formatter == "" {
 | 
						if config.Log.Level == "" && config.Log.Formatter == "" {
 | 
				
			||||||
		// If no config for logging is set, fallback to deprecated "Loglevel".
 | 
							// If no config for logging is set, fallback to deprecated "Loglevel".
 | 
				
			||||||
		log.SetLevel(logLevel(config.Loglevel))
 | 
							log.SetLevel(logLevel(config.Loglevel))
 | 
				
			||||||
		ctx = ctxu.WithLogger(ctx, ctxu.GetLogger(ctx, "version"))
 | 
							ctx = context.WithLogger(ctx, context.GetLogger(ctx, "version"))
 | 
				
			||||||
		return ctx, nil
 | 
							return ctx, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -236,8 +235,8 @@ func configureLogging(ctx context.Context, config *configuration.Configuration)
 | 
				
			||||||
			fields = append(fields, k)
 | 
								fields = append(fields, k)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ctx = ctxu.WithValues(ctx, config.Log.Fields)
 | 
							ctx = context.WithValues(ctx, config.Log.Fields)
 | 
				
			||||||
		ctx = ctxu.WithLogger(ctx, ctxu.GetLogger(ctx, fields...))
 | 
							ctx = context.WithLogger(ctx, context.GetLogger(ctx, fields...))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return ctx, nil
 | 
						return ctx, nil
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,7 @@
 | 
				
			||||||
package context
 | 
					package context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"code.google.com/p/go-uuid/uuid"
 | 
				
			||||||
	"golang.org/x/net/context"
 | 
						"golang.org/x/net/context"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,9 +10,31 @@ type Context interface {
 | 
				
			||||||
	context.Context
 | 
						context.Context
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Background returns a non-nil, empty 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"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ic *instanceContext) Value(key interface{}) interface{} {
 | 
				
			||||||
 | 
						if key == "instance.id" {
 | 
				
			||||||
 | 
							return ic.id
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ic.Context.Value(key)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var background = &instanceContext{
 | 
				
			||||||
 | 
						Context: context.Background(),
 | 
				
			||||||
 | 
						id:      uuid.New(),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 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 {
 | 
					func Background() Context {
 | 
				
			||||||
	return context.Background()
 | 
						return background
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// WithValue returns a copy of parent in which the value associated with key is
 | 
					// WithValue returns a copy of parent in which the value associated with key is
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,99 @@
 | 
				
			||||||
 | 
					package context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"runtime"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"code.google.com/p/go-uuid/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 elapased 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.New(),
 | 
				
			||||||
 | 
							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").
 | 
				
			||||||
 | 
								Infof(format, a...) // info may be too chatty.
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 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)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,85 @@
 | 
				
			||||||
 | 
					package context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"runtime"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TestWithTrace ensures that tracing has the expected values in the context.
 | 
				
			||||||
 | 
					func TestWithTrace(t *testing.T) {
 | 
				
			||||||
 | 
						pc, file, _, _ := runtime.Caller(0) // get current caller.
 | 
				
			||||||
 | 
						f := runtime.FuncForPC(pc)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						base := []valueTestCase{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								key:           "trace.id",
 | 
				
			||||||
 | 
								notnilorempty: true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								key:           "trace.file",
 | 
				
			||||||
 | 
								expected:      file,
 | 
				
			||||||
 | 
								notnilorempty: true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								key:           "trace.line",
 | 
				
			||||||
 | 
								notnilorempty: true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								key:           "trace.start",
 | 
				
			||||||
 | 
								notnilorempty: true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx, done := WithTrace(Background())
 | 
				
			||||||
 | 
						defer done("this will be emitted at end of test")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						checkContextForValues(t, ctx, append(base, valueTestCase{
 | 
				
			||||||
 | 
							key:      "trace.func",
 | 
				
			||||||
 | 
							expected: f.Name(),
 | 
				
			||||||
 | 
						}))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						traced := func() {
 | 
				
			||||||
 | 
							parentID := ctx.Value("trace.id") // ensure the parent trace id is correct.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							pc, _, _, _ := runtime.Caller(0) // get current caller.
 | 
				
			||||||
 | 
							f := runtime.FuncForPC(pc)
 | 
				
			||||||
 | 
							ctx, done := WithTrace(ctx)
 | 
				
			||||||
 | 
							defer done("this should be subordinate to the other trace")
 | 
				
			||||||
 | 
							time.Sleep(time.Second)
 | 
				
			||||||
 | 
							checkContextForValues(t, ctx, append(base, valueTestCase{
 | 
				
			||||||
 | 
								key:      "trace.func",
 | 
				
			||||||
 | 
								expected: f.Name(),
 | 
				
			||||||
 | 
							}, valueTestCase{
 | 
				
			||||||
 | 
								key:      "trace.parent.id",
 | 
				
			||||||
 | 
								expected: parentID,
 | 
				
			||||||
 | 
							}))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						traced()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						time.Sleep(time.Second)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type valueTestCase struct {
 | 
				
			||||||
 | 
						key           string
 | 
				
			||||||
 | 
						expected      interface{}
 | 
				
			||||||
 | 
						notnilorempty bool // just check not empty/not nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func checkContextForValues(t *testing.T, ctx Context, values []valueTestCase) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, testcase := range values {
 | 
				
			||||||
 | 
							v := ctx.Value(testcase.key)
 | 
				
			||||||
 | 
							if testcase.notnilorempty {
 | 
				
			||||||
 | 
								if v == nil || v == "" {
 | 
				
			||||||
 | 
									t.Fatalf("value was nil or empty for %q: %#v", testcase.key, v)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if v != testcase.expected {
 | 
				
			||||||
 | 
								t.Fatalf("unexpected value for key %q: %v != %v", testcase.key, v, testcase.expected)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -8,7 +8,6 @@ import (
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.google.com/p/go-uuid/uuid"
 | 
					 | 
				
			||||||
	"github.com/docker/distribution"
 | 
						"github.com/docker/distribution"
 | 
				
			||||||
	"github.com/docker/distribution/configuration"
 | 
						"github.com/docker/distribution/configuration"
 | 
				
			||||||
	ctxu "github.com/docker/distribution/context"
 | 
						ctxu "github.com/docker/distribution/context"
 | 
				
			||||||
| 
						 | 
					@ -32,11 +31,8 @@ import (
 | 
				
			||||||
// fields should be protected.
 | 
					// fields should be protected.
 | 
				
			||||||
type App struct {
 | 
					type App struct {
 | 
				
			||||||
	context.Context
 | 
						context.Context
 | 
				
			||||||
	Config configuration.Configuration
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// InstanceID is a unique id assigned to the application on each creation.
 | 
						Config configuration.Configuration
 | 
				
			||||||
	// Provides information in the logs and context to identify restarts.
 | 
					 | 
				
			||||||
	InstanceID string
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	router           *mux.Router                 // main application router, configured with dispatchers
 | 
						router           *mux.Router                 // main application router, configured with dispatchers
 | 
				
			||||||
	driver           storagedriver.StorageDriver // driver maintains the app global storage driver instance.
 | 
						driver           storagedriver.StorageDriver // driver maintains the app global storage driver instance.
 | 
				
			||||||
| 
						 | 
					@ -52,29 +48,17 @@ type App struct {
 | 
				
			||||||
	redis *redis.Pool
 | 
						redis *redis.Pool
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Value intercepts calls context.Context.Value, returning the current app id,
 | 
					 | 
				
			||||||
// if requested.
 | 
					 | 
				
			||||||
func (app *App) Value(key interface{}) interface{} {
 | 
					 | 
				
			||||||
	switch key {
 | 
					 | 
				
			||||||
	case "app.id":
 | 
					 | 
				
			||||||
		return app.InstanceID
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return app.Context.Value(key)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// NewApp takes a configuration and returns a configured app, ready to serve
 | 
					// NewApp takes a configuration and returns a configured app, ready to serve
 | 
				
			||||||
// requests. The app only implements ServeHTTP and can be wrapped in other
 | 
					// requests. The app only implements ServeHTTP and can be wrapped in other
 | 
				
			||||||
// handlers accordingly.
 | 
					// handlers accordingly.
 | 
				
			||||||
func NewApp(ctx context.Context, configuration configuration.Configuration) *App {
 | 
					func NewApp(ctx context.Context, configuration configuration.Configuration) *App {
 | 
				
			||||||
	app := &App{
 | 
						app := &App{
 | 
				
			||||||
		Config:     configuration,
 | 
							Config:  configuration,
 | 
				
			||||||
		Context:    ctx,
 | 
							Context: ctx,
 | 
				
			||||||
		InstanceID: uuid.New(),
 | 
							router:  v2.RouterWithPrefix(configuration.HTTP.Prefix),
 | 
				
			||||||
		router:     v2.RouterWithPrefix(configuration.HTTP.Prefix),
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	app.Context = ctxu.WithLogger(app.Context, ctxu.GetLogger(app, "app.id"))
 | 
						app.Context = ctxu.WithLogger(app.Context, ctxu.GetLogger(app, "instance.id"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Register the handler dispatchers.
 | 
						// Register the handler dispatchers.
 | 
				
			||||||
	app.register(v2.RouteNameBase, func(ctx *Context, r *http.Request) http.Handler {
 | 
						app.register(v2.RouteNameBase, func(ctx *Context, r *http.Request) http.Handler {
 | 
				
			||||||
| 
						 | 
					@ -200,7 +184,7 @@ func (app *App) configureEvents(configuration *configuration.Configuration) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	app.events.source = notifications.SourceRecord{
 | 
						app.events.source = notifications.SourceRecord{
 | 
				
			||||||
		Addr:       hostname,
 | 
							Addr:       hostname,
 | 
				
			||||||
		InstanceID: app.InstanceID,
 | 
							InstanceID: ctxu.GetStringValue(app, "instance.id"),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -40,6 +40,7 @@ package base
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/docker/distribution/context"
 | 
				
			||||||
	storagedriver "github.com/docker/distribution/registry/storage/driver"
 | 
						storagedriver "github.com/docker/distribution/registry/storage/driver"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -51,6 +52,9 @@ type Base struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetContent wraps GetContent of underlying storage driver.
 | 
					// GetContent wraps GetContent of underlying storage driver.
 | 
				
			||||||
func (base *Base) GetContent(path string) ([]byte, error) {
 | 
					func (base *Base) GetContent(path string) ([]byte, error) {
 | 
				
			||||||
 | 
						_, done := context.WithTrace(context.Background())
 | 
				
			||||||
 | 
						defer done("Base.GetContent")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !storagedriver.PathRegexp.MatchString(path) {
 | 
						if !storagedriver.PathRegexp.MatchString(path) {
 | 
				
			||||||
		return nil, storagedriver.InvalidPathError{Path: path}
 | 
							return nil, storagedriver.InvalidPathError{Path: path}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -60,6 +64,9 @@ func (base *Base) GetContent(path string) ([]byte, error) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// PutContent wraps PutContent of underlying storage driver.
 | 
					// PutContent wraps PutContent of underlying storage driver.
 | 
				
			||||||
func (base *Base) PutContent(path string, content []byte) error {
 | 
					func (base *Base) PutContent(path string, content []byte) error {
 | 
				
			||||||
 | 
						_, done := context.WithTrace(context.Background())
 | 
				
			||||||
 | 
						defer done("Base.PutContent")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !storagedriver.PathRegexp.MatchString(path) {
 | 
						if !storagedriver.PathRegexp.MatchString(path) {
 | 
				
			||||||
		return storagedriver.InvalidPathError{Path: path}
 | 
							return storagedriver.InvalidPathError{Path: path}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -69,6 +76,9 @@ func (base *Base) PutContent(path string, content []byte) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ReadStream wraps ReadStream of underlying storage driver.
 | 
					// ReadStream wraps ReadStream of underlying storage driver.
 | 
				
			||||||
func (base *Base) ReadStream(path string, offset int64) (io.ReadCloser, error) {
 | 
					func (base *Base) ReadStream(path string, offset int64) (io.ReadCloser, error) {
 | 
				
			||||||
 | 
						_, done := context.WithTrace(context.Background())
 | 
				
			||||||
 | 
						defer done("Base.ReadStream")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if offset < 0 {
 | 
						if offset < 0 {
 | 
				
			||||||
		return nil, storagedriver.InvalidOffsetError{Path: path, Offset: offset}
 | 
							return nil, storagedriver.InvalidOffsetError{Path: path, Offset: offset}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -82,6 +92,9 @@ func (base *Base) ReadStream(path string, offset int64) (io.ReadCloser, error) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// WriteStream wraps WriteStream of underlying storage driver.
 | 
					// WriteStream wraps WriteStream of underlying storage driver.
 | 
				
			||||||
func (base *Base) WriteStream(path string, offset int64, reader io.Reader) (nn int64, err error) {
 | 
					func (base *Base) WriteStream(path string, offset int64, reader io.Reader) (nn int64, err error) {
 | 
				
			||||||
 | 
						_, done := context.WithTrace(context.Background())
 | 
				
			||||||
 | 
						defer done("Base.WriteStream")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if offset < 0 {
 | 
						if offset < 0 {
 | 
				
			||||||
		return 0, storagedriver.InvalidOffsetError{Path: path, Offset: offset}
 | 
							return 0, storagedriver.InvalidOffsetError{Path: path, Offset: offset}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -95,6 +108,9 @@ func (base *Base) WriteStream(path string, offset int64, reader io.Reader) (nn i
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Stat wraps Stat of underlying storage driver.
 | 
					// Stat wraps Stat of underlying storage driver.
 | 
				
			||||||
func (base *Base) Stat(path string) (storagedriver.FileInfo, error) {
 | 
					func (base *Base) Stat(path string) (storagedriver.FileInfo, error) {
 | 
				
			||||||
 | 
						_, done := context.WithTrace(context.Background())
 | 
				
			||||||
 | 
						defer done("Base.Stat")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !storagedriver.PathRegexp.MatchString(path) {
 | 
						if !storagedriver.PathRegexp.MatchString(path) {
 | 
				
			||||||
		return nil, storagedriver.InvalidPathError{Path: path}
 | 
							return nil, storagedriver.InvalidPathError{Path: path}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -104,6 +120,9 @@ func (base *Base) Stat(path string) (storagedriver.FileInfo, error) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// List wraps List of underlying storage driver.
 | 
					// List wraps List of underlying storage driver.
 | 
				
			||||||
func (base *Base) List(path string) ([]string, error) {
 | 
					func (base *Base) List(path string) ([]string, error) {
 | 
				
			||||||
 | 
						_, done := context.WithTrace(context.Background())
 | 
				
			||||||
 | 
						defer done("Base.List")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !storagedriver.PathRegexp.MatchString(path) && path != "/" {
 | 
						if !storagedriver.PathRegexp.MatchString(path) && path != "/" {
 | 
				
			||||||
		return nil, storagedriver.InvalidPathError{Path: path}
 | 
							return nil, storagedriver.InvalidPathError{Path: path}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -113,6 +132,9 @@ func (base *Base) List(path string) ([]string, error) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Move wraps Move of underlying storage driver.
 | 
					// Move wraps Move of underlying storage driver.
 | 
				
			||||||
func (base *Base) Move(sourcePath string, destPath string) error {
 | 
					func (base *Base) Move(sourcePath string, destPath string) error {
 | 
				
			||||||
 | 
						_, done := context.WithTrace(context.Background())
 | 
				
			||||||
 | 
						defer done("Base.Move")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !storagedriver.PathRegexp.MatchString(sourcePath) {
 | 
						if !storagedriver.PathRegexp.MatchString(sourcePath) {
 | 
				
			||||||
		return storagedriver.InvalidPathError{Path: sourcePath}
 | 
							return storagedriver.InvalidPathError{Path: sourcePath}
 | 
				
			||||||
	} else if !storagedriver.PathRegexp.MatchString(destPath) {
 | 
						} else if !storagedriver.PathRegexp.MatchString(destPath) {
 | 
				
			||||||
| 
						 | 
					@ -124,6 +146,9 @@ func (base *Base) Move(sourcePath string, destPath string) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Delete wraps Delete of underlying storage driver.
 | 
					// Delete wraps Delete of underlying storage driver.
 | 
				
			||||||
func (base *Base) Delete(path string) error {
 | 
					func (base *Base) Delete(path string) error {
 | 
				
			||||||
 | 
						_, done := context.WithTrace(context.Background())
 | 
				
			||||||
 | 
						defer done("Base.Move")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !storagedriver.PathRegexp.MatchString(path) {
 | 
						if !storagedriver.PathRegexp.MatchString(path) {
 | 
				
			||||||
		return storagedriver.InvalidPathError{Path: path}
 | 
							return storagedriver.InvalidPathError{Path: path}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -133,6 +158,9 @@ func (base *Base) Delete(path string) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// URLFor wraps URLFor of underlying storage driver.
 | 
					// URLFor wraps URLFor of underlying storage driver.
 | 
				
			||||||
func (base *Base) URLFor(path string, options map[string]interface{}) (string, error) {
 | 
					func (base *Base) URLFor(path string, options map[string]interface{}) (string, error) {
 | 
				
			||||||
 | 
						_, done := context.WithTrace(context.Background())
 | 
				
			||||||
 | 
						defer done("Base.URLFor")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !storagedriver.PathRegexp.MatchString(path) {
 | 
						if !storagedriver.PathRegexp.MatchString(path) {
 | 
				
			||||||
		return "", storagedriver.InvalidPathError{Path: path}
 | 
							return "", storagedriver.InvalidPathError{Path: path}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue