github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/configs/userconfig/config_test.go (about)

     1  package userconfig
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"strconv"
     7  	"strings"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/go-kit/log"
    12  	"github.com/prometheus/common/model"
    13  	"github.com/prometheus/prometheus/model/labels"
    14  	"github.com/prometheus/prometheus/model/rulefmt"
    15  	"github.com/prometheus/prometheus/promql/parser"
    16  	"github.com/prometheus/prometheus/rules"
    17  	"github.com/stretchr/testify/assert"
    18  	"github.com/stretchr/testify/require"
    19  	"gopkg.in/yaml.v3"
    20  
    21  	util_log "github.com/grafana/loki/pkg/util/log"
    22  )
    23  
    24  var legacyRulesFile = `ALERT TestAlert
    25  IF up == 0
    26  FOR 5m
    27  LABELS { severity = "critical" }
    28  ANNOTATIONS {
    29  	message = "I am a message"
    30  }`
    31  
    32  var ruleFile = `groups:
    33  - name: example
    34    rules:
    35    - alert: TestAlert
    36      expr: up == 0
    37      for: 5m
    38      labels:
    39        severity: critical
    40      annotations:
    41        message: I am a message`
    42  
    43  func TestUnmarshalJSONLegacyConfigWithMissingRuleFormatVersionSucceeds(t *testing.T) {
    44  	actual := Config{}
    45  	buf := []byte(`{"rules_files": {"a": "b"}}`)
    46  	assert.Nil(t, json.Unmarshal(buf, &actual))
    47  
    48  	expected := Config{
    49  		RulesConfig: RulesConfig{
    50  			Files: map[string]string{
    51  				"a": "b",
    52  			},
    53  			FormatVersion: RuleFormatV1,
    54  		},
    55  	}
    56  
    57  	assert.Equal(t, expected, actual)
    58  }
    59  
    60  func TestUnmarshalYAMLLegacyConfigWithMissingRuleFormatVersionSucceeds(t *testing.T) {
    61  	actual := Config{}
    62  	buf := []byte(strings.TrimSpace(`
    63  rule_format_version: '1'
    64  rules_files:
    65    a: b
    66  `))
    67  	assert.Nil(t, yaml.Unmarshal(buf, &actual))
    68  
    69  	expected := Config{
    70  		RulesConfig: RulesConfig{
    71  			Files: map[string]string{
    72  				"a": "b",
    73  			},
    74  			FormatVersion: RuleFormatV1,
    75  		},
    76  	}
    77  
    78  	assert.Equal(t, expected, actual)
    79  }
    80  
    81  func TestParseLegacyAlerts(t *testing.T) {
    82  	parsed, err := parser.ParseExpr("up == 0")
    83  	require.NoError(t, err)
    84  	rule := rules.NewAlertingRule(
    85  		"TestAlert",
    86  		parsed,
    87  		5*time.Minute,
    88  		labels.Labels{
    89  			labels.Label{Name: "severity", Value: "critical"},
    90  		},
    91  		labels.Labels{
    92  			labels.Label{Name: "message", Value: "I am a message"},
    93  		},
    94  		nil,
    95  		"",
    96  		true,
    97  		log.With(util_log.Logger, "alert", "TestAlert"),
    98  	)
    99  
   100  	for i, tc := range []struct {
   101  		cfg      RulesConfig
   102  		expected map[string][]rules.Rule
   103  		wantErr  error
   104  	}{
   105  		{
   106  			cfg: RulesConfig{
   107  				FormatVersion: RuleFormatV1,
   108  				Files: map[string]string{
   109  					"legacy.rules": `
   110  		ALERT TestAlert
   111  		IF up == 0
   112  		FOR 5m
   113  		LABELS { severity = "critical" }
   114  		ANNOTATIONS {
   115  			message = "I am a message"
   116  		}
   117  		`,
   118  				},
   119  			},
   120  			expected: map[string][]rules.Rule{
   121  				"legacy.rules": {rule},
   122  			},
   123  			wantErr: fmt.Errorf("version 0 isn't supported"),
   124  		},
   125  		{
   126  			cfg: RulesConfig{
   127  				FormatVersion: RuleFormatV2,
   128  				Files: map[string]string{
   129  					"alerts.yaml": `
   130  groups:
   131  - name: example
   132    rules:
   133    - alert: TestAlert
   134      expr: up == 0
   135      for: 5m
   136      labels:
   137        severity: critical
   138      annotations:
   139        message: I am a message
   140  `,
   141  				},
   142  			},
   143  			expected: map[string][]rules.Rule{
   144  				"example;alerts.yaml": {rule},
   145  			},
   146  			wantErr: nil,
   147  		},
   148  	} {
   149  		t.Run(strconv.Itoa(i), func(t *testing.T) {
   150  			rules, err := tc.cfg.Parse()
   151  			if tc.wantErr != nil {
   152  				require.EqualError(t, err, tc.wantErr.Error())
   153  			} else {
   154  				require.NoError(t, err)
   155  				require.Equal(t, tc.expected, rules)
   156  			}
   157  		})
   158  	}
   159  }
   160  
   161  func TestParseFormatted(t *testing.T) {
   162  	dur, err := model.ParseDuration("5m")
   163  	require.NoError(t, err)
   164  
   165  	rulesV1 := []rulefmt.RuleNode{
   166  		{
   167  			Alert: yaml.Node{Value: "TestAlert"},
   168  			Expr:  yaml.Node{Value: "up == 0"},
   169  			For:   dur,
   170  			Labels: map[string]string{
   171  				"severity": "critical",
   172  			},
   173  			Annotations: map[string]string{
   174  				"message": "I am a message",
   175  			},
   176  		},
   177  	}
   178  
   179  	alertNode := yaml.Node{Line: 4, Column: 12}
   180  	alertNode.SetString("TestAlert")
   181  	exprNode := yaml.Node{Line: 5, Column: 11}
   182  	exprNode.SetString("up == 0")
   183  	rulesV2 := []rulefmt.RuleNode{
   184  		{
   185  			Alert: alertNode,
   186  			Expr:  exprNode,
   187  			For:   dur,
   188  			Labels: map[string]string{
   189  				"severity": "critical",
   190  			},
   191  			Annotations: map[string]string{
   192  				"message": "I am a message",
   193  			},
   194  		},
   195  	}
   196  
   197  	for i, tc := range []struct {
   198  		cfg      RulesConfig
   199  		expected map[string]rulefmt.RuleGroups
   200  		wantErr  error
   201  	}{
   202  		{
   203  			cfg: RulesConfig{
   204  				FormatVersion: RuleFormatV1,
   205  				Files: map[string]string{
   206  					"legacy.rules": legacyRulesFile,
   207  				},
   208  			},
   209  			expected: map[string]rulefmt.RuleGroups{
   210  				"legacy.rules": {
   211  					Groups: []rulefmt.RuleGroup{
   212  						{
   213  							Name:  "rg:legacy.rules",
   214  							Rules: rulesV1,
   215  						},
   216  					},
   217  				},
   218  			},
   219  			wantErr: fmt.Errorf("version 0 isn't supported"),
   220  		},
   221  		{
   222  			cfg: RulesConfig{
   223  				FormatVersion: RuleFormatV2,
   224  				Files: map[string]string{
   225  					"alerts.yaml": ruleFile,
   226  				},
   227  			},
   228  			expected: map[string]rulefmt.RuleGroups{
   229  				"alerts.yaml": {
   230  					Groups: []rulefmt.RuleGroup{
   231  						{
   232  							Name:  "example",
   233  							Rules: rulesV2,
   234  						},
   235  					},
   236  				},
   237  			},
   238  			wantErr: nil,
   239  		},
   240  	} {
   241  		t.Run(strconv.Itoa(i), func(t *testing.T) {
   242  			rules, err := tc.cfg.ParseFormatted()
   243  			if tc.wantErr != nil {
   244  				require.EqualError(t, err, tc.wantErr.Error())
   245  			} else {
   246  				require.NoError(t, err)
   247  				require.Equal(t, tc.expected, rules)
   248  			}
   249  		})
   250  	}
   251  }