github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/dbnode/namespace/kvadmin/ns_admin_test.go (about)

     1  // Copyright (c) 2019 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 kvadmin
    22  
    23  import (
    24  	"errors"
    25  	"testing"
    26  
    27  	"github.com/golang/mock/gomock"
    28  	"github.com/stretchr/testify/require"
    29  
    30  	"github.com/m3db/m3/src/cluster/kv"
    31  	"github.com/m3db/m3/src/cluster/kv/mem"
    32  	nsproto "github.com/m3db/m3/src/dbnode/generated/proto/namespace"
    33  	"github.com/m3db/m3/src/dbnode/namespace"
    34  	"github.com/m3db/m3/src/x/ident"
    35  )
    36  
    37  const (
    38  	mainProtoStr = `syntax = "proto3";
    39  
    40  package mainpkg;
    41  
    42  import "mainpkg/imported.proto";
    43  
    44  message TestMessage {
    45    double latitude = 1;
    46    double longitude = 2;
    47    int64 epoch = 3;
    48    bytes deliveryID = 4;
    49    map<string, string> attributes = 5;
    50    ImportedMessage an_imported_message = 6;
    51  }
    52  `
    53  	importedProtoStr = `
    54  syntax = "proto3";
    55  
    56  package mainpkg;
    57  
    58  message ImportedMessage {
    59    double latitude = 1;
    60    double longitude = 2;
    61    int64 epoch = 3;
    62    bytes deliveryID = 4;
    63  }
    64  `
    65  
    66  	nsRegKey        = "nsRegKey"
    67  	testNamespaceID = "test-namespace"
    68  )
    69  
    70  func TestAdminService_DeploySchema(t *testing.T) {
    71  	ctrl := gomock.NewController(t)
    72  	defer ctrl.Finish()
    73  
    74  	storeMock := kv.NewMockStore(ctrl)
    75  	as := NewAdminService(storeMock, nsRegKey, func() string { return testNamespaceID })
    76  	require.NotNil(t, as)
    77  
    78  	currentMeta, err := namespace.NewMetadata(ident.StringID("ns1"), namespace.NewOptions())
    79  	require.NoError(t, err)
    80  	currentMap, err := namespace.NewMap([]namespace.Metadata{currentMeta})
    81  	require.NoError(t, err)
    82  	currentReg, err := namespace.ToProto(currentMap)
    83  	require.NoError(t, err)
    84  
    85  	protoFile := "mainpkg/test.proto"
    86  	protoMsg := "mainpkg.TestMessage"
    87  	protoMap := map[string]string{protoFile: mainProtoStr, "mainpkg/imported.proto": importedProtoStr}
    88  
    89  	expectedSchemaOpt, err := namespace.
    90  		AppendSchemaOptions(nil, protoFile, protoMsg, protoMap, testNamespaceID)
    91  	require.NoError(t, err)
    92  	expectedSh, err := namespace.LoadSchemaHistory(expectedSchemaOpt)
    93  	require.NoError(t, err)
    94  	expectedMeta, err := namespace.NewMetadata(ident.StringID("ns1"),
    95  		namespace.NewOptions().SetSchemaHistory(expectedSh))
    96  	require.NoError(t, err)
    97  	expectedMap, err := namespace.NewMap([]namespace.Metadata{expectedMeta})
    98  	require.NoError(t, err)
    99  
   100  	mValue := kv.NewMockValue(ctrl)
   101  	mValue.EXPECT().Unmarshal(gomock.Any()).Return(nil).Do(func(reg *nsproto.Registry) {
   102  		*reg = *currentReg
   103  	})
   104  	mValue.EXPECT().Version().Return(1)
   105  	storeMock.EXPECT().Get(nsRegKey).Return(mValue, nil)
   106  	storeMock.EXPECT().CheckAndSet(nsRegKey, 1, gomock.Any()).Return(2, nil).Do(
   107  		func(k string, version int, actualReg *nsproto.Registry) {
   108  			actualMap, err := namespace.FromProto(*actualReg)
   109  			require.NoError(t, err)
   110  			require.NotEmpty(t, actualMap)
   111  			require.True(t, actualMap.Equal(expectedMap))
   112  		})
   113  	_, err = as.DeploySchema("ns1", protoFile, protoMsg, protoMap)
   114  	require.NoError(t, err)
   115  }
   116  
   117  func TestAdminService_ResetSchema(t *testing.T) {
   118  	ctrl := gomock.NewController(t)
   119  	defer ctrl.Finish()
   120  
   121  	storeMock := kv.NewMockStore(ctrl)
   122  	as := NewAdminService(storeMock, nsRegKey, func() string { return testNamespaceID })
   123  	require.NotNil(t, as)
   124  
   125  	protoFile := "mainpkg/test.proto"
   126  	protoMsg := "mainpkg.TestMessage"
   127  	protoMap := map[string]string{protoFile: mainProtoStr, "mainpkg/imported.proto": importedProtoStr}
   128  	currentSchemaOpt, err := namespace.
   129  		AppendSchemaOptions(nil, protoFile, protoMsg, protoMap, testNamespaceID)
   130  	require.NoError(t, err)
   131  	currentSchemaHist, err := namespace.LoadSchemaHistory(currentSchemaOpt)
   132  	require.NoError(t, err)
   133  
   134  	currentMeta, err := namespace.NewMetadata(ident.StringID("ns1"),
   135  		namespace.NewOptions().SetSchemaHistory(currentSchemaHist))
   136  	require.NoError(t, err)
   137  	currentMap, err := namespace.NewMap([]namespace.Metadata{currentMeta})
   138  	require.NoError(t, err)
   139  	currentReg, err := namespace.ToProto(currentMap)
   140  	require.NoError(t, err)
   141  
   142  	expectedMeta, err := namespace.NewMetadata(ident.StringID("ns1"),
   143  		namespace.NewOptions())
   144  	require.NoError(t, err)
   145  	expectedMap, err := namespace.NewMap([]namespace.Metadata{expectedMeta})
   146  	require.NoError(t, err)
   147  
   148  	mValue := kv.NewMockValue(ctrl)
   149  	mValue.EXPECT().Unmarshal(gomock.Any()).Return(nil).Do(func(reg *nsproto.Registry) {
   150  		*reg = *currentReg
   151  	})
   152  	mValue.EXPECT().Version().Return(1)
   153  	storeMock.EXPECT().Get(nsRegKey).Return(mValue, nil)
   154  	storeMock.EXPECT().CheckAndSet(nsRegKey, 1, gomock.Any()).Return(2, nil).Do(
   155  		func(k string, version int, actualReg *nsproto.Registry) {
   156  			actualMap, err := namespace.FromProto(*actualReg)
   157  			require.NoError(t, err)
   158  			require.NotEmpty(t, actualMap)
   159  			require.True(t, actualMap.Equal(expectedMap))
   160  		})
   161  	err = as.ResetSchema("ns1")
   162  	require.NoError(t, err)
   163  }
   164  
   165  func TestAdminService_Crud(t *testing.T) {
   166  	ctrl := gomock.NewController(t)
   167  	defer ctrl.Finish()
   168  
   169  	store := mem.NewStore()
   170  	as := NewAdminService(store, nsRegKey, func() string { return testNamespaceID })
   171  	require.NotNil(t, as)
   172  
   173  	expectedOpt := namespace.NewOptions()
   174  
   175  	optProto, err := namespace.OptionsToProto(expectedOpt)
   176  	require.NoError(t, err)
   177  
   178  	require.NoError(t, as.Add("ns1", optProto))
   179  	require.Error(t, as.Add("ns1", optProto))
   180  	require.NoError(t, as.Set("ns1", optProto))
   181  	require.Error(t, as.Set("ns2", optProto))
   182  	require.NoError(t, as.Add("ns3", optProto))
   183  
   184  	nsOpt, err := as.Get("ns1")
   185  	require.NoError(t, err)
   186  	require.NotNil(t, nsOpt)
   187  	nsMeta, err := namespace.ToMetadata("ns1", nsOpt)
   188  	require.NoError(t, err)
   189  	require.True(t, nsMeta.Options().Equal(expectedOpt))
   190  
   191  	_, err = as.Get("ns2")
   192  	require.Error(t, err)
   193  
   194  	nsReg, err := as.GetAll()
   195  	require.NoError(t, err)
   196  	require.Len(t, nsReg.Namespaces, 2)
   197  
   198  	err = as.Delete("ns1")
   199  	require.NoError(t, err)
   200  
   201  	nsReg, err = as.GetAll()
   202  	require.NoError(t, err)
   203  	require.Len(t, nsReg.Namespaces, 1)
   204  }
   205  
   206  func TestAdminService_DeleteOneNamespace(t *testing.T) {
   207  	ctrl := gomock.NewController(t)
   208  	defer ctrl.Finish()
   209  
   210  	storeMock := kv.NewMockStore(ctrl)
   211  	as := NewAdminService(storeMock, nsRegKey, func() string { return testNamespaceID })
   212  
   213  	currentMeta1, err := namespace.NewMetadata(ident.StringID("ns1"), namespace.NewOptions())
   214  	require.NoError(t, err)
   215  	currentMeta2, err := namespace.NewMetadata(ident.StringID("ns2"), namespace.NewOptions())
   216  	require.NoError(t, err)
   217  
   218  	currentMap, err := namespace.NewMap([]namespace.Metadata{currentMeta1, currentMeta2})
   219  	require.NoError(t, err)
   220  	currentReg, err := namespace.ToProto(currentMap)
   221  	require.NoError(t, err)
   222  
   223  	expectedMeta, err := namespace.NewMetadata(ident.StringID("ns2"), namespace.NewOptions())
   224  	require.NoError(t, err)
   225  	expectedMap, err := namespace.NewMap([]namespace.Metadata{expectedMeta})
   226  	require.NoError(t, err)
   227  
   228  	mValue := kv.NewMockValue(ctrl)
   229  	mValue.EXPECT().Unmarshal(gomock.Any()).Return(nil).Do(func(reg *nsproto.Registry) {
   230  		*reg = *currentReg
   231  	})
   232  	mValue.EXPECT().Version().Return(1)
   233  	storeMock.EXPECT().Get(nsRegKey).Return(mValue, nil)
   234  	storeMock.EXPECT().CheckAndSet(nsRegKey, 1, gomock.Any()).Return(2, nil).Do(
   235  		func(k string, version int, actualReg *nsproto.Registry) {
   236  			actualMap, err := namespace.FromProto(*actualReg)
   237  			require.NoError(t, err)
   238  			require.True(t, actualMap.Equal(expectedMap))
   239  		},
   240  	)
   241  
   242  	err = as.Delete("ns1")
   243  	require.NoError(t, err)
   244  }
   245  
   246  func TestAdminService_DeleteOneNamespaceFailedSetting(t *testing.T) {
   247  	ctrl := gomock.NewController(t)
   248  	defer ctrl.Finish()
   249  
   250  	storeMock := kv.NewMockStore(ctrl)
   251  	as := NewAdminService(storeMock, nsRegKey, func() string { return testNamespaceID })
   252  
   253  	currentMeta1, err := namespace.NewMetadata(ident.StringID("ns1"), namespace.NewOptions())
   254  	require.NoError(t, err)
   255  	currentMeta2, err := namespace.NewMetadata(ident.StringID("ns2"), namespace.NewOptions())
   256  	require.NoError(t, err)
   257  
   258  	currentMap, err := namespace.NewMap([]namespace.Metadata{currentMeta1, currentMeta2})
   259  	require.NoError(t, err)
   260  	currentReg, err := namespace.ToProto(currentMap)
   261  	require.NoError(t, err)
   262  
   263  	mValue := kv.NewMockValue(ctrl)
   264  	mValue.EXPECT().Unmarshal(gomock.Any()).Return(nil).Do(func(reg *nsproto.Registry) {
   265  		*reg = *currentReg
   266  	})
   267  	mValue.EXPECT().Version().Return(1)
   268  	storeMock.EXPECT().Get(nsRegKey).Return(mValue, nil)
   269  	storeMock.EXPECT().CheckAndSet(nsRegKey, 1, gomock.Any()).Return(-1, errors.New("some error"))
   270  
   271  	err = as.Delete("ns1")
   272  	require.Error(t, err)
   273  }
   274  
   275  func TestAdminService_DeleteLastNamespace(t *testing.T) {
   276  	ctrl := gomock.NewController(t)
   277  	defer ctrl.Finish()
   278  
   279  	storeMock := kv.NewMockStore(ctrl)
   280  	as := NewAdminService(storeMock, nsRegKey, func() string { return testNamespaceID })
   281  
   282  	currentMeta, err := namespace.NewMetadata(ident.StringID("ns1"), namespace.NewOptions())
   283  	require.NoError(t, err)
   284  
   285  	currentMap, err := namespace.NewMap([]namespace.Metadata{currentMeta})
   286  	require.NoError(t, err)
   287  	currentReg, err := namespace.ToProto(currentMap)
   288  	require.NoError(t, err)
   289  
   290  	mValue := kv.NewMockValue(ctrl)
   291  	mValue.EXPECT().Unmarshal(gomock.Any()).Return(nil).Do(func(reg *nsproto.Registry) {
   292  		*reg = *currentReg
   293  	})
   294  	mValue.EXPECT().Version().Return(1)
   295  	storeMock.EXPECT().Get(nsRegKey).Return(mValue, nil)
   296  	storeMock.EXPECT().Delete(nsRegKey).Return(nil, nil)
   297  
   298  	err = as.Delete("ns1")
   299  	require.NoError(t, err)
   300  }
   301  
   302  func TestAdminService_DeleteLastNamespaceFailed(t *testing.T) {
   303  	ctrl := gomock.NewController(t)
   304  	defer ctrl.Finish()
   305  
   306  	storeMock := kv.NewMockStore(ctrl)
   307  	as := NewAdminService(storeMock, nsRegKey, func() string { return testNamespaceID })
   308  
   309  	currentMeta, err := namespace.NewMetadata(ident.StringID("ns1"), namespace.NewOptions())
   310  	require.NoError(t, err)
   311  
   312  	currentMap, err := namespace.NewMap([]namespace.Metadata{currentMeta})
   313  	require.NoError(t, err)
   314  	currentReg, err := namespace.ToProto(currentMap)
   315  	require.NoError(t, err)
   316  
   317  	mValue := kv.NewMockValue(ctrl)
   318  	mValue.EXPECT().Unmarshal(gomock.Any()).Return(nil).Do(func(reg *nsproto.Registry) {
   319  		*reg = *currentReg
   320  	})
   321  	mValue.EXPECT().Version().Return(1)
   322  	storeMock.EXPECT().Get(nsRegKey).Return(mValue, nil)
   323  	storeMock.EXPECT().Delete(nsRegKey).Return(nil, errors.New("some error"))
   324  
   325  	err = as.Delete("ns1")
   326  	require.Error(t, err)
   327  }
   328  
   329  func TestAdminService_DeleteMissingNamespace(t *testing.T) {
   330  	ctrl := gomock.NewController(t)
   331  	defer ctrl.Finish()
   332  
   333  	storeMock := kv.NewMockStore(ctrl)
   334  	as := NewAdminService(storeMock, nsRegKey, func() string { return testNamespaceID })
   335  
   336  	currentMeta, err := namespace.NewMetadata(ident.StringID("ns1"), namespace.NewOptions())
   337  	require.NoError(t, err)
   338  
   339  	currentMap, err := namespace.NewMap([]namespace.Metadata{currentMeta})
   340  	require.NoError(t, err)
   341  	currentReg, err := namespace.ToProto(currentMap)
   342  	require.NoError(t, err)
   343  
   344  	mValue := kv.NewMockValue(ctrl)
   345  	mValue.EXPECT().Unmarshal(gomock.Any()).Return(nil).Do(func(reg *nsproto.Registry) {
   346  		*reg = *currentReg
   347  	})
   348  	mValue.EXPECT().Version().Return(1)
   349  	storeMock.EXPECT().Get(nsRegKey).Return(mValue, nil)
   350  
   351  	err = as.Delete("missing-namespace")
   352  	require.EqualError(t, ErrNamespaceNotFound, err.Error())
   353  }
   354  
   355  func TestAdminService_DeleteNilRegistry(t *testing.T) {
   356  	ctrl := gomock.NewController(t)
   357  	defer ctrl.Finish()
   358  
   359  	storeMock := kv.NewMockStore(ctrl)
   360  	as := NewAdminService(storeMock, nsRegKey, func() string { return testNamespaceID })
   361  
   362  	storeMock.EXPECT().Get(nsRegKey).Return(nil, errors.New("some error"))
   363  
   364  	err := as.Delete("missing-namespace")
   365  	require.Error(t, err)
   366  }