12167: Propagate X-Request-Id in API calls.
authorTom Clegg <tclegg@veritasgenetics.com>
Tue, 24 Apr 2018 03:17:41 +0000 (23:17 -0400)
committerTom Clegg <tclegg@veritasgenetics.com>
Tue, 24 Apr 2018 20:12:41 +0000 (16:12 -0400)
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg@veritasgenetics.com>

sdk/go/arvados/client.go
sdk/go/httpserver/id_generator.go
services/keep-web/handler.go
services/keep-web/server.go

index 24f3faac16053fd6b40457a6111a7ac4d954f994..ce2c5aea3db7c34bb480b3e4c74e14aecc26ab31 100644 (file)
@@ -6,6 +6,7 @@ package arvados
 
 import (
        "bytes"
+       "context"
        "crypto/tls"
        "encoding/json"
        "fmt"
@@ -19,6 +20,8 @@ import (
        "regexp"
        "strings"
        "time"
+
+       "git.curoverse.com/arvados.git/sdk/go/httpserver"
 )
 
 // A Client is an HTTP client with an API endpoint and a set of
@@ -50,6 +53,8 @@ type Client struct {
        KeepServiceURIs []string `json:",omitempty"`
 
        dd *DiscoveryDocument
+
+       ctx context.Context
 }
 
 // The default http.Client used by a Client with Insecure==true and
@@ -92,11 +97,25 @@ func NewClientFromEnv() *Client {
        }
 }
 
-// Do adds authentication headers and then calls (*http.Client)Do().
+var reqIDGen = httpserver.IDGenerator{Prefix: "req-"}
+
+// Do adds Authorization and X-Request-Id headers and then calls
+// (*http.Client)Do().
 func (c *Client) Do(req *http.Request) (*http.Response, error) {
        if c.AuthToken != "" {
                req.Header.Add("Authorization", "OAuth2 "+c.AuthToken)
        }
+
+       reqid, ok := c.context().Value(contextKeyRequestID).(string)
+       if !ok {
+               reqid = reqIDGen.Next()
+       }
+       if req.Header.Get("X-Request-Id") == "" {
+               if req.Header == nil {
+                       req.Header = http.Header{}
+               }
+               req.Header.Set("X-Request-Id", reqid)
+       }
        return c.httpClient().Do(req)
 }
 
@@ -225,6 +244,23 @@ func (c *Client) UpdateBody(rsc resource) io.Reader {
        return bytes.NewBufferString(v.Encode())
 }
 
+type contextKey string
+
+var contextKeyRequestID contextKey = "X-Request-Id"
+
+func (c *Client) WithRequestID(reqid string) *Client {
+       cc := *c
+       cc.ctx = context.WithValue(cc.context(), contextKeyRequestID, reqid)
+       return &cc
+}
+
+func (c *Client) context() context.Context {
+       if c.ctx == nil {
+               return context.Background()
+       }
+       return c.ctx
+}
+
 func (c *Client) httpClient() *http.Client {
        switch {
        case c.Client != nil:
index d2c3a41f2108e2bc852f56119b747a7ec9423e7a..6452136d85eede6896f1dca1648e00b4ba6ae8e7 100644 (file)
@@ -45,6 +45,9 @@ func AddRequestIDs(h http.Handler) http.Handler {
        gen := &IDGenerator{Prefix: "req-"}
        return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
                if req.Header.Get("X-Request-Id") == "" {
+                       if req.Header == nil {
+                               req.Header = http.Header{}
+                       }
                        req.Header.Set("X-Request-Id", gen.Next())
                }
                h.ServeHTTP(w, req)
index 8b61b54b97564d9d40dccfe9b5587acebc021e08..4ffac26797514de7317099307f36e3be57b3da89 100644 (file)
@@ -424,11 +424,11 @@ func (h *handler) ServeHTTP(wOrig http.ResponseWriter, r *http.Request) {
        }
        applyContentDispositionHdr(w, r, basename, attachment)
 
-       client := &arvados.Client{
+       client := (&arvados.Client{
                APIHost:   arv.ApiServer,
                AuthToken: arv.ApiToken,
                Insecure:  arv.ApiInsecure,
-       }
+       }).WithRequestID(r.Header.Get("X-Request-Id"))
 
        fs, err := collection.FileSystem(client, kc)
        if err != nil {
@@ -529,11 +529,11 @@ func (h *handler) serveSiteFS(w http.ResponseWriter, r *http.Request, tokens []s
                http.Error(w, err.Error(), http.StatusInternalServerError)
                return
        }
-       client := &arvados.Client{
+       client := (&arvados.Client{
                APIHost:   arv.ApiServer,
                AuthToken: arv.ApiToken,
                Insecure:  arv.ApiInsecure,
-       }
+       }).WithRequestID(r.Header.Get("X-Request-Id"))
        fs := client.SiteFileSystem(kc)
        f, err := fs.Open(r.URL.Path)
        if os.IsNotExist(err) {
index 0edcf31708b6a0d1e9688536d8523a24827c0a29..aed2989219cab297adcc0629c0162ae1f1610f36 100644 (file)
@@ -14,7 +14,7 @@ type server struct {
 }
 
 func (srv *server) Start() error {
-       srv.Handler = &handler{Config: srv.Config}
+       srv.Handler = httpserver.AddRequestIDs(&handler{Config: srv.Config})
        srv.Addr = srv.Config.Listen
        return srv.Server.Start()
 }