Expose TargetsFromGroup/AlertmanagerFromGroup func and reuse this for (#9343)

static/file sd config check in promtool

Signed-off-by: DrAuYueng <ouyang1204@gmail.com>
This commit is contained in:
DrAuYueng 2021-10-28 08:01:28 +08:00 committed by GitHub
parent 8207b132fd
commit 69e309d202
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 160 additions and 19 deletions

View file

@ -44,13 +44,16 @@ import (
yaml "gopkg.in/yaml.v2" yaml "gopkg.in/yaml.v2"
"github.com/prometheus/prometheus/config" "github.com/prometheus/prometheus/config"
"github.com/prometheus/prometheus/discovery"
"github.com/prometheus/prometheus/discovery/file" "github.com/prometheus/prometheus/discovery/file"
_ "github.com/prometheus/prometheus/discovery/install" // Register service discovery implementations. _ "github.com/prometheus/prometheus/discovery/install" // Register service discovery implementations.
"github.com/prometheus/prometheus/discovery/kubernetes" "github.com/prometheus/prometheus/discovery/kubernetes"
"github.com/prometheus/prometheus/discovery/targetgroup" "github.com/prometheus/prometheus/discovery/targetgroup"
"github.com/prometheus/prometheus/notifier"
"github.com/prometheus/prometheus/pkg/labels" "github.com/prometheus/prometheus/pkg/labels"
"github.com/prometheus/prometheus/pkg/rulefmt" "github.com/prometheus/prometheus/pkg/rulefmt"
"github.com/prometheus/prometheus/promql" "github.com/prometheus/prometheus/promql"
"github.com/prometheus/prometheus/scrape"
) )
func main() { func main() {
@ -363,19 +366,60 @@ func checkConfig(filename string) ([]string, error) {
} }
if len(files) != 0 { if len(files) != 0 {
for _, f := range files { for _, f := range files {
err = checkSDFile(f) var targetGroups []*targetgroup.Group
targetGroups, err = checkSDFile(f)
if err != nil { if err != nil {
return nil, errors.Errorf("checking SD file %q: %v", file, err) return nil, errors.Errorf("checking SD file %q: %v", file, err)
} }
if err := checkTargetGroupsForScrapeConfig(targetGroups, scfg); err != nil {
return nil, err
}
} }
continue continue
} }
fmt.Printf(" WARNING: file %q for file_sd in scrape job %q does not exist\n", file, scfg.JobName) fmt.Printf(" WARNING: file %q for file_sd in scrape job %q does not exist\n", file, scfg.JobName)
} }
case discovery.StaticConfig:
if err := checkTargetGroupsForScrapeConfig(c, scfg); err != nil {
return nil, err
}
} }
} }
} }
alertConfig := cfg.AlertingConfig
for _, amcfg := range alertConfig.AlertmanagerConfigs {
for _, c := range amcfg.ServiceDiscoveryConfigs {
switch c := c.(type) {
case *file.SDConfig:
for _, file := range c.Files {
files, err := filepath.Glob(file)
if err != nil {
return nil, err
}
if len(files) != 0 {
for _, f := range files {
var targetGroups []*targetgroup.Group
targetGroups, err = checkSDFile(f)
if err != nil {
return nil, errors.Errorf("checking SD file %q: %v", file, err)
}
if err := checkTargetGroupsForAlertmanager(targetGroups, amcfg); err != nil {
return nil, err
}
}
continue
}
fmt.Printf(" WARNING: file %q for file_sd in alertmanager config does not exist\n", file)
}
case discovery.StaticConfig:
if err := checkTargetGroupsForAlertmanager(c, amcfg); err != nil {
return nil, err
}
}
}
}
return ruleFiles, nil return ruleFiles, nil
} }
@ -397,16 +441,16 @@ func checkTLSConfig(tlsConfig config_util.TLSConfig) error {
return nil return nil
} }
func checkSDFile(filename string) error { func checkSDFile(filename string) ([]*targetgroup.Group, error) {
fd, err := os.Open(filename) fd, err := os.Open(filename)
if err != nil { if err != nil {
return err return nil, err
} }
defer fd.Close() defer fd.Close()
content, err := ioutil.ReadAll(fd) content, err := ioutil.ReadAll(fd)
if err != nil { if err != nil {
return err return nil, err
} }
var targetGroups []*targetgroup.Group var targetGroups []*targetgroup.Group
@ -414,23 +458,23 @@ func checkSDFile(filename string) error {
switch ext := filepath.Ext(filename); strings.ToLower(ext) { switch ext := filepath.Ext(filename); strings.ToLower(ext) {
case ".json": case ".json":
if err := json.Unmarshal(content, &targetGroups); err != nil { if err := json.Unmarshal(content, &targetGroups); err != nil {
return err return nil, err
} }
case ".yml", ".yaml": case ".yml", ".yaml":
if err := yaml.UnmarshalStrict(content, &targetGroups); err != nil { if err := yaml.UnmarshalStrict(content, &targetGroups); err != nil {
return err return nil, err
} }
default: default:
return errors.Errorf("invalid file extension: %q", ext) return nil, errors.Errorf("invalid file extension: %q", ext)
} }
for i, tg := range targetGroups { for i, tg := range targetGroups {
if tg == nil { if tg == nil {
return errors.Errorf("nil target group item found (index %d)", i) return nil, errors.Errorf("nil target group item found (index %d)", i)
} }
} }
return nil return targetGroups, nil
} }
// CheckRules validates rule files. // CheckRules validates rule files.
@ -981,3 +1025,25 @@ func importRules(url *url.URL, start, end, outputDir string, evalInterval time.D
return nil return nil
} }
func checkTargetGroupsForAlertmanager(targetGroups []*targetgroup.Group, amcfg *config.AlertmanagerConfig) error {
for _, tg := range targetGroups {
if _, _, err := notifier.AlertmanagerFromGroup(tg, amcfg); err != nil {
return err
}
}
return nil
}
func checkTargetGroupsForScrapeConfig(targetGroups []*targetgroup.Group, scfg *config.ScrapeConfig) error {
for _, tg := range targetGroups {
_, failures := scrape.TargetsFromGroup(tg, scfg)
if len(failures) > 0 {
first := failures[0]
return first
}
}
return nil
}

View file

@ -111,7 +111,7 @@ func TestCheckSDFile(t *testing.T) {
} }
for _, test := range cases { for _, test := range cases {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
err := checkSDFile(test.file) _, err := checkSDFile(test.file)
if test.err != "" { if test.err != "" {
require.Equalf(t, test.err, err.Error(), "Expected error %q, got %q", test.err, err.Error()) require.Equalf(t, test.err, err.Error(), "Expected error %q, got %q", test.err, err.Error())
return return
@ -163,3 +163,42 @@ func BenchmarkCheckDuplicates(b *testing.B) {
checkDuplicates(rgs.Groups) checkDuplicates(rgs.Groups)
} }
} }
func TestCheckTargetConfig(t *testing.T) {
cases := []struct {
name string
file string
err string
}{
{
name: "url_in_scrape_targetgroup_with_relabel_config.good",
file: "url_in_scrape_targetgroup_with_relabel_config.good.yml",
err: "",
},
{
name: "url_in_alert_targetgroup_with_relabel_config.good",
file: "url_in_alert_targetgroup_with_relabel_config.good.yml",
err: "",
},
{
name: "url_in_scrape_targetgroup_with_relabel_config.bad",
file: "url_in_scrape_targetgroup_with_relabel_config.bad.yml",
err: "instance 0 in group 0: \"http://bad\" is not a valid hostname",
},
{
name: "url_in_alert_targetgroup_with_relabel_config.bad",
file: "url_in_alert_targetgroup_with_relabel_config.bad.yml",
err: "\"http://bad\" is not a valid hostname",
},
}
for _, test := range cases {
t.Run(test.name, func(t *testing.T) {
_, err := checkConfig("testdata/" + test.file)
if test.err != "" {
require.Equalf(t, test.err, err.Error(), "Expected error %q, got %q", test.err, err.Error())
return
}
require.NoError(t, err)
})
}
}

View file

@ -0,0 +1,8 @@
alerting:
alertmanagers:
- relabel_configs:
- source_labels: [__address__]
target_label: __param_target
static_configs:
- targets:
- http://bad

View file

@ -0,0 +1,10 @@
alerting:
alertmanagers:
- relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- target_label: __address__
replacement: good
static_configs:
- targets:
- http://bad

View file

@ -0,0 +1,8 @@
scrape_configs:
- job_name: prometheus
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
static_configs:
- targets:
- http://bad

View file

@ -0,0 +1,10 @@
scrape_configs:
- job_name: prometheus
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- target_label: __address__
replacement: good
static_configs:
- targets:
- http://good

View file

@ -602,7 +602,7 @@ func (n *Manager) Stop() {
n.cancel() n.cancel()
} }
// alertmanager holds Alertmanager endpoint information. // Alertmanager holds Alertmanager endpoint information.
type alertmanager interface { type alertmanager interface {
url() *url.URL url() *url.URL
} }
@ -654,7 +654,7 @@ func (s *alertmanagerSet) sync(tgs []*targetgroup.Group) {
allDroppedAms := []alertmanager{} allDroppedAms := []alertmanager{}
for _, tg := range tgs { for _, tg := range tgs {
ams, droppedAms, err := alertmanagerFromGroup(tg, s.cfg) ams, droppedAms, err := AlertmanagerFromGroup(tg, s.cfg)
if err != nil { if err != nil {
level.Error(s.logger).Log("msg", "Creating discovered Alertmanagers failed", "err", err) level.Error(s.logger).Log("msg", "Creating discovered Alertmanagers failed", "err", err)
continue continue
@ -691,9 +691,9 @@ func postPath(pre string, v config.AlertmanagerAPIVersion) string {
return path.Join("/", pre, alertPushEndpoint) return path.Join("/", pre, alertPushEndpoint)
} }
// alertmanagerFromGroup extracts a list of alertmanagers from a target group // AlertmanagerFromGroup extracts a list of alertmanagers from a target group
// and an associated AlertmanagerConfig. // and an associated AlertmanagerConfig.
func alertmanagerFromGroup(tg *targetgroup.Group, cfg *config.AlertmanagerConfig) ([]alertmanager, []alertmanager, error) { func AlertmanagerFromGroup(tg *targetgroup.Group, cfg *config.AlertmanagerConfig) ([]alertmanager, []alertmanager, error) {
var res []alertmanager var res []alertmanager
var droppedAlertManagers []alertmanager var droppedAlertManagers []alertmanager

View file

@ -447,7 +447,7 @@ func (a alertmanagerMock) url() *url.URL {
func TestLabelSetNotReused(t *testing.T) { func TestLabelSetNotReused(t *testing.T) {
tg := makeInputTargetGroup() tg := makeInputTargetGroup()
_, _, err := alertmanagerFromGroup(tg, &config.AlertmanagerConfig{}) _, _, err := AlertmanagerFromGroup(tg, &config.AlertmanagerConfig{})
require.NoError(t, err) require.NoError(t, err)

View file

@ -472,7 +472,7 @@ func (sp *scrapePool) Sync(tgs []*targetgroup.Group) {
var all []*Target var all []*Target
sp.droppedTargets = []*Target{} sp.droppedTargets = []*Target{}
for _, tg := range tgs { for _, tg := range tgs {
targets, failures := targetsFromGroup(tg, sp.config) targets, failures := TargetsFromGroup(tg, sp.config)
for _, err := range failures { for _, err := range failures {
level.Error(sp.logger).Log("msg", "Creating target failed", "err", err) level.Error(sp.logger).Log("msg", "Creating target failed", "err", err)
} }

View file

@ -469,8 +469,8 @@ func populateLabels(lset labels.Labels, cfg *config.ScrapeConfig) (res, orig lab
return res, preRelabelLabels, nil return res, preRelabelLabels, nil
} }
// targetsFromGroup builds targets based on the given TargetGroup and config. // TargetsFromGroup builds targets based on the given TargetGroup and config.
func targetsFromGroup(tg *targetgroup.Group, cfg *config.ScrapeConfig) ([]*Target, []error) { func TargetsFromGroup(tg *targetgroup.Group, cfg *config.ScrapeConfig) ([]*Target, []error) {
targets := make([]*Target, 0, len(tg.Targets)) targets := make([]*Target, 0, len(tg.Targets))
failures := []error{} failures := []error{}

View file

@ -371,7 +371,7 @@ func TestNewClientWithBadTLSConfig(t *testing.T) {
func TestTargetsFromGroup(t *testing.T) { func TestTargetsFromGroup(t *testing.T) {
expectedError := "instance 0 in group : no address" expectedError := "instance 0 in group : no address"
targets, failures := targetsFromGroup(&targetgroup.Group{Targets: []model.LabelSet{{}, {model.AddressLabel: "localhost:9090"}}}, &config.ScrapeConfig{}) targets, failures := TargetsFromGroup(&targetgroup.Group{Targets: []model.LabelSet{{}, {model.AddressLabel: "localhost:9090"}}}, &config.ScrapeConfig{})
if len(targets) != 1 { if len(targets) != 1 {
t.Fatalf("Expected 1 target, got %v", len(targets)) t.Fatalf("Expected 1 target, got %v", len(targets))
} }