diff --git a/vendor/github.com/weaveworks/common/errors/error.go b/vendor/github.com/weaveworks/common/errors/error.go new file mode 100644 index 000000000..6d1f97995 --- /dev/null +++ b/vendor/github.com/weaveworks/common/errors/error.go @@ -0,0 +1,6 @@ +package errors + +// Error see https://dave.cheney.net/2016/04/07/constant-errors. +type Error string + +func (e Error) Error() string { return string(e) } diff --git a/vendor/github.com/weaveworks/common/httpgrpc/httpgrpc.go b/vendor/github.com/weaveworks/common/httpgrpc/httpgrpc.go index a59a6eba1..87d612390 100644 --- a/vendor/github.com/weaveworks/common/httpgrpc/httpgrpc.go +++ b/vendor/github.com/weaveworks/common/httpgrpc/httpgrpc.go @@ -65,18 +65,27 @@ type Client struct { conn *grpc.ClientConn } -// NewClient makes a new Client, given a kubernetes service address. Expects -// an address of the form .: -func NewClient(address string) (*Client, error) { +// ParseKubernetesAddress splits up an address of the form (.): +// into its consistuent parts. Namespace will be "default" if missing. +func ParseKubernetesAddress(address string) (service, namespace, port string, err error) { host, port, err := net.SplitHostPort(address) if err != nil { - return nil, err + return "", "", "", err } parts := strings.SplitN(host, ".", 2) - service, namespace := parts[0], "default" + service, namespace = parts[0], "default" if len(parts) == 2 { namespace = parts[1] } + return service, namespace, port, nil +} + +// NewClient makes a new Client, given a kubernetes service address. +func NewClient(address string) (*Client, error) { + service, namespace, port, err := ParseKubernetesAddress(address) + if err != nil { + return nil, err + } return &Client{ service: service, namespace: namespace, diff --git a/vendor/github.com/weaveworks/common/instrument/instrument.go b/vendor/github.com/weaveworks/common/instrument/instrument.go index 6afb72b19..e6af59298 100644 --- a/vendor/github.com/weaveworks/common/instrument/instrument.go +++ b/vendor/github.com/weaveworks/common/instrument/instrument.go @@ -22,7 +22,7 @@ func ErrorCode(err error) string { // "500". It will also emit an OpenTracing span if you have a global tracer configured. // // If you want more complicated logic for translating errors into statuses, -// use 'TimeRequestStatus'. +// use 'TimeRequestHistogramStatus'. func TimeRequestHistogram(ctx context.Context, method string, metric *prometheus.HistogramVec, f func(context.Context) error) error { return TimeRequestHistogramStatus(ctx, method, metric, ErrorCode, f) } diff --git a/vendor/github.com/weaveworks/common/middleware/grpc_auth.go b/vendor/github.com/weaveworks/common/middleware/grpc_auth.go index 634dd8e89..35f548792 100644 --- a/vendor/github.com/weaveworks/common/middleware/grpc_auth.go +++ b/vendor/github.com/weaveworks/common/middleware/grpc_auth.go @@ -1,58 +1,28 @@ package middleware import ( - "fmt" - "golang.org/x/net/context" "google.golang.org/grpc" - "google.golang.org/grpc/metadata" "github.com/weaveworks/common/user" ) // ClientUserHeaderInterceptor propagates the user ID from the context to gRPC metadata, which eventually ends up as a HTTP2 header. func ClientUserHeaderInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { - userID, err := user.GetID(ctx) + ctx, err := user.InjectIntoGRPCRequest(ctx) if err != nil { return err } - md, ok := metadata.FromContext(ctx) - if !ok { - md = metadata.New(map[string]string{}) - } - - newCtx := ctx - if userIDs, ok := md[user.LowerOrgIDHeaderName]; ok { - switch len(userIDs) { - case 1: - if userIDs[0] != userID { - return fmt.Errorf("wrong user ID found") - } - default: - return fmt.Errorf("multiple user IDs found") - } - } else { - md = md.Copy() - md[user.LowerOrgIDHeaderName] = []string{userID} - newCtx = metadata.NewContext(ctx, md) - } - - return invoker(newCtx, method, req, reply, cc, opts...) + return invoker(ctx, method, req, reply, cc, opts...) } // ServerUserHeaderInterceptor propagates the user ID from the gRPC metadata back to our context. func ServerUserHeaderInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { - md, ok := metadata.FromContext(ctx) - if !ok { - return nil, fmt.Errorf("no metadata") + _, ctx, err := user.ExtractFromGRPCRequest(ctx) + if err != nil { + return nil, err } - userIDs, ok := md[user.LowerOrgIDHeaderName] - if !ok || len(userIDs) != 1 { - return nil, fmt.Errorf("no user id") - } - - newCtx := user.WithID(ctx, userIDs[0]) - return handler(newCtx, req) + return handler(ctx, req) } diff --git a/vendor/github.com/weaveworks/common/middleware/grpc_logging.go b/vendor/github.com/weaveworks/common/middleware/grpc_logging.go index 1b3f4ef1f..9a781216e 100644 --- a/vendor/github.com/weaveworks/common/middleware/grpc_logging.go +++ b/vendor/github.com/weaveworks/common/middleware/grpc_logging.go @@ -11,15 +11,13 @@ import ( const gRPC = "gRPC" // ServerLoggingInterceptor logs gRPC requests, errors and latency. -func ServerLoggingInterceptor(logSuccess bool) grpc.UnaryServerInterceptor { - return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { - begin := time.Now() - resp, err := handler(ctx, req) - if err != nil { - log.Errorf("%s %s (%v) %s", gRPC, info.FullMethod, err, time.Since(begin)) - } else if logSuccess { - log.Infof("%s %s (success) %s", gRPC, info.FullMethod, time.Since(begin)) - } - return resp, err +var ServerLoggingInterceptor = func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { + begin := time.Now() + resp, err := handler(ctx, req) + if err != nil { + log.Warnf("%s %s (%v) %s", gRPC, info.FullMethod, err, time.Since(begin)) + } else { + log.Debugf("%s %s (success) %s", gRPC, info.FullMethod, time.Since(begin)) } + return resp, err } diff --git a/vendor/github.com/weaveworks/common/middleware/http_auth.go b/vendor/github.com/weaveworks/common/middleware/http_auth.go new file mode 100644 index 000000000..11f1e7d6d --- /dev/null +++ b/vendor/github.com/weaveworks/common/middleware/http_auth.go @@ -0,0 +1,19 @@ +package middleware + +import ( + "net/http" + + "github.com/weaveworks/common/user" +) + +// AuthenticateUser propagates the user ID from HTTP headers back to the request's context. +var AuthenticateUser = Func(func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _, ctx, err := user.ExtractFromHTTPRequest(r) + if err != nil { + http.Error(w, err.Error(), http.StatusUnauthorized) + return + } + next.ServeHTTP(w, r.WithContext(ctx)) + }) +}) diff --git a/vendor/github.com/weaveworks/common/middleware/instrument.go b/vendor/github.com/weaveworks/common/middleware/instrument.go index 875bb1da0..2be1001a3 100644 --- a/vendor/github.com/weaveworks/common/middleware/instrument.go +++ b/vendor/github.com/weaveworks/common/middleware/instrument.go @@ -67,7 +67,7 @@ func (i Instrument) getRouteName(r *http.Request) string { if name := routeMatch.Route.GetName(); name != "" { return name } - if tmpl, err := routeMatch.Route.GetPathTemplate(); err != nil { + if tmpl, err := routeMatch.Route.GetPathTemplate(); err == nil { return MakeLabelValue(tmpl) } } diff --git a/vendor/github.com/weaveworks/common/middleware/logging.go b/vendor/github.com/weaveworks/common/middleware/logging.go index 19d29cd84..e526ac02b 100644 --- a/vendor/github.com/weaveworks/common/middleware/logging.go +++ b/vendor/github.com/weaveworks/common/middleware/logging.go @@ -13,7 +13,6 @@ import ( // Log middleware logs http requests type Log struct { - LogSuccess bool // LogSuccess true -> log successful queries; false -> only log failed queries LogRequestHeaders bool // LogRequestHeaders true -> dump http headers at debug log level } @@ -33,24 +32,17 @@ func (l Log) Wrap(next http.Handler) http.Handler { } i := &interceptor{ResponseWriter: w, statusCode: http.StatusOK} next.ServeHTTP(i, r) - if l.LogSuccess || !(100 <= i.statusCode && i.statusCode < 400) { - log.Infof("%s %s (%d) %s", r.Method, uri, i.statusCode, time.Since(begin)) + if 100 <= i.statusCode && i.statusCode < 400 { + log.Debugf("%s %s (%d) %s", r.Method, uri, i.statusCode, time.Since(begin)) + } else { + log.Warnf("%s %s (%d) %s", r.Method, uri, i.statusCode, time.Since(begin)) } }) } // Logging middleware logs each HTTP request method, path, response code and // duration for all HTTP requests. -var Logging = Log{ - LogSuccess: true, - LogRequestHeaders: false, -} - -// LogFailed middleware logs each HTTP request method, path, response code and -// duration for non-2xx HTTP requests. -var LogFailed = Log{ - LogSuccess: false, -} +var Logging = Log{} // interceptor implements WriteHeader to intercept status codes. WriteHeader // may not be called on success, so initialize statusCode with the status you diff --git a/vendor/github.com/weaveworks/common/server/server.go b/vendor/github.com/weaveworks/common/server/server.go new file mode 100644 index 000000000..6f9a11b1c --- /dev/null +++ b/vendor/github.com/weaveworks/common/server/server.go @@ -0,0 +1,172 @@ +package server + +import ( + "flag" + "fmt" + "net" + "net/http" + _ "net/http/pprof" // anonymous import to get the pprof handler registered + "time" + + log "github.com/Sirupsen/logrus" + "github.com/gorilla/mux" + "github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc" + "github.com/mwitkow/go-grpc-middleware" + "github.com/opentracing-contrib/go-stdlib/nethttp" + "github.com/opentracing/opentracing-go" + "github.com/prometheus/client_golang/prometheus" + "golang.org/x/net/context" + "google.golang.org/grpc" + + "github.com/weaveworks-experiments/loki/pkg/client" + "github.com/weaveworks/common/httpgrpc" + "github.com/weaveworks/common/middleware" + "github.com/weaveworks/common/signals" +) + +func init() { + tracer, err := loki.NewTracer() + if err != nil { + panic(fmt.Sprintf("Failed to create tracer: %v", err)) + } else { + opentracing.InitGlobalTracer(tracer) + } +} + +// Config for a Server +type Config struct { + MetricsNamespace string + HTTPListenPort int + GRPCListenPort int + + ServerGracefulShutdownTimeout time.Duration + HTTPServerReadTimeout time.Duration + HTTPServerWriteTimeout time.Duration + HTTPServerIdleTimeout time.Duration + + GRPCMiddleware []grpc.UnaryServerInterceptor + HTTPMiddleware []middleware.Interface +} + +// RegisterFlags adds the flags required to config this to the given FlagSet +func (cfg *Config) RegisterFlags(f *flag.FlagSet) { + f.IntVar(&cfg.HTTPListenPort, "server.http-listen-port", 80, "HTTP server listen port.") + f.IntVar(&cfg.GRPCListenPort, "server.grpc-listen-port", 9095, "gRPC server listen port.") + f.DurationVar(&cfg.ServerGracefulShutdownTimeout, "server.graceful-shutdown-timeout", 5*time.Second, "Timeout for graceful shutdowns") + f.DurationVar(&cfg.HTTPServerReadTimeout, "server.http-read-timeout", 5*time.Second, "Read timeout for HTTP server") + f.DurationVar(&cfg.HTTPServerWriteTimeout, "server.http-write-timeout", 5*time.Second, "Write timeout for HTTP server") + f.DurationVar(&cfg.HTTPServerIdleTimeout, "server.http-idle-timeout", 120*time.Second, "Idle timeout for HTTP server") +} + +// Server wraps a HTTP and gRPC server, and some common initialization. +// +// Servers will be automatically instrumented for Prometheus metrics +// and Loki tracing. HTTP over gRPC +type Server struct { + cfg Config + handler *signals.Handler + httpListener net.Listener + grpcListener net.Listener + httpServer *http.Server + + HTTP *mux.Router + GRPC *grpc.Server +} + +// New makes a new Server +func New(cfg Config) (*Server, error) { + // Setup listeners first, so we can fail early if the port is in use. + httpListener, err := net.Listen("tcp", fmt.Sprintf(":%d", cfg.HTTPListenPort)) + if err != nil { + return nil, err + } + + grpcListener, err := net.Listen("tcp", fmt.Sprintf(":%d", cfg.GRPCListenPort)) + if err != nil { + return nil, err + } + + // Prometheus histograms for requests. + requestDuration := prometheus.NewHistogramVec(prometheus.HistogramOpts{ + Namespace: cfg.MetricsNamespace, + Name: "request_duration_seconds", + Help: "Time (in seconds) spent serving HTTP requests.", + Buckets: prometheus.DefBuckets, + }, []string{"method", "route", "status_code", "ws"}) + prometheus.MustRegister(requestDuration) + + // Setup gRPC server + grpcMiddleware := []grpc.UnaryServerInterceptor{ + middleware.ServerLoggingInterceptor, + middleware.ServerInstrumentInterceptor(requestDuration), + otgrpc.OpenTracingServerInterceptor(opentracing.GlobalTracer()), + } + grpcMiddleware = append(grpcMiddleware, cfg.GRPCMiddleware...) + grpcServer := grpc.NewServer( + grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer( + grpcMiddleware..., + )), + ) + + // Setup HTTP server + router := mux.NewRouter() + router.Handle("/metrics", prometheus.Handler()) + router.Handle("/traces", loki.Handler()) + router.PathPrefix("/debug/pprof").Handler(http.DefaultServeMux) + httpMiddleware := []middleware.Interface{ + middleware.Log{}, + middleware.Instrument{ + Duration: requestDuration, + RouteMatcher: router, + }, + middleware.Func(func(handler http.Handler) http.Handler { + return nethttp.Middleware(opentracing.GlobalTracer(), handler) + }), + } + httpMiddleware = append(httpMiddleware, cfg.HTTPMiddleware...) + httpServer := &http.Server{ + ReadTimeout: cfg.HTTPServerReadTimeout, + WriteTimeout: cfg.HTTPServerWriteTimeout, + IdleTimeout: cfg.HTTPServerIdleTimeout, + Handler: middleware.Merge(httpMiddleware...).Wrap(router), + } + + return &Server{ + cfg: cfg, + httpListener: httpListener, + grpcListener: grpcListener, + httpServer: httpServer, + handler: signals.NewHandler(log.StandardLogger()), + + HTTP: router, + GRPC: grpcServer, + }, nil +} + +// Run the server; blocks until SIGTERM is received. +func (s *Server) Run() { + go s.httpServer.Serve(s.httpListener) + + // Setup gRPC server + // for HTTP over gRPC, ensure we don't double-count the middleware + httpgrpc.RegisterHTTPServer(s.GRPC, httpgrpc.NewServer(s.HTTP)) + go s.GRPC.Serve(s.grpcListener) + defer s.GRPC.GracefulStop() + + // Wait for a signal + s.handler.Loop() +} + +// Stop unblocks Run(). +func (s *Server) Stop() { + s.handler.Stop() +} + +// Shutdown the server, gracefully. Should be defered after New(). +func (s *Server) Shutdown() { + ctx, cancel := context.WithTimeout(context.Background(), s.cfg.ServerGracefulShutdownTimeout) + defer cancel() // releases resources if httpServer.Churdown completes before timeout elapses + + s.httpServer.Shutdown(ctx) + s.GRPC.Stop() +} diff --git a/vendor/github.com/weaveworks/common/signals/signals.go b/vendor/github.com/weaveworks/common/signals/signals.go index 61a3ceb2c..cca60b9b5 100644 --- a/vendor/github.com/weaveworks/common/signals/signals.go +++ b/vendor/github.com/weaveworks/common/signals/signals.go @@ -18,24 +18,58 @@ type Logger interface { Infof(format string, args ...interface{}) } -// SignalHandlerLoop blocks until it receives a SIGINT, SIGTERM or SIGQUIT. -// For SIGINT and SIGTERM, it exits; for SIGQUIT is print a goroutine stack -// dump. -func SignalHandlerLoop(log Logger, ss ...SignalReceiver) { +// Handler handles signals, can be interrupted. +// On SIGINT or SIGTERM it will exit, on SIGQUIT it +// will dump goroutine stacks to the Logger. +type Handler struct { + log Logger + receivers []SignalReceiver + quit chan struct{} +} + +// NewHandler makes a new Handler. +func NewHandler(log Logger, receivers ...SignalReceiver) *Handler { + return &Handler{ + log: log, + receivers: receivers, + quit: make(chan struct{}), + } +} + +// Stop the handler +func (h *Handler) Stop() { + close(h.quit) +} + +// Loop handles signals. +func (h *Handler) Loop() { sigs := make(chan os.Signal, 1) signal.Notify(sigs, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM) buf := make([]byte, 1<<20) for { - switch <-sigs { - case syscall.SIGINT, syscall.SIGTERM: - log.Infof("=== received SIGINT/SIGTERM ===\n*** exiting") - for _, subsystem := range ss { - subsystem.Stop() - } + select { + case <-h.quit: + h.log.Infof("=== Handler.Stop()'d ===") return - case syscall.SIGQUIT: - stacklen := runtime.Stack(buf, true) - log.Infof("=== received SIGQUIT ===\n*** goroutine dump...\n%s\n*** end", buf[:stacklen]) + case sig := <-sigs: + switch sig { + case syscall.SIGINT, syscall.SIGTERM: + h.log.Infof("=== received SIGINT/SIGTERM ===\n*** exiting") + for _, subsystem := range h.receivers { + subsystem.Stop() + } + return + case syscall.SIGQUIT: + stacklen := runtime.Stack(buf, true) + h.log.Infof("=== received SIGQUIT ===\n*** goroutine dump...\n%s\n*** end", buf[:stacklen]) + } } } } + +// SignalHandlerLoop blocks until it receives a SIGINT, SIGTERM or SIGQUIT. +// For SIGINT and SIGTERM, it exits; for SIGQUIT is print a goroutine stack +// dump. +func SignalHandlerLoop(log Logger, ss ...SignalReceiver) { + NewHandler(log, ss...).Loop() +} diff --git a/vendor/github.com/weaveworks/common/test/exec/exec.go b/vendor/github.com/weaveworks/common/test/exec/exec.go index 58b04904f..a18f11824 100644 --- a/vendor/github.com/weaveworks/common/test/exec/exec.go +++ b/vendor/github.com/weaveworks/common/test/exec/exec.go @@ -10,11 +10,6 @@ import ( type mockCmd struct { io.ReadCloser - quit chan struct{} -} - -type blockingReader struct { - quit chan struct{} } // NewMockCmdString creates a new mock Cmd which has s on its stdout pipe @@ -27,7 +22,6 @@ func NewMockCmdString(s string) exec.Cmd { bytes.NewBufferString(s), ioutil.NopCloser(nil), }, - quit: make(chan struct{}), } } @@ -35,7 +29,6 @@ func NewMockCmdString(s string) exec.Cmd { func NewMockCmd(rc io.ReadCloser) exec.Cmd { return &mockCmd{ ReadCloser: rc, - quit: make(chan struct{}), } } @@ -52,11 +45,10 @@ func (c *mockCmd) StdoutPipe() (io.ReadCloser, error) { } func (c *mockCmd) StderrPipe() (io.ReadCloser, error) { - return &blockingReader{c.quit}, nil + return ioutil.NopCloser(bytes.NewReader(nil)), nil } func (c *mockCmd) Kill() error { - close(c.quit) return nil } @@ -69,13 +61,3 @@ func (c *mockCmd) Run() error { } func (c *mockCmd) SetEnv([]string) {} - -func (b *blockingReader) Read(p []byte) (n int, err error) { - <-b.quit - return 0, nil -} - -func (b *blockingReader) Close() error { - <-b.quit - return nil -} diff --git a/vendor/github.com/weaveworks/common/user/grpc.go b/vendor/github.com/weaveworks/common/user/grpc.go new file mode 100644 index 000000000..acbb4e18a --- /dev/null +++ b/vendor/github.com/weaveworks/common/user/grpc.go @@ -0,0 +1,51 @@ +package user + +import ( + "golang.org/x/net/context" + "google.golang.org/grpc/metadata" +) + +// ExtractFromGRPCRequest extracts the user ID from the request metadata and returns +// the user ID and a context with the user ID injected. +func ExtractFromGRPCRequest(ctx context.Context) (string, context.Context, error) { + md, ok := metadata.FromContext(ctx) + if !ok { + return "", ctx, ErrNoUserID + } + + userIDs, ok := md[lowerOrgIDHeaderName] + if !ok || len(userIDs) != 1 { + return "", ctx, ErrNoUserID + } + + return userIDs[0], Inject(ctx, userIDs[0]), nil +} + +// InjectIntoGRPCRequest injects the userID from the context into the request metadata. +func InjectIntoGRPCRequest(ctx context.Context) (context.Context, error) { + userID, err := Extract(ctx) + if err != nil { + return ctx, err + } + + md, ok := metadata.FromContext(ctx) + if !ok { + md = metadata.New(map[string]string{}) + } + newCtx := ctx + if userIDs, ok := md[lowerOrgIDHeaderName]; ok { + if len(userIDs) == 1 { + if userIDs[0] != userID { + return ctx, ErrDifferentIDPresent + } + } else { + return ctx, ErrTooManyUserIDs + } + } else { + md = md.Copy() + md[lowerOrgIDHeaderName] = []string{userID} + newCtx = metadata.NewContext(ctx, md) + } + + return newCtx, nil +} diff --git a/vendor/github.com/weaveworks/common/user/http.go b/vendor/github.com/weaveworks/common/user/http.go new file mode 100644 index 000000000..9ed6e2f3a --- /dev/null +++ b/vendor/github.com/weaveworks/common/user/http.go @@ -0,0 +1,31 @@ +package user + +import ( + "net/http" + + "golang.org/x/net/context" +) + +// ExtractFromHTTPRequest extracts the user ID from the request headers and returns +// the user ID and a context with the user ID embbedded. +func ExtractFromHTTPRequest(r *http.Request) (string, context.Context, error) { + userID := r.Header.Get(orgIDHeaderName) + if userID == "" { + return "", r.Context(), ErrNoUserID + } + return userID, Inject(r.Context(), userID), nil +} + +// InjectIntoHTTPRequest injects the userID from the context into the request headers. +func InjectIntoHTTPRequest(ctx context.Context, r *http.Request) error { + userID, err := Extract(ctx) + if err != nil { + return err + } + existingID := r.Header.Get(orgIDHeaderName) + if existingID != "" && existingID != userID { + return ErrDifferentIDPresent + } + r.Header.Set(orgIDHeaderName, userID) + return nil +} diff --git a/vendor/github.com/weaveworks/common/user/id.go b/vendor/github.com/weaveworks/common/user/id.go index 4b90a2a55..5f1467a10 100644 --- a/vendor/github.com/weaveworks/common/user/id.go +++ b/vendor/github.com/weaveworks/common/user/id.go @@ -1,32 +1,41 @@ package user import ( - "fmt" - "golang.org/x/net/context" + + "github.com/weaveworks/common/errors" ) -// UserIDContextKey is the key used in contexts to find the userid type contextKey int -const userIDContextKey contextKey = 0 +const ( + // UserIDContextKey is the key used in contexts to find the userid + userIDContextKey contextKey = 0 -// OrgIDHeaderName is a legacy from scope as a service. -const OrgIDHeaderName = "X-Scope-OrgID" + // orgIDHeaderName is a legacy from scope as a service. + orgIDHeaderName = "X-Scope-OrgID" -// LowerOrgIDHeaderName as gRPC / HTTP2.0 headers are lowercased. -const LowerOrgIDHeaderName = "x-scope-orgid" + // LowerOrgIDHeaderName as gRPC / HTTP2.0 headers are lowercased. + lowerOrgIDHeaderName = "x-scope-orgid" +) -// GetID returns the user -func GetID(ctx context.Context) (string, error) { - userid, ok := ctx.Value(userIDContextKey).(string) +// Errors that we return +const ( + ErrNoUserID = errors.Error("no user id") + ErrDifferentIDPresent = errors.Error("different user ID already present") + ErrTooManyUserIDs = errors.Error("multiple user IDs present") +) + +// Extract gets the user ID from the context +func Extract(ctx context.Context) (string, error) { + userID, ok := ctx.Value(userIDContextKey).(string) if !ok { - return "", fmt.Errorf("no user id") + return "", ErrNoUserID } - return userid, nil + return userID, nil } -// WithID returns a derived context containing the user ID. -func WithID(ctx context.Context, userID string) context.Context { +// Inject returns a derived context containing the user ID. +func Inject(ctx context.Context, userID string) context.Context { return context.WithValue(ctx, interface{}(userIDContextKey), userID) } diff --git a/vendor/manifest b/vendor/manifest index 3e3ce967a..ba9f93e02 100644 --- a/vendor/manifest +++ b/vendor/manifest @@ -1414,7 +1414,7 @@ "importpath": "github.com/weaveworks/common", "repository": "https://github.com/weaveworks/common", "vcs": "git", - "revision": "8da456c848efd2a13ffb5a3f9f4507c52c3f52e1", + "revision": "f94043b3da140c7a735b1f2f286d72d19014b200", "branch": "master", "notests": true },