github.com/m3db/m3@v1.5.0/src/query/api/v1/handler/namespace/ready_test.go (about)

     1  // Copyright (c) 2020  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 namespace
    22  
    23  import (
    24  	"fmt"
    25  	"io/ioutil"
    26  	"net/http"
    27  	"net/http/httptest"
    28  	"testing"
    29  
    30  	clusterclient "github.com/m3db/m3/src/cluster/client"
    31  	"github.com/m3db/m3/src/cluster/kv"
    32  	"github.com/m3db/m3/src/dbnode/client"
    33  	nsproto "github.com/m3db/m3/src/dbnode/generated/proto/namespace"
    34  	"github.com/m3db/m3/src/dbnode/storage/index"
    35  	"github.com/m3db/m3/src/m3ninx/idx"
    36  	"github.com/m3db/m3/src/query/storage/m3"
    37  	"github.com/m3db/m3/src/x/ident"
    38  	"github.com/m3db/m3/src/x/instrument"
    39  	xjson "github.com/m3db/m3/src/x/json"
    40  	xtest "github.com/m3db/m3/src/x/test"
    41  
    42  	"github.com/golang/mock/gomock"
    43  	"github.com/stretchr/testify/require"
    44  )
    45  
    46  func TestNamespaceReadyHandler(t *testing.T) {
    47  	ctrl := xtest.NewController(t)
    48  	defer ctrl.Finish()
    49  
    50  	mockClient, mockKV, nsID := testSetup(t, ctrl)
    51  	mockKV.EXPECT().CheckAndSet(M3DBNodeNamespacesKey, gomock.Any(), gomock.Not(nil)).Return(1, nil)
    52  
    53  	mockSession := client.NewMockSession(ctrl)
    54  	testClusterNs := testClusterNamespace{
    55  		session: mockSession,
    56  		id:      nsID,
    57  		options: m3.ClusterNamespaceOptions{},
    58  	}
    59  	testClusters := testClusters{
    60  		configType:         m3.ClusterConfigTypeDynamic,
    61  		nonReadyNamespaces: []m3.ClusterNamespace{&testClusterNs},
    62  	}
    63  
    64  	mockSession.EXPECT().
    65  		FetchTaggedIDs(gomock.Any(), testClusterNs.NamespaceID(), index.Query{Query: idx.NewAllQuery()},
    66  			index.QueryOptions{SeriesLimit: 1, DocsLimit: 1}).
    67  		Return(nil, client.FetchResponseMetadata{}, nil)
    68  
    69  	readyHandler := NewReadyHandler(mockClient, &testClusters, instrument.NewOptions())
    70  
    71  	w := httptest.NewRecorder()
    72  
    73  	jsonInput := xjson.Map{
    74  		"name": "testNamespace",
    75  	}
    76  
    77  	req := httptest.NewRequest("POST", "/namespace/ready",
    78  		xjson.MustNewTestReader(t, jsonInput))
    79  	require.NotNil(t, req)
    80  
    81  	readyHandler.ServeHTTP(svcDefaults, w, req)
    82  
    83  	resp := w.Result()
    84  	body, err := ioutil.ReadAll(resp.Body)
    85  	require.NoError(t, err)
    86  	require.Equal(t, http.StatusOK, resp.StatusCode)
    87  
    88  	expected := xtest.MustPrettyJSONMap(t,
    89  		xjson.Map{
    90  			"ready": true,
    91  		})
    92  	actual := xtest.MustPrettyJSONString(t, string(body))
    93  	require.Equal(t, expected, actual, xtest.Diff(expected, actual))
    94  }
    95  
    96  func TestNamespaceReadyHandlerWithForce(t *testing.T) {
    97  	ctrl := xtest.NewController(t)
    98  	defer ctrl.Finish()
    99  
   100  	mockClient, mockKV, _ := testSetup(t, ctrl)
   101  	mockKV.EXPECT().CheckAndSet(M3DBNodeNamespacesKey, gomock.Any(), gomock.Not(nil)).Return(1, nil)
   102  
   103  	readyHandler := NewReadyHandler(mockClient, nil, instrument.NewOptions())
   104  
   105  	w := httptest.NewRecorder()
   106  
   107  	jsonInput := xjson.Map{
   108  		"name":  "testNamespace",
   109  		"force": true,
   110  	}
   111  
   112  	req := httptest.NewRequest("POST", "/namespace/ready",
   113  		xjson.MustNewTestReader(t, jsonInput))
   114  	require.NotNil(t, req)
   115  
   116  	readyHandler.ServeHTTP(svcDefaults, w, req)
   117  
   118  	resp := w.Result()
   119  	body, err := ioutil.ReadAll(resp.Body)
   120  	require.NoError(t, err)
   121  	require.Equal(t, http.StatusOK, resp.StatusCode)
   122  
   123  	expected := xtest.MustPrettyJSONMap(t,
   124  		xjson.Map{
   125  			"ready": true,
   126  		})
   127  	actual := xtest.MustPrettyJSONString(t, string(body))
   128  	require.Equal(t, expected, actual, xtest.Diff(expected, actual))
   129  }
   130  
   131  func TestNamespaceReadyFailIfNoClusters(t *testing.T) {
   132  	ctrl := xtest.NewController(t)
   133  	defer ctrl.Finish()
   134  
   135  	mockClient, _, _ := testSetup(t, ctrl)
   136  
   137  	readyHandler := NewReadyHandler(mockClient, nil, instrument.NewOptions())
   138  
   139  	w := httptest.NewRecorder()
   140  
   141  	jsonInput := xjson.Map{
   142  		"name": "testNamespace",
   143  	}
   144  
   145  	req := httptest.NewRequest("POST", "/namespace/ready",
   146  		xjson.MustNewTestReader(t, jsonInput))
   147  	require.NotNil(t, req)
   148  
   149  	readyHandler.ServeHTTP(svcDefaults, w, req)
   150  
   151  	resp := w.Result()
   152  	require.Equal(t, http.StatusBadRequest, resp.StatusCode,
   153  		fmt.Sprintf("response: %s", w.Body.String()))
   154  }
   155  
   156  func TestNamespaceReadyFailIfNamespaceMissing(t *testing.T) {
   157  	ctrl := xtest.NewController(t)
   158  	defer ctrl.Finish()
   159  
   160  	mockClient, _, _ := testSetup(t, ctrl)
   161  
   162  	readyHandler := NewReadyHandler(mockClient, nil, instrument.NewOptions())
   163  
   164  	w := httptest.NewRecorder()
   165  
   166  	jsonInput := xjson.Map{
   167  		"name": "missingNamespace",
   168  	}
   169  
   170  	req := httptest.NewRequest("POST", "/namespace/ready",
   171  		xjson.MustNewTestReader(t, jsonInput))
   172  	require.NotNil(t, req)
   173  
   174  	readyHandler.ServeHTTP(svcDefaults, w, req)
   175  
   176  	resp := w.Result()
   177  	require.Equal(t, http.StatusBadRequest, resp.StatusCode)
   178  }
   179  
   180  func TestNamespaceReadyHandlerStaticConf(t *testing.T) {
   181  	ctrl := xtest.NewController(t)
   182  	defer ctrl.Finish()
   183  
   184  	mockClient := clusterclient.NewMockClient(ctrl)
   185  	require.NotNil(t, mockClient)
   186  
   187  	tc := testClusters{
   188  		configType: m3.ClusterConfigTypeStatic,
   189  	}
   190  
   191  	readyHandler := NewReadyHandler(mockClient, &tc, instrument.NewOptions())
   192  
   193  	jsonInput := xjson.Map{
   194  		"name": "testNamespace",
   195  	}
   196  
   197  	req := httptest.NewRequest("POST", "/namespace/ready",
   198  		xjson.MustNewTestReader(t, jsonInput))
   199  	require.NotNil(t, req)
   200  
   201  	w := httptest.NewRecorder()
   202  	readyHandler.ServeHTTP(svcDefaults, w, req)
   203  
   204  	resp := w.Result()
   205  	defer resp.Body.Close()
   206  
   207  	body, err := ioutil.ReadAll(resp.Body)
   208  	require.NoError(t, err)
   209  	require.Equal(t, http.StatusOK, resp.StatusCode)
   210  
   211  	expected := xtest.MustPrettyJSONMap(t,
   212  		xjson.Map{
   213  			"ready": true,
   214  		})
   215  	actual := xtest.MustPrettyJSONString(t, string(body))
   216  	require.Equal(t, expected, actual, xtest.Diff(expected, actual))
   217  }
   218  
   219  func testSetup(t *testing.T, ctrl *gomock.Controller) (*clusterclient.MockClient, *kv.MockStore, ident.ID) {
   220  	mockClient, mockKV := setupNamespaceTest(t, ctrl)
   221  	mockClient.EXPECT().Store(gomock.Any()).Return(mockKV, nil)
   222  
   223  	nsID := ident.StringID("testNamespace")
   224  	registry := nsproto.Registry{
   225  		Namespaces: map[string]*nsproto.NamespaceOptions{
   226  			nsID.String(): {
   227  				// Required for validation
   228  				RetentionOptions: &nsproto.RetentionOptions{
   229  					RetentionPeriodNanos:                     172800000000000,
   230  					BlockSizeNanos:                           7200000000000,
   231  					BufferFutureNanos:                        600000000000,
   232  					BufferPastNanos:                          600000000000,
   233  					BlockDataExpiry:                          true,
   234  					BlockDataExpiryAfterNotAccessPeriodNanos: 3600000000000,
   235  				},
   236  			},
   237  		},
   238  	}
   239  
   240  	mockValue := kv.NewMockValue(ctrl)
   241  	mockValue.EXPECT().Unmarshal(gomock.Any()).Return(nil).SetArg(0, registry)
   242  	mockValue.EXPECT().Version().Return(0)
   243  	mockKV.EXPECT().Get(M3DBNodeNamespacesKey).Return(mockValue, nil)
   244  	return mockClient, mockKV, nsID
   245  }
   246  
   247  type testClusterNamespace struct {
   248  	session client.Session
   249  	id      ident.ID
   250  	options m3.ClusterNamespaceOptions
   251  }
   252  
   253  func (t *testClusterNamespace) NamespaceID() ident.ID {
   254  	return t.id
   255  }
   256  
   257  func (t *testClusterNamespace) Options() m3.ClusterNamespaceOptions {
   258  	return t.options
   259  }
   260  
   261  func (t *testClusterNamespace) Session() client.Session {
   262  	return t.session
   263  }
   264  
   265  type testClusters struct {
   266  	configType         m3.ClusterConfigType
   267  	nonReadyNamespaces m3.ClusterNamespaces
   268  }
   269  
   270  func (t *testClusters) ClusterNamespaces() m3.ClusterNamespaces {
   271  	panic("implement me")
   272  }
   273  
   274  func (t *testClusters) NonReadyClusterNamespaces() m3.ClusterNamespaces {
   275  	return t.nonReadyNamespaces
   276  }
   277  
   278  func (t *testClusters) Close() error {
   279  	panic("implement me")
   280  }
   281  
   282  func (t *testClusters) UnaggregatedClusterNamespace() (m3.ClusterNamespace, bool) {
   283  	panic("implement me")
   284  }
   285  
   286  func (t *testClusters) AggregatedClusterNamespace(attrs m3.RetentionResolution) (m3.ClusterNamespace, bool) {
   287  	panic("implement me")
   288  }
   289  
   290  func (t *testClusters) ConfigType() m3.ClusterConfigType {
   291  	return t.configType
   292  }