expectedDc, responseDc)
}
// Confirm the block has been deleted
- _, err := vols[0].Get(TestHash)
+ buf := make([]byte, BlockSize)
+ _, err := vols[0].Get(TestHash, buf)
var blockDeleted = os.IsNotExist(err)
if !blockDeleted {
t.Error("superuserExistingBlockReq: block not deleted")
expectedDc, responseDc)
}
// Confirm the block has NOT been deleted.
- _, err = vols[0].Get(TestHash)
+ _, err = vols[0].Get(TestHash, buf)
if err != nil {
t.Errorf("testing delete on new block: %s\n", err)
}
if rt.apiToken != "" {
req.Header.Set("Authorization", "OAuth2 "+rt.apiToken)
}
- loggingRouter := MakeLoggingRESTRouter()
+ loggingRouter := MakeRESTRouter()
loggingRouter.ServeHTTP(response, req)
return response
}
}
}
+type notifyingResponseRecorder struct {
+ *httptest.ResponseRecorder
+ closer chan bool
+}
+
+func (r *notifyingResponseRecorder) CloseNotify() <-chan bool {
+ return r.closer
+}
+
+func TestGetHandlerClientDisconnect(t *testing.T) {
+ defer func(was bool) {
+ enforcePermissions = was
+ }(enforcePermissions)
+ enforcePermissions = false
+
+ defer func(orig *bufferPool) {
+ bufs = orig
+ }(bufs)
+ bufs = newBufferPool(1, BlockSize)
+ defer bufs.Put(bufs.Get(BlockSize))
+
+ KeepVM = MakeTestVolumeManager(2)
+ defer KeepVM.Close()
+
+ if err := KeepVM.AllWritable()[0].Put(TestHash, TestBlock); err != nil {
+ t.Error(err)
+ }
+
+ resp := ¬ifyingResponseRecorder{
+ ResponseRecorder: httptest.NewRecorder(),
+ closer: make(chan bool, 1),
+ }
+ if _, ok := http.ResponseWriter(resp).(http.CloseNotifier); !ok {
+ t.Fatal("notifyingResponseRecorder is broken")
+ }
+ // If anyone asks, the client has disconnected.
+ resp.closer <- true
+
+ ok := make(chan struct{})
+ go func() {
+ req, _ := http.NewRequest("GET", fmt.Sprintf("/%s+%d", TestHash, len(TestBlock)), nil)
+ (&LoggingRESTRouter{MakeRESTRouter()}).ServeHTTP(resp, req)
+ ok <- struct{}{}
+ }()
+
+ select {
+ case <-time.After(20 * time.Second):
+ t.Fatal("request took >20s, close notifier must be broken")
+ case <-ok:
+ }
+
+ ExpectStatusCode(t, "client disconnect", http.StatusServiceUnavailable, resp.ResponseRecorder)
+ for i, v := range KeepVM.AllWritable() {
+ if calls := v.(*MockVolume).called["GET"]; calls != 0 {
+ t.Errorf("volume %d got %d calls, expected 0", i, calls)
+ }
+ }
+}
+
// Invoke the GetBlockHandler a bunch of times to test for bufferpool resource
// leak.
func TestGetHandlerNoBufferleak(t *testing.T) {
}
}
-func TestUndeleteHandler(t *testing.T) {
+func TestUntrashHandler(t *testing.T) {
defer teardown()
// Set up Keep volumes
vols := KeepVM.AllWritable()
vols[0].Put(TestHash, TestBlock)
- // Only the datamanager user should be allowed to undelete blocks
dataManagerToken = "DATA MANAGER TOKEN"
// unauthenticatedReq => UnauthorizedError
unauthenticatedReq := &RequestTester{
method: "PUT",
- uri: "/undelete/" + TestHash,
+ uri: "/untrash/" + TestHash,
}
response := IssueRequest(unauthenticatedReq)
ExpectStatusCode(t,
- "enforcePermissions on, unauthenticated request",
+ "Unauthenticated request",
UnauthorizedError.HTTPCode,
response)
// notDataManagerReq => UnauthorizedError
notDataManagerReq := &RequestTester{
method: "PUT",
- uri: "/undelete/" + TestHash,
+ uri: "/untrash/" + TestHash,
apiToken: knownToken,
}
response = IssueRequest(notDataManagerReq)
ExpectStatusCode(t,
- "permissions on, unauthenticated /index/prefix request",
+ "Non-datamanager token",
UnauthorizedError.HTTPCode,
response)
// datamanagerWithBadHashReq => StatusBadRequest
datamanagerWithBadHashReq := &RequestTester{
method: "PUT",
- uri: "/undelete/thisisnotalocator",
+ uri: "/untrash/thisisnotalocator",
apiToken: dataManagerToken,
}
response = IssueRequest(datamanagerWithBadHashReq)
ExpectStatusCode(t,
- "permissions on, authenticated request, non-superuser",
+ "Bad locator in untrash request",
http.StatusBadRequest,
response)
// datamanagerWrongMethodReq => StatusBadRequest
datamanagerWrongMethodReq := &RequestTester{
method: "GET",
- uri: "/undelete/" + TestHash,
+ uri: "/untrash/" + TestHash,
apiToken: dataManagerToken,
}
response = IssueRequest(datamanagerWrongMethodReq)
ExpectStatusCode(t,
- "permissions on, authenticated request, non-superuser",
+ "Only PUT method is supported for untrash",
http.StatusBadRequest,
response)
// datamanagerReq => StatusOK
datamanagerReq := &RequestTester{
method: "PUT",
- uri: "/undelete/" + TestHash,
+ uri: "/untrash/" + TestHash,
apiToken: dataManagerToken,
}
response = IssueRequest(datamanagerReq)
ExpectStatusCode(t,
- "permissions on, authenticated request, non-superuser",
+ "",
http.StatusOK,
response)
- expected := `Untrashed on volume`
- match, _ := regexp.MatchString(expected, response.Body.String())
- if !match {
+ expected := "Successfully untrashed on: [MockVolume],[MockVolume]"
+ if response.Body.String() != expected {
t.Errorf(
- "Undelete response mismatched: expected %s, got:\n%s",
+ "Untrash response mismatched: expected %s, got:\n%s",
expected, response.Body.String())
}
}
+
+func TestUntrashHandlerWithNoWritableVolumes(t *testing.T) {
+ defer teardown()
+
+ // Set up readonly Keep volumes
+ vols := []*MockVolume{CreateMockVolume(), CreateMockVolume()}
+ vols[0].Readonly = true
+ vols[1].Readonly = true
+ KeepVM = MakeRRVolumeManager([]Volume{vols[0], vols[1]})
+ defer KeepVM.Close()
+
+ dataManagerToken = "DATA MANAGER TOKEN"
+
+ // datamanagerReq => StatusOK
+ datamanagerReq := &RequestTester{
+ method: "PUT",
+ uri: "/untrash/" + TestHash,
+ apiToken: dataManagerToken,
+ }
+ response := IssueRequest(datamanagerReq)
+ ExpectStatusCode(t,
+ "No writable volumes",
+ http.StatusNotFound,
+ response)
+}