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  }