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  }