Skip to content

Commit

Permalink
add validate-config command (#4)
Browse files Browse the repository at this point in the history
  • Loading branch information
dstathis committed Jan 25, 2023
1 parent 6943dc7 commit fb48333
Show file tree
Hide file tree
Showing 11 changed files with 173 additions and 11 deletions.
27 changes: 24 additions & 3 deletions cmd/root/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ var app = &cli.App{
},
},
{
Name: "validate",
Aliases: []string{"v", "lint", "l"},
Name: "validate-rules",
Aliases: []string{"v", "lint", "l", "validate"},
Action: func(c *cli.Context) error {
args := c.Args()

Expand All @@ -75,12 +75,33 @@ var app = &cli.App{
return err
}

_, err = validator.Validate(data)
_, err = validator.ValidateRules(data)
if err != nil {
return cli.Exit(err, 1)
}
}

return nil
},
},
{
Name: "validate-config",
Action: func(c *cli.Context) error {
args := c.Args()

if args.Len() < 1 {
log.Fatal("Expected at least one rule file to validate.")
}

validator := c.Context.Value("impl").(tool.Checker)

for _, f := range args.Slice() {
err := validator.ValidateConfig(f)
if err != nil {
return err
}
}

return nil
},
},
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.18
require (
github.com/Masterminds/sprig/v3 v3.2.2
github.com/dustin/go-humanize v1.0.0
github.com/go-kit/log v0.2.1
github.com/grafana/regexp v0.0.0-20220304100321-149c8afcd6cb
github.com/json-iterator/go v1.1.12
github.com/pkg/errors v0.9.1
Expand All @@ -29,7 +30,6 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dennwc/varint v1.0.0 // indirect
github.com/edsrzf/mmap-go v1.1.0 // indirect
github.com/go-kit/log v0.2.1 // indirect
github.com/go-logfmt/logfmt v0.5.1 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
Expand Down
3 changes: 2 additions & 1 deletion pkg/tool/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ type LogQL struct {

type Checker interface {
Transform(arg string, matchers *map[string]string) (string, error)
Validate(data []byte) (*rulefmt.RuleGroups, error)
ValidateRules(data []byte) (*rulefmt.RuleGroups, error)
ValidateConfig(filename string) (error)
}

func GetLabelMatchers(flags []string) (map[string]string, error) {
Expand Down
4 changes: 2 additions & 2 deletions pkg/tool/logql_alert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (

func TestParseLokiAlertFileSuccess(t *testing.T) {
p := &tool.LogQL{}
_, errs := p.Validate(readFile(filepath.Join("testdata/loki_alerts", "basic.yaml")))
_, errs := p.ValidateRules(readFile(filepath.Join("testdata/loki_alerts", "basic.yaml")))
assert.Nil(t, errs, "unexpected errors parsing file")
}

Expand All @@ -31,7 +31,7 @@ func TestParseLokiAlertFileFailure(t *testing.T) {
p := &tool.LogQL{}

for _, c := range table {
_, errs := p.Validate(readFile(filepath.Join("testdata/loki_alerts", c.filename)))
_, errs := p.ValidateRules(readFile(filepath.Join("testdata/loki_alerts", c.filename)))
assert.NotNil(t, errs, "Expected error parsing %s but got none", c.filename)
assert.Contains(t, errs.Error(), c.errMsg, "Expected error for %s.", c.filename)
}
Expand Down
6 changes: 5 additions & 1 deletion pkg/tool/logql_transform.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"github.com/prometheus/prometheus/model/rulefmt"
)

func (p *LogQL) Validate(data []byte) (*rulefmt.RuleGroups, error) {
func (p *LogQL) ValidateRules(data []byte) (*rulefmt.RuleGroups, error) {
// Expose the backend parser
rg, errs := lokiruler.Load(data)

Expand All @@ -21,6 +21,10 @@ func (p *LogQL) Validate(data []byte) (*rulefmt.RuleGroups, error) {
return rg, nil
}

func (p *LogQL) ValidateConfig(filename string) (error) {
return fmt.Errorf("Loki not supported for validate-config")
}

func (p *LogQL) Transform(arg string, matchers *map[string]string) (string, error) {
exp, err := parser.ParseExpr(arg)

Expand Down
4 changes: 2 additions & 2 deletions pkg/tool/promql_alert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func readFile(filepath string) []byte {
}
func TestParsePromAlertFileSuccess(t *testing.T) {
p := &tool.PromQL{}
_, errs := p.Validate(readFile(filepath.Join("testdata/prom_alerts", "basic.yaml")))
_, errs := p.ValidateRules(readFile(filepath.Join("testdata/prom_alerts", "basic.yaml")))
assert.Nil(t, errs, "unexpected errors parsing file")
}

Expand All @@ -37,7 +37,7 @@ func TestParsePromAlertFileFailure(t *testing.T) {
p := &tool.PromQL{}

for _, c := range table {
_, errs := p.Validate(readFile(filepath.Join("testdata/prom_alerts", c.filename)))
_, errs := p.ValidateRules(readFile(filepath.Join("testdata/prom_alerts", c.filename)))
assert.NotNil(t, errs, "Expected error parsing %s but got none", c.filename)
assert.Contains(t, errs.Error(), c.errMsg, "Expected error for %s.", c.filename)
}
Expand Down
40 changes: 40 additions & 0 deletions pkg/tool/promql_config_check_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package tool_test

import (
"fmt"
"testing"

"github.com/canonical/cos-tool/pkg/tool"
"github.com/stretchr/testify/assert"
)

func TestValidateConfig(t *testing.T) {
test_cases := []struct {
filename string
err bool
}{
{
filename: "good_config.yml",
err: false,
},
{
filename: "bad_yaml.yml",
err: true,
},
{
filename: "bad_key.yml",
err: true,
},
}

checker := &tool.PromQL{}

for _, test_case := range test_cases {
err := checker.ValidateConfig(fmt.Sprintf("testdata/prom_configs/%s", test_case.filename))
if test_case.err == true {
assert.NotNil(t, err, "ValidateConfig returned unexpected result")
} else {
assert.Nil(t, err, "ValidateConfig returned unexpected result")
}
}
}
14 changes: 13 additions & 1 deletion pkg/tool/promql_transform.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ package tool

import (
"fmt"
"github.com/go-kit/log"
"github.com/prometheus/prometheus/model/labels"
"github.com/prometheus/prometheus/model/rulefmt"
"github.com/prometheus/prometheus/promql/parser"
"github.com/prometheus/prometheus/config"
)

func (p *PromQL) Validate(data []byte) (*rulefmt.RuleGroups, error) {
func (p *PromQL) ValidateRules(data []byte) (*rulefmt.RuleGroups, error) {
// Expose the backend parser for alert rule validation
rg, errs := rulefmt.Parse(data)

Expand All @@ -17,6 +19,16 @@ func (p *PromQL) Validate(data []byte) (*rulefmt.RuleGroups, error) {
return rg, nil
}

// This function only checks syntax. If more in depth checking is needed, it must be expanded.
func(p *PromQL) ValidateConfig(filename string) (error) {
// Assuming here that agent mode is false. If we support agent mode in the future, this needs to be revisited.
_, err := config.LoadFile(filename, false, false, log.NewNopLogger())
if err != nil {
return err
}
return nil
}

func (p *PromQL) Transform(arg string, matchers *map[string]string) (string, error) {
exp, err := parser.ParseExpr(arg)

Expand Down
28 changes: 28 additions & 0 deletions pkg/tool/testdata/prom_configs/bad_key.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
global:
evaluation_interval: 1m
scrape_interval: 1m
scrape_timeout: 10s
rule_files:
- /etc/prometheus/rules/juju_*.rules
scrape_configs:
- honor_labels: true
job_name: my_scrape_job
metrics_path: /metrics
relabel_configs:
- regex: (.*)
separator: _
source_labels:
- juju_model
- juju_model_uuid
- juju_application
- juju_unit
target_label: instance
static_configs:
- labels:
juju_application: loki
juju_charm: loki-k8s
juju_model: lma
juju_model_uuid: 6e33e98f-61fe-4ced-85ec-6414252f6d30
juju_unit: loki/0
not_a_real_prometheus_key:
- 192.168.1.1:3100
28 changes: 28 additions & 0 deletions pkg/tool/testdata/prom_configs/bad_yaml.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
global:
evaluation_interval: 1m
scrape_interval: 1m
scrape_timeout: 10s
rule_files:
- /etc/prometheus/rules/juju_*.rules
scrape_configs:
- honor_labels: true
job_name: my_scrape_job
metrics_path: /metrics
relabel_configs:
- regex: (.*)
separator: _
source_labels:
- juju_model
- juju_model_uuid
- juju_application
- juju_unit
target_label: instance
static_configs:
- labels:
juju_application: loki
juju_charm: loki-k8s
juju_model: lma
juju_model_uuid: 6e33e98f-61fe-4ced-85ec-6414252f6d30
juju_unit: loki/0
targets:
- *:3100
28 changes: 28 additions & 0 deletions pkg/tool/testdata/prom_configs/good_config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
global:
evaluation_interval: 1m
scrape_interval: 1m
scrape_timeout: 10s
rule_files:
- /etc/prometheus/rules/juju_*.rules
scrape_configs:
- honor_labels: true
job_name: my_scrape_job
metrics_path: /metrics
relabel_configs:
- regex: (.*)
separator: _
source_labels:
- juju_model
- juju_model_uuid
- juju_application
- juju_unit
target_label: instance
static_configs:
- labels:
juju_application: loki
juju_charm: loki-k8s
juju_model: lma
juju_model_uuid: 6e33e98f-61fe-4ced-85ec-6414252f6d30
juju_unit: loki/0
targets:
- 192.168.1.1:3100

0 comments on commit fb48333

Please sign in to comment.