github.com/m3db/m3@v1.5.0/src/metrics/rules/validator/config_test.go (about) 1 // Copyright (c) 2017 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 validator 22 23 import ( 24 "testing" 25 26 "github.com/m3db/m3/src/cluster/client" 27 "github.com/m3db/m3/src/cluster/kv" 28 "github.com/m3db/m3/src/cluster/kv/mem" 29 "github.com/m3db/m3/src/metrics/aggregation" 30 "github.com/m3db/m3/src/metrics/filters" 31 "github.com/m3db/m3/src/metrics/metric" 32 "github.com/m3db/m3/src/metrics/policy" 33 34 "github.com/golang/mock/gomock" 35 "github.com/stretchr/testify/require" 36 yaml "gopkg.in/yaml.v2" 37 ) 38 39 func TestNamespaceValidatorConfigurationNoConfigurationProvided(t *testing.T) { 40 cfgStr := "" 41 var cfg namespaceValidatorConfiguration 42 require.NoError(t, yaml.Unmarshal([]byte(cfgStr), &cfg)) 43 _, err := cfg.NewNamespaceValidator(nil) 44 require.Equal(t, errNoNamespaceValidatorConfiguration, err) 45 } 46 47 func TestNamespaceValidatorConfigurationMultipleConfigurationProvided(t *testing.T) { 48 cfgStr := ` 49 kv: 50 kvConfig: 51 zone: testZone 52 environment: testEnvironment 53 initWatchTimeout: 5ms 54 validNamespacesKey: testValidNamespaces 55 static: 56 validationResult: valid 57 ` 58 var cfg namespaceValidatorConfiguration 59 require.NoError(t, yaml.Unmarshal([]byte(cfgStr), &cfg)) 60 _, err := cfg.NewNamespaceValidator(nil) 61 require.Equal(t, errMultipleNamespaceValidatorConfigurations, err) 62 } 63 64 func TestNamespaceValidatorConfigurationKV(t *testing.T) { 65 ctrl := gomock.NewController(t) 66 defer ctrl.Finish() 67 68 cfgStr := ` 69 kv: 70 kvConfig: 71 zone: testZone 72 environment: testEnvironment 73 initWatchTimeout: 5ms 74 validNamespacesKey: testValidNamespaces 75 ` 76 var cfg namespaceValidatorConfiguration 77 require.NoError(t, yaml.Unmarshal([]byte(cfgStr), &cfg)) 78 79 kvStore := mem.NewStore() 80 kvOpts := kv.NewOverrideOptions().SetZone("testZone").SetEnvironment("testEnvironment") 81 kvClient := client.NewMockClient(ctrl) 82 kvClient.EXPECT().Store(kvOpts).Return(kvStore, nil) 83 _, err := cfg.NewNamespaceValidator(kvClient) 84 require.NoError(t, err) 85 } 86 87 func TestNewValidator(t *testing.T) { 88 cfgStr := ` 89 namespace: 90 static: 91 validationResult: valid 92 requiredRollupTags: 93 - tag1 94 - tag2 95 maxTransformationDerivativeOrder: 2 96 maxRollupLevels: 1 97 filterInvalidTagNames: 98 - foobar 99 metricTypes: 100 typeTag: type 101 allowed: 102 - counter 103 - timer 104 - gauge 105 policies: 106 defaultAllowed: 107 storagePolicies: 108 - 10s:2d 109 - 1m:40d 110 nonFirstLevelAggregationTypes: 111 - Sum 112 - Last 113 overrides: 114 - type: counter 115 allowed: 116 firstLevelAggregationTypes: 117 - Sum 118 - type: timer 119 allowed: 120 storagePolicies: 121 - 10s:2d 122 firstLevelAggregationTypes: 123 - P50 124 - P9999 125 - type: gauge 126 allowed: 127 firstLevelAggregationTypes: 128 - Last 129 ` 130 131 var cfg Configuration 132 require.NoError(t, yaml.Unmarshal([]byte(cfgStr), &cfg)) 133 opts := cfg.newValidatorOptions(nil) 134 135 inputs := []struct { 136 metricType metric.Type 137 allowedStoragePolicies policy.StoragePolicies 138 disallowedStoragePolicies policy.StoragePolicies 139 allowedFirstLevelAggTypes aggregation.Types 140 disallowedFirstLevelAggTypes aggregation.Types 141 allowedNonFirstLevelAggTypes aggregation.Types 142 disallowedNonFirstLevelAggTypes aggregation.Types 143 }{ 144 { 145 metricType: metric.CounterType, 146 allowedStoragePolicies: policy.StoragePolicies{ 147 policy.MustParseStoragePolicy("10s:2d"), 148 policy.MustParseStoragePolicy("1m:40d"), 149 }, 150 disallowedStoragePolicies: policy.StoragePolicies{ 151 policy.MustParseStoragePolicy("1m:2d"), 152 policy.MustParseStoragePolicy("10s:40d"), 153 }, 154 allowedFirstLevelAggTypes: aggregation.Types{ 155 aggregation.Sum, 156 }, 157 disallowedFirstLevelAggTypes: aggregation.Types{ 158 aggregation.Last, 159 }, 160 allowedNonFirstLevelAggTypes: aggregation.Types{ 161 aggregation.Sum, 162 aggregation.Last, 163 }, 164 disallowedNonFirstLevelAggTypes: aggregation.Types{ 165 aggregation.Min, 166 aggregation.P99, 167 }, 168 }, 169 { 170 metricType: metric.TimerType, 171 allowedStoragePolicies: policy.StoragePolicies{ 172 policy.MustParseStoragePolicy("10s:2d"), 173 }, 174 disallowedStoragePolicies: policy.StoragePolicies{ 175 policy.MustParseStoragePolicy("1m:2d"), 176 policy.MustParseStoragePolicy("1m:40d"), 177 }, 178 allowedFirstLevelAggTypes: aggregation.Types{ 179 aggregation.P50, 180 aggregation.P9999, 181 }, 182 disallowedFirstLevelAggTypes: aggregation.Types{ 183 aggregation.Last, 184 }, 185 allowedNonFirstLevelAggTypes: aggregation.Types{ 186 aggregation.Sum, 187 aggregation.Last, 188 }, 189 disallowedNonFirstLevelAggTypes: aggregation.Types{ 190 aggregation.Min, 191 aggregation.P99, 192 }, 193 }, 194 { 195 metricType: metric.GaugeType, 196 allowedStoragePolicies: policy.StoragePolicies{ 197 policy.MustParseStoragePolicy("10s:2d"), 198 policy.MustParseStoragePolicy("1m:40d"), 199 }, 200 disallowedStoragePolicies: policy.StoragePolicies{ 201 policy.MustParseStoragePolicy("1m:2d"), 202 policy.MustParseStoragePolicy("10s:40d"), 203 }, 204 allowedFirstLevelAggTypes: aggregation.Types{ 205 aggregation.Last, 206 }, 207 disallowedFirstLevelAggTypes: aggregation.Types{ 208 aggregation.Sum, 209 }, 210 allowedNonFirstLevelAggTypes: aggregation.Types{ 211 aggregation.Sum, 212 aggregation.Last, 213 }, 214 disallowedNonFirstLevelAggTypes: aggregation.Types{ 215 aggregation.Min, 216 aggregation.P99, 217 }, 218 }, 219 } 220 221 for _, input := range inputs { 222 for _, storagePolicy := range input.allowedStoragePolicies { 223 require.True(t, opts.IsAllowedStoragePolicyFor(input.metricType, storagePolicy)) 224 } 225 for _, storagePolicy := range input.disallowedStoragePolicies { 226 require.False(t, opts.IsAllowedStoragePolicyFor(input.metricType, storagePolicy)) 227 } 228 for _, aggregationType := range input.allowedFirstLevelAggTypes { 229 require.True(t, opts.IsAllowedFirstLevelAggregationTypeFor(input.metricType, aggregationType)) 230 } 231 for _, aggregationType := range input.disallowedFirstLevelAggTypes { 232 require.False(t, opts.IsAllowedFirstLevelAggregationTypeFor(input.metricType, aggregationType)) 233 } 234 for _, aggregationType := range input.allowedNonFirstLevelAggTypes { 235 require.True(t, opts.IsAllowedNonFirstLevelAggregationTypeFor(input.metricType, aggregationType)) 236 } 237 for _, aggregationType := range input.disallowedNonFirstLevelAggTypes { 238 require.False(t, opts.IsAllowedNonFirstLevelAggregationTypeFor(input.metricType, aggregationType)) 239 } 240 } 241 242 require.Error(t, opts.CheckFilterTagNameValid("foobar")) 243 } 244 245 func TestNamespaceValidatorConfigurationStatic(t *testing.T) { 246 cfgStr := ` 247 static: 248 validationResult: valid 249 ` 250 var cfg namespaceValidatorConfiguration 251 require.NoError(t, yaml.Unmarshal([]byte(cfgStr), &cfg)) 252 _, err := cfg.NewNamespaceValidator(nil) 253 require.NoError(t, err) 254 } 255 256 func TestConfigurationRequiredRollupTags(t *testing.T) { 257 cfg := ` 258 requiredRollupTags: 259 - tag1 260 - tag2 261 ` 262 var c Configuration 263 require.NoError(t, yaml.Unmarshal([]byte(cfg), &c)) 264 require.Equal(t, []string{"tag1", "tag2"}, c.RequiredRollupTags) 265 } 266 267 func TestConfigurationTagNameInvalidChars(t *testing.T) { 268 cfg := `tagNameInvalidChars: "%\n"` 269 var c Configuration 270 require.NoError(t, yaml.Unmarshal([]byte(cfg), &c)) 271 require.Equal(t, []rune{'%', '\n'}, toRunes(c.TagNameInvalidChars)) 272 } 273 274 func TestConfigurationMetricNameInvalidChars(t *testing.T) { 275 cfg := `metricNameInvalidChars: "%\n"` 276 var c Configuration 277 require.NoError(t, yaml.Unmarshal([]byte(cfg), &c)) 278 require.Equal(t, []rune{'%', '\n'}, toRunes(c.MetricNameInvalidChars)) 279 } 280 281 func TestNewMetricTypesFn(t *testing.T) { 282 cfg := ` 283 typeTag: type 284 allowed: 285 - counter 286 - timer 287 - gauge 288 ` 289 290 var c metricTypesValidationConfiguration 291 require.NoError(t, yaml.Unmarshal([]byte(cfg), &c)) 292 fn := c.NewMetricTypesFn() 293 294 inputs := []struct { 295 filters filters.TagFilterValueMap 296 expectedTypes []metric.Type 297 }{ 298 { 299 filters: nil, 300 expectedTypes: []metric.Type{metric.CounterType, metric.TimerType, metric.GaugeType}, 301 }, 302 { 303 filters: filters.TagFilterValueMap{ 304 "randomTag": filters.FilterValue{Pattern: "counter"}, 305 }, 306 expectedTypes: []metric.Type{metric.CounterType, metric.TimerType, metric.GaugeType}, 307 }, 308 { 309 filters: filters.TagFilterValueMap{ 310 "type": filters.FilterValue{Pattern: "counter"}, 311 }, 312 expectedTypes: []metric.Type{metric.CounterType}, 313 }, 314 { 315 filters: filters.TagFilterValueMap{ 316 "type": filters.FilterValue{Pattern: "timer"}, 317 }, 318 expectedTypes: []metric.Type{metric.TimerType}, 319 }, 320 { 321 filters: filters.TagFilterValueMap{ 322 "type": filters.FilterValue{Pattern: "gauge"}, 323 }, 324 expectedTypes: []metric.Type{metric.GaugeType}, 325 }, 326 { 327 filters: filters.TagFilterValueMap{ 328 "type": filters.FilterValue{Pattern: "*er"}, 329 }, 330 expectedTypes: []metric.Type{metric.CounterType, metric.TimerType}, 331 }, 332 } 333 334 for _, input := range inputs { 335 res, err := fn(input.filters) 336 require.NoError(t, err) 337 require.Equal(t, input.expectedTypes, res) 338 } 339 } 340 341 func TestNewMetricTypesFnError(t *testing.T) { 342 cfg := ` 343 typeTag: type 344 allowed: 345 - counter 346 - timer 347 - gauge 348 ` 349 350 var c metricTypesValidationConfiguration 351 require.NoError(t, yaml.Unmarshal([]byte(cfg), &c)) 352 fn := c.NewMetricTypesFn() 353 354 inputs := []filters.TagFilterValueMap{ 355 filters.TagFilterValueMap{ 356 "type": filters.FilterValue{Pattern: "a[b"}, 357 }, 358 filters.TagFilterValueMap{ 359 "type": filters.FilterValue{Pattern: "ab{"}, 360 }, 361 } 362 for _, input := range inputs { 363 res, err := fn(input) 364 require.Error(t, err) 365 require.Nil(t, res) 366 } 367 } 368 369 func TestToRunes(t *testing.T) { 370 s := "%\n 6s[:\\" 371 require.Equal(t, []rune{'%', '\n', ' ', '6', 's', '[', ':', '\\'}, toRunes(s)) 372 }