github.com/uber/kraken@v0.1.4/lib/backend/hdfsbackend/webhdfs/client_test.go (about) 1 // Copyright (c) 2016-2019 Uber Technologies, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 package webhdfs 15 16 import ( 17 "bytes" 18 "encoding/json" 19 "fmt" 20 "io/ioutil" 21 "net/http" 22 "os" 23 "path" 24 "testing" 25 26 "github.com/uber/kraken/lib/backend/backenderrors" 27 "github.com/uber/kraken/utils/randutil" 28 "github.com/uber/kraken/utils/rwutil" 29 "github.com/uber/kraken/utils/testutil" 30 31 "github.com/pressly/chi" 32 "github.com/stretchr/testify/require" 33 ) 34 35 const _testFile = "/root/test" 36 37 type testServer struct { 38 getName, getData, putName, putData http.HandlerFunc 39 } 40 41 func (s *testServer) handler() http.Handler { 42 r := chi.NewRouter() 43 r.Get("/webhdfs/v1*", s.getName) 44 r.Get("/datanode/webhdfs/v1*", s.getData) 45 r.Put("/webhdfs/v1*", s.putName) 46 r.Put("/datanode/webhdfs/v1*", s.putData) 47 return r 48 } 49 50 func redirectToDataNode(w http.ResponseWriter, r *http.Request) { 51 datanode := fmt.Sprintf( 52 "http://%s/%s?%s", 53 r.Host, path.Join("datanode", r.URL.Path), r.URL.Query().Encode()) 54 http.Redirect(w, r, datanode, http.StatusTemporaryRedirect) 55 } 56 57 func writeResponse(status int, body []byte) http.HandlerFunc { 58 return func(w http.ResponseWriter, r *http.Request) { 59 w.WriteHeader(status) 60 w.Write(body) 61 } 62 } 63 64 func checkBody(t *testing.T, expected []byte) http.HandlerFunc { 65 return func(w http.ResponseWriter, r *http.Request) { 66 b, err := ioutil.ReadAll(r.Body) 67 require.NoError(t, err) 68 require.Equal(t, string(expected), string(b)) 69 w.WriteHeader(http.StatusCreated) 70 } 71 } 72 73 func newClient(nodes ...string) Client { 74 c, err := NewClient(Config{}, nodes, "") 75 if err != nil { 76 panic(err) 77 } 78 return c 79 } 80 81 func TestNewClientError(t *testing.T) { 82 require := require.New(t) 83 84 _, err := NewClient(Config{}, nil, "") 85 require.Error(err) 86 } 87 88 func TestClientOpen(t *testing.T) { 89 require := require.New(t) 90 91 data := randutil.Text(64) 92 93 server := &testServer{ 94 getName: redirectToDataNode, 95 getData: writeResponse(http.StatusOK, data), 96 } 97 addr, stop := testutil.StartServer(server.handler()) 98 defer stop() 99 100 client := newClient(addr) 101 102 var b bytes.Buffer 103 require.NoError(client.Open(_testFile, &b)) 104 require.Equal(data, b.Bytes()) 105 } 106 107 func TestClientOpenRetriesNextNameNode(t *testing.T) { 108 require := require.New(t) 109 110 data := randutil.Text(64) 111 112 server1 := &testServer{ 113 getName: redirectToDataNode, 114 getData: writeResponse(http.StatusForbidden, nil), 115 } 116 addr1, stop := testutil.StartServer(server1.handler()) 117 defer stop() 118 119 server2 := &testServer{ 120 getName: redirectToDataNode, 121 getData: writeResponse(http.StatusOK, data), 122 } 123 addr2, stop := testutil.StartServer(server2.handler()) 124 defer stop() 125 126 client := newClient(addr1, addr2) 127 128 var b bytes.Buffer 129 require.NoError(client.Open(_testFile, &b)) 130 require.Equal(data, b.Bytes()) 131 } 132 133 func TestClientOpenErrBlobNotFound(t *testing.T) { 134 require := require.New(t) 135 136 server := &testServer{ 137 getName: writeResponse(http.StatusNotFound, []byte("file not found")), 138 } 139 addr, stop := testutil.StartServer(server.handler()) 140 defer stop() 141 142 client := newClient(addr) 143 144 f, err := ioutil.TempFile("", "hdfs3test") 145 require.NoError(err) 146 defer os.Remove(f.Name()) 147 148 var b bytes.Buffer 149 require.Equal(backenderrors.ErrBlobNotFound, client.Open(_testFile, &b)) 150 } 151 152 func TestClientCreate(t *testing.T) { 153 require := require.New(t) 154 155 data := randutil.Text(64) 156 157 server := &testServer{ 158 putName: redirectToDataNode, 159 putData: checkBody(t, data), 160 } 161 addr, stop := testutil.StartServer(server.handler()) 162 defer stop() 163 164 client := newClient(addr) 165 166 require.NoError(client.Create(_testFile, bytes.NewReader(data))) 167 } 168 169 func TestClientCreateUnknownFailure(t *testing.T) { 170 require := require.New(t) 171 172 server := &testServer{ 173 putName: redirectToDataNode, 174 putData: writeResponse(http.StatusInternalServerError, []byte("unknown error")), 175 } 176 addr, stop := testutil.StartServer(server.handler()) 177 defer stop() 178 179 client := newClient(addr) 180 181 data := randutil.Text(64) 182 183 require.Error(client.Create(_testFile, bytes.NewReader(data))) 184 } 185 186 func TestClientCreateRetriesNextNameNode(t *testing.T) { 187 tests := []struct { 188 desc string 189 server1 *testServer 190 }{ 191 { 192 "name node forbidden", 193 &testServer{ 194 putName: writeResponse(http.StatusForbidden, nil), 195 }, 196 }, { 197 "data node forbidden", 198 &testServer{ 199 putName: redirectToDataNode, 200 putData: writeResponse(http.StatusForbidden, nil), 201 }, 202 }, 203 } 204 for _, test := range tests { 205 t.Run(test.desc, func(t *testing.T) { 206 require := require.New(t) 207 208 data := randutil.Text(64) 209 210 addr1, stop := testutil.StartServer(test.server1.handler()) 211 defer stop() 212 213 server2 := &testServer{ 214 putName: redirectToDataNode, 215 putData: checkBody(t, data), 216 } 217 addr2, stop := testutil.StartServer(server2.handler()) 218 defer stop() 219 220 client := newClient(addr1, addr2) 221 222 require.NoError(client.Create(_testFile, bytes.NewReader(data))) 223 224 // Ensure bytes.Buffer can replay data. 225 require.NoError(client.Create(_testFile, bytes.NewBuffer(data))) 226 227 // Ensure non-buffer non-seekers can replay data. 228 require.NoError(client.Create(_testFile, rwutil.PlainReader(data))) 229 }) 230 } 231 } 232 233 func TestClientCreateErrorsWhenExceedsBufferGuard(t *testing.T) { 234 require := require.New(t) 235 236 client, err := NewClient(Config{BufferGuard: 50}, []string{"dummy-addr"}, "") 237 require.NoError(err) 238 239 // Exceeds BufferGuard. 240 data := randutil.Text(100) 241 242 err = client.Create(_testFile, rwutil.PlainReader(data)) 243 require.Error(err) 244 _, ok := err.(drainSrcError).err.(exceededCapError) 245 require.True(ok) 246 } 247 248 func TestClientRename(t *testing.T) { 249 require := require.New(t) 250 251 from := "/root/from" 252 to := "/root/to" 253 254 called := false 255 256 server := &testServer{ 257 putName: redirectToDataNode, 258 putData: func(w http.ResponseWriter, r *http.Request) { 259 called = true 260 require.Equal("/datanode/webhdfs/v1"+from, r.URL.Path) 261 require.Equal(to, r.URL.Query().Get("destination")) 262 }, 263 } 264 addr, stop := testutil.StartServer(server.handler()) 265 defer stop() 266 267 client := newClient(addr) 268 269 require.NoError(client.Rename(from, to)) 270 require.True(called) 271 } 272 273 func TestClientMkdirs(t *testing.T) { 274 require := require.New(t) 275 276 called := false 277 278 server := &testServer{ 279 putName: redirectToDataNode, 280 putData: func(w http.ResponseWriter, r *http.Request) { 281 called = true 282 require.Equal("/datanode/webhdfs/v1"+_testFile, r.URL.Path) 283 }, 284 } 285 addr, stop := testutil.StartServer(server.handler()) 286 defer stop() 287 288 client := newClient(addr) 289 290 require.NoError(client.Mkdirs(_testFile)) 291 require.True(called) 292 } 293 294 func TestClientGetFileStatus(t *testing.T) { 295 require := require.New(t) 296 297 var resp fileStatusResponse 298 resp.FileStatus.Length = 32 299 b, err := json.Marshal(resp) 300 require.NoError(err) 301 302 server := &testServer{ 303 getName: redirectToDataNode, 304 getData: writeResponse(http.StatusOK, b), 305 } 306 addr, stop := testutil.StartServer(server.handler()) 307 defer stop() 308 309 client := newClient(addr) 310 311 fs, err := client.GetFileStatus(_testFile) 312 require.NoError(err) 313 require.Equal(resp.FileStatus, fs) 314 } 315 316 func TestClientGetFileStatusErrBlobNotFound(t *testing.T) { 317 require := require.New(t) 318 319 server := &testServer{ 320 getName: redirectToDataNode, 321 getData: writeResponse(http.StatusNotFound, nil), 322 } 323 addr, stop := testutil.StartServer(server.handler()) 324 defer stop() 325 326 client := newClient(addr) 327 328 _, err := client.GetFileStatus(_testFile) 329 require.Equal(backenderrors.ErrBlobNotFound, err) 330 } 331 332 func TestClientListFileStatus(t *testing.T) { 333 require := require.New(t) 334 335 data := fmt.Sprintf(` 336 { 337 "FileStatuses": { 338 "FileStatus": [{ 339 "accessTime" : 1320171722771, 340 "blockSize" : 33554432, 341 "group" : "supergroup", 342 "length" : 24930, 343 "modificationTime": 1320171722771, 344 "owner" : "webuser", 345 "pathSuffix" : %q, 346 "permission" : "644", 347 "replication" : 1, 348 "type" : "FILE" 349 }] 350 } 351 } 352 `, _testFile) 353 354 server := &testServer{ 355 getName: redirectToDataNode, 356 getData: writeResponse(http.StatusOK, []byte(data)), 357 } 358 addr, stop := testutil.StartServer(server.handler()) 359 defer stop() 360 361 client := newClient(addr) 362 363 result, err := client.ListFileStatus("/root") 364 require.NoError(err) 365 require.Equal([]FileStatus{{ 366 PathSuffix: _testFile, 367 Type: "FILE", 368 Length: 24930, 369 }}, result) 370 }