diff --git a/CHANGELOG.md b/CHANGELOG.md index 9eb34bc..ec30bdc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [2.14.0] +- Added: new param `defaultTenant` to the `hasSourceTenantsForMetrics` validator to set the default tenant for the rule group. + ## [2.13.0] - 2024-03-08 - Added: support for disabling validation using comments in yaml rules per rule group and for the whole file. - Docs: Updated/improved documentation on how to disable validations and validation rules. diff --git a/docs/validations.md b/docs/validations.md index c946043..34cc2e2 100644 --- a/docs/validations.md +++ b/docs/validations.md @@ -317,6 +317,7 @@ Fails, if the rule uses metric, that matches the specified regular expression fo ```yaml params: + defaultTenant: # Optional, if set, the tenant that will be assumed if the group does not have the `source_tenants` option set sourceTenants: : - regexp: # The regexp will be fully anchored (surrounded by ^...$) diff --git a/pkg/validator/others.go b/pkg/validator/others.go index 2380b36..313a151 100644 --- a/pkg/validator/others.go +++ b/pkg/validator/others.go @@ -20,6 +20,7 @@ type SourceTenantMetrics struct { func newHasSourceTenantsForMetrics(paramsConfig yaml.Node) (Validator, error) { params := struct { SourceTenants map[string][]SourceTenantMetrics `yaml:"sourceTenants"` + DefaultTenant string `yaml:"defaultTenant"` }{} if err := paramsConfig.Decode(¶ms); err != nil { return nil, err @@ -27,7 +28,7 @@ func newHasSourceTenantsForMetrics(paramsConfig yaml.Node) (Validator, error) { if params.SourceTenants == nil || len(params.SourceTenants) == 0 { return nil, fmt.Errorf("sourceTenants metrics mapping needs to be set") } - validator := hasSourceTenantsForMetrics{sourceTenants: map[string][]tenantMetrics{}} + validator := hasSourceTenantsForMetrics{sourceTenants: map[string][]tenantMetrics{}, defaultTenant: params.DefaultTenant} for tenant, metrics := range params.SourceTenants { m := make([]tenantMetrics, len(metrics)) for i, metric := range metrics { @@ -52,6 +53,7 @@ type tenantMetrics struct { type hasSourceTenantsForMetrics struct { sourceTenants map[string][]tenantMetrics + defaultTenant string } func (h hasSourceTenantsForMetrics) String() string { @@ -79,7 +81,13 @@ func (h hasSourceTenantsForMetrics) Validate(group unmarshaler.RuleGroup, rule r for _, usedMetric := range usedMetrics { for tenant, metrics := range h.sourceTenants { for _, metric := range metrics { - if metric.regexp.MatchString(usedMetric.Name) && !slices.Contains(group.SourceTenants, tenant) { + if !metric.regexp.MatchString(usedMetric.Name) { + continue + } + if len(group.SourceTenants) == 0 && h.defaultTenant == tenant { + continue + } + if !slices.Contains(group.SourceTenants, tenant) { errs = append(errs, fmt.Errorf("rule uses metric `%s` of the tenant `%s` tenant, you should set the tenant in the groups source_tenants settings", tenant, usedMetric.Name)) } } diff --git a/pkg/validator/validator_test.go b/pkg/validator/validator_test.go index 4e5c585..9f844bd 100644 --- a/pkg/validator/validator_test.go +++ b/pkg/validator/validator_test.go @@ -201,6 +201,8 @@ var testCases = []struct { {name: "usesMetricWithSourceTenantAndGroupDoesNotHaveSourceTenant", validator: hasSourceTenantsForMetrics{sourceTenants: map[string][]tenantMetrics{"tenant1": {{regexp: regexp.MustCompile(`^teanant1_metric$`)}}}}, group: unmarshaler.RuleGroup{SourceTenants: []string{"tenant2"}}, rule: rulefmt.Rule{Expr: `teanant1_metric{foo="bar"}`}, expectedErrors: 1}, {name: "usesMetricWithSourceTenantAndGroupHasMultipleSourceTenants", validator: hasSourceTenantsForMetrics{sourceTenants: map[string][]tenantMetrics{"tenant1": {{regexp: regexp.MustCompile(`^teanant1_metric$`)}}}}, group: unmarshaler.RuleGroup{SourceTenants: []string{"tenant2", "tenant1"}}, rule: rulefmt.Rule{Expr: `teanant1_metric{foo="bar"}`}, expectedErrors: 0}, {name: "usesMetricWithSourceTenantAndGroupHasMultipleSourceTenantsAndOneIsMissing", validator: hasSourceTenantsForMetrics{sourceTenants: map[string][]tenantMetrics{"tenant1": {{regexp: regexp.MustCompile(`^teanant1_metric$`)}}}}, group: unmarshaler.RuleGroup{SourceTenants: []string{"tenant2", "tenant3"}}, rule: rulefmt.Rule{Expr: `teanant1_metric{foo="bar"}`}, expectedErrors: 1}, + {name: "doesNotHaveSourceTenantForMetricButIsDefault", validator: hasSourceTenantsForMetrics{defaultTenant: "tenant1", sourceTenants: map[string][]tenantMetrics{"tenant1": {{regexp: regexp.MustCompile(`^teanant1_metric$`)}}}}, group: unmarshaler.RuleGroup{SourceTenants: []string{}}, rule: rulefmt.Rule{Expr: `teanant1_metric{foo="bar"}`}, expectedErrors: 0}, + {name: "doesNotHaveSourceTenantForMetricAndIsNotDefault", validator: hasSourceTenantsForMetrics{defaultTenant: "tenant2", sourceTenants: map[string][]tenantMetrics{"tenant1": {{regexp: regexp.MustCompile(`^teanant1_metric$`)}}}}, group: unmarshaler.RuleGroup{SourceTenants: []string{}}, rule: rulefmt.Rule{Expr: `teanant1_metric{foo="bar"}`}, expectedErrors: 1}, // hasAllowedSourceTenants {name: "emptyAllowedSourceTenantsAndGroupSourceTenants", validator: hasAllowedSourceTenants{allowedSourceTenants: []string{}}, group: unmarshaler.RuleGroup{SourceTenants: []string{}}, rule: rulefmt.Rule{Expr: `up{foo="bar"}`}, expectedErrors: 0},