Browse Source

config: Make remote-write required for Agent mode (#9618)

* config: Make remote-write required for Agent mode

Signed-off-by: ArthurSens <arthursens2005@gmail.com>
pull/9620/head
Arthur Silva Sens 3 years ago committed by GitHub
parent
commit
be2599c853
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      cmd/prometheus/main.go
  2. 23
      cmd/prometheus/main_test.go
  3. 11
      cmd/promtool/main.go
  4. 2
      cmd/promtool/main_test.go
  5. 21
      config/config.go
  6. 36
      config/config_test.go
  7. 22
      documentation/examples/prometheus-agent.yml

4
cmd/prometheus/main.go

@ -452,7 +452,7 @@ func main() {
// Throw error for invalid config before starting other components.
var cfgFile *config.Config
if cfgFile, err = config.LoadFile(cfg.configFile, false, log.NewNopLogger()); err != nil {
if cfgFile, err = config.LoadFile(cfg.configFile, agentMode, false, log.NewNopLogger()); err != nil {
level.Error(logger).Log("msg", fmt.Sprintf("Error loading config (--config.file=%s)", cfg.configFile), "err", err)
os.Exit(2)
}
@ -1162,7 +1162,7 @@ func reloadConfig(filename string, expandExternalLabels bool, enableExemplarStor
}
}()
conf, err := config.LoadFile(filename, expandExternalLabels, logger)
conf, err := config.LoadFile(filename, agentMode, expandExternalLabels, logger)
if err != nil {
return errors.Wrapf(err, "couldn't load configuration (--config.file=%q)", filename)
}

23
cmd/prometheus/main_test.go

@ -37,6 +37,7 @@ import (
var promPath = os.Args[0]
var promConfig = filepath.Join("..", "..", "documentation", "examples", "prometheus.yml")
var agentConfig = filepath.Join("..", "..", "documentation", "examples", "prometheus-agent.yml")
var promData = filepath.Join(os.TempDir(), "data")
func TestMain(m *testing.M) {
@ -349,7 +350,7 @@ func getCurrentGaugeValuesFor(t *testing.T, reg prometheus.Gatherer, metricNames
}
func TestAgentSuccessfulStartup(t *testing.T) {
prom := exec.Command(promPath, "-test.main", "--agent", "--config.file="+promConfig)
prom := exec.Command(promPath, "-test.main", "--agent", "--config.file="+agentConfig)
err := prom.Start()
require.NoError(t, err)
@ -367,3 +368,23 @@ func TestAgentSuccessfulStartup(t *testing.T) {
}
require.Equal(t, expectedExitStatus, actualExitStatus)
}
func TestAgentStartupWithInvalidConfig(t *testing.T) {
prom := exec.Command(promPath, "-test.main", "--agent", "--config.file="+promConfig)
err := prom.Start()
require.NoError(t, err)
expectedExitStatus := 2
actualExitStatus := 0
done := make(chan error, 1)
go func() { done <- prom.Wait() }()
select {
case err := <-done:
t.Logf("prometheus agent should not be running: %v", err)
actualExitStatus = prom.ProcessState.ExitCode()
case <-time.After(5 * time.Second):
prom.Process.Kill()
}
require.Equal(t, expectedExitStatus, actualExitStatus)
}

11
cmd/promtool/main.go

@ -82,6 +82,7 @@ func main() {
).Required().ExistingFiles()
checkMetricsCmd := checkCmd.Command("metrics", checkMetricsUsage)
agentMode := checkConfigCmd.Flag("agent", "Check config file for Prometheus in Agent mode.").Bool()
queryCmd := app.Command("query", "Run query against a Prometheus server.")
queryCmdFmt := queryCmd.Flag("format", "Output format of the query.").Short('o').Default("promql").Enum("promql", "json")
@ -202,7 +203,7 @@ func main() {
switch parsedCmd {
case checkConfigCmd.FullCommand():
os.Exit(CheckConfig(*configFiles...))
os.Exit(CheckConfig(*agentMode, *configFiles...))
case checkWebConfigCmd.FullCommand():
os.Exit(CheckWebConfig(*webConfigFiles...))
@ -258,11 +259,11 @@ func main() {
}
// CheckConfig validates configuration files.
func CheckConfig(files ...string) int {
func CheckConfig(agentMode bool, files ...string) int {
failed := false
for _, f := range files {
ruleFiles, err := checkConfig(f)
ruleFiles, err := checkConfig(agentMode, f)
if err != nil {
fmt.Fprintln(os.Stderr, " FAILED:", err)
failed = true
@ -317,10 +318,10 @@ func checkFileExists(fn string) error {
return err
}
func checkConfig(filename string) ([]string, error) {
func checkConfig(agentMode bool, filename string) ([]string, error) {
fmt.Println("Checking", filename)
cfg, err := config.LoadFile(filename, false, log.NewNopLogger())
cfg, err := config.LoadFile(filename, agentMode, false, log.NewNopLogger())
if err != nil {
return nil, err
}

2
cmd/promtool/main_test.go

@ -193,7 +193,7 @@ func TestCheckTargetConfig(t *testing.T) {
}
for _, test := range cases {
t.Run(test.name, func(t *testing.T) {
_, err := checkConfig("testdata/" + test.file)
_, err := checkConfig(false, "testdata/"+test.file)
if test.err != "" {
require.Equalf(t, test.err, err.Error(), "Expected error %q, got %q", test.err, err.Error())
return

21
config/config.go

@ -99,7 +99,7 @@ func Load(s string, expandExternalLabels bool, logger log.Logger) (*Config, erro
}
// LoadFile parses the given YAML file into a Config.
func LoadFile(filename string, expandExternalLabels bool, logger log.Logger) (*Config, error) {
func LoadFile(filename string, agentMode bool, expandExternalLabels bool, logger log.Logger) (*Config, error) {
content, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
@ -108,6 +108,25 @@ func LoadFile(filename string, expandExternalLabels bool, logger log.Logger) (*C
if err != nil {
return nil, errors.Wrapf(err, "parsing YAML file %s", filename)
}
if agentMode {
if len(cfg.RemoteWriteConfigs) == 0 {
return nil, errors.New("at least one remote_write target must be specified in agent mode")
}
if len(cfg.AlertingConfig.AlertmanagerConfigs) > 0 || len(cfg.AlertingConfig.AlertRelabelConfigs) > 0 {
return nil, errors.New("field alerting is not allowed in agent mode")
}
if len(cfg.RuleFiles) > 0 {
return nil, errors.New("field rule_files is not allowed in agent mode")
}
if len(cfg.RemoteReadConfigs) > 0 {
return nil, errors.New("field remote_read is not allowed in agent mode")
}
}
cfg.SetDirectory(filepath.Dir(filename))
return cfg, nil
}

36
config/config_test.go

@ -986,7 +986,7 @@ var expectedConf = &Config{
}
func TestYAMLRoundtrip(t *testing.T) {
want, err := LoadFile("testdata/roundtrip.good.yml", false, log.NewNopLogger())
want, err := LoadFile("testdata/roundtrip.good.yml", false, false, log.NewNopLogger())
require.NoError(t, err)
out, err := yaml.Marshal(want)
@ -999,7 +999,7 @@ func TestYAMLRoundtrip(t *testing.T) {
}
func TestRemoteWriteRetryOnRateLimit(t *testing.T) {
want, err := LoadFile("testdata/remote_write_retry_on_rate_limit.good.yml", false, log.NewNopLogger())
want, err := LoadFile("testdata/remote_write_retry_on_rate_limit.good.yml", false, false, log.NewNopLogger())
require.NoError(t, err)
out, err := yaml.Marshal(want)
@ -1015,16 +1015,16 @@ func TestRemoteWriteRetryOnRateLimit(t *testing.T) {
func TestLoadConfig(t *testing.T) {
// Parse a valid file that sets a global scrape timeout. This tests whether parsing
// an overwritten default field in the global config permanently changes the default.
_, err := LoadFile("testdata/global_timeout.good.yml", false, log.NewNopLogger())
_, err := LoadFile("testdata/global_timeout.good.yml", false, false, log.NewNopLogger())
require.NoError(t, err)
c, err := LoadFile("testdata/conf.good.yml", false, log.NewNopLogger())
c, err := LoadFile("testdata/conf.good.yml", false, false, log.NewNopLogger())
require.NoError(t, err)
require.Equal(t, expectedConf, c)
}
func TestScrapeIntervalLarger(t *testing.T) {
c, err := LoadFile("testdata/scrape_interval_larger.good.yml", false, log.NewNopLogger())
c, err := LoadFile("testdata/scrape_interval_larger.good.yml", false, false, log.NewNopLogger())
require.NoError(t, err)
require.Equal(t, 1, len(c.ScrapeConfigs))
for _, sc := range c.ScrapeConfigs {
@ -1034,7 +1034,7 @@ func TestScrapeIntervalLarger(t *testing.T) {
// YAML marshaling must not reveal authentication credentials.
func TestElideSecrets(t *testing.T) {
c, err := LoadFile("testdata/conf.good.yml", false, log.NewNopLogger())
c, err := LoadFile("testdata/conf.good.yml", false, false, log.NewNopLogger())
require.NoError(t, err)
secretRe := regexp.MustCompile(`\\u003csecret\\u003e|<secret>`)
@ -1051,31 +1051,31 @@ func TestElideSecrets(t *testing.T) {
func TestLoadConfigRuleFilesAbsolutePath(t *testing.T) {
// Parse a valid file that sets a rule files with an absolute path
c, err := LoadFile(ruleFilesConfigFile, false, log.NewNopLogger())
c, err := LoadFile(ruleFilesConfigFile, false, false, log.NewNopLogger())
require.NoError(t, err)
require.Equal(t, ruleFilesExpectedConf, c)
}
func TestKubernetesEmptyAPIServer(t *testing.T) {
_, err := LoadFile("testdata/kubernetes_empty_apiserver.good.yml", false, log.NewNopLogger())
_, err := LoadFile("testdata/kubernetes_empty_apiserver.good.yml", false, false, log.NewNopLogger())
require.NoError(t, err)
}
func TestKubernetesWithKubeConfig(t *testing.T) {
_, err := LoadFile("testdata/kubernetes_kubeconfig_without_apiserver.good.yml", false, log.NewNopLogger())
_, err := LoadFile("testdata/kubernetes_kubeconfig_without_apiserver.good.yml", false, false, log.NewNopLogger())
require.NoError(t, err)
}
func TestKubernetesSelectors(t *testing.T) {
_, err := LoadFile("testdata/kubernetes_selectors_endpoints.good.yml", false, log.NewNopLogger())
_, err := LoadFile("testdata/kubernetes_selectors_endpoints.good.yml", false, false, log.NewNopLogger())
require.NoError(t, err)
_, err = LoadFile("testdata/kubernetes_selectors_node.good.yml", false, log.NewNopLogger())
_, err = LoadFile("testdata/kubernetes_selectors_node.good.yml", false, false, log.NewNopLogger())
require.NoError(t, err)
_, err = LoadFile("testdata/kubernetes_selectors_ingress.good.yml", false, log.NewNopLogger())
_, err = LoadFile("testdata/kubernetes_selectors_ingress.good.yml", false, false, log.NewNopLogger())
require.NoError(t, err)
_, err = LoadFile("testdata/kubernetes_selectors_pod.good.yml", false, log.NewNopLogger())
_, err = LoadFile("testdata/kubernetes_selectors_pod.good.yml", false, false, log.NewNopLogger())
require.NoError(t, err)
_, err = LoadFile("testdata/kubernetes_selectors_service.good.yml", false, log.NewNopLogger())
_, err = LoadFile("testdata/kubernetes_selectors_service.good.yml", false, false, log.NewNopLogger())
require.NoError(t, err)
}
@ -1381,7 +1381,7 @@ var expectedErrors = []struct {
func TestBadConfigs(t *testing.T) {
for _, ee := range expectedErrors {
_, err := LoadFile("testdata/"+ee.filename, false, log.NewNopLogger())
_, err := LoadFile("testdata/"+ee.filename, false, false, log.NewNopLogger())
require.Error(t, err, "%s", ee.filename)
require.Contains(t, err.Error(), ee.errMsg,
"Expected error for %s to contain %q but got: %s", ee.filename, ee.errMsg, err)
@ -1415,20 +1415,20 @@ func TestExpandExternalLabels(t *testing.T) {
// Cleanup ant TEST env variable that could exist on the system.
os.Setenv("TEST", "")
c, err := LoadFile("testdata/external_labels.good.yml", false, log.NewNopLogger())
c, err := LoadFile("testdata/external_labels.good.yml", false, false, log.NewNopLogger())
require.NoError(t, err)
require.Equal(t, labels.Label{Name: "bar", Value: "foo"}, c.GlobalConfig.ExternalLabels[0])
require.Equal(t, labels.Label{Name: "baz", Value: "foo${TEST}bar"}, c.GlobalConfig.ExternalLabels[1])
require.Equal(t, labels.Label{Name: "foo", Value: "${TEST}"}, c.GlobalConfig.ExternalLabels[2])
c, err = LoadFile("testdata/external_labels.good.yml", true, log.NewNopLogger())
c, err = LoadFile("testdata/external_labels.good.yml", false, true, log.NewNopLogger())
require.NoError(t, err)
require.Equal(t, labels.Label{Name: "bar", Value: "foo"}, c.GlobalConfig.ExternalLabels[0])
require.Equal(t, labels.Label{Name: "baz", Value: "foobar"}, c.GlobalConfig.ExternalLabels[1])
require.Equal(t, labels.Label{Name: "foo", Value: ""}, c.GlobalConfig.ExternalLabels[2])
os.Setenv("TEST", "TestValue")
c, err = LoadFile("testdata/external_labels.good.yml", true, log.NewNopLogger())
c, err = LoadFile("testdata/external_labels.good.yml", false, true, log.NewNopLogger())
require.NoError(t, err)
require.Equal(t, labels.Label{Name: "bar", Value: "foo"}, c.GlobalConfig.ExternalLabels[0])
require.Equal(t, labels.Label{Name: "baz", Value: "fooTestValuebar"}, c.GlobalConfig.ExternalLabels[1])

22
documentation/examples/prometheus-agent.yml

@ -0,0 +1,22 @@
# my global config
global:
scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
# scrape_timeout is set to the global default (10s).
# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
- job_name: "prometheus"
# metrics_path defaults to '/metrics'
# scheme defaults to 'http'.
static_configs:
- targets: ["localhost:9090"]
# When running prometheus in Agent mode, remote-write is required.
remote_write:
# Agent is able to run with a invalid remote-write URL, but, of course, will fail to push timeseries.
- url: "http://remote-write-url"
Loading…
Cancel
Save