github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/ctl/service/r2/store/kv/store_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 kv 22 23 import ( 24 "testing" 25 "time" 26 27 "github.com/m3db/m3/src/ctl/service/r2" 28 r2store "github.com/m3db/m3/src/ctl/service/r2/store" 29 "github.com/m3db/m3/src/metrics/aggregation" 30 merrors "github.com/m3db/m3/src/metrics/errors" 31 "github.com/m3db/m3/src/metrics/pipeline" 32 "github.com/m3db/m3/src/metrics/policy" 33 "github.com/m3db/m3/src/metrics/rules" 34 "github.com/m3db/m3/src/metrics/rules/view" 35 "github.com/m3db/m3/src/metrics/rules/view/changes" 36 "github.com/m3db/m3/src/x/clock" 37 38 "github.com/golang/mock/gomock" 39 "github.com/stretchr/testify/require" 40 ) 41 42 func TestUpdateRuleSet(t *testing.T) { 43 helper := rules.NewRuleSetUpdateHelper(time.Minute) 44 initialRuleSet, err := testRuleSet(1, helper.NewUpdateMetadata(100, "validUser")) 45 require.NoError(t, err) 46 47 mrs, err := initialRuleSet.MappingRules() 48 require.NoError(t, err) 49 rrs, err := initialRuleSet.RollupRules() 50 require.NoError(t, err) 51 rsChanges := newTestRuleSetChanges(mrs, rrs) 52 require.NoError(t, err) 53 54 proto, err := initialRuleSet.ToMutableRuleSet().Proto() 55 require.NoError(t, err) 56 expected, err := rules.NewRuleSetFromProto(1, proto, rules.NewOptions()) 57 require.NoError(t, err) 58 expectedMutable := expected.ToMutableRuleSet() 59 err = expectedMutable.ApplyRuleSetChanges(rsChanges, helper.NewUpdateMetadata(200, "validUser")) 60 require.NoError(t, err) 61 62 ctrl := gomock.NewController(t) 63 defer ctrl.Finish() 64 mockedStore := rules.NewMockStore(ctrl) 65 mockedStore.EXPECT().ReadRuleSet("testNamespace").Return( 66 initialRuleSet, 67 nil, 68 ).Times(2) 69 70 mockedStore.EXPECT().WriteRuleSet(gomock.Any()).Do(func(rs rules.MutableRuleSet) { 71 // mock library can not match rules.MutableRuleSet interface so use this function 72 expectedProto, err := expectedMutable.Proto() 73 require.NoError(t, err) 74 rsProto, err := rs.Proto() 75 require.NoError(t, err) 76 require.Equal(t, expectedProto, rsProto) 77 }).Return(nil) 78 79 storeOpts := NewStoreOptions().SetClockOptions( 80 clock.NewOptions().SetNowFn(func() time.Time { 81 return time.Unix(0, 200) 82 }), 83 ) 84 rulesStore := NewStore(mockedStore, storeOpts) 85 uOpts := r2store.NewUpdateOptions().SetAuthor("validUser") 86 _, err = rulesStore.UpdateRuleSet(rsChanges, 1, uOpts) 87 require.NoError(t, err) 88 } 89 90 func TestUpdateRuleSetVersionMisMatch(t *testing.T) { 91 helper := rules.NewRuleSetUpdateHelper(time.Minute) 92 initialRuleSet, err := newEmptyTestRuleSet(2, helper.NewUpdateMetadata(100, "validUser")) 93 require.NoError(t, err) 94 95 rsChanges := newTestRuleSetChanges( 96 view.MappingRules{}, 97 view.RollupRules{}, 98 ) 99 100 ctrl := gomock.NewController(t) 101 defer ctrl.Finish() 102 mockedStore := rules.NewMockStore(ctrl) 103 mockedStore.EXPECT().ReadRuleSet("testNamespace").Return( 104 initialRuleSet, 105 nil, 106 ) 107 108 storeOpts := NewStoreOptions().SetClockOptions( 109 clock.NewOptions().SetNowFn(func() time.Time { 110 return time.Unix(0, 200) 111 }), 112 ) 113 rulesStore := NewStore(mockedStore, storeOpts) 114 uOpts := r2store.NewUpdateOptions().SetAuthor("validUser") 115 _, err = rulesStore.UpdateRuleSet(rsChanges, 1, uOpts) 116 require.Error(t, err) 117 require.IsType(t, r2.NewConflictError(""), err) 118 } 119 120 func TestUpdateRuleSetFetchNotFound(t *testing.T) { 121 rsChanges := newTestRuleSetChanges( 122 view.MappingRules{}, 123 view.RollupRules{}, 124 ) 125 126 ctrl := gomock.NewController(t) 127 defer ctrl.Finish() 128 mockedStore := rules.NewMockStore(ctrl) 129 mockedStore.EXPECT().ReadRuleSet("testNamespace").Return( 130 nil, 131 merrors.NewNotFoundError("something bad has happened"), 132 ) 133 134 storeOpts := NewStoreOptions().SetClockOptions( 135 clock.NewOptions().SetNowFn(func() time.Time { 136 return time.Unix(0, 200) 137 }), 138 ) 139 rulesStore := NewStore(mockedStore, storeOpts) 140 uOpts := r2store.NewUpdateOptions().SetAuthor("validUser") 141 _, err := rulesStore.UpdateRuleSet(rsChanges, 1, uOpts) 142 require.Error(t, err) 143 require.IsType(t, r2.NewNotFoundError(""), err) 144 } 145 146 func TestUpdateRuleSetFetchFailure(t *testing.T) { 147 rsChanges := newTestRuleSetChanges( 148 view.MappingRules{}, 149 view.RollupRules{}, 150 ) 151 152 ctrl := gomock.NewController(t) 153 defer ctrl.Finish() 154 mockedStore := rules.NewMockStore(ctrl) 155 mockedStore.EXPECT().ReadRuleSet("testNamespace").Return( 156 nil, 157 merrors.NewValidationError("something bad has happened"), 158 ) 159 160 storeOpts := NewStoreOptions().SetClockOptions( 161 clock.NewOptions().SetNowFn(func() time.Time { 162 return time.Unix(0, 200) 163 }), 164 ) 165 rulesStore := NewStore(mockedStore, storeOpts) 166 uOpts := r2store.NewUpdateOptions().SetAuthor("validUser") 167 _, err := rulesStore.UpdateRuleSet(rsChanges, 1, uOpts) 168 require.Error(t, err) 169 require.IsType(t, r2.NewBadInputError(""), err) 170 } 171 172 func TestUpdateRuleSetMutationFail(t *testing.T) { 173 helper := rules.NewRuleSetUpdateHelper(time.Minute) 174 initialRuleSet, err := newEmptyTestRuleSet(1, helper.NewUpdateMetadata(100, "validUser")) 175 176 rsChanges := newTestRuleSetChanges( 177 view.MappingRules{ 178 "invalidMappingRule": []view.MappingRule{}, 179 }, 180 view.RollupRules{}, 181 ) 182 require.NoError(t, err) 183 184 ctrl := gomock.NewController(t) 185 defer ctrl.Finish() 186 mockedStore := rules.NewMockStore(ctrl) 187 mockedStore.EXPECT().ReadRuleSet("testNamespace").Return( 188 initialRuleSet, 189 nil, 190 ) 191 192 storeOpts := NewStoreOptions().SetClockOptions( 193 clock.NewOptions().SetNowFn(func() time.Time { 194 return time.Unix(0, 200) 195 }), 196 ) 197 rulesStore := NewStore(mockedStore, storeOpts) 198 uOpts := r2store.NewUpdateOptions().SetAuthor("validUser") 199 _, err = rulesStore.UpdateRuleSet(rsChanges, 1, uOpts) 200 require.Error(t, err) 201 require.IsType(t, r2.NewConflictError(""), err) 202 } 203 204 func TestUpdateRuleSetWriteFailure(t *testing.T) { 205 helper := rules.NewRuleSetUpdateHelper(time.Minute) 206 initialRuleSet, err := testRuleSet(1, helper.NewUpdateMetadata(100, "validUser")) 207 require.NoError(t, err) 208 209 mrs, err := initialRuleSet.MappingRules() 210 require.NoError(t, err) 211 rrs, err := initialRuleSet.RollupRules() 212 require.NoError(t, err) 213 rsChanges := newTestRuleSetChanges(mrs, rrs) 214 require.NoError(t, err) 215 216 proto, err := initialRuleSet.ToMutableRuleSet().Proto() 217 require.NoError(t, err) 218 expected, err := rules.NewRuleSetFromProto(1, proto, rules.NewOptions()) 219 require.NoError(t, err) 220 expectedMutable := expected.ToMutableRuleSet() 221 err = expectedMutable.ApplyRuleSetChanges(rsChanges, helper.NewUpdateMetadata(200, "validUser")) 222 require.NoError(t, err) 223 224 ctrl := gomock.NewController(t) 225 defer ctrl.Finish() 226 mockedStore := rules.NewMockStore(ctrl) 227 mockedStore.EXPECT().ReadRuleSet("testNamespace").Return( 228 initialRuleSet, 229 nil, 230 ) 231 232 mockedStore.EXPECT().WriteRuleSet(gomock.Any()).Do(func(rs rules.MutableRuleSet) { 233 // mock library can not match rules.MutableRuleSet interface so use this function 234 expectedProto, err := expectedMutable.Proto() 235 require.NoError(t, err) 236 rsProto, err := rs.Proto() 237 require.NoError(t, err) 238 require.Equal(t, expectedProto, rsProto) 239 }).Return(merrors.NewStaleDataError("something has gone wrong")) 240 241 storeOpts := NewStoreOptions().SetClockOptions( 242 clock.NewOptions().SetNowFn(func() time.Time { 243 return time.Unix(0, 200) 244 }), 245 ) 246 rulesStore := NewStore(mockedStore, storeOpts) 247 uOpts := r2store.NewUpdateOptions().SetAuthor("validUser") 248 _, err = rulesStore.UpdateRuleSet(rsChanges, 1, uOpts) 249 require.Error(t, err) 250 require.IsType(t, r2.NewConflictError(""), err) 251 } 252 253 func newTestRuleSetChanges(mrs view.MappingRules, rrs view.RollupRules) changes.RuleSetChanges { 254 mrChanges := make([]changes.MappingRuleChange, 0, len(mrs)) 255 for uuid := range mrs { 256 mrChanges = append( 257 mrChanges, 258 changes.MappingRuleChange{ 259 Op: changes.ChangeOp, 260 RuleID: &uuid, 261 RuleData: &view.MappingRule{ 262 ID: uuid, 263 Name: "updateMappingRule", 264 }, 265 }, 266 ) 267 } 268 269 rrChanges := make([]changes.RollupRuleChange, 0, len(rrs)) 270 for uuid := range rrs { 271 rrChanges = append( 272 rrChanges, 273 changes.RollupRuleChange{ 274 Op: changes.ChangeOp, 275 RuleID: &uuid, 276 RuleData: &view.RollupRule{ 277 ID: uuid, 278 Name: "updateRollupRule", 279 }, 280 }, 281 ) 282 } 283 284 return changes.RuleSetChanges{ 285 Namespace: "testNamespace", 286 RollupRuleChanges: rrChanges, 287 MappingRuleChanges: mrChanges, 288 } 289 } 290 291 // nolint: unparam 292 func testRuleSet(version int, meta rules.UpdateMetadata) (rules.RuleSet, error) { 293 mutable := rules.NewEmptyRuleSet("testNamespace", meta) 294 rr1, err := pipeline.NewRollupOp( 295 pipeline.GroupByRollupType, 296 "testTarget", 297 []string{"tag1", "tag2"}, 298 aggregation.MustCompressTypes(aggregation.Min), 299 ) 300 if err != nil { 301 return nil, err 302 } 303 304 err = mutable.ApplyRuleSetChanges( 305 changes.RuleSetChanges{ 306 Namespace: "testNamespace", 307 RollupRuleChanges: []changes.RollupRuleChange{ 308 { 309 Op: changes.AddOp, 310 RuleData: &view.RollupRule{ 311 Name: "rollupRule3", 312 Targets: []view.RollupTarget{ 313 { 314 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 315 { 316 Type: pipeline.RollupOpType, 317 Rollup: rr1, 318 }, 319 }), 320 StoragePolicies: policy.StoragePolicies{ 321 policy.MustParseStoragePolicy("1m:10d"), 322 }, 323 }, 324 }, 325 }, 326 }, 327 }, 328 MappingRuleChanges: []changes.MappingRuleChange{ 329 { 330 Op: changes.AddOp, 331 RuleData: &view.MappingRule{ 332 Name: "mappingRule3", 333 StoragePolicies: policy.StoragePolicies{ 334 policy.MustParseStoragePolicy("1s:6h"), 335 }, 336 }, 337 }, 338 }, 339 }, 340 meta, 341 ) 342 if err != nil { 343 return nil, err 344 } 345 proto, err := mutable.Proto() 346 if err != nil { 347 return nil, err 348 } 349 ruleSet, err := rules.NewRuleSetFromProto(version, proto, rules.NewOptions()) 350 if err != nil { 351 return nil, err 352 } 353 354 return ruleSet, nil 355 } 356 357 func newEmptyTestRuleSet(version int, meta rules.UpdateMetadata) (rules.RuleSet, error) { 358 proto, err := rules.NewEmptyRuleSet("testNamespace", meta).Proto() 359 if err != nil { 360 return nil, err 361 } 362 ruleSet, err := rules.NewRuleSetFromProto(version, proto, rules.NewOptions()) 363 if err != nil { 364 return nil, err 365 } 366 367 return ruleSet, nil 368 }