[ARVADOS] created: 1.2.0-106-ge9a3b4476
Git user
git at public.curoverse.com
Thu Sep 27 12:30:32 EDT 2018
at e9a3b4476759582e49404a0a1f8e820ad5d97fcd (commit)
commit e9a3b4476759582e49404a0a1f8e820ad5d97fcd
Author: Peter Amstutz <pamstutz at veritasgenetics.com>
Date: Thu Sep 27 12:29:35 2018 -0400
13619: MultiClusterQuery passes test
Arvados-DCO-1.1-Signed-off-by: Peter Amstutz <pamstutz at veritasgenetics.com>
diff --git a/lib/controller/federation.go b/lib/controller/federation.go
index dc03e039f..caa84ca5f 100644
--- a/lib/controller/federation.go
+++ b/lib/controller/federation.go
@@ -14,7 +14,6 @@ import (
"fmt"
"io"
"io/ioutil"
- "log"
"net/http"
"net/url"
"regexp"
@@ -129,7 +128,11 @@ func (c *responseCollector) collectResponse(resp *http.Response, requestError er
defer c.mtx.Unlock()
if err == nil {
- c.responses = append(c.responses, loadInto["items"].([]interface{})...)
+ if resp.StatusCode != http.StatusOK {
+ c.errors = append(c.errors, fmt.Errorf("error %v", loadInto["errors"]))
+ } else {
+ c.responses = append(c.responses, loadInto["items"].([]interface{})...)
+ }
} else {
c.errors = append(c.errors, err)
}
@@ -186,19 +189,17 @@ func (h *genericFederatedRequestHandler) handleMultiClusterQuery(w http.Response
wg.Done()
<-sem
}()
- remoteReq := *req
+ var remoteReq http.Request
+ remoteReq.Header = req.Header
remoteReq.Method = "POST"
- remoteReq.URL = &url.URL{
- Path: req.URL.Path,
- RawPath: req.URL.RawPath,
- }
+ remoteReq.URL = &url.URL{Path: req.URL.Path}
remoteParams := make(url.Values)
remoteParams["_method"] = []string{"GET"}
content, err := json.Marshal(v)
if err != nil {
rc.mtx.Lock()
+ defer rc.mtx.Unlock()
rc.errors = append(rc.errors, err)
- rc.mtx.Unlock()
return
}
remoteParams["filters"] = []string{fmt.Sprintf(`[["uuid", "in", %s]]`, content)}
@@ -206,8 +207,8 @@ func (h *genericFederatedRequestHandler) handleMultiClusterQuery(w http.Response
remoteReq.Body = ioutil.NopCloser(bytes.NewBufferString(enc))
if k == h.handler.Cluster.ClusterID {
- h.handler.proxy.Do(w, &remoteReq, remoteReq.URL,
- h.handler.secureClient, rc.collectResponse)
+ h.handler.localClusterRequest(w, &remoteReq,
+ rc.collectResponse)
} else {
h.handler.remoteClusterRequest(k, w, &remoteReq,
rc.collectResponse)
@@ -222,17 +223,13 @@ func (h *genericFederatedRequestHandler) handleMultiClusterQuery(w http.Response
for _, e := range rc.errors {
strerr = append(strerr, e.Error())
}
- httpserver.Errors(w, strerr, http.StatusBadRequest)
+ httpserver.Errors(w, strerr, http.StatusBadGateway)
} else {
- log.Printf("Sending status ok %+v", rc)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
itemList := make(map[string]interface{})
itemList["items"] = rc.responses
- //x, _ := json.Marshal(itemList)
- //log.Printf("Sending response %v", string(x))
json.NewEncoder(w).Encode(itemList)
- log.Printf("Sent?")
}
return true
@@ -285,7 +282,7 @@ func (h *genericFederatedRequestHandler) ServeHTTP(w http.ResponseWriter, req *h
return
}
}
- log.Printf("Clusterid is %q", clusterId)
+ //log.Printf("Clusterid is %q", clusterId)
if clusterId == "" || clusterId == h.handler.Cluster.ClusterID {
h.next.ServeHTTP(w, req)
@@ -405,11 +402,7 @@ func (rw rewriteSignaturesClusterId) rewriteSignatures(resp *http.Response, requ
return resp, nil
}
-type searchLocalClusterForPDH struct {
- sentResponse bool
-}
-
-func (s *searchLocalClusterForPDH) filterLocalClusterResponse(resp *http.Response, requestError error) (newResponse *http.Response, err error) {
+func filterLocalClusterResponse(resp *http.Response, requestError error) (newResponse *http.Response, err error) {
if requestError != nil {
return resp, requestError
}
@@ -417,10 +410,8 @@ func (s *searchLocalClusterForPDH) filterLocalClusterResponse(resp *http.Respons
if resp.StatusCode == 404 {
// Suppress returning this result, because we want to
// search the federation.
- s.sentResponse = false
return nil, nil
}
- s.sentResponse = true
return resp, nil
}
@@ -526,26 +517,7 @@ func (h *collectionFederatedRequestHandler) ServeHTTP(w http.ResponseWriter, req
// Request for collection by PDH. Search the federation.
// First, query the local cluster.
- urlOut, insecure, err := findRailsAPI(h.handler.Cluster, h.handler.NodeProfile)
- if err != nil {
- httpserver.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
-
- urlOut = &url.URL{
- Scheme: urlOut.Scheme,
- Host: urlOut.Host,
- Path: req.URL.Path,
- RawPath: req.URL.RawPath,
- RawQuery: req.URL.RawQuery,
- }
- client := h.handler.secureClient
- if insecure {
- client = h.handler.insecureClient
- }
- sf := &searchLocalClusterForPDH{}
- h.handler.proxy.Do(w, req, urlOut, client, sf.filterLocalClusterResponse)
- if sf.sentResponse {
+ if h.handler.localClusterRequest(w, req, filterLocalClusterResponse) {
return
}
diff --git a/lib/controller/federation_test.go b/lib/controller/federation_test.go
index b94efa6ae..113fa9eeb 100644
--- a/lib/controller/federation_test.go
+++ b/lib/controller/federation_test.go
@@ -8,7 +8,6 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
- "log"
"net/http"
"net/http/httptest"
"net/url"
@@ -304,12 +303,10 @@ func (s *FederationSuite) checkJSONErrorMatches(c *check.C, resp *http.Response,
c.Check(jresp.Errors[0], check.Matches, re)
}
-func (s *FederationSuite) localServiceReturns404(c *check.C) *httpserver.Server {
+func (s *FederationSuite) localServiceHandler(c *check.C, h http.Handler) *httpserver.Server {
srv := &httpserver.Server{
Server: http.Server{
- Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
- w.WriteHeader(404)
- }),
+ Handler: h,
},
}
@@ -325,6 +322,12 @@ func (s *FederationSuite) localServiceReturns404(c *check.C) *httpserver.Server
return srv
}
+func (s *FederationSuite) localServiceReturns404(c *check.C) *httpserver.Server {
+ return s.localServiceHandler(c, http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
+ w.WriteHeader(404)
+ }))
+}
+
func (s *FederationSuite) TestGetLocalCollection(c *check.C) {
np := arvados.NodeProfile{
Controller: arvados.SystemServiceInstance{Listen: ":"},
@@ -629,14 +632,23 @@ func (s *FederationSuite) TestListRemoteContainer(c *check.C) {
}
func (s *FederationSuite) TestListMultiRemoteContainers(c *check.C) {
- defer s.localServiceReturns404(c).Close()
+ defer s.localServiceHandler(c, http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
+ bd, _ := ioutil.ReadAll(req.Body)
+ c.Check(string(bd), check.Equals, `_method=GET&filters=%5B%5B%22uuid%22%2C+%22in%22%2C+%5B%22zhome-xvhdp-cr5queuedcontnr%22%5D%5D%5D`)
+ w.WriteHeader(200)
+ w.Write([]byte(`{"items": [{"uuid": "zhome-xvhdp-cr5queuedcontnr"}]}`))
+ })).Close()
req := httptest.NewRequest("GET", "/arvados/v1/containers?filters="+
url.QueryEscape(fmt.Sprintf(`[["uuid", "in", ["%v", "zhome-xvhdp-cr5queuedcontnr"]]]`, arvadostest.QueuedContainerUUID)), nil)
req.Header.Set("Authorization", "Bearer "+arvadostest.ActiveToken)
resp := s.testRequest(req)
- log.Printf("got %+v", resp)
- c.Assert(resp.StatusCode, check.Equals, http.StatusOK)
+ c.Check(resp.StatusCode, check.Equals, http.StatusOK)
var cn arvados.ContainerList
c.Check(json.NewDecoder(resp.Body).Decode(&cn), check.IsNil)
- c.Check(cn.Items[0].UUID, check.Equals, arvadostest.QueuedContainerUUID)
+ if cn.Items[0].UUID == arvadostest.QueuedContainerUUID {
+ c.Check(cn.Items[1].UUID, check.Equals, "zhome-xvhdp-cr5queuedcontnr")
+ } else {
+ c.Check(cn.Items[1].UUID, check.Equals, arvadostest.QueuedContainerUUID)
+ c.Check(cn.Items[0].UUID, check.Equals, "zhome-xvhdp-cr5queuedcontnr")
+ }
}
diff --git a/lib/controller/handler.go b/lib/controller/handler.go
index 2b41aba6b..0c31815cb 100644
--- a/lib/controller/handler.go
+++ b/lib/controller/handler.go
@@ -121,11 +121,14 @@ func prepend(next http.Handler, middleware middlewareFunc) http.Handler {
})
}
-func (h *Handler) proxyRailsAPI(w http.ResponseWriter, req *http.Request, next http.Handler) {
+// localClusterRequest sets up a request so it can be proxied to the
+// local API server using proxy.Do(). Returns true if a response was
+// written, false if not.
+func (h *Handler) localClusterRequest(w http.ResponseWriter, req *http.Request, filter ResponseFilter) bool {
urlOut, insecure, err := findRailsAPI(h.Cluster, h.NodeProfile)
if err != nil {
httpserver.Error(w, err.Error(), http.StatusInternalServerError)
- return
+ return true
}
urlOut = &url.URL{
Scheme: urlOut.Scheme,
@@ -138,7 +141,13 @@ func (h *Handler) proxyRailsAPI(w http.ResponseWriter, req *http.Request, next h
if insecure {
client = h.insecureClient
}
- h.proxy.Do(w, req, urlOut, client, nil)
+ return h.proxy.Do(w, req, urlOut, client, filter)
+}
+
+func (h *Handler) proxyRailsAPI(w http.ResponseWriter, req *http.Request, next http.Handler) {
+ if !h.localClusterRequest(w, req, nil) && next != nil {
+ next.ServeHTTP(w, req)
+ }
}
// For now, findRailsAPI always uses the rails API running on this
diff --git a/lib/controller/proxy.go b/lib/controller/proxy.go
index 373b42e8f..951cb9d25 100644
--- a/lib/controller/proxy.go
+++ b/lib/controller/proxy.go
@@ -36,11 +36,15 @@ var dropHeaders = map[string]bool{
type ResponseFilter func(*http.Response, error) (*http.Response, error)
+// Do sends a request, passes the result to the filter (if provided)
+// and then if the result is not suppressed by the filter, sends the
+// request to the ResponseWriter. Returns true if a response was written,
+// false if not.
func (p *proxy) Do(w http.ResponseWriter,
reqIn *http.Request,
urlOut *url.URL,
client *http.Client,
- filter ResponseFilter) {
+ filter ResponseFilter) bool {
// Copy headers from incoming request, then add/replace proxy
// headers like Via and X-Forwarded-For.
@@ -78,7 +82,7 @@ func (p *proxy) Do(w http.ResponseWriter,
resp, err := client.Do(reqOut)
if filter == nil && err != nil {
httpserver.Error(w, err.Error(), http.StatusBadGateway)
- return
+ return true
}
// make sure original response body gets closed
@@ -95,13 +99,13 @@ func (p *proxy) Do(w http.ResponseWriter,
if err != nil {
httpserver.Error(w, err.Error(), http.StatusBadGateway)
- return
+ return true
}
if resp == nil {
// filter() returned a nil response, this means suppress
// writing a response, for the case where there might
// be multiple response writers.
- return
+ return false
}
// the filter gave us a new response body, make sure that gets closed too.
@@ -120,4 +124,5 @@ func (p *proxy) Do(w http.ResponseWriter,
if err != nil {
httpserver.Logger(reqIn).WithError(err).WithField("bytesCopied", n).Error("error copying response body")
}
+ return true
}
commit 342ec439e7965d889a7aca306475424d3a3ff986
Author: Peter Amstutz <pamstutz at veritasgenetics.com>
Date: Thu Sep 27 10:53:22 2018 -0400
13619: Federated multi-object list wip
Arvados-DCO-1.1-Signed-off-by: Peter Amstutz <pamstutz at veritasgenetics.com>
diff --git a/lib/controller/federation.go b/lib/controller/federation.go
index cc22a674d..dc03e039f 100644
--- a/lib/controller/federation.go
+++ b/lib/controller/federation.go
@@ -14,6 +14,7 @@ import (
"fmt"
"io"
"io/ioutil"
+ "log"
"net/http"
"net/url"
"regexp"
@@ -26,7 +27,7 @@ import (
"git.curoverse.com/arvados.git/sdk/go/keepclient"
)
-var pathPattern = `^/arvados/v1/%s(/([0-9a-z]{5})-%s-)?.*$`
+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"))
@@ -73,44 +74,218 @@ func (h *Handler) remoteClusterRequest(remoteID string, w http.ResponseWriter, r
h.proxy.Do(w, req, urlOut, client, filter)
}
+func loadParamsFromForm(req *http.Request, params url.Values) error {
+ body, err := ioutil.ReadAll(req.Body)
+ if err != nil {
+ return err
+ }
+ req.Body = ioutil.NopCloser(bytes.NewBuffer(body))
+ var v2 url.Values
+ if v2, err = url.ParseQuery(string(body)); err != nil {
+ return err
+ }
+ for k, v := range v2 {
+ params[k] = append(params[k], v...)
+ }
+ return nil
+}
+
+func loadParamsFromJson(req *http.Request, loadInto interface{}) error {
+ var cl int64
+ if req.ContentLength > 0 {
+ cl = req.ContentLength
+ }
+ postBody := bytes.NewBuffer(make([]byte, 0, cl))
+ defer req.Body.Close()
+
+ rdr := io.TeeReader(req.Body, postBody)
+
+ err := json.NewDecoder(rdr).Decode(loadInto)
+ if err != nil {
+ return err
+ }
+ req.Body = ioutil.NopCloser(postBody)
+ return nil
+}
+
+type responseCollector struct {
+ mtx sync.Mutex
+ responses []interface{}
+ errors []error
+}
+
+func (c *responseCollector) collectResponse(resp *http.Response, requestError error) (newResponse *http.Response, err error) {
+ if requestError != nil {
+ c.mtx.Lock()
+ defer c.mtx.Unlock()
+ c.errors = append(c.errors, requestError)
+ return nil, nil
+ }
+ defer resp.Body.Close()
+ loadInto := make(map[string]interface{})
+ err = json.NewDecoder(resp.Body).Decode(&loadInto)
+
+ c.mtx.Lock()
+ defer c.mtx.Unlock()
+
+ if err == nil {
+ c.responses = append(c.responses, loadInto["items"].([]interface{})...)
+ } else {
+ c.errors = append(c.errors, err)
+ }
+
+ return nil, nil
+}
+
+func (h *genericFederatedRequestHandler) handleMultiClusterQuery(w http.ResponseWriter, req *http.Request,
+ params url.Values, clusterId *string) bool {
+
+ var filters [][]interface{}
+ err := json.Unmarshal([]byte(params["filters"][0]), &filters)
+ if err != nil {
+ httpserver.Error(w, err.Error(), http.StatusBadRequest)
+ return true
+ }
+ queryClusters := make(map[string][]string)
+ if len(filters) == 1 && len(filters[0]) == 3 {
+ f1 := filters[0]
+ lhs := f1[0].(string)
+ op := f1[1].(string)
+ rhs := f1[2].([]interface{})
+ if lhs == "uuid" && op == "in" {
+ for _, i := range rhs {
+ u := i.(string)
+ *clusterId = u[0:5]
+ queryClusters[u[0:5]] = append(queryClusters[u[0:5]], u)
+ }
+ }
+ }
+
+ if len(queryClusters) <= 1 {
+ return false
+ }
+
+ wg := sync.WaitGroup{}
+ //var errors []string
+ //var errorCode int = 404
+
+ // use channel as a semaphore to limit it to 4
+ // parallel requests at a time
+ sem := make(chan bool, 4)
+ defer close(sem)
+ req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+
+ rc := responseCollector{}
+ for k, v := range queryClusters {
+ // blocks until it can put a value into the
+ // channel (which has a max queue capacity)
+ sem <- true
+ wg.Add(1)
+ go func(k string, v []string) {
+ defer func() {
+ wg.Done()
+ <-sem
+ }()
+ remoteReq := *req
+ remoteReq.Method = "POST"
+ remoteReq.URL = &url.URL{
+ Path: req.URL.Path,
+ RawPath: req.URL.RawPath,
+ }
+ remoteParams := make(url.Values)
+ remoteParams["_method"] = []string{"GET"}
+ content, err := json.Marshal(v)
+ if err != nil {
+ rc.mtx.Lock()
+ rc.errors = append(rc.errors, err)
+ rc.mtx.Unlock()
+ return
+ }
+ remoteParams["filters"] = []string{fmt.Sprintf(`[["uuid", "in", %s]]`, content)}
+ enc := remoteParams.Encode()
+ remoteReq.Body = ioutil.NopCloser(bytes.NewBufferString(enc))
+
+ if k == h.handler.Cluster.ClusterID {
+ h.handler.proxy.Do(w, &remoteReq, remoteReq.URL,
+ h.handler.secureClient, rc.collectResponse)
+ } else {
+ h.handler.remoteClusterRequest(k, w, &remoteReq,
+ rc.collectResponse)
+ }
+ }(k, v)
+ }
+ wg.Wait()
+
+ if len(rc.errors) > 0 {
+ // parallel query
+ var strerr []string
+ for _, e := range rc.errors {
+ strerr = append(strerr, e.Error())
+ }
+ httpserver.Errors(w, strerr, http.StatusBadRequest)
+ } else {
+ log.Printf("Sending status ok %+v", rc)
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ itemList := make(map[string]interface{})
+ itemList["items"] = rc.responses
+ //x, _ := json.Marshal(itemList)
+ //log.Printf("Sending response %v", string(x))
+ json.NewEncoder(w).Encode(itemList)
+ log.Printf("Sent?")
+ }
+
+ return true
+}
+
func (h *genericFederatedRequestHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
m := h.matcher.FindStringSubmatch(req.URL.Path)
clusterId := ""
- if len(m) == 3 {
+ if len(m) > 0 && m[2] != "" {
clusterId = m[2]
}
- if clusterId == "" {
- if values, err := url.ParseQuery(req.URL.RawQuery); err == nil {
- if len(values["cluster_id"]) == 1 {
- clusterId = values["cluster_id"][0]
- }
+ var params url.Values
+ var err error
+ if params, err = url.ParseQuery(req.URL.RawQuery); err != nil {
+ httpserver.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+
+ if req.Method == "POST" && req.Header.Get("Content-Type") == "application/x-www-form-urlencoded" {
+ if err = loadParamsFromForm(req, params); err != nil {
+ httpserver.Error(w, err.Error(), http.StatusBadRequest)
+ return
}
}
+ if len(params["cluster_id"]) == 1 {
+ clusterId = params["cluster_id"][0]
+ }
+
if clusterId == "" && req.Method == "POST" && req.Header.Get("Content-Type") == "application/json" {
var hasClusterId struct {
ClusterID string `json:"cluster_id"`
}
- var cl int64
- if req.ContentLength > 0 {
- cl = req.ContentLength
+ if err = loadParamsFromJson(req, &hasClusterId); err != nil {
+ httpserver.Error(w, err.Error(), http.StatusBadRequest)
+ return
}
- postBody := bytes.NewBuffer(make([]byte, 0, cl))
- defer req.Body.Close()
+ clusterId = hasClusterId.ClusterID
+ }
- rdr := io.TeeReader(req.Body, postBody)
+ effectiveMethod := req.Method
+ if req.Method == "POST" && len(params["_method"]) == 1 {
+ effectiveMethod = params["_method"][0]
+ }
- err := json.NewDecoder(rdr).Decode(&hasClusterId)
- if err != nil {
- httpserver.Error(w, err.Error(), http.StatusBadRequest)
+ if effectiveMethod == "GET" && clusterId == "" && len(params["filters"]) == 1 {
+ if h.handleMultiClusterQuery(w, req, params, &clusterId) {
return
}
- req.Body = ioutil.NopCloser(postBody)
-
- clusterId = hasClusterId.ClusterID
}
+ log.Printf("Clusterid is %q", clusterId)
if clusterId == "" || clusterId == h.handler.Cluster.ClusterID {
h.next.ServeHTTP(w, req)
@@ -331,7 +506,7 @@ func (h *collectionFederatedRequestHandler) ServeHTTP(w http.ResponseWriter, req
m = collectionRe.FindStringSubmatch(req.URL.Path)
clusterId := ""
- if len(m) == 3 {
+ if len(m) > 0 {
clusterId = m[2]
}
@@ -423,7 +598,7 @@ func (h *Handler) setupProxyRemoteCluster(next http.Handler) http.Handler {
mux := http.NewServeMux()
mux.Handle("/arvados/v1/workflows", &genericFederatedRequestHandler{next, h, wfRe})
mux.Handle("/arvados/v1/workflows/", &genericFederatedRequestHandler{next, h, wfRe})
- mux.Handle("/arvados/v1/containers", next)
+ mux.Handle("/arvados/v1/containers", &genericFederatedRequestHandler{next, h, containersRe})
mux.Handle("/arvados/v1/containers/", &genericFederatedRequestHandler{next, h, containersRe})
mux.Handle("/arvados/v1/container_requests", &genericFederatedRequestHandler{next, h, containerRequestsRe})
mux.Handle("/arvados/v1/container_requests/", &genericFederatedRequestHandler{next, h, containerRequestsRe})
diff --git a/lib/controller/federation_test.go b/lib/controller/federation_test.go
index 3b11625f6..b94efa6ae 100644
--- a/lib/controller/federation_test.go
+++ b/lib/controller/federation_test.go
@@ -6,7 +6,9 @@ package controller
import (
"encoding/json"
+ "fmt"
"io/ioutil"
+ "log"
"net/http"
"net/http/httptest"
"net/url"
@@ -613,3 +615,28 @@ func (s *FederationSuite) TestGetRemoteContainer(c *check.C) {
c.Check(json.NewDecoder(resp.Body).Decode(&cn), check.IsNil)
c.Check(cn.UUID, check.Equals, arvadostest.QueuedContainerUUID)
}
+
+func (s *FederationSuite) TestListRemoteContainer(c *check.C) {
+ defer s.localServiceReturns404(c).Close()
+ req := httptest.NewRequest("GET", "/arvados/v1/containers?filters="+
+ url.QueryEscape(fmt.Sprintf(`[["uuid", "in", ["%v"]]]`, arvadostest.QueuedContainerUUID)), nil)
+ req.Header.Set("Authorization", "Bearer "+arvadostest.ActiveToken)
+ resp := s.testRequest(req)
+ c.Check(resp.StatusCode, check.Equals, http.StatusOK)
+ var cn arvados.ContainerList
+ c.Check(json.NewDecoder(resp.Body).Decode(&cn), check.IsNil)
+ c.Check(cn.Items[0].UUID, check.Equals, arvadostest.QueuedContainerUUID)
+}
+
+func (s *FederationSuite) TestListMultiRemoteContainers(c *check.C) {
+ defer s.localServiceReturns404(c).Close()
+ req := httptest.NewRequest("GET", "/arvados/v1/containers?filters="+
+ url.QueryEscape(fmt.Sprintf(`[["uuid", "in", ["%v", "zhome-xvhdp-cr5queuedcontnr"]]]`, arvadostest.QueuedContainerUUID)), nil)
+ req.Header.Set("Authorization", "Bearer "+arvadostest.ActiveToken)
+ resp := s.testRequest(req)
+ log.Printf("got %+v", resp)
+ c.Assert(resp.StatusCode, check.Equals, http.StatusOK)
+ var cn arvados.ContainerList
+ c.Check(json.NewDecoder(resp.Body).Decode(&cn), check.IsNil)
+ c.Check(cn.Items[0].UUID, check.Equals, arvadostest.QueuedContainerUUID)
+}
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list