14198: Expand federation support for collections and links
authorPeter Amstutz <pamstutz@veritasgenetics.com>
Fri, 9 Nov 2018 19:24:00 +0000 (14:24 -0500)
committerPeter Amstutz <pamstutz@veritasgenetics.com>
Fri, 9 Nov 2018 19:24:00 +0000 (14:24 -0500)
Add collections and links to the list of endpoints where requests
bearing cluster_id are routed through controller to remote clusters.

Arvados-DCO-1.1-Signed-off-by: Peter Amstutz <pamstutz@veritasgenetics.com>

lib/controller/fed_collections.go
lib/controller/fed_generic.go
lib/controller/federation.go

index 024af83c2b36cdd254ec1248ed3df6e3a71e02e0..ab49e39d12656c3f960e840f82c9f4974e59d32d 100644 (file)
@@ -22,11 +22,6 @@ import (
        "git.curoverse.com/arvados.git/sdk/go/keepclient"
 )
 
-type collectionFederatedRequestHandler struct {
-       next    http.Handler
-       handler *Handler
-}
-
 func rewriteSignatures(clusterID string, expectHash string,
        resp *http.Response, requestError error) (newResponse *http.Response, err error) {
 
@@ -159,35 +154,52 @@ type searchRemoteClusterForPDH struct {
        statusCode    *int
 }
 
-func (h *collectionFederatedRequestHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
-       if req.Method != "GET" {
+func fetchRemoteCollectionByUUID(
+       h *genericFederatedRequestHandler,
+       effectiveMethod string,
+       clusterId *string,
+       uuid string,
+       remainder string,
+       w http.ResponseWriter,
+       req *http.Request) bool {
+
+       if effectiveMethod != "GET" {
                // Only handle GET requests right now
-               h.next.ServeHTTP(w, req)
-               return
+               return false
        }
 
-       m := collectionByPDHRe.FindStringSubmatch(req.URL.Path)
-       if len(m) != 2 {
-               // Not a collection PDH GET request
-               m = collectionRe.FindStringSubmatch(req.URL.Path)
-               clusterId := ""
-
-               if len(m) > 0 {
-                       clusterId = m[2]
-               }
-
-               if clusterId != "" && clusterId != h.handler.Cluster.ClusterID {
+       if uuid != "" {
+               // Collection UUID GET request
+               *clusterId = uuid[0:5]
+               if *clusterId != "" && *clusterId != h.handler.Cluster.ClusterID {
                        // request for remote collection by uuid
-                       resp, err := h.handler.remoteClusterRequest(clusterId, req)
-                       newResponse, err := rewriteSignatures(clusterId, "", resp, err)
+                       resp, err := h.handler.remoteClusterRequest(*clusterId, req)
+                       newResponse, err := rewriteSignatures(*clusterId, "", resp, err)
                        h.handler.proxy.ForwardResponse(w, newResponse, err)
-                       return
+                       return true
                }
-               // not a collection UUID request, or it is a request
-               // for a local UUID, either way, continue down the
-               // handler stack.
-               h.next.ServeHTTP(w, req)
-               return
+       }
+
+       return false
+}
+
+func fetchRemoteCollectionByPDH(
+       h *genericFederatedRequestHandler,
+       effectiveMethod string,
+       clusterId *string,
+       uuid string,
+       remainder string,
+       w http.ResponseWriter,
+       req *http.Request) bool {
+
+       if effectiveMethod != "GET" {
+               // Only handle GET requests right now
+               return false
+       }
+
+       m := collectionsByPDHRe.FindStringSubmatch(req.URL.Path)
+       if len(m) != 2 {
+               return false
        }
 
        // Request for collection by PDH.  Search the federation.
@@ -197,7 +209,7 @@ func (h *collectionFederatedRequestHandler) ServeHTTP(w http.ResponseWriter, req
        newResp, err := filterLocalClusterResponse(resp, err)
        if newResp != nil || err != nil {
                h.handler.proxy.ForwardResponse(w, newResp, err)
-               return
+               return true
        }
 
        // Create a goroutine for each cluster in the
@@ -280,7 +292,7 @@ func (h *collectionFederatedRequestHandler) ServeHTTP(w http.ResponseWriter, req
                select {
                case newResp = <-success:
                        h.handler.proxy.ForwardResponse(w, newResp, nil)
-                       return
+                       return true
                case <-sharedContext.Done():
                        var errors []string
                        for len(errorChan) > 0 {
@@ -293,7 +305,10 @@ func (h *collectionFederatedRequestHandler) ServeHTTP(w http.ResponseWriter, req
                                errors = append(errors, err.Error())
                        }
                        httpserver.Errors(w, errors, errorCode)
-                       return
+                       return true
                }
        }
+
+       // shouldn't ever get here
+       return true
 }
index 6c8135cf91253a004971399ef0a49a5fe07bf34d..9c8b1614bcdcceaa4be70bcba15fa694e26940dc 100644 (file)
@@ -295,8 +295,13 @@ func (h *genericFederatedRequestHandler) ServeHTTP(w http.ResponseWriter, req *h
                return
        }
 
+       var uuid string
+       if len(m[1]) > 0 {
+               // trim leading slash
+               uuid = m[1][1:]
+       }
        for _, d := range h.delegates {
-               if d(h, effectiveMethod, &clusterId, m[1], m[3], w, req) {
+               if d(h, effectiveMethod, &clusterId, uuid, m[3], w, req) {
                        return
                }
        }
index e08a1c16742a6d5ea9b251d2906b24f6d5b00e61..1a0b78be2b394c5f2de7a094cb45480fee38e988 100644 (file)
@@ -25,8 +25,9 @@ var pathPattern = `^/arvados/v1/%s(/([0-9a-z]{5})-%s-[0-9a-z]{15})?(.*)$`
 var wfRe = regexp.MustCompile(fmt.Sprintf(pathPattern, "workflows", "7fd4e"))
 var containersRe = regexp.MustCompile(fmt.Sprintf(pathPattern, "containers", "dz642"))
 var containerRequestsRe = regexp.MustCompile(fmt.Sprintf(pathPattern, "container_requests", "xvhdp"))
-var collectionRe = regexp.MustCompile(fmt.Sprintf(pathPattern, "collections", "4zz18"))
-var collectionByPDHRe = regexp.MustCompile(`^/arvados/v1/collections/([0-9a-fA-F]{32}\+[0-9]+)+$`)
+var collectionsRe = regexp.MustCompile(fmt.Sprintf(pathPattern, "collections", "4zz18"))
+var collectionsByPDHRe = regexp.MustCompile(`^/arvados/v1/collections/([0-9a-fA-F]{32}\+[0-9]+)+$`)
+var linksRe = regexp.MustCompile(fmt.Sprintf(pathPattern, "links", "o0j2j"))
 
 func (h *Handler) remoteClusterRequest(remoteID string, req *http.Request) (*http.Response, error) {
        remote, ok := h.Cluster.RemoteClusters[remoteID]
@@ -89,6 +90,9 @@ func (h *Handler) setupProxyRemoteCluster(next http.Handler) http.Handler {
        containersHandler := &genericFederatedRequestHandler{next, h, containersRe, nil}
        containerRequestsHandler := &genericFederatedRequestHandler{next, h, containerRequestsRe,
                []federatedRequestDelegate{remoteContainerRequestCreate}}
+       collectionsRequestsHandler := &genericFederatedRequestHandler{next, h, collectionsRe,
+               []federatedRequestDelegate{fetchRemoteCollectionByUUID, fetchRemoteCollectionByPDH}}
+       linksRequestsHandler := &genericFederatedRequestHandler{next, h, linksRe, nil}
 
        mux.Handle("/arvados/v1/workflows", wfHandler)
        mux.Handle("/arvados/v1/workflows/", wfHandler)
@@ -96,8 +100,10 @@ func (h *Handler) setupProxyRemoteCluster(next http.Handler) http.Handler {
        mux.Handle("/arvados/v1/containers/", containersHandler)
        mux.Handle("/arvados/v1/container_requests", containerRequestsHandler)
        mux.Handle("/arvados/v1/container_requests/", containerRequestsHandler)
-       mux.Handle("/arvados/v1/collections", next)
-       mux.Handle("/arvados/v1/collections/", &collectionFederatedRequestHandler{next, h})
+       mux.Handle("/arvados/v1/collections", collectionsRequestsHandler)
+       mux.Handle("/arvados/v1/collections/", collectionsRequestsHandler)
+       mux.Handle("/arvados/v1/links", linksRequestsHandler)
+       mux.Handle("/arvados/v1/links/", linksRequestsHandler)
        mux.Handle("/", next)
 
        return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {