github.com/m3db/m3@v1.5.0/src/cluster/placementhandler/get_test.go (about)

     1  // Copyright (c) 2018 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package placementhandler
    22  
    23  import (
    24  	"fmt"
    25  	"io/ioutil"
    26  	"net/http"
    27  	"net/http/httptest"
    28  	"testing"
    29  
    30  	"github.com/m3db/m3/src/cluster/client"
    31  	"github.com/m3db/m3/src/cluster/generated/proto/placementpb"
    32  	"github.com/m3db/m3/src/cluster/kv"
    33  	"github.com/m3db/m3/src/cluster/kv/mem"
    34  	"github.com/m3db/m3/src/cluster/placement"
    35  	"github.com/m3db/m3/src/cluster/placement/service"
    36  	"github.com/m3db/m3/src/cluster/placement/storage"
    37  	"github.com/m3db/m3/src/cluster/placementhandler/handleroptions"
    38  	"github.com/m3db/m3/src/cluster/services"
    39  	"github.com/m3db/m3/src/x/instrument"
    40  
    41  	"github.com/golang/mock/gomock"
    42  	"github.com/stretchr/testify/assert"
    43  	"github.com/stretchr/testify/require"
    44  )
    45  
    46  func SetupPlacementTest(t *testing.T, ctrl *gomock.Controller) (*client.MockClient, *placement.MockService) {
    47  	mockClient := client.NewMockClient(ctrl)
    48  	require.NotNil(t, mockClient)
    49  
    50  	mockServices := services.NewMockServices(ctrl)
    51  	require.NotNil(t, mockServices)
    52  
    53  	mockPlacementService := placement.NewMockService(ctrl)
    54  	require.NotNil(t, mockPlacementService)
    55  
    56  	mockClient.EXPECT().Services(gomock.Any()).Return(mockServices, nil).AnyTimes()
    57  	mockServices.EXPECT().PlacementService(gomock.Any(), gomock.Any()).Return(mockPlacementService, nil).AnyTimes()
    58  
    59  	return mockClient, mockPlacementService
    60  }
    61  
    62  func setupPlacementTest(t *testing.T, ctrl *gomock.Controller, initPlacement placement.Placement) *client.MockClient {
    63  	mockClient := client.NewMockClient(ctrl)
    64  	require.NotNil(t, mockClient)
    65  
    66  	mockServices := services.NewMockServices(ctrl)
    67  	require.NotNil(t, mockServices)
    68  
    69  	mockClient.EXPECT().Services(gomock.Any()).Return(mockServices, nil).AnyTimes()
    70  	mockServices.EXPECT().PlacementService(gomock.Any(), gomock.Any()).DoAndReturn(
    71  		func(_ interface{}, opts placement.Options) (placement.Service, error) {
    72  			ps := service.NewPlacementService(
    73  				storage.NewPlacementStorage(mem.NewStore(), "", opts),
    74  				service.WithPlacementOptions(opts))
    75  			if initPlacement != nil {
    76  				_, err := ps.Set(initPlacement)
    77  				require.NoError(t, err)
    78  			}
    79  			return ps, nil
    80  		},
    81  	).AnyTimes()
    82  
    83  	return mockClient
    84  }
    85  
    86  func TestPlacementGetHandler(t *testing.T) {
    87  	runForAllAllowedServices(func(serviceName string) {
    88  		ctrl := gomock.NewController(t)
    89  		defer ctrl.Finish()
    90  
    91  		mockClient, mockPlacementService := SetupPlacementTest(t, ctrl)
    92  		handlerOpts, err := NewHandlerOptions(
    93  			mockClient, placement.Configuration{}, nil, instrument.NewOptions())
    94  		require.NoError(t, err)
    95  		handler := NewGetHandler(handlerOpts)
    96  
    97  		// Test successful get
    98  		w := httptest.NewRecorder()
    99  		req := httptest.NewRequest(GetHTTPMethod, M3DBGetURL, nil)
   100  		require.NotNil(t, req)
   101  
   102  		placementProto := &placementpb.Placement{
   103  			Instances: map[string]*placementpb.Instance{
   104  				"host1": &placementpb.Instance{
   105  					Id:             "host1",
   106  					IsolationGroup: "rack1",
   107  					Zone:           "test",
   108  					Weight:         1,
   109  					Endpoint:       "http://host1:1234",
   110  					Hostname:       "host1",
   111  					Port:           1234,
   112  					Metadata: &placementpb.InstanceMetadata{
   113  						DebugPort: 1,
   114  					},
   115  				},
   116  				"host2": &placementpb.Instance{
   117  					Id:             "host2",
   118  					IsolationGroup: "rack1",
   119  					Zone:           "test",
   120  					Weight:         1,
   121  					Endpoint:       "http://host2:1234",
   122  					Hostname:       "host2",
   123  					Port:           1234,
   124  					Metadata: &placementpb.InstanceMetadata{
   125  						DebugPort: 2,
   126  					},
   127  				},
   128  			},
   129  		}
   130  
   131  		const placementJSON = `{"placement":{"instances":{"host1":{"id":"host1","isolationGroup":"rack1","zone":"test","weight":1,"endpoint":"http://host1:1234","shards":[],"shardSetId":0,"hostname":"host1","port":1234,"metadata":{"debugPort":1}},"host2":{"id":"host2","isolationGroup":"rack1","zone":"test","weight":1,"endpoint":"http://host2:1234","shards":[],"shardSetId":0,"hostname":"host2","port":1234,"metadata":{"debugPort":2}}},"replicaFactor":0,"numShards":0,"isSharded":false,"cutoverTime":"0","isMirrored":false,"maxShardSetId":0},"version":%d}`
   132  
   133  		placementObj, err := placement.NewPlacementFromProto(placementProto)
   134  		require.NoError(t, err)
   135  
   136  		svcDefaults := handleroptions.ServiceNameAndDefaults{
   137  			ServiceName: serviceName,
   138  		}
   139  
   140  		mockPlacementService.EXPECT().Placement().Return(placementObj, nil)
   141  		handler.ServeHTTP(svcDefaults, w, req)
   142  
   143  		resp := w.Result()
   144  		body, _ := ioutil.ReadAll(resp.Body)
   145  		assert.Equal(t, http.StatusOK, resp.StatusCode)
   146  		assert.Equal(t, fmt.Sprintf(placementJSON, 0), string(body))
   147  
   148  		// Test error case
   149  		w = httptest.NewRecorder()
   150  		req = httptest.NewRequest(GetHTTPMethod, M3DBGetURL, nil)
   151  		require.NotNil(t, req)
   152  
   153  		mockPlacementService.EXPECT().Placement().Return(nil, kv.ErrNotFound)
   154  		handler.ServeHTTP(svcDefaults, w, req)
   155  
   156  		resp = w.Result()
   157  		assert.Equal(t, http.StatusNotFound, resp.StatusCode)
   158  
   159  		// With bad version request
   160  		w = httptest.NewRecorder()
   161  		req = httptest.NewRequest(GetHTTPMethod, "/placement/get?version=foo", nil)
   162  		require.NotNil(t, req)
   163  
   164  		handler.ServeHTTP(svcDefaults, w, req)
   165  		resp = w.Result()
   166  		assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
   167  
   168  		// With valid version request
   169  		w = httptest.NewRecorder()
   170  		req = httptest.NewRequest(GetHTTPMethod, "/placement/get?version=12", nil)
   171  		require.NotNil(t, req)
   172  
   173  		mockPlacementService.EXPECT().PlacementForVersion(12).Return(placementObj.Clone().SetVersion(12), nil)
   174  
   175  		handler.ServeHTTP(svcDefaults, w, req)
   176  		resp = w.Result()
   177  		body, _ = ioutil.ReadAll(resp.Body)
   178  		assert.Equal(t, http.StatusOK, resp.StatusCode)
   179  		assert.Equal(t, fmt.Sprintf(placementJSON, 12), string(body))
   180  	})
   181  }