vendored distribution is quite old, and current distribution contains an
API break, which means it's not possible to vendor a bugfixed
distribution and a docker/docker at the same time.
Signed-off-by: Mike Lundy <mike@fluffypenguin.org>
| ... | ... |
@@ -3,10 +3,10 @@ |
| 3 | 3 |
package distribution // import "github.com/docker/docker/distribution" |
| 4 | 4 |
|
| 5 | 5 |
import ( |
| 6 |
+ "context" |
|
| 6 | 7 |
"runtime" |
| 7 | 8 |
|
| 8 | 9 |
"github.com/docker/distribution" |
| 9 |
- "github.com/docker/distribution/context" |
|
| 10 | 10 |
"github.com/docker/distribution/manifest/manifestlist" |
| 11 | 11 |
"github.com/sirupsen/logrus" |
| 12 | 12 |
) |
| ... | ... |
@@ -1,6 +1,7 @@ |
| 1 | 1 |
package distribution // import "github.com/docker/docker/distribution" |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "context" |
|
| 4 | 5 |
"errors" |
| 5 | 6 |
"fmt" |
| 6 | 7 |
"net/http" |
| ... | ... |
@@ -11,7 +12,6 @@ import ( |
| 11 | 11 |
"strings" |
| 12 | 12 |
|
| 13 | 13 |
"github.com/docker/distribution" |
| 14 |
- "github.com/docker/distribution/context" |
|
| 15 | 14 |
"github.com/docker/distribution/manifest/manifestlist" |
| 16 | 15 |
"github.com/docker/distribution/manifest/schema2" |
| 17 | 16 |
"github.com/docker/distribution/registry/client/transport" |
| ... | ... |
@@ -1,13 +1,13 @@ |
| 1 | 1 |
package distribution // import "github.com/docker/docker/distribution" |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "context" |
|
| 4 | 5 |
"net/http" |
| 5 | 6 |
"net/url" |
| 6 | 7 |
"reflect" |
| 7 | 8 |
"testing" |
| 8 | 9 |
|
| 9 | 10 |
"github.com/docker/distribution" |
| 10 |
- "github.com/docker/distribution/context" |
|
| 11 | 11 |
"github.com/docker/distribution/manifest/schema2" |
| 12 | 12 |
"github.com/docker/distribution/reference" |
| 13 | 13 |
"github.com/docker/distribution/registry/api/errcode" |
| ... | ... |
@@ -131,7 +131,7 @@ func NewV2Repository(ctx context.Context, repoInfo *registry.RepositoryInfo, end |
| 131 | 131 |
} |
| 132 | 132 |
} |
| 133 | 133 |
|
| 134 |
- repo, err = client.NewRepository(ctx, repoNameRef, endpoint.URL.String(), tr) |
|
| 134 |
+ repo, err = client.NewRepository(repoNameRef, endpoint.URL.String(), tr) |
|
| 135 | 135 |
if err != nil {
|
| 136 | 136 |
err = fallbackError{
|
| 137 | 137 |
err: err, |
| ... | ... |
@@ -59,7 +59,7 @@ github.com/miekg/dns 75e6e86cc601825c5dbcd4e0c209eab180997cd7 |
| 59 | 59 |
github.com/ishidawataru/sctp 07191f837fedd2f13d1ec7b5f885f0f3ec54b1cb |
| 60 | 60 |
|
| 61 | 61 |
# get graph and distribution packages |
| 62 |
-github.com/docker/distribution edc3ab29cdff8694dd6feb85cfeb4b5f1b38ed9c |
|
| 62 |
+github.com/docker/distribution 83389a148052d74ac602f5f1d62f86ff2f3c4aa5 |
|
| 63 | 63 |
github.com/vbatts/tar-split v0.10.2 |
| 64 | 64 |
github.com/opencontainers/go-digest v1.0.0-rc1 |
| 65 | 65 |
|
| 14 | 14 |
deleted file mode 100644 |
| ... | ... |
@@ -1,85 +0,0 @@ |
| 1 |
-package context |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "sync" |
|
| 5 |
- |
|
| 6 |
- "github.com/docker/distribution/uuid" |
|
| 7 |
- "golang.org/x/net/context" |
|
| 8 |
-) |
|
| 9 |
- |
|
| 10 |
-// Context is a copy of Context from the golang.org/x/net/context package. |
|
| 11 |
-type Context interface {
|
|
| 12 |
- context.Context |
|
| 13 |
-} |
|
| 14 |
- |
|
| 15 |
-// instanceContext is a context that provides only an instance id. It is |
|
| 16 |
-// provided as the main background context. |
|
| 17 |
-type instanceContext struct {
|
|
| 18 |
- Context |
|
| 19 |
- id string // id of context, logged as "instance.id" |
|
| 20 |
- once sync.Once // once protect generation of the id |
|
| 21 |
-} |
|
| 22 |
- |
|
| 23 |
-func (ic *instanceContext) Value(key interface{}) interface{} {
|
|
| 24 |
- if key == "instance.id" {
|
|
| 25 |
- ic.once.Do(func() {
|
|
| 26 |
- // We want to lazy initialize the UUID such that we don't |
|
| 27 |
- // call a random generator from the package initialization |
|
| 28 |
- // code. For various reasons random could not be available |
|
| 29 |
- // https://github.com/docker/distribution/issues/782 |
|
| 30 |
- ic.id = uuid.Generate().String() |
|
| 31 |
- }) |
|
| 32 |
- return ic.id |
|
| 33 |
- } |
|
| 34 |
- |
|
| 35 |
- return ic.Context.Value(key) |
|
| 36 |
-} |
|
| 37 |
- |
|
| 38 |
-var background = &instanceContext{
|
|
| 39 |
- Context: context.Background(), |
|
| 40 |
-} |
|
| 41 |
- |
|
| 42 |
-// Background returns a non-nil, empty Context. The background context |
|
| 43 |
-// provides a single key, "instance.id" that is globally unique to the |
|
| 44 |
-// process. |
|
| 45 |
-func Background() Context {
|
|
| 46 |
- return background |
|
| 47 |
-} |
|
| 48 |
- |
|
| 49 |
-// WithValue returns a copy of parent in which the value associated with key is |
|
| 50 |
-// val. Use context Values only for request-scoped data that transits processes |
|
| 51 |
-// and APIs, not for passing optional parameters to functions. |
|
| 52 |
-func WithValue(parent Context, key, val interface{}) Context {
|
|
| 53 |
- return context.WithValue(parent, key, val) |
|
| 54 |
-} |
|
| 55 |
- |
|
| 56 |
-// stringMapContext is a simple context implementation that checks a map for a |
|
| 57 |
-// key, falling back to a parent if not present. |
|
| 58 |
-type stringMapContext struct {
|
|
| 59 |
- context.Context |
|
| 60 |
- m map[string]interface{}
|
|
| 61 |
-} |
|
| 62 |
- |
|
| 63 |
-// WithValues returns a context that proxies lookups through a map. Only |
|
| 64 |
-// supports string keys. |
|
| 65 |
-func WithValues(ctx context.Context, m map[string]interface{}) context.Context {
|
|
| 66 |
- mo := make(map[string]interface{}, len(m)) // make our own copy.
|
|
| 67 |
- for k, v := range m {
|
|
| 68 |
- mo[k] = v |
|
| 69 |
- } |
|
| 70 |
- |
|
| 71 |
- return stringMapContext{
|
|
| 72 |
- Context: ctx, |
|
| 73 |
- m: mo, |
|
| 74 |
- } |
|
| 75 |
-} |
|
| 76 |
- |
|
| 77 |
-func (smc stringMapContext) Value(key interface{}) interface{} {
|
|
| 78 |
- if ks, ok := key.(string); ok {
|
|
| 79 |
- if v, ok := smc.m[ks]; ok {
|
|
| 80 |
- return v |
|
| 81 |
- } |
|
| 82 |
- } |
|
| 83 |
- |
|
| 84 |
- return smc.Context.Value(key) |
|
| 85 |
-} |
| 86 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,89 +0,0 @@ |
| 1 |
-// Package context provides several utilities for working with |
|
| 2 |
-// golang.org/x/net/context in http requests. Primarily, the focus is on |
|
| 3 |
-// logging relevant request information but this package is not limited to |
|
| 4 |
-// that purpose. |
|
| 5 |
-// |
|
| 6 |
-// The easiest way to get started is to get the background context: |
|
| 7 |
-// |
|
| 8 |
-// ctx := context.Background() |
|
| 9 |
-// |
|
| 10 |
-// The returned context should be passed around your application and be the |
|
| 11 |
-// root of all other context instances. If the application has a version, this |
|
| 12 |
-// line should be called before anything else: |
|
| 13 |
-// |
|
| 14 |
-// ctx := context.WithVersion(context.Background(), version) |
|
| 15 |
-// |
|
| 16 |
-// The above will store the version in the context and will be available to |
|
| 17 |
-// the logger. |
|
| 18 |
-// |
|
| 19 |
-// Logging |
|
| 20 |
-// |
|
| 21 |
-// The most useful aspect of this package is GetLogger. This function takes |
|
| 22 |
-// any context.Context interface and returns the current logger from the |
|
| 23 |
-// context. Canonical usage looks like this: |
|
| 24 |
-// |
|
| 25 |
-// GetLogger(ctx).Infof("something interesting happened")
|
|
| 26 |
-// |
|
| 27 |
-// GetLogger also takes optional key arguments. The keys will be looked up in |
|
| 28 |
-// the context and reported with the logger. The following example would |
|
| 29 |
-// return a logger that prints the version with each log message: |
|
| 30 |
-// |
|
| 31 |
-// ctx := context.Context(context.Background(), "version", version) |
|
| 32 |
-// GetLogger(ctx, "version").Infof("this log message has a version field")
|
|
| 33 |
-// |
|
| 34 |
-// The above would print out a log message like this: |
|
| 35 |
-// |
|
| 36 |
-// INFO[0000] this log message has a version field version=v2.0.0-alpha.2.m |
|
| 37 |
-// |
|
| 38 |
-// When used with WithLogger, we gain the ability to decorate the context with |
|
| 39 |
-// loggers that have information from disparate parts of the call stack. |
|
| 40 |
-// Following from the version example, we can build a new context with the |
|
| 41 |
-// configured logger such that we always print the version field: |
|
| 42 |
-// |
|
| 43 |
-// ctx = WithLogger(ctx, GetLogger(ctx, "version")) |
|
| 44 |
-// |
|
| 45 |
-// Since the logger has been pushed to the context, we can now get the version |
|
| 46 |
-// field for free with our log messages. Future calls to GetLogger on the new |
|
| 47 |
-// context will have the version field: |
|
| 48 |
-// |
|
| 49 |
-// GetLogger(ctx).Infof("this log message has a version field")
|
|
| 50 |
-// |
|
| 51 |
-// This becomes more powerful when we start stacking loggers. Let's say we |
|
| 52 |
-// have the version logger from above but also want a request id. Using the |
|
| 53 |
-// context above, in our request scoped function, we place another logger in |
|
| 54 |
-// the context: |
|
| 55 |
-// |
|
| 56 |
-// ctx = context.WithValue(ctx, "http.request.id", "unique id") // called when building request context |
|
| 57 |
-// ctx = WithLogger(ctx, GetLogger(ctx, "http.request.id")) |
|
| 58 |
-// |
|
| 59 |
-// When GetLogger is called on the new context, "http.request.id" will be |
|
| 60 |
-// included as a logger field, along with the original "version" field: |
|
| 61 |
-// |
|
| 62 |
-// INFO[0000] this log message has a version field http.request.id=unique id version=v2.0.0-alpha.2.m |
|
| 63 |
-// |
|
| 64 |
-// Note that this only affects the new context, the previous context, with the |
|
| 65 |
-// version field, can be used independently. Put another way, the new logger, |
|
| 66 |
-// added to the request context, is unique to that context and can have |
|
| 67 |
-// request scoped variables. |
|
| 68 |
-// |
|
| 69 |
-// HTTP Requests |
|
| 70 |
-// |
|
| 71 |
-// This package also contains several methods for working with http requests. |
|
| 72 |
-// The concepts are very similar to those described above. We simply place the |
|
| 73 |
-// request in the context using WithRequest. This makes the request variables |
|
| 74 |
-// available. GetRequestLogger can then be called to get request specific |
|
| 75 |
-// variables in a log line: |
|
| 76 |
-// |
|
| 77 |
-// ctx = WithRequest(ctx, req) |
|
| 78 |
-// GetRequestLogger(ctx).Infof("request variables")
|
|
| 79 |
-// |
|
| 80 |
-// Like above, if we want to include the request data in all log messages in |
|
| 81 |
-// the context, we push the logger to a new context and use that one: |
|
| 82 |
-// |
|
| 83 |
-// ctx = WithLogger(ctx, GetRequestLogger(ctx)) |
|
| 84 |
-// |
|
| 85 |
-// The concept is fairly powerful and ensures that calls throughout the stack |
|
| 86 |
-// can be traced in log messages. Using the fields like "http.request.id", one |
|
| 87 |
-// can analyze call flow for a particular request with a simple grep of the |
|
| 88 |
-// logs. |
|
| 89 |
-package context |
| 90 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,366 +0,0 @@ |
| 1 |
-package context |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "errors" |
|
| 5 |
- "net" |
|
| 6 |
- "net/http" |
|
| 7 |
- "strings" |
|
| 8 |
- "sync" |
|
| 9 |
- "time" |
|
| 10 |
- |
|
| 11 |
- "github.com/docker/distribution/uuid" |
|
| 12 |
- "github.com/gorilla/mux" |
|
| 13 |
- log "github.com/sirupsen/logrus" |
|
| 14 |
-) |
|
| 15 |
- |
|
| 16 |
-// Common errors used with this package. |
|
| 17 |
-var ( |
|
| 18 |
- ErrNoRequestContext = errors.New("no http request in context")
|
|
| 19 |
- ErrNoResponseWriterContext = errors.New("no http response in context")
|
|
| 20 |
-) |
|
| 21 |
- |
|
| 22 |
-func parseIP(ipStr string) net.IP {
|
|
| 23 |
- ip := net.ParseIP(ipStr) |
|
| 24 |
- if ip == nil {
|
|
| 25 |
- log.Warnf("invalid remote IP address: %q", ipStr)
|
|
| 26 |
- } |
|
| 27 |
- return ip |
|
| 28 |
-} |
|
| 29 |
- |
|
| 30 |
-// RemoteAddr extracts the remote address of the request, taking into |
|
| 31 |
-// account proxy headers. |
|
| 32 |
-func RemoteAddr(r *http.Request) string {
|
|
| 33 |
- if prior := r.Header.Get("X-Forwarded-For"); prior != "" {
|
|
| 34 |
- proxies := strings.Split(prior, ",") |
|
| 35 |
- if len(proxies) > 0 {
|
|
| 36 |
- remoteAddr := strings.Trim(proxies[0], " ") |
|
| 37 |
- if parseIP(remoteAddr) != nil {
|
|
| 38 |
- return remoteAddr |
|
| 39 |
- } |
|
| 40 |
- } |
|
| 41 |
- } |
|
| 42 |
- // X-Real-Ip is less supported, but worth checking in the |
|
| 43 |
- // absence of X-Forwarded-For |
|
| 44 |
- if realIP := r.Header.Get("X-Real-Ip"); realIP != "" {
|
|
| 45 |
- if parseIP(realIP) != nil {
|
|
| 46 |
- return realIP |
|
| 47 |
- } |
|
| 48 |
- } |
|
| 49 |
- |
|
| 50 |
- return r.RemoteAddr |
|
| 51 |
-} |
|
| 52 |
- |
|
| 53 |
-// RemoteIP extracts the remote IP of the request, taking into |
|
| 54 |
-// account proxy headers. |
|
| 55 |
-func RemoteIP(r *http.Request) string {
|
|
| 56 |
- addr := RemoteAddr(r) |
|
| 57 |
- |
|
| 58 |
- // Try parsing it as "IP:port" |
|
| 59 |
- if ip, _, err := net.SplitHostPort(addr); err == nil {
|
|
| 60 |
- return ip |
|
| 61 |
- } |
|
| 62 |
- |
|
| 63 |
- return addr |
|
| 64 |
-} |
|
| 65 |
- |
|
| 66 |
-// WithRequest places the request on the context. The context of the request |
|
| 67 |
-// is assigned a unique id, available at "http.request.id". The request itself |
|
| 68 |
-// is available at "http.request". Other common attributes are available under |
|
| 69 |
-// the prefix "http.request.". If a request is already present on the context, |
|
| 70 |
-// this method will panic. |
|
| 71 |
-func WithRequest(ctx Context, r *http.Request) Context {
|
|
| 72 |
- if ctx.Value("http.request") != nil {
|
|
| 73 |
- // NOTE(stevvooe): This needs to be considered a programming error. It |
|
| 74 |
- // is unlikely that we'd want to have more than one request in |
|
| 75 |
- // context. |
|
| 76 |
- panic("only one request per context")
|
|
| 77 |
- } |
|
| 78 |
- |
|
| 79 |
- return &httpRequestContext{
|
|
| 80 |
- Context: ctx, |
|
| 81 |
- startedAt: time.Now(), |
|
| 82 |
- id: uuid.Generate().String(), |
|
| 83 |
- r: r, |
|
| 84 |
- } |
|
| 85 |
-} |
|
| 86 |
- |
|
| 87 |
-// GetRequest returns the http request in the given context. Returns |
|
| 88 |
-// ErrNoRequestContext if the context does not have an http request associated |
|
| 89 |
-// with it. |
|
| 90 |
-func GetRequest(ctx Context) (*http.Request, error) {
|
|
| 91 |
- if r, ok := ctx.Value("http.request").(*http.Request); r != nil && ok {
|
|
| 92 |
- return r, nil |
|
| 93 |
- } |
|
| 94 |
- return nil, ErrNoRequestContext |
|
| 95 |
-} |
|
| 96 |
- |
|
| 97 |
-// GetRequestID attempts to resolve the current request id, if possible. An |
|
| 98 |
-// error is return if it is not available on the context. |
|
| 99 |
-func GetRequestID(ctx Context) string {
|
|
| 100 |
- return GetStringValue(ctx, "http.request.id") |
|
| 101 |
-} |
|
| 102 |
- |
|
| 103 |
-// WithResponseWriter returns a new context and response writer that makes |
|
| 104 |
-// interesting response statistics available within the context. |
|
| 105 |
-func WithResponseWriter(ctx Context, w http.ResponseWriter) (Context, http.ResponseWriter) {
|
|
| 106 |
- if closeNotifier, ok := w.(http.CloseNotifier); ok {
|
|
| 107 |
- irwCN := &instrumentedResponseWriterCN{
|
|
| 108 |
- instrumentedResponseWriter: instrumentedResponseWriter{
|
|
| 109 |
- ResponseWriter: w, |
|
| 110 |
- Context: ctx, |
|
| 111 |
- }, |
|
| 112 |
- CloseNotifier: closeNotifier, |
|
| 113 |
- } |
|
| 114 |
- |
|
| 115 |
- return irwCN, irwCN |
|
| 116 |
- } |
|
| 117 |
- |
|
| 118 |
- irw := instrumentedResponseWriter{
|
|
| 119 |
- ResponseWriter: w, |
|
| 120 |
- Context: ctx, |
|
| 121 |
- } |
|
| 122 |
- return &irw, &irw |
|
| 123 |
-} |
|
| 124 |
- |
|
| 125 |
-// GetResponseWriter returns the http.ResponseWriter from the provided |
|
| 126 |
-// context. If not present, ErrNoResponseWriterContext is returned. The |
|
| 127 |
-// returned instance provides instrumentation in the context. |
|
| 128 |
-func GetResponseWriter(ctx Context) (http.ResponseWriter, error) {
|
|
| 129 |
- v := ctx.Value("http.response")
|
|
| 130 |
- |
|
| 131 |
- rw, ok := v.(http.ResponseWriter) |
|
| 132 |
- if !ok || rw == nil {
|
|
| 133 |
- return nil, ErrNoResponseWriterContext |
|
| 134 |
- } |
|
| 135 |
- |
|
| 136 |
- return rw, nil |
|
| 137 |
-} |
|
| 138 |
- |
|
| 139 |
-// getVarsFromRequest let's us change request vars implementation for testing |
|
| 140 |
-// and maybe future changes. |
|
| 141 |
-var getVarsFromRequest = mux.Vars |
|
| 142 |
- |
|
| 143 |
-// WithVars extracts gorilla/mux vars and makes them available on the returned |
|
| 144 |
-// context. Variables are available at keys with the prefix "vars.". For |
|
| 145 |
-// example, if looking for the variable "name", it can be accessed as |
|
| 146 |
-// "vars.name". Implementations that are accessing values need not know that |
|
| 147 |
-// the underlying context is implemented with gorilla/mux vars. |
|
| 148 |
-func WithVars(ctx Context, r *http.Request) Context {
|
|
| 149 |
- return &muxVarsContext{
|
|
| 150 |
- Context: ctx, |
|
| 151 |
- vars: getVarsFromRequest(r), |
|
| 152 |
- } |
|
| 153 |
-} |
|
| 154 |
- |
|
| 155 |
-// GetRequestLogger returns a logger that contains fields from the request in |
|
| 156 |
-// the current context. If the request is not available in the context, no |
|
| 157 |
-// fields will display. Request loggers can safely be pushed onto the context. |
|
| 158 |
-func GetRequestLogger(ctx Context) Logger {
|
|
| 159 |
- return GetLogger(ctx, |
|
| 160 |
- "http.request.id", |
|
| 161 |
- "http.request.method", |
|
| 162 |
- "http.request.host", |
|
| 163 |
- "http.request.uri", |
|
| 164 |
- "http.request.referer", |
|
| 165 |
- "http.request.useragent", |
|
| 166 |
- "http.request.remoteaddr", |
|
| 167 |
- "http.request.contenttype") |
|
| 168 |
-} |
|
| 169 |
- |
|
| 170 |
-// GetResponseLogger reads the current response stats and builds a logger. |
|
| 171 |
-// Because the values are read at call time, pushing a logger returned from |
|
| 172 |
-// this function on the context will lead to missing or invalid data. Only |
|
| 173 |
-// call this at the end of a request, after the response has been written. |
|
| 174 |
-func GetResponseLogger(ctx Context) Logger {
|
|
| 175 |
- l := getLogrusLogger(ctx, |
|
| 176 |
- "http.response.written", |
|
| 177 |
- "http.response.status", |
|
| 178 |
- "http.response.contenttype") |
|
| 179 |
- |
|
| 180 |
- duration := Since(ctx, "http.request.startedat") |
|
| 181 |
- |
|
| 182 |
- if duration > 0 {
|
|
| 183 |
- l = l.WithField("http.response.duration", duration.String())
|
|
| 184 |
- } |
|
| 185 |
- |
|
| 186 |
- return l |
|
| 187 |
-} |
|
| 188 |
- |
|
| 189 |
-// httpRequestContext makes information about a request available to context. |
|
| 190 |
-type httpRequestContext struct {
|
|
| 191 |
- Context |
|
| 192 |
- |
|
| 193 |
- startedAt time.Time |
|
| 194 |
- id string |
|
| 195 |
- r *http.Request |
|
| 196 |
-} |
|
| 197 |
- |
|
| 198 |
-// Value returns a keyed element of the request for use in the context. To get |
|
| 199 |
-// the request itself, query "request". For other components, access them as |
|
| 200 |
-// "request.<component>". For example, r.RequestURI |
|
| 201 |
-func (ctx *httpRequestContext) Value(key interface{}) interface{} {
|
|
| 202 |
- if keyStr, ok := key.(string); ok {
|
|
| 203 |
- if keyStr == "http.request" {
|
|
| 204 |
- return ctx.r |
|
| 205 |
- } |
|
| 206 |
- |
|
| 207 |
- if !strings.HasPrefix(keyStr, "http.request.") {
|
|
| 208 |
- goto fallback |
|
| 209 |
- } |
|
| 210 |
- |
|
| 211 |
- parts := strings.Split(keyStr, ".") |
|
| 212 |
- |
|
| 213 |
- if len(parts) != 3 {
|
|
| 214 |
- goto fallback |
|
| 215 |
- } |
|
| 216 |
- |
|
| 217 |
- switch parts[2] {
|
|
| 218 |
- case "uri": |
|
| 219 |
- return ctx.r.RequestURI |
|
| 220 |
- case "remoteaddr": |
|
| 221 |
- return RemoteAddr(ctx.r) |
|
| 222 |
- case "method": |
|
| 223 |
- return ctx.r.Method |
|
| 224 |
- case "host": |
|
| 225 |
- return ctx.r.Host |
|
| 226 |
- case "referer": |
|
| 227 |
- referer := ctx.r.Referer() |
|
| 228 |
- if referer != "" {
|
|
| 229 |
- return referer |
|
| 230 |
- } |
|
| 231 |
- case "useragent": |
|
| 232 |
- return ctx.r.UserAgent() |
|
| 233 |
- case "id": |
|
| 234 |
- return ctx.id |
|
| 235 |
- case "startedat": |
|
| 236 |
- return ctx.startedAt |
|
| 237 |
- case "contenttype": |
|
| 238 |
- ct := ctx.r.Header.Get("Content-Type")
|
|
| 239 |
- if ct != "" {
|
|
| 240 |
- return ct |
|
| 241 |
- } |
|
| 242 |
- } |
|
| 243 |
- } |
|
| 244 |
- |
|
| 245 |
-fallback: |
|
| 246 |
- return ctx.Context.Value(key) |
|
| 247 |
-} |
|
| 248 |
- |
|
| 249 |
-type muxVarsContext struct {
|
|
| 250 |
- Context |
|
| 251 |
- vars map[string]string |
|
| 252 |
-} |
|
| 253 |
- |
|
| 254 |
-func (ctx *muxVarsContext) Value(key interface{}) interface{} {
|
|
| 255 |
- if keyStr, ok := key.(string); ok {
|
|
| 256 |
- if keyStr == "vars" {
|
|
| 257 |
- return ctx.vars |
|
| 258 |
- } |
|
| 259 |
- |
|
| 260 |
- if strings.HasPrefix(keyStr, "vars.") {
|
|
| 261 |
- keyStr = strings.TrimPrefix(keyStr, "vars.") |
|
| 262 |
- } |
|
| 263 |
- |
|
| 264 |
- if v, ok := ctx.vars[keyStr]; ok {
|
|
| 265 |
- return v |
|
| 266 |
- } |
|
| 267 |
- } |
|
| 268 |
- |
|
| 269 |
- return ctx.Context.Value(key) |
|
| 270 |
-} |
|
| 271 |
- |
|
| 272 |
-// instrumentedResponseWriterCN provides response writer information in a |
|
| 273 |
-// context. It implements http.CloseNotifier so that users can detect |
|
| 274 |
-// early disconnects. |
|
| 275 |
-type instrumentedResponseWriterCN struct {
|
|
| 276 |
- instrumentedResponseWriter |
|
| 277 |
- http.CloseNotifier |
|
| 278 |
-} |
|
| 279 |
- |
|
| 280 |
-// instrumentedResponseWriter provides response writer information in a |
|
| 281 |
-// context. This variant is only used in the case where CloseNotifier is not |
|
| 282 |
-// implemented by the parent ResponseWriter. |
|
| 283 |
-type instrumentedResponseWriter struct {
|
|
| 284 |
- http.ResponseWriter |
|
| 285 |
- Context |
|
| 286 |
- |
|
| 287 |
- mu sync.Mutex |
|
| 288 |
- status int |
|
| 289 |
- written int64 |
|
| 290 |
-} |
|
| 291 |
- |
|
| 292 |
-func (irw *instrumentedResponseWriter) Write(p []byte) (n int, err error) {
|
|
| 293 |
- n, err = irw.ResponseWriter.Write(p) |
|
| 294 |
- |
|
| 295 |
- irw.mu.Lock() |
|
| 296 |
- irw.written += int64(n) |
|
| 297 |
- |
|
| 298 |
- // Guess the likely status if not set. |
|
| 299 |
- if irw.status == 0 {
|
|
| 300 |
- irw.status = http.StatusOK |
|
| 301 |
- } |
|
| 302 |
- |
|
| 303 |
- irw.mu.Unlock() |
|
| 304 |
- |
|
| 305 |
- return |
|
| 306 |
-} |
|
| 307 |
- |
|
| 308 |
-func (irw *instrumentedResponseWriter) WriteHeader(status int) {
|
|
| 309 |
- irw.ResponseWriter.WriteHeader(status) |
|
| 310 |
- |
|
| 311 |
- irw.mu.Lock() |
|
| 312 |
- irw.status = status |
|
| 313 |
- irw.mu.Unlock() |
|
| 314 |
-} |
|
| 315 |
- |
|
| 316 |
-func (irw *instrumentedResponseWriter) Flush() {
|
|
| 317 |
- if flusher, ok := irw.ResponseWriter.(http.Flusher); ok {
|
|
| 318 |
- flusher.Flush() |
|
| 319 |
- } |
|
| 320 |
-} |
|
| 321 |
- |
|
| 322 |
-func (irw *instrumentedResponseWriter) Value(key interface{}) interface{} {
|
|
| 323 |
- if keyStr, ok := key.(string); ok {
|
|
| 324 |
- if keyStr == "http.response" {
|
|
| 325 |
- return irw |
|
| 326 |
- } |
|
| 327 |
- |
|
| 328 |
- if !strings.HasPrefix(keyStr, "http.response.") {
|
|
| 329 |
- goto fallback |
|
| 330 |
- } |
|
| 331 |
- |
|
| 332 |
- parts := strings.Split(keyStr, ".") |
|
| 333 |
- |
|
| 334 |
- if len(parts) != 3 {
|
|
| 335 |
- goto fallback |
|
| 336 |
- } |
|
| 337 |
- |
|
| 338 |
- irw.mu.Lock() |
|
| 339 |
- defer irw.mu.Unlock() |
|
| 340 |
- |
|
| 341 |
- switch parts[2] {
|
|
| 342 |
- case "written": |
|
| 343 |
- return irw.written |
|
| 344 |
- case "status": |
|
| 345 |
- return irw.status |
|
| 346 |
- case "contenttype": |
|
| 347 |
- contentType := irw.Header().Get("Content-Type")
|
|
| 348 |
- if contentType != "" {
|
|
| 349 |
- return contentType |
|
| 350 |
- } |
|
| 351 |
- } |
|
| 352 |
- } |
|
| 353 |
- |
|
| 354 |
-fallback: |
|
| 355 |
- return irw.Context.Value(key) |
|
| 356 |
-} |
|
| 357 |
- |
|
| 358 |
-func (irw *instrumentedResponseWriterCN) Value(key interface{}) interface{} {
|
|
| 359 |
- if keyStr, ok := key.(string); ok {
|
|
| 360 |
- if keyStr == "http.response" {
|
|
| 361 |
- return irw |
|
| 362 |
- } |
|
| 363 |
- } |
|
| 364 |
- |
|
| 365 |
- return irw.instrumentedResponseWriter.Value(key) |
|
| 366 |
-} |
| 367 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,116 +0,0 @@ |
| 1 |
-package context |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "fmt" |
|
| 5 |
- |
|
| 6 |
- "github.com/sirupsen/logrus" |
|
| 7 |
- "runtime" |
|
| 8 |
-) |
|
| 9 |
- |
|
| 10 |
-// Logger provides a leveled-logging interface. |
|
| 11 |
-type Logger interface {
|
|
| 12 |
- // standard logger methods |
|
| 13 |
- Print(args ...interface{})
|
|
| 14 |
- Printf(format string, args ...interface{})
|
|
| 15 |
- Println(args ...interface{})
|
|
| 16 |
- |
|
| 17 |
- Fatal(args ...interface{})
|
|
| 18 |
- Fatalf(format string, args ...interface{})
|
|
| 19 |
- Fatalln(args ...interface{})
|
|
| 20 |
- |
|
| 21 |
- Panic(args ...interface{})
|
|
| 22 |
- Panicf(format string, args ...interface{})
|
|
| 23 |
- Panicln(args ...interface{})
|
|
| 24 |
- |
|
| 25 |
- // Leveled methods, from logrus |
|
| 26 |
- Debug(args ...interface{})
|
|
| 27 |
- Debugf(format string, args ...interface{})
|
|
| 28 |
- Debugln(args ...interface{})
|
|
| 29 |
- |
|
| 30 |
- Error(args ...interface{})
|
|
| 31 |
- Errorf(format string, args ...interface{})
|
|
| 32 |
- Errorln(args ...interface{})
|
|
| 33 |
- |
|
| 34 |
- Info(args ...interface{})
|
|
| 35 |
- Infof(format string, args ...interface{})
|
|
| 36 |
- Infoln(args ...interface{})
|
|
| 37 |
- |
|
| 38 |
- Warn(args ...interface{})
|
|
| 39 |
- Warnf(format string, args ...interface{})
|
|
| 40 |
- Warnln(args ...interface{})
|
|
| 41 |
-} |
|
| 42 |
- |
|
| 43 |
-// WithLogger creates a new context with provided logger. |
|
| 44 |
-func WithLogger(ctx Context, logger Logger) Context {
|
|
| 45 |
- return WithValue(ctx, "logger", logger) |
|
| 46 |
-} |
|
| 47 |
- |
|
| 48 |
-// GetLoggerWithField returns a logger instance with the specified field key |
|
| 49 |
-// and value without affecting the context. Extra specified keys will be |
|
| 50 |
-// resolved from the context. |
|
| 51 |
-func GetLoggerWithField(ctx Context, key, value interface{}, keys ...interface{}) Logger {
|
|
| 52 |
- return getLogrusLogger(ctx, keys...).WithField(fmt.Sprint(key), value) |
|
| 53 |
-} |
|
| 54 |
- |
|
| 55 |
-// GetLoggerWithFields returns a logger instance with the specified fields |
|
| 56 |
-// without affecting the context. Extra specified keys will be resolved from |
|
| 57 |
-// the context. |
|
| 58 |
-func GetLoggerWithFields(ctx Context, fields map[interface{}]interface{}, keys ...interface{}) Logger {
|
|
| 59 |
- // must convert from interface{} -> interface{} to string -> interface{} for logrus.
|
|
| 60 |
- lfields := make(logrus.Fields, len(fields)) |
|
| 61 |
- for key, value := range fields {
|
|
| 62 |
- lfields[fmt.Sprint(key)] = value |
|
| 63 |
- } |
|
| 64 |
- |
|
| 65 |
- return getLogrusLogger(ctx, keys...).WithFields(lfields) |
|
| 66 |
-} |
|
| 67 |
- |
|
| 68 |
-// GetLogger returns the logger from the current context, if present. If one |
|
| 69 |
-// or more keys are provided, they will be resolved on the context and |
|
| 70 |
-// included in the logger. While context.Value takes an interface, any key |
|
| 71 |
-// argument passed to GetLogger will be passed to fmt.Sprint when expanded as |
|
| 72 |
-// a logging key field. If context keys are integer constants, for example, |
|
| 73 |
-// its recommended that a String method is implemented. |
|
| 74 |
-func GetLogger(ctx Context, keys ...interface{}) Logger {
|
|
| 75 |
- return getLogrusLogger(ctx, keys...) |
|
| 76 |
-} |
|
| 77 |
- |
|
| 78 |
-// GetLogrusLogger returns the logrus logger for the context. If one more keys |
|
| 79 |
-// are provided, they will be resolved on the context and included in the |
|
| 80 |
-// logger. Only use this function if specific logrus functionality is |
|
| 81 |
-// required. |
|
| 82 |
-func getLogrusLogger(ctx Context, keys ...interface{}) *logrus.Entry {
|
|
| 83 |
- var logger *logrus.Entry |
|
| 84 |
- |
|
| 85 |
- // Get a logger, if it is present. |
|
| 86 |
- loggerInterface := ctx.Value("logger")
|
|
| 87 |
- if loggerInterface != nil {
|
|
| 88 |
- if lgr, ok := loggerInterface.(*logrus.Entry); ok {
|
|
| 89 |
- logger = lgr |
|
| 90 |
- } |
|
| 91 |
- } |
|
| 92 |
- |
|
| 93 |
- if logger == nil {
|
|
| 94 |
- fields := logrus.Fields{}
|
|
| 95 |
- |
|
| 96 |
- // Fill in the instance id, if we have it. |
|
| 97 |
- instanceID := ctx.Value("instance.id")
|
|
| 98 |
- if instanceID != nil {
|
|
| 99 |
- fields["instance.id"] = instanceID |
|
| 100 |
- } |
|
| 101 |
- |
|
| 102 |
- fields["go.version"] = runtime.Version() |
|
| 103 |
- // If no logger is found, just return the standard logger. |
|
| 104 |
- logger = logrus.StandardLogger().WithFields(fields) |
|
| 105 |
- } |
|
| 106 |
- |
|
| 107 |
- fields := logrus.Fields{}
|
|
| 108 |
- for _, key := range keys {
|
|
| 109 |
- v := ctx.Value(key) |
|
| 110 |
- if v != nil {
|
|
| 111 |
- fields[fmt.Sprint(key)] = v |
|
| 112 |
- } |
|
| 113 |
- } |
|
| 114 |
- |
|
| 115 |
- return logger.WithFields(fields) |
|
| 116 |
-} |
| 117 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,104 +0,0 @@ |
| 1 |
-package context |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "runtime" |
|
| 5 |
- "time" |
|
| 6 |
- |
|
| 7 |
- "github.com/docker/distribution/uuid" |
|
| 8 |
-) |
|
| 9 |
- |
|
| 10 |
-// WithTrace allocates a traced timing span in a new context. This allows a |
|
| 11 |
-// caller to track the time between calling WithTrace and the returned done |
|
| 12 |
-// function. When the done function is called, a log message is emitted with a |
|
| 13 |
-// "trace.duration" field, corresponding to the elapsed time and a |
|
| 14 |
-// "trace.func" field, corresponding to the function that called WithTrace. |
|
| 15 |
-// |
|
| 16 |
-// The logging keys "trace.id" and "trace.parent.id" are provided to implement |
|
| 17 |
-// dapper-like tracing. This function should be complemented with a WithSpan |
|
| 18 |
-// method that could be used for tracing distributed RPC calls. |
|
| 19 |
-// |
|
| 20 |
-// The main benefit of this function is to post-process log messages or |
|
| 21 |
-// intercept them in a hook to provide timing data. Trace ids and parent ids |
|
| 22 |
-// can also be linked to provide call tracing, if so required. |
|
| 23 |
-// |
|
| 24 |
-// Here is an example of the usage: |
|
| 25 |
-// |
|
| 26 |
-// func timedOperation(ctx Context) {
|
|
| 27 |
-// ctx, done := WithTrace(ctx) |
|
| 28 |
-// defer done("this will be the log message")
|
|
| 29 |
-// // ... function body ... |
|
| 30 |
-// } |
|
| 31 |
-// |
|
| 32 |
-// If the function ran for roughly 1s, such a usage would emit a log message |
|
| 33 |
-// as follows: |
|
| 34 |
-// |
|
| 35 |
-// INFO[0001] this will be the log message trace.duration=1.004575763s trace.func=github.com/docker/distribution/context.traceOperation trace.id=<id> ... |
|
| 36 |
-// |
|
| 37 |
-// Notice that the function name is automatically resolved, along with the |
|
| 38 |
-// package and a trace id is emitted that can be linked with parent ids. |
|
| 39 |
-func WithTrace(ctx Context) (Context, func(format string, a ...interface{})) {
|
|
| 40 |
- if ctx == nil {
|
|
| 41 |
- ctx = Background() |
|
| 42 |
- } |
|
| 43 |
- |
|
| 44 |
- pc, file, line, _ := runtime.Caller(1) |
|
| 45 |
- f := runtime.FuncForPC(pc) |
|
| 46 |
- ctx = &traced{
|
|
| 47 |
- Context: ctx, |
|
| 48 |
- id: uuid.Generate().String(), |
|
| 49 |
- start: time.Now(), |
|
| 50 |
- parent: GetStringValue(ctx, "trace.id"), |
|
| 51 |
- fnname: f.Name(), |
|
| 52 |
- file: file, |
|
| 53 |
- line: line, |
|
| 54 |
- } |
|
| 55 |
- |
|
| 56 |
- return ctx, func(format string, a ...interface{}) {
|
|
| 57 |
- GetLogger(ctx, |
|
| 58 |
- "trace.duration", |
|
| 59 |
- "trace.id", |
|
| 60 |
- "trace.parent.id", |
|
| 61 |
- "trace.func", |
|
| 62 |
- "trace.file", |
|
| 63 |
- "trace.line"). |
|
| 64 |
- Debugf(format, a...) |
|
| 65 |
- } |
|
| 66 |
-} |
|
| 67 |
- |
|
| 68 |
-// traced represents a context that is traced for function call timing. It |
|
| 69 |
-// also provides fast lookup for the various attributes that are available on |
|
| 70 |
-// the trace. |
|
| 71 |
-type traced struct {
|
|
| 72 |
- Context |
|
| 73 |
- id string |
|
| 74 |
- parent string |
|
| 75 |
- start time.Time |
|
| 76 |
- fnname string |
|
| 77 |
- file string |
|
| 78 |
- line int |
|
| 79 |
-} |
|
| 80 |
- |
|
| 81 |
-func (ts *traced) Value(key interface{}) interface{} {
|
|
| 82 |
- switch key {
|
|
| 83 |
- case "trace.start": |
|
| 84 |
- return ts.start |
|
| 85 |
- case "trace.duration": |
|
| 86 |
- return time.Since(ts.start) |
|
| 87 |
- case "trace.id": |
|
| 88 |
- return ts.id |
|
| 89 |
- case "trace.parent.id": |
|
| 90 |
- if ts.parent == "" {
|
|
| 91 |
- return nil // must return nil to signal no parent. |
|
| 92 |
- } |
|
| 93 |
- |
|
| 94 |
- return ts.parent |
|
| 95 |
- case "trace.func": |
|
| 96 |
- return ts.fnname |
|
| 97 |
- case "trace.file": |
|
| 98 |
- return ts.file |
|
| 99 |
- case "trace.line": |
|
| 100 |
- return ts.line |
|
| 101 |
- } |
|
| 102 |
- |
|
| 103 |
- return ts.Context.Value(key) |
|
| 104 |
-} |
| 105 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,24 +0,0 @@ |
| 1 |
-package context |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "time" |
|
| 5 |
-) |
|
| 6 |
- |
|
| 7 |
-// Since looks up key, which should be a time.Time, and returns the duration |
|
| 8 |
-// since that time. If the key is not found, the value returned will be zero. |
|
| 9 |
-// This is helpful when inferring metrics related to context execution times. |
|
| 10 |
-func Since(ctx Context, key interface{}) time.Duration {
|
|
| 11 |
- if startedAt, ok := ctx.Value(key).(time.Time); ok {
|
|
| 12 |
- return time.Since(startedAt) |
|
| 13 |
- } |
|
| 14 |
- return 0 |
|
| 15 |
-} |
|
| 16 |
- |
|
| 17 |
-// GetStringValue returns a string value from the context. The empty string |
|
| 18 |
-// will be returned if not found. |
|
| 19 |
-func GetStringValue(ctx Context, key interface{}) (value string) {
|
|
| 20 |
- if valuev, ok := ctx.Value(key).(string); ok {
|
|
| 21 |
- value = valuev |
|
| 22 |
- } |
|
| 23 |
- return value |
|
| 24 |
-} |
| 25 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,16 +0,0 @@ |
| 1 |
-package context |
|
| 2 |
- |
|
| 3 |
-// WithVersion stores the application version in the context. The new context |
|
| 4 |
-// gets a logger to ensure log messages are marked with the application |
|
| 5 |
-// version. |
|
| 6 |
-func WithVersion(ctx Context, version string) Context {
|
|
| 7 |
- ctx = WithValue(ctx, "version", version) |
|
| 8 |
- // push a new logger onto the stack |
|
| 9 |
- return WithLogger(ctx, GetLogger(ctx, "version")) |
|
| 10 |
-} |
|
| 11 |
- |
|
| 12 |
-// GetVersion returns the application version from the context. An empty |
|
| 13 |
-// string may returned if the version was not set on the context. |
|
| 14 |
-func GetVersion(ctx Context) string {
|
|
| 15 |
- return GetStringValue(ctx, "version") |
|
| 16 |
-} |
| ... | ... |
@@ -1,6 +1,7 @@ |
| 1 | 1 |
package schema1 |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "context" |
|
| 4 | 5 |
"crypto/sha512" |
| 5 | 6 |
"encoding/json" |
| 6 | 7 |
"errors" |
| ... | ... |
@@ -8,7 +9,6 @@ import ( |
| 8 | 8 |
"time" |
| 9 | 9 |
|
| 10 | 10 |
"github.com/docker/distribution" |
| 11 |
- "github.com/docker/distribution/context" |
|
| 12 | 11 |
"github.com/docker/distribution/manifest" |
| 13 | 12 |
"github.com/docker/distribution/reference" |
| 14 | 13 |
"github.com/docker/libtrust" |
| ... | ... |
@@ -1,11 +1,11 @@ |
| 1 | 1 |
package schema1 |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "context" |
|
| 5 |
+ "errors" |
|
| 4 | 6 |
"fmt" |
| 5 | 7 |
|
| 6 |
- "errors" |
|
| 7 | 8 |
"github.com/docker/distribution" |
| 8 |
- "github.com/docker/distribution/context" |
|
| 9 | 9 |
"github.com/docker/distribution/manifest" |
| 10 | 10 |
"github.com/docker/distribution/reference" |
| 11 | 11 |
"github.com/docker/libtrust" |
| 11 | 11 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,13 @@ |
| 0 |
+package metrics |
|
| 1 |
+ |
|
| 2 |
+import "github.com/docker/go-metrics" |
|
| 3 |
+ |
|
| 4 |
+const ( |
|
| 5 |
+ // NamespacePrefix is the namespace of prometheus metrics |
|
| 6 |
+ NamespacePrefix = "registry" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+var ( |
|
| 10 |
+ // StorageNamespace is the prometheus namespace of blob/cache related operations |
|
| 11 |
+ StorageNamespace = metrics.NewNamespace(NamespacePrefix, "storage", nil) |
|
| 12 |
+) |
| ... | ... |
@@ -1,7 +1,8 @@ |
| 1 | 1 |
package distribution |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
- "github.com/docker/distribution/context" |
|
| 4 |
+ "context" |
|
| 5 |
+ |
|
| 5 | 6 |
"github.com/docker/distribution/reference" |
| 6 | 7 |
) |
| 7 | 8 |
|
| ... | ... |
@@ -72,6 +73,21 @@ func (o WithTagOption) Apply(m ManifestService) error {
|
| 72 | 72 |
return nil |
| 73 | 73 |
} |
| 74 | 74 |
|
| 75 |
+// WithManifestMediaTypes lists the media types the client wishes |
|
| 76 |
+// the server to provide. |
|
| 77 |
+func WithManifestMediaTypes(mediaTypes []string) ManifestServiceOption {
|
|
| 78 |
+ return WithManifestMediaTypesOption{mediaTypes}
|
|
| 79 |
+} |
|
| 80 |
+ |
|
| 81 |
+// WithManifestMediaTypesOption holds a list of accepted media types |
|
| 82 |
+type WithManifestMediaTypesOption struct{ MediaTypes []string }
|
|
| 83 |
+ |
|
| 84 |
+// Apply conforms to the ManifestServiceOption interface |
|
| 85 |
+func (o WithManifestMediaTypesOption) Apply(m ManifestService) error {
|
|
| 86 |
+ // no implementation |
|
| 87 |
+ return nil |
|
| 88 |
+} |
|
| 89 |
+ |
|
| 75 | 90 |
// Repository is a named collection of manifests and layers. |
| 76 | 91 |
type Repository interface {
|
| 77 | 92 |
// Named returns the name of the repository. |
| ... | ... |
@@ -45,13 +45,13 @@ type Manager interface {
|
| 45 | 45 |
// to a backend. |
| 46 | 46 |
func NewSimpleManager() Manager {
|
| 47 | 47 |
return &simpleManager{
|
| 48 |
- Challanges: make(map[string][]Challenge), |
|
| 48 |
+ Challenges: make(map[string][]Challenge), |
|
| 49 | 49 |
} |
| 50 | 50 |
} |
| 51 | 51 |
|
| 52 | 52 |
type simpleManager struct {
|
| 53 | 53 |
sync.RWMutex |
| 54 |
- Challanges map[string][]Challenge |
|
| 54 |
+ Challenges map[string][]Challenge |
|
| 55 | 55 |
} |
| 56 | 56 |
|
| 57 | 57 |
func normalizeURL(endpoint *url.URL) {
|
| ... | ... |
@@ -64,7 +64,7 @@ func (m *simpleManager) GetChallenges(endpoint url.URL) ([]Challenge, error) {
|
| 64 | 64 |
|
| 65 | 65 |
m.RLock() |
| 66 | 66 |
defer m.RUnlock() |
| 67 |
- challenges := m.Challanges[endpoint.String()] |
|
| 67 |
+ challenges := m.Challenges[endpoint.String()] |
|
| 68 | 68 |
return challenges, nil |
| 69 | 69 |
} |
| 70 | 70 |
|
| ... | ... |
@@ -82,7 +82,7 @@ func (m *simpleManager) AddResponse(resp *http.Response) error {
|
| 82 | 82 |
|
| 83 | 83 |
m.Lock() |
| 84 | 84 |
defer m.Unlock() |
| 85 |
- m.Challanges[urlCopy.String()] = challenges |
|
| 85 |
+ m.Challenges[urlCopy.String()] = challenges |
|
| 86 | 86 |
return nil |
| 87 | 87 |
} |
| 88 | 88 |
|
| ... | ... |
@@ -13,7 +13,6 @@ import ( |
| 13 | 13 |
"github.com/docker/distribution/registry/client" |
| 14 | 14 |
"github.com/docker/distribution/registry/client/auth/challenge" |
| 15 | 15 |
"github.com/docker/distribution/registry/client/transport" |
| 16 |
- "github.com/sirupsen/logrus" |
|
| 17 | 16 |
) |
| 18 | 17 |
|
| 19 | 18 |
var ( |
| ... | ... |
@@ -135,6 +134,8 @@ type tokenHandler struct {
|
| 135 | 135 |
tokenLock sync.Mutex |
| 136 | 136 |
tokenCache string |
| 137 | 137 |
tokenExpiration time.Time |
| 138 |
+ |
|
| 139 |
+ logger Logger |
|
| 138 | 140 |
} |
| 139 | 141 |
|
| 140 | 142 |
// Scope is a type which is serializable to a string |
| ... | ... |
@@ -176,6 +177,18 @@ func (rs RegistryScope) String() string {
|
| 176 | 176 |
return fmt.Sprintf("registry:%s:%s", rs.Name, strings.Join(rs.Actions, ","))
|
| 177 | 177 |
} |
| 178 | 178 |
|
| 179 |
+// Logger defines the injectable logging interface, used on TokenHandlers. |
|
| 180 |
+type Logger interface {
|
|
| 181 |
+ Debugf(format string, args ...interface{})
|
|
| 182 |
+} |
|
| 183 |
+ |
|
| 184 |
+func logDebugf(logger Logger, format string, args ...interface{}) {
|
|
| 185 |
+ if logger == nil {
|
|
| 186 |
+ return |
|
| 187 |
+ } |
|
| 188 |
+ logger.Debugf(format, args...) |
|
| 189 |
+} |
|
| 190 |
+ |
|
| 179 | 191 |
// TokenHandlerOptions is used to configure a new token handler |
| 180 | 192 |
type TokenHandlerOptions struct {
|
| 181 | 193 |
Transport http.RoundTripper |
| ... | ... |
@@ -185,6 +198,7 @@ type TokenHandlerOptions struct {
|
| 185 | 185 |
ForceOAuth bool |
| 186 | 186 |
ClientID string |
| 187 | 187 |
Scopes []Scope |
| 188 |
+ Logger Logger |
|
| 188 | 189 |
} |
| 189 | 190 |
|
| 190 | 191 |
// An implementation of clock for providing real time data. |
| ... | ... |
@@ -220,6 +234,7 @@ func NewTokenHandlerWithOptions(options TokenHandlerOptions) AuthenticationHandl |
| 220 | 220 |
clientID: options.ClientID, |
| 221 | 221 |
scopes: options.Scopes, |
| 222 | 222 |
clock: realClock{},
|
| 223 |
+ logger: options.Logger, |
|
| 223 | 224 |
} |
| 224 | 225 |
|
| 225 | 226 |
return handler |
| ... | ... |
@@ -264,6 +279,9 @@ func (th *tokenHandler) getToken(params map[string]string, additionalScopes ...s |
| 264 | 264 |
} |
| 265 | 265 |
var addedScopes bool |
| 266 | 266 |
for _, scope := range additionalScopes {
|
| 267 |
+ if hasScope(scopes, scope) {
|
|
| 268 |
+ continue |
|
| 269 |
+ } |
|
| 267 | 270 |
scopes = append(scopes, scope) |
| 268 | 271 |
addedScopes = true |
| 269 | 272 |
} |
| ... | ... |
@@ -287,6 +305,15 @@ func (th *tokenHandler) getToken(params map[string]string, additionalScopes ...s |
| 287 | 287 |
return th.tokenCache, nil |
| 288 | 288 |
} |
| 289 | 289 |
|
| 290 |
+func hasScope(scopes []string, scope string) bool {
|
|
| 291 |
+ for _, s := range scopes {
|
|
| 292 |
+ if s == scope {
|
|
| 293 |
+ return true |
|
| 294 |
+ } |
|
| 295 |
+ } |
|
| 296 |
+ return false |
|
| 297 |
+} |
|
| 298 |
+ |
|
| 290 | 299 |
type postTokenResponse struct {
|
| 291 | 300 |
AccessToken string `json:"access_token"` |
| 292 | 301 |
RefreshToken string `json:"refresh_token"` |
| ... | ... |
@@ -348,7 +375,7 @@ func (th *tokenHandler) fetchTokenWithOAuth(realm *url.URL, refreshToken, servic |
| 348 | 348 |
if tr.ExpiresIn < minimumTokenLifetimeSeconds {
|
| 349 | 349 |
// The default/minimum lifetime. |
| 350 | 350 |
tr.ExpiresIn = minimumTokenLifetimeSeconds |
| 351 |
- logrus.Debugf("Increasing token expiration to: %d seconds", tr.ExpiresIn)
|
|
| 351 |
+ logDebugf(th.logger, "Increasing token expiration to: %d seconds", tr.ExpiresIn) |
|
| 352 | 352 |
} |
| 353 | 353 |
|
| 354 | 354 |
if tr.IssuedAt.IsZero() {
|
| ... | ... |
@@ -439,7 +466,7 @@ func (th *tokenHandler) fetchTokenWithBasicAuth(realm *url.URL, service string, |
| 439 | 439 |
if tr.ExpiresIn < minimumTokenLifetimeSeconds {
|
| 440 | 440 |
// The default/minimum lifetime. |
| 441 | 441 |
tr.ExpiresIn = minimumTokenLifetimeSeconds |
| 442 |
- logrus.Debugf("Increasing token expiration to: %d seconds", tr.ExpiresIn)
|
|
| 442 |
+ logDebugf(th.logger, "Increasing token expiration to: %d seconds", tr.ExpiresIn) |
|
| 443 | 443 |
} |
| 444 | 444 |
|
| 445 | 445 |
if tr.IssuedAt.IsZero() {
|
| ... | ... |
@@ -2,6 +2,7 @@ package client |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"bytes" |
| 5 |
+ "context" |
|
| 5 | 6 |
"fmt" |
| 6 | 7 |
"io" |
| 7 | 8 |
"io/ioutil" |
| ... | ... |
@@ -9,7 +10,6 @@ import ( |
| 9 | 9 |
"time" |
| 10 | 10 |
|
| 11 | 11 |
"github.com/docker/distribution" |
| 12 |
- "github.com/docker/distribution/context" |
|
| 13 | 12 |
) |
| 14 | 13 |
|
| 15 | 14 |
type httpBlobUpload struct {
|
| ... | ... |
@@ -2,6 +2,7 @@ package client |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"bytes" |
| 5 |
+ "context" |
|
| 5 | 6 |
"encoding/json" |
| 6 | 7 |
"errors" |
| 7 | 8 |
"fmt" |
| ... | ... |
@@ -14,7 +15,6 @@ import ( |
| 14 | 14 |
"time" |
| 15 | 15 |
|
| 16 | 16 |
"github.com/docker/distribution" |
| 17 |
- "github.com/docker/distribution/context" |
|
| 18 | 17 |
"github.com/docker/distribution/reference" |
| 19 | 18 |
"github.com/docker/distribution/registry/api/v2" |
| 20 | 19 |
"github.com/docker/distribution/registry/client/transport" |
| ... | ... |
@@ -62,7 +62,7 @@ func checkHTTPRedirect(req *http.Request, via []*http.Request) error {
|
| 62 | 62 |
} |
| 63 | 63 |
|
| 64 | 64 |
// NewRegistry creates a registry namespace which can be used to get a listing of repositories |
| 65 |
-func NewRegistry(ctx context.Context, baseURL string, transport http.RoundTripper) (Registry, error) {
|
|
| 65 |
+func NewRegistry(baseURL string, transport http.RoundTripper) (Registry, error) {
|
|
| 66 | 66 |
ub, err := v2.NewURLBuilderFromString(baseURL, false) |
| 67 | 67 |
if err != nil {
|
| 68 | 68 |
return nil, err |
| ... | ... |
@@ -75,9 +75,8 @@ func NewRegistry(ctx context.Context, baseURL string, transport http.RoundTrippe |
| 75 | 75 |
} |
| 76 | 76 |
|
| 77 | 77 |
return ®istry{
|
| 78 |
- client: client, |
|
| 79 |
- ub: ub, |
|
| 80 |
- context: ctx, |
|
| 78 |
+ client: client, |
|
| 79 |
+ ub: ub, |
|
| 81 | 80 |
}, nil |
| 82 | 81 |
} |
| 83 | 82 |
|
| ... | ... |
@@ -133,7 +132,7 @@ func (r *registry) Repositories(ctx context.Context, entries []string, last stri |
| 133 | 133 |
} |
| 134 | 134 |
|
| 135 | 135 |
// NewRepository creates a new Repository for the given repository name and base URL. |
| 136 |
-func NewRepository(ctx context.Context, name reference.Named, baseURL string, transport http.RoundTripper) (distribution.Repository, error) {
|
|
| 136 |
+func NewRepository(name reference.Named, baseURL string, transport http.RoundTripper) (distribution.Repository, error) {
|
|
| 137 | 137 |
ub, err := v2.NewURLBuilderFromString(baseURL, false) |
| 138 | 138 |
if err != nil {
|
| 139 | 139 |
return nil, err |
| ... | ... |
@@ -146,10 +145,9 @@ func NewRepository(ctx context.Context, name reference.Named, baseURL string, tr |
| 146 | 146 |
} |
| 147 | 147 |
|
| 148 | 148 |
return &repository{
|
| 149 |
- client: client, |
|
| 150 |
- ub: ub, |
|
| 151 |
- name: name, |
|
| 152 |
- context: ctx, |
|
| 149 |
+ client: client, |
|
| 150 |
+ ub: ub, |
|
| 151 |
+ name: name, |
|
| 153 | 152 |
}, nil |
| 154 | 153 |
} |
| 155 | 154 |
|
| ... | ... |
@@ -190,32 +188,35 @@ func (r *repository) Manifests(ctx context.Context, options ...distribution.Mani |
| 190 | 190 |
|
| 191 | 191 |
func (r *repository) Tags(ctx context.Context) distribution.TagService {
|
| 192 | 192 |
return &tags{
|
| 193 |
- client: r.client, |
|
| 194 |
- ub: r.ub, |
|
| 195 |
- context: r.context, |
|
| 196 |
- name: r.Named(), |
|
| 193 |
+ client: r.client, |
|
| 194 |
+ ub: r.ub, |
|
| 195 |
+ name: r.Named(), |
|
| 197 | 196 |
} |
| 198 | 197 |
} |
| 199 | 198 |
|
| 200 | 199 |
// tags implements remote tagging operations. |
| 201 | 200 |
type tags struct {
|
| 202 |
- client *http.Client |
|
| 203 |
- ub *v2.URLBuilder |
|
| 204 |
- context context.Context |
|
| 205 |
- name reference.Named |
|
| 201 |
+ client *http.Client |
|
| 202 |
+ ub *v2.URLBuilder |
|
| 203 |
+ name reference.Named |
|
| 206 | 204 |
} |
| 207 | 205 |
|
| 208 | 206 |
// All returns all tags |
| 209 | 207 |
func (t *tags) All(ctx context.Context) ([]string, error) {
|
| 210 | 208 |
var tags []string |
| 211 | 209 |
|
| 212 |
- u, err := t.ub.BuildTagsURL(t.name) |
|
| 210 |
+ listURLStr, err := t.ub.BuildTagsURL(t.name) |
|
| 211 |
+ if err != nil {
|
|
| 212 |
+ return tags, err |
|
| 213 |
+ } |
|
| 214 |
+ |
|
| 215 |
+ listURL, err := url.Parse(listURLStr) |
|
| 213 | 216 |
if err != nil {
|
| 214 | 217 |
return tags, err |
| 215 | 218 |
} |
| 216 | 219 |
|
| 217 | 220 |
for {
|
| 218 |
- resp, err := t.client.Get(u) |
|
| 221 |
+ resp, err := t.client.Get(listURL.String()) |
|
| 219 | 222 |
if err != nil {
|
| 220 | 223 |
return tags, err |
| 221 | 224 |
} |
| ... | ... |
@@ -235,7 +236,13 @@ func (t *tags) All(ctx context.Context) ([]string, error) {
|
| 235 | 235 |
} |
| 236 | 236 |
tags = append(tags, tagsResponse.Tags...) |
| 237 | 237 |
if link := resp.Header.Get("Link"); link != "" {
|
| 238 |
- u = strings.Trim(strings.Split(link, ";")[0], "<>") |
|
| 238 |
+ linkURLStr := strings.Trim(strings.Split(link, ";")[0], "<>") |
|
| 239 |
+ linkURL, err := url.Parse(linkURLStr) |
|
| 240 |
+ if err != nil {
|
|
| 241 |
+ return tags, err |
|
| 242 |
+ } |
|
| 243 |
+ |
|
| 244 |
+ listURL = listURL.ResolveReference(linkURL) |
|
| 239 | 245 |
} else {
|
| 240 | 246 |
return tags, nil |
| 241 | 247 |
} |
| ... | ... |
@@ -321,7 +328,8 @@ func (t *tags) Get(ctx context.Context, tag string) (distribution.Descriptor, er |
| 321 | 321 |
defer resp.Body.Close() |
| 322 | 322 |
|
| 323 | 323 |
switch {
|
| 324 |
- case resp.StatusCode >= 200 && resp.StatusCode < 400: |
|
| 324 |
+ case resp.StatusCode >= 200 && resp.StatusCode < 400 && len(resp.Header.Get("Docker-Content-Digest")) > 0:
|
|
| 325 |
+ // if the response is a success AND a Docker-Content-Digest can be retrieved from the headers |
|
| 325 | 326 |
return descriptorFromResponse(resp) |
| 326 | 327 |
default: |
| 327 | 328 |
// if the response is an error - there will be no body to decode. |
| ... | ... |
@@ -421,18 +429,22 @@ func (ms *manifests) Get(ctx context.Context, dgst digest.Digest, options ...dis |
| 421 | 421 |
ref reference.Named |
| 422 | 422 |
err error |
| 423 | 423 |
contentDgst *digest.Digest |
| 424 |
+ mediaTypes []string |
|
| 424 | 425 |
) |
| 425 | 426 |
|
| 426 | 427 |
for _, option := range options {
|
| 427 |
- if opt, ok := option.(distribution.WithTagOption); ok {
|
|
| 428 |
+ switch opt := option.(type) {
|
|
| 429 |
+ case distribution.WithTagOption: |
|
| 428 | 430 |
digestOrTag = opt.Tag |
| 429 | 431 |
ref, err = reference.WithTag(ms.name, opt.Tag) |
| 430 | 432 |
if err != nil {
|
| 431 | 433 |
return nil, err |
| 432 | 434 |
} |
| 433 |
- } else if opt, ok := option.(contentDigestOption); ok {
|
|
| 435 |
+ case contentDigestOption: |
|
| 434 | 436 |
contentDgst = opt.digest |
| 435 |
- } else {
|
|
| 437 |
+ case distribution.WithManifestMediaTypesOption: |
|
| 438 |
+ mediaTypes = opt.MediaTypes |
|
| 439 |
+ default: |
|
| 436 | 440 |
err := option.Apply(ms) |
| 437 | 441 |
if err != nil {
|
| 438 | 442 |
return nil, err |
| ... | ... |
@@ -448,6 +460,10 @@ func (ms *manifests) Get(ctx context.Context, dgst digest.Digest, options ...dis |
| 448 | 448 |
} |
| 449 | 449 |
} |
| 450 | 450 |
|
| 451 |
+ if len(mediaTypes) == 0 {
|
|
| 452 |
+ mediaTypes = distribution.ManifestMediaTypes() |
|
| 453 |
+ } |
|
| 454 |
+ |
|
| 451 | 455 |
u, err := ms.ub.BuildManifestURL(ref) |
| 452 | 456 |
if err != nil {
|
| 453 | 457 |
return nil, err |
| ... | ... |
@@ -458,7 +474,7 @@ func (ms *manifests) Get(ctx context.Context, dgst digest.Digest, options ...dis |
| 458 | 458 |
return nil, err |
| 459 | 459 |
} |
| 460 | 460 |
|
| 461 |
- for _, t := range distribution.ManifestMediaTypes() {
|
|
| 461 |
+ for _, t := range mediaTypes {
|
|
| 462 | 462 |
req.Header.Add("Accept", t)
|
| 463 | 463 |
} |
| 464 | 464 |
|
| ... | ... |
@@ -1,10 +1,11 @@ |
| 1 | 1 |
package cache |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
- "github.com/docker/distribution/context" |
|
| 5 |
- "github.com/opencontainers/go-digest" |
|
| 4 |
+ "context" |
|
| 6 | 5 |
|
| 7 | 6 |
"github.com/docker/distribution" |
| 7 |
+ prometheus "github.com/docker/distribution/metrics" |
|
| 8 |
+ "github.com/opencontainers/go-digest" |
|
| 8 | 9 |
) |
| 9 | 10 |
|
| 10 | 11 |
// Metrics is used to hold metric counters |
| ... | ... |
@@ -16,12 +17,20 @@ type Metrics struct {
|
| 16 | 16 |
Misses uint64 |
| 17 | 17 |
} |
| 18 | 18 |
|
| 19 |
+// Logger can be provided on the MetricsTracker to log errors. |
|
| 20 |
+// |
|
| 21 |
+// Usually, this is just a proxy to dcontext.GetLogger. |
|
| 22 |
+type Logger interface {
|
|
| 23 |
+ Errorf(format string, args ...interface{})
|
|
| 24 |
+} |
|
| 25 |
+ |
|
| 19 | 26 |
// MetricsTracker represents a metric tracker |
| 20 | 27 |
// which simply counts the number of hits and misses. |
| 21 | 28 |
type MetricsTracker interface {
|
| 22 | 29 |
Hit() |
| 23 | 30 |
Miss() |
| 24 | 31 |
Metrics() Metrics |
| 32 |
+ Logger(context.Context) Logger |
|
| 25 | 33 |
} |
| 26 | 34 |
|
| 27 | 35 |
type cachedBlobStatter struct {
|
| ... | ... |
@@ -30,6 +39,11 @@ type cachedBlobStatter struct {
|
| 30 | 30 |
tracker MetricsTracker |
| 31 | 31 |
} |
| 32 | 32 |
|
| 33 |
+var ( |
|
| 34 |
+ // cacheCount is the number of total cache request received/hits/misses |
|
| 35 |
+ cacheCount = prometheus.StorageNamespace.NewLabeledCounter("cache", "The number of cache request received", "type")
|
|
| 36 |
+) |
|
| 37 |
+ |
|
| 33 | 38 |
// NewCachedBlobStatter creates a new statter which prefers a cache and |
| 34 | 39 |
// falls back to a backend. |
| 35 | 40 |
func NewCachedBlobStatter(cache distribution.BlobDescriptorService, backend distribution.BlobDescriptorService) distribution.BlobDescriptorService {
|
| ... | ... |
@@ -50,20 +64,22 @@ func NewCachedBlobStatterWithMetrics(cache distribution.BlobDescriptorService, b |
| 50 | 50 |
} |
| 51 | 51 |
|
| 52 | 52 |
func (cbds *cachedBlobStatter) Stat(ctx context.Context, dgst digest.Digest) (distribution.Descriptor, error) {
|
| 53 |
+ cacheCount.WithValues("Request").Inc(1)
|
|
| 53 | 54 |
desc, err := cbds.cache.Stat(ctx, dgst) |
| 54 | 55 |
if err != nil {
|
| 55 | 56 |
if err != distribution.ErrBlobUnknown {
|
| 56 |
- context.GetLogger(ctx).Errorf("error retrieving descriptor from cache: %v", err)
|
|
| 57 |
+ logErrorf(ctx, cbds.tracker, "error retrieving descriptor from cache: %v", err) |
|
| 57 | 58 |
} |
| 58 | 59 |
|
| 59 | 60 |
goto fallback |
| 60 | 61 |
} |
| 61 |
- |
|
| 62 |
+ cacheCount.WithValues("Hit").Inc(1)
|
|
| 62 | 63 |
if cbds.tracker != nil {
|
| 63 | 64 |
cbds.tracker.Hit() |
| 64 | 65 |
} |
| 65 | 66 |
return desc, nil |
| 66 | 67 |
fallback: |
| 68 |
+ cacheCount.WithValues("Miss").Inc(1)
|
|
| 67 | 69 |
if cbds.tracker != nil {
|
| 68 | 70 |
cbds.tracker.Miss() |
| 69 | 71 |
} |
| ... | ... |
@@ -73,7 +89,7 @@ fallback: |
| 73 | 73 |
} |
| 74 | 74 |
|
| 75 | 75 |
if err := cbds.cache.SetDescriptor(ctx, dgst, desc); err != nil {
|
| 76 |
- context.GetLogger(ctx).Errorf("error adding descriptor %v to cache: %v", desc.Digest, err)
|
|
| 76 |
+ logErrorf(ctx, cbds.tracker, "error adding descriptor %v to cache: %v", desc.Digest, err) |
|
| 77 | 77 |
} |
| 78 | 78 |
|
| 79 | 79 |
return desc, err |
| ... | ... |
@@ -95,7 +111,19 @@ func (cbds *cachedBlobStatter) Clear(ctx context.Context, dgst digest.Digest) er |
| 95 | 95 |
|
| 96 | 96 |
func (cbds *cachedBlobStatter) SetDescriptor(ctx context.Context, dgst digest.Digest, desc distribution.Descriptor) error {
|
| 97 | 97 |
if err := cbds.cache.SetDescriptor(ctx, dgst, desc); err != nil {
|
| 98 |
- context.GetLogger(ctx).Errorf("error adding descriptor %v to cache: %v", desc.Digest, err)
|
|
| 98 |
+ logErrorf(ctx, cbds.tracker, "error adding descriptor %v to cache: %v", desc.Digest, err) |
|
| 99 | 99 |
} |
| 100 | 100 |
return nil |
| 101 | 101 |
} |
| 102 |
+ |
|
| 103 |
+func logErrorf(ctx context.Context, tracker MetricsTracker, format string, args ...interface{}) {
|
|
| 104 |
+ if tracker == nil {
|
|
| 105 |
+ return |
|
| 106 |
+ } |
|
| 107 |
+ |
|
| 108 |
+ logger := tracker.Logger(ctx) |
|
| 109 |
+ if logger == nil {
|
|
| 110 |
+ return |
|
| 111 |
+ } |
|
| 112 |
+ logger.Errorf(format, args...) |
|
| 113 |
+} |
| ... | ... |
@@ -1,10 +1,10 @@ |
| 1 | 1 |
package memory |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "context" |
|
| 4 | 5 |
"sync" |
| 5 | 6 |
|
| 6 | 7 |
"github.com/docker/distribution" |
| 7 |
- "github.com/docker/distribution/context" |
|
| 8 | 8 |
"github.com/docker/distribution/reference" |
| 9 | 9 |
"github.com/docker/distribution/registry/storage/cache" |
| 10 | 10 |
"github.com/opencontainers/go-digest" |
| ... | ... |
@@ -1,26 +1,32 @@ |
| 1 | 1 |
github.com/Azure/azure-sdk-for-go 088007b3b08cc02b27f2eadfdcd870958460ce7e |
| 2 | 2 |
github.com/Azure/go-autorest ec5f4903f77ed9927ac95b19ab8e44ada64c1356 |
| 3 | 3 |
github.com/sirupsen/logrus 3d4380f53a34dcdc95f0c1db702615992b38d9a4 |
| 4 |
-github.com/aws/aws-sdk-go c6fc52983ea2375810aa38ddb5370e9cdf611716 |
|
| 4 |
+github.com/aws/aws-sdk-go 5bcc0a238d880469f949fc7cd24e35f32ab80cbd |
|
| 5 | 5 |
github.com/bshuster-repo/logrus-logstash-hook d2c0ecc1836d91814e15e23bb5dc309c3ef51f4a |
| 6 |
+github.com/beorn7/perks 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9 |
|
| 6 | 7 |
github.com/bugsnag/bugsnag-go b1d153021fcd90ca3f080db36bec96dc690fb274 |
| 7 | 8 |
github.com/bugsnag/osext 0dd3f918b21bec95ace9dc86c7e70266cfc5c702 |
| 8 | 9 |
github.com/bugsnag/panicwrap e2c28503fcd0675329da73bf48b33404db873782 |
| 9 | 10 |
github.com/denverdino/aliyungo afedced274aa9a7fcdd47ac97018f0f8db4e5de2 |
| 10 | 11 |
github.com/dgrijalva/jwt-go a601269ab70c205d26370c16f7c81e9017c14e04 |
| 12 |
+github.com/docker/go-metrics 399ea8c73916000c64c2c76e8da00ca82f8387ab |
|
| 11 | 13 |
github.com/docker/goamz f0a21f5b2e12f83a505ecf79b633bb2035cf6f85 |
| 12 | 14 |
github.com/docker/libtrust fa567046d9b14f6aa788882a950d69651d230b21 |
| 13 | 15 |
github.com/garyburd/redigo 535138d7bcd717d6531c701ef5933d98b1866257 |
| 14 | 16 |
github.com/go-ini/ini 2ba15ac2dc9cdf88c110ec2dc0ced7fa45f5678c |
| 15 | 17 |
github.com/golang/protobuf 8d92cf5fc15a4382f8964b08e1f42a75c0591aa3 |
| 16 |
-github.com/gorilla/context 14f550f51af52180c2eefed15e5fd18d63c0a64a |
|
| 17 | 18 |
github.com/gorilla/handlers 60c7bfde3e33c201519a200a4507a158cc03a17b |
| 18 | 19 |
github.com/gorilla/mux 599cba5e7b6137d46ddf58fb1765f5d928e69604 |
| 19 | 20 |
github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 |
| 20 | 21 |
github.com/jmespath/go-jmespath bd40a432e4c76585ef6b72d3fd96fb9b6dc7b68d |
| 22 |
+github.com/matttproud/golang_protobuf_extensions c12348ce28de40eed0136aa2b644d0ee0650e56c |
|
| 21 | 23 |
github.com/miekg/dns 271c58e0c14f552178ea321a545ff9af38930f39 |
| 22 | 24 |
github.com/mitchellh/mapstructure 482a9fd5fa83e8c4e7817413b80f3eb8feec03ef |
| 23 | 25 |
github.com/ncw/swift b964f2ca856aac39885e258ad25aec08d5f64ee6 |
| 26 |
+github.com/prometheus/client_golang c332b6f63c0658a65eca15c0e5247ded801cf564 |
|
| 27 |
+github.com/prometheus/client_model 99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c |
|
| 28 |
+github.com/prometheus/common 89604d197083d4781071d3c65855d24ecfb0a563 |
|
| 29 |
+github.com/prometheus/procfs cb4147076ac75738c9a7d279075a253c0cc5acbd |
|
| 24 | 30 |
github.com/spf13/cobra 312092086bed4968099259622145a0c9ae280064 |
| 25 | 31 |
github.com/spf13/pflag 5644820622454e71517561946e3d94b9f9db6842 |
| 26 | 32 |
github.com/stevvooe/resumable 2aaf90b2ceea5072cb503ef2a620b08ff3119870 |