Also, added some more comments and golint checks for the newly added code.
var MissingArvadosApiHost = errors.New("Missing required environment variable ARVADOS_API_HOST")
var MissingArvadosApiToken = errors.New("Missing required environment variable ARVADOS_API_TOKEN")
var InvalidLocatorError = errors.New("Invalid locator")
-var NoSuchKeepServer = errors.New("No keep server matching the given UUID is found")
-var GetIndexError = errors.New("Error getting index")
-var IncompleteIndexError = errors.New("Got incomplete index")
+
+// ErrorGetIndex is the generic GetIndex error
+var ErrorGetIndex = errors.New("Error getting index")
+
+// ErrorNoSuchKeepServer is returned when GetIndex is invoked with a UUID with no matching keep server
+var ErrorNoSuchKeepServer = errors.New("No keep server matching the given UUID is found")
+
+// ErrorIncompleteIndex is returned when the Index response does not end with a new empty line
+var ErrorIncompleteIndex = errors.New("Got incomplete index")
const X_Keep_Desired_Replicas = "X-Keep-Desired-Replicas"
const X_Keep_Replicas_Stored = "X-Keep-Replicas-Stored"
url := kc.LocalRoots()[keepServiceUUID]
if url == "" {
log.Printf("No such keep server found: %v", keepServiceUUID)
- return nil, NoSuchKeepServer
+ return nil, ErrorNoSuchKeepServer
}
url += "/index"
req, err := http.NewRequest("GET", url, nil)
if err != nil {
log.Printf("GET index error: %v", err)
- return nil, GetIndexError
+ return nil, ErrorGetIndex
}
req.Header.Add("Authorization", fmt.Sprintf("OAuth2 %s", kc.Arvados.ApiToken))
resp, err := kc.Client.Do(req)
if err != nil || resp.StatusCode != http.StatusOK {
log.Printf("GET index error: %v; status code: %v", err, resp.StatusCode)
- return nil, GetIndexError
+ return nil, ErrorGetIndex
}
- var respbody []byte
+ var respBody []byte
if resp.Body != nil {
- respbody, err = ioutil.ReadAll(resp.Body)
+ respBody, err = ioutil.ReadAll(resp.Body)
if err != nil {
log.Printf("GET index error: %v", err)
- return nil, GetIndexError
+ return nil, ErrorGetIndex
}
// Got index; verify that it is complete
- if !strings.HasSuffix(string(respbody), "\n\n") {
+ // The response should be "\n" if no locators matched the prefix
+ // Else, it should be a list of locators followed by a blank line
+ if (!strings.HasSuffix(string(respBody), "\n\n")) && (string(respBody) != "\n") {
log.Printf("Got incomplete index for %v", url)
- return nil, IncompleteIndexError
+ return nil, ErrorIncompleteIndex
}
}
- // Got complete index or "" if no locators matching prefix
- return strings.NewReader(string(respbody)), nil
+ // Got complete index
+ return strings.NewReader(string(respBody)), nil
}
// LocalRoots() returns the map of local (i.e., disk and proxy) Keep
func (s *StandaloneSuite) TestGetIndexWithNoSuchPrefix(c *C) {
st := StubGetIndexHandler{
c,
- "/index/xyz",
+ "/index/abcd",
"abc123",
http.StatusOK,
- []byte("")}
+ []byte("\n")}
ks := RunFakeKeepServer(st)
defer ks.listener.Close()
arv.ApiToken = "abc123"
kc.SetServiceRoots(map[string]string{"x": ks.url}, map[string]string{ks.url: ""}, nil)
- _, err = kc.GetIndex("x", "xyz")
- c.Check((err != nil), Equals, true)
+ r, err := kc.GetIndex("x", "abcd")
+ c.Check(err, Equals, nil)
+
+ content, err2 := ioutil.ReadAll(r)
+ c.Check(err2, Equals, nil)
+ c.Check(content, DeepEquals, st.body)
}
rest.Handle(`/index`, IndexHandler{kc, t}).Methods("GET")
// List blocks whose hash has the given prefix
- rest.Handle(`/index/{prefix}`, IndexHandler{kc, t}).Methods("GET")
+ rest.Handle(`/index/{prefix:[0-9a-f]{0,32}}`, IndexHandler{kc, t}).Methods("GET")
}
if enable_put {
}
}
-func (this IndexHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
+// ServeHTTP implemenation for IndexHandler
+// Supports only GET requests for /index/{prefix:[0-9a-f]{0,32}}
+// For each keep server found in LocalRoots:
+// Invokes GetIndex using keepclient
+// Expects "complete" response (terminating with blank new line)
+// Aborts on any errors
+// Concatenates responses from all those keep servers and returns
+func (handler IndexHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
SetCorsHeaders(resp)
prefix := mux.Vars(req)["prefix"]
}
}()
- kc := *this.KeepClient
+ kc := *handler.KeepClient
var pass bool
var tok string
- if pass, tok = CheckAuthorizationHeader(kc, this.ApiTokenCache, req); !pass {
+ if pass, tok = CheckAuthorizationHeader(kc, handler.ApiTokenCache, req); !pass {
status, err = http.StatusForbidden, BadAuthorizationHeader
return
}
switch req.Method {
case "GET":
- for uuid, _ := range kc.LocalRoots() {
+ for uuid := range kc.LocalRoots() {
reader, err = kc.GetIndex(uuid, prefix)
if err != nil {
break
}
// Got index; verify that it is complete
- if !strings.HasSuffix(string(readBytes), "\n\n") {
+ // The response should be "\n" if no locators matched the prefix
+ // Else, it should be a list of locators followed by a blank line
+ if (!strings.HasSuffix(string(readBytes), "\n\n")) && (string(readBytes) != "\n") {
err = errors.New("Got incomplete index")
}
+ // Trim the extra empty new line found in response from each server
indexResp = append(indexResp, (readBytes[0 : len(readBytes)-1])...)
}
+
+ // Append empty line at the end of concatenation of all server responses
indexResp = append(indexResp, ([]byte("\n"))...)
default:
status, err = http.StatusNotImplemented, MethodNotSupported
}
+// Test GetIndex
+// Put one block, with 2 replicas
+// With no prefix (expect the block locator, twice)
+// With an existing prefix (expect the block locator, twice)
+// With a valid but non-existing prefix (expect "\n")
+// With an invalid prefix (expect error)
func (s *ServerRequiredSuite) TestGetIndex(c *C) {
kc := runProxy(c, []string{"keepproxy"}, 28852, false)
waitForListener()
count := 0
for _, locator := range locators {
if strings.HasPrefix(locator, hash) {
- count += 1
+ count++
}
}
c.Assert(2, Equals, count)
count = 0
for _, locator := range locators {
if strings.HasPrefix(locator, hash) {
- count += 1
+ count++
}
}
c.Assert(2, Equals, count)
- // GetIndex with no such prefix
+ // GetIndex with valid but no such prefix
+ indexReader, err = kc.GetIndex("proxy", "abcd")
+ c.Assert(err, Equals, nil)
+ indexResp, err = ioutil.ReadAll(indexReader)
+ c.Assert(string(indexResp), Equals, "\n")
+
+ // GetIndex with invalid prefix
indexReader, err = kc.GetIndex("proxy", "xyz")
c.Assert((err != nil), Equals, true)
}
uri: "/index/" + TestHash[0:3],
apiToken: dataManagerToken,
}
+ superuserNoSuchPrefixReq := &RequestTester{
+ method: "GET",
+ uri: "/index/abcd",
+ apiToken: dataManagerToken,
+ }
+ superuserInvalidPrefixReq := &RequestTester{
+ method: "GET",
+ uri: "/index/xyz",
+ apiToken: dataManagerToken,
+ }
// -------------------------------------------------------------
// Only the superuser should be allowed to issue /index requests.
"permissions on, superuser /index/prefix request: expected %s, got:\n%s",
expected, response.Body.String())
}
+
+ // superuser /index/{no-such-prefix} request
+ // => OK
+ response = IssueRequest(superuserNoSuchPrefixReq)
+ ExpectStatusCode(t,
+ "permissions on, superuser request",
+ http.StatusOK,
+ response)
+
+ if "\n" != response.Body.String() {
+ t.Errorf("Expected empty response for %s. Found %s", superuserNoSuchPrefixReq.uri, response.Body.String())
+ }
+
+ // superuser /index/{invalid-prefix} request
+ // => StatusBadRequest
+ response = IssueRequest(superuserInvalidPrefixReq)
+ ExpectStatusCode(t,
+ "permissions on, superuser request",
+ http.StatusBadRequest,
+ response)
}
// TestDeleteHandler