Skip to content

Commit

Permalink
Copy cortex/pkg/configs package dependency into Loki (#5139)
Browse files Browse the repository at this point in the history
* Fork cortex `configs` into Loki.

* Use new forked `configs` package.

* Remove duplicated import.

* Fix lint issues.

* Use new forked util/logger.

* Drop support for promql rules v1.

- This allow us to remove the `legacy_promql` implementation
- If a rulesconfig is received in v1 format, we output a specific error,
  so we can differentiate between a unknown version and a v1 one.

* Modify tests to expect error for legacy rules format.

* Add changelog and upgrading guide.

- Added them as we dropped support for legacy promql rules configuration
  which might be a breaking change for some environments.

* Update vendor/modules after rebasing.
  • Loading branch information
DylanGuedes authored Jan 27, 2022
1 parent 4a70f8e commit 3d135e5
Show file tree
Hide file tree
Showing 21 changed files with 354 additions and 6,891 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## Main

* [5139](https://github.com/grafana/loki/pull/5139) **DylanGuedes**: Drop support for legacy configuration rules format.
* [4911](https://github.com/grafana/loki/pull/4911) **jeschkies**: Support Docker service discovery in Promtail.
* [5107](https://github.com/grafana/loki/pull/5107) **chaudum** Fix bug in fluentd plugin that caused log lines containing non UTF-8 characters to be dropped.
* [5187](https://github.com/grafana/loki/pull/5187) **aknuds1** Rename metric `cortex_experimental_features_in_use_total` to `loki_experimental_features_in_use_total` and metric `log_messages_total` to `loki_log_messages_total`.
Expand Down
32 changes: 32 additions & 0 deletions docs/sources/upgrading/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,38 @@ The output is incredibly verbose as it shows the entire internal config struct u

### Loki

#### Dropped support for old Prometheus rules configuration format

Alerting rules previously could be specified in two formats: 1.x format (legacy one, named `v0` internally) and 2.x.
We decided to drop support for format `1.x` as it is fairly old and keeping support for it required a lot of code.

In case you're still using the legacy format, take a look at
[Alerting Rules](https://prometheus.io/docs/prometheus/latest/configuration/alerting_rules/) for instructions
on how to write alerting rules in the new format.

For reference, the newer format follows a structure similar to the one below:
```yaml
groups:
- name: example
rules:
- alert: HighErrorRate
expr: job:request_latency_seconds:mean5m{job="myjob"} > 0.5
for: 10m
labels:
severity: page
annotations:
summary: High request latency
```
Meanwhile, the legacy format is a string in the following format:
```
ALERT <alert name>
IF <expression>
[ FOR <duration> ]
[ LABELS <label set> ]
[ ANNOTATIONS <label set> ]
```

#### Error responses from API

The body of HTTP error responses from API endpoints changed from plain text to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import (
"github.com/prometheus/common/version"
"github.com/weaveworks/common/instrument"

"github.com/cortexproject/cortex/pkg/configs/userconfig"
util_log "github.com/cortexproject/cortex/pkg/util/log"
"github.com/grafana/loki/pkg/configs/userconfig"
util_log "github.com/grafana/loki/pkg/util/log"
)

var (
Expand Down
54 changes: 54 additions & 0 deletions pkg/configs/client/configs_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package client

import (
"net/http"
"net/http/httptest"
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/grafana/loki/pkg/configs/userconfig"
)

var response = `{
"configs": {
"2": {
"id": 1,
"config": {
"rules_files": {
"recording.rules": "groups:\n- name: demo-service-alerts\n interval: 15s\n rules:\n - alert: SomethingIsUp\n expr: up == 1\n"
},
"rule_format_version": "2"
}
}
}
}
`

func TestDoRequest(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, err := w.Write([]byte(response))
require.NoError(t, err)
}))
defer server.Close()

resp, err := doRequest(server.URL, 1*time.Second, nil, 0)
assert.Nil(t, err)

expected := ConfigsResponse{Configs: map[string]userconfig.View{
"2": {
ID: 1,
Config: userconfig.Config{
RulesConfig: userconfig.RulesConfig{
Files: map[string]string{
"recording.rules": "groups:\n- name: demo-service-alerts\n interval: 15s\n rules:\n - alert: SomethingIsUp\n expr: up == 1\n",
},
FormatVersion: userconfig.RuleFormatV2,
},
},
},
}}
assert.Equal(t, &expected, resp)
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,13 @@ import (

"github.com/go-kit/log"
"github.com/pkg/errors"
"github.com/prometheus/common/model"
"github.com/prometheus/prometheus/model/labels"
"github.com/prometheus/prometheus/model/rulefmt"
"github.com/prometheus/prometheus/promql/parser"
"github.com/prometheus/prometheus/rules"
"gopkg.in/yaml.v3"

legacy_promql "github.com/cortexproject/cortex/pkg/configs/legacy_promql"
util_log "github.com/cortexproject/cortex/pkg/util/log"
util_log "github.com/grafana/loki/pkg/util/log"
)

// An ID is the ID of a single users's Cortex configuration. When a
Expand Down Expand Up @@ -235,7 +233,7 @@ func (c RulesConfig) Equal(o RulesConfig) bool {
func (c RulesConfig) Parse() (map[string][]rules.Rule, error) {
switch c.FormatVersion {
case RuleFormatV1:
return c.parseV1()
return nil, fmt.Errorf("version %v isn't supported", c.FormatVersion)
case RuleFormatV2:
return c.parseV2()
default:
Expand All @@ -248,7 +246,7 @@ func (c RulesConfig) Parse() (map[string][]rules.Rule, error) {
func (c RulesConfig) ParseFormatted() (map[string]rulefmt.RuleGroups, error) {
switch c.FormatVersion {
case RuleFormatV1:
return c.parseV1Formatted()
return nil, fmt.Errorf("version %v isn't supported", c.FormatVersion)
case RuleFormatV2:
return c.parseV2Formatted()
default:
Expand All @@ -272,63 +270,6 @@ func (c RulesConfig) parseV2Formatted() (map[string]rulefmt.RuleGroups, error) {
return ruleMap, nil
}

// parseV1 parses and validates the content of the rule files in a RulesConfig
// according to the Prometheus 1.x rule format.
func (c RulesConfig) parseV1Formatted() (map[string]rulefmt.RuleGroups, error) {
result := map[string]rulefmt.RuleGroups{}
for fn, content := range c.Files {
stmts, err := legacy_promql.ParseStmts(content)
if err != nil {
return nil, fmt.Errorf("error parsing %s: %s", fn, err)
}

ra := []rulefmt.RuleNode{}
for _, stmt := range stmts {
var rule rulefmt.RuleNode
switch r := stmt.(type) {
case *legacy_promql.AlertStmt:
_, err := parser.ParseExpr(r.Expr.String())
if err != nil {
return nil, err
}

rule = rulefmt.RuleNode{
Alert: yaml.Node{Value: r.Name},
Expr: yaml.Node{Value: r.Expr.String()},
For: model.Duration(r.Duration),
Labels: r.Labels.Map(),
Annotations: r.Annotations.Map(),
}

case *legacy_promql.RecordStmt:
_, err := parser.ParseExpr(r.Expr.String())
if err != nil {
return nil, err
}

rule = rulefmt.RuleNode{
Record: yaml.Node{Value: r.Name},
Expr: yaml.Node{Value: r.Expr.String()},
Labels: r.Labels.Map(),
}

default:
return nil, fmt.Errorf("ruler.GetRules: unknown statement type")
}
ra = append(ra, rule)
}
result[fn] = rulefmt.RuleGroups{
Groups: []rulefmt.RuleGroup{
{
Name: "rg:" + fn,
Rules: ra,
},
},
}
}
return result, nil
}

// parseV2 parses and validates the content of the rule files in a RulesConfig
// according to the Prometheus 2.x rule format.
//
Expand Down Expand Up @@ -388,63 +329,6 @@ func (c RulesConfig) parseV2() (map[string][]rules.Rule, error) {
return groups, nil
}

// parseV1 parses and validates the content of the rule files in a RulesConfig
// according to the Prometheus 1.x rule format.
//
// The same comment about rule groups as on ParseV2() applies here.
func (c RulesConfig) parseV1() (map[string][]rules.Rule, error) {
result := map[string][]rules.Rule{}
for fn, content := range c.Files {
stmts, err := legacy_promql.ParseStmts(content)
if err != nil {
return nil, fmt.Errorf("error parsing %s: %s", fn, err)
}
ra := []rules.Rule{}
for _, stmt := range stmts {
var rule rules.Rule

switch r := stmt.(type) {
case *legacy_promql.AlertStmt:
// legacy_promql.ParseStmts has parsed the whole rule for us.
// Ideally we'd just use r.Expr and pass that to rules.NewAlertingRule,
// but it is of the type legacy_proql.Expr and not promql.Expr.
// So we convert it back to a string, and then parse it again with the
// upstream parser to get it into the right type.
expr, err := parser.ParseExpr(r.Expr.String())
if err != nil {
return nil, err
}

rule = rules.NewAlertingRule(
r.Name,
expr,
r.Duration,
r.Labels,
r.Annotations,
nil,
"",
true,
log.With(util_log.Logger, "alert", r.Name),
)

case *legacy_promql.RecordStmt:
expr, err := parser.ParseExpr(r.Expr.String())
if err != nil {
return nil, err
}

rule = rules.NewRecordingRule(r.Name, expr, r.Labels)

default:
return nil, fmt.Errorf("ruler.GetRules: unknown statement type")
}
ra = append(ra, rule)
}
result[fn] = ra
}
return result, nil
}

// VersionedRulesConfig is a RulesConfig together with a version.
// `data Versioned a = Versioned { id :: ID , config :: a }`
type VersionedRulesConfig struct {
Expand Down
Loading

0 comments on commit 3d135e5

Please sign in to comment.