diff --git a/cmd/prometheus/main.go b/cmd/prometheus/main.go index e0b8f9e5f..c18f6aa55 100644 --- a/cmd/prometheus/main.go +++ b/cmd/prometheus/main.go @@ -132,7 +132,7 @@ func extractFlagName(pc *kingpin.ParseContext) string { if !ok { continue } - return fc.Model().Name + return "--" + fc.Model().Name } panic("extractFlagName not called from a kingpin PreAction. This is a bug, please report to Prometheus.") } @@ -200,6 +200,9 @@ func (c *flagConfig) setFeatureListOptions(logger log.Logger) error { case "new-service-discovery-manager": c.enableNewSDManager = true level.Info(logger).Log("msg", "Experimental service discovery manager") + case "agent": + agentMode = true + level.Info(logger).Log("msg", "Experimental agent mode enabled.") case "": continue default: @@ -238,8 +241,6 @@ func main() { a.HelpFlag.Short('h') - a.Flag("agent", "Agent mode.").BoolVar(&agentMode) - a.Flag("config.file", "Prometheus configuration file path."). Default("prometheus.yml").StringVar(&cfg.configFile) @@ -335,16 +336,16 @@ func main() { PreAction(agentOnlySetting()). Default("data-agent/").StringVar(&cfg.localStoragePath) - a.Flag("storage.agent.segment-size", + a.Flag("storage.agent.wal-segment-size", "Size at which to split WAL segment files. Example: 100MB"). PreAction(agentOnlySetting()). Hidden().PlaceHolder("").BytesVar(&cfg.agent.WALSegmentSize) - a.Flag("storage.agent.compression", "Compress the agent WAL."). + a.Flag("storage.agent.wal-compression", "Compress the agent WAL."). PreAction(agentOnlySetting()). Default("true").BoolVar(&cfg.agent.WALCompression) - a.Flag("storage.agent.truncate-frequency", + a.Flag("storage.agent.wal-truncate-frequency", "The frequency at which to truncate the WAL and remove old data."). PreAction(agentOnlySetting()). Hidden().PlaceHolder("").SetValue(&cfg.agent.TruncateFrequency) @@ -434,6 +435,16 @@ func main() { os.Exit(1) } + if agentMode && len(serverOnlyFlags) > 0 { + fmt.Fprintf(os.Stderr, "The following flag(s) can not be used in agent mode: %q", serverOnlyFlags) + os.Exit(3) + } + + if !agentMode && len(agentOnlyFlags) > 0 { + fmt.Fprintf(os.Stderr, "The following flag(s) can only be used in agent mode: %q", agentOnlyFlags) + os.Exit(3) + } + cfg.web.ExternalURL, err = computeExternalURL(cfg.prometheusURL, cfg.web.ListenAddress) if err != nil { fmt.Fprintln(os.Stderr, errors.Wrapf(err, "parse external URL %q", cfg.prometheusURL)) @@ -1006,7 +1017,7 @@ func main() { level.Info(logger).Log("msg", "Starting WAL storage ...") if cfg.agent.WALSegmentSize != 0 { if cfg.agent.WALSegmentSize < 10*1024*1024 || cfg.agent.WALSegmentSize > 256*1024*1024 { - return errors.New("flag 'storage.agent.segment-size' must be set between 10MB and 256MB") + return errors.New("flag 'storage.agent.wal-segment-size' must be set between 10MB and 256MB") } } db, err := agent.Open( diff --git a/cmd/prometheus/main_test.go b/cmd/prometheus/main_test.go index 6f4d58b94..86ecce1ac 100644 --- a/cmd/prometheus/main_test.go +++ b/cmd/prometheus/main_test.go @@ -350,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="+agentConfig) + prom := exec.Command(promPath, "-test.main", "--enable-feature=agent", "--config.file="+agentConfig) err := prom.Start() require.NoError(t, err) @@ -370,7 +370,7 @@ func TestAgentSuccessfulStartup(t *testing.T) { } func TestAgentStartupWithInvalidConfig(t *testing.T) { - prom := exec.Command(promPath, "-test.main", "--agent", "--config.file="+promConfig) + prom := exec.Command(promPath, "-test.main", "--enable-feature=agent", "--config.file="+promConfig) err := prom.Start() require.NoError(t, err) @@ -388,3 +388,66 @@ func TestAgentStartupWithInvalidConfig(t *testing.T) { } require.Equal(t, expectedExitStatus, actualExitStatus) } + +func TestModeSpecificFlags(t *testing.T) { + if testing.Short() { + t.Skip("skipping test in short mode.") + } + + testcases := []struct { + mode string + arg string + exitStatus int + }{ + {"agent", "--storage.agent.path", 0}, + {"server", "--storage.tsdb.path", 0}, + {"server", "--storage.agent.path", 3}, + {"agent", "--storage.tsdb.path", 3}, + } + + for _, tc := range testcases { + t.Run(fmt.Sprintf("%s mode with option %s", tc.mode, tc.arg), func(t *testing.T) { + args := []string{"-test.main", tc.arg, t.TempDir()} + + if tc.mode == "agent" { + args = append(args, "--enable-feature=agent", "--config.file="+agentConfig) + } else { + args = append(args, "--config.file="+promConfig) + } + + prom := exec.Command(promPath, args...) + + // Log stderr in case of failure. + stderr, err := prom.StderrPipe() + require.NoError(t, err) + go func() { + slurp, _ := ioutil.ReadAll(stderr) + t.Log(string(slurp)) + }() + + err = prom.Start() + require.NoError(t, err) + + if tc.exitStatus == 0 { + done := make(chan error, 1) + go func() { done <- prom.Wait() }() + select { + case err := <-done: + t.Errorf("prometheus should be still running: %v", err) + case <-time.After(5 * time.Second): + prom.Process.Kill() + } + return + } + + err = prom.Wait() + require.Error(t, err) + if exitError, ok := err.(*exec.ExitError); ok { + status := exitError.Sys().(syscall.WaitStatus) + require.Equal(t, tc.exitStatus, status.ExitStatus()) + } else { + t.Errorf("unable to retrieve the exit status for prometheus: %v", err) + } + }) + } +} diff --git a/docs/feature_flags.md b/docs/feature_flags.md index adef7fc93..e5683c8ab 100644 --- a/docs/feature_flags.md +++ b/docs/feature_flags.md @@ -86,3 +86,13 @@ issues upstream. In future releases, this new service discovery manager will become the default and this feature flag will be ignored. + +## Prometheus agent + +`--enable-feature=agent` + +When enabled, Prometheus runs in agent mode. The agent mode is limited to +discovery, scrape and remote write. + +This is useful when you do not need to query the Prometheus data locally, but +only from a central [remote endpoint](https://prometheus.io/docs/operating/integrations/#remote-endpoints-and-storage).