Skip to content

Commit

Permalink
feat: add promtool http config support
Browse files Browse the repository at this point in the history
Signed-off-by: Martin Chodur <m.chodur@seznam.cz>
  • Loading branch information
FUSAKLA committed Feb 10, 2023
1 parent af1d9e0 commit 3ebe4b4
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 22 deletions.
67 changes: 48 additions & 19 deletions cmd/promtool/main.go
Expand Up @@ -45,6 +45,7 @@ import (
"gopkg.in/yaml.v2"

dto "github.com/prometheus/client_model/go"
promconfig "github.com/prometheus/common/config"
"github.com/prometheus/common/expfmt"

"github.com/prometheus/prometheus/config"
Expand Down Expand Up @@ -74,6 +75,12 @@ const (
var lintOptions = []string{lintOptionAll, lintOptionDuplicateRules, lintOptionNone}

func main() {
var (
httpRoundTripper = api.DefaultRoundTripper
serverURL *url.URL
httpConfigFilePath string
)

app := kingpin.New(filepath.Base(os.Args[0]), "Tooling for the Prometheus monitoring system.").UsageWriter(os.Stdout)
app.Version(version.Print("promtool"))
app.HelpFlag.Short('h')
Expand Down Expand Up @@ -124,22 +131,23 @@ func main() {

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")
queryCmd.Flag("http.config.file", "HTTP client configuration file for promtool to connect to Prometheus.").PlaceHolder("<filename>").ExistingFileVar(&httpConfigFilePath)

queryInstantCmd := queryCmd.Command("instant", "Run instant query.")
queryInstantServer := queryInstantCmd.Arg("server", "Prometheus server to query.").Required().URL()
queryInstantCmd.Arg("server", "Prometheus server to query.").Required().URLVar(&serverURL)
queryInstantExpr := queryInstantCmd.Arg("expr", "PromQL query expression.").Required().String()
queryInstantTime := queryInstantCmd.Flag("time", "Query evaluation time (RFC3339 or Unix timestamp).").String()

queryRangeCmd := queryCmd.Command("range", "Run range query.")
queryRangeServer := queryRangeCmd.Arg("server", "Prometheus server to query.").Required().URL()
queryRangeCmd.Arg("server", "Prometheus server to query.").Required().URLVar(&serverURL)
queryRangeExpr := queryRangeCmd.Arg("expr", "PromQL query expression.").Required().String()
queryRangeHeaders := queryRangeCmd.Flag("header", "Extra headers to send to server.").StringMap()
queryRangeBegin := queryRangeCmd.Flag("start", "Query range start time (RFC3339 or Unix timestamp).").String()
queryRangeEnd := queryRangeCmd.Flag("end", "Query range end time (RFC3339 or Unix timestamp).").String()
queryRangeStep := queryRangeCmd.Flag("step", "Query step size (duration).").Duration()

querySeriesCmd := queryCmd.Command("series", "Run series query.")
querySeriesServer := querySeriesCmd.Arg("server", "Prometheus server to query.").Required().URL()
querySeriesCmd.Arg("server", "Prometheus server to query.").Required().URLVar(&serverURL)
querySeriesMatch := querySeriesCmd.Flag("match", "Series selector. Can be specified multiple times.").Required().Strings()
querySeriesBegin := querySeriesCmd.Flag("start", "Start time (RFC3339 or Unix timestamp).").String()
querySeriesEnd := querySeriesCmd.Flag("end", "End time (RFC3339 or Unix timestamp).").String()
Expand All @@ -153,7 +161,7 @@ func main() {
debugAllServer := debugAllCmd.Arg("server", "Prometheus server to get all debug information from.").Required().String()

queryLabelsCmd := queryCmd.Command("labels", "Run labels query.")
queryLabelsServer := queryLabelsCmd.Arg("server", "Prometheus server to query.").Required().URL()
queryLabelsCmd.Arg("server", "Prometheus server to query.").Required().URLVar(&serverURL)
queryLabelsName := queryLabelsCmd.Arg("name", "Label name to provide label values for.").Required().String()
queryLabelsBegin := queryLabelsCmd.Flag("start", "Start time (RFC3339 or Unix timestamp).").String()
queryLabelsEnd := queryLabelsCmd.Flag("end", "End time (RFC3339 or Unix timestamp).").String()
Expand Down Expand Up @@ -200,7 +208,8 @@ func main() {
importFilePath := openMetricsImportCmd.Arg("input file", "OpenMetrics file to read samples from.").Required().String()
importDBPath := openMetricsImportCmd.Arg("output directory", "Output directory for generated blocks.").Default(defaultDBPath).String()
importRulesCmd := importCmd.Command("rules", "Create blocks of data for new recording rules.")
importRulesURL := importRulesCmd.Flag("url", "The URL for the Prometheus API with the data where the rule will be backfilled from.").Default("http://localhost:9090").URL()
importRulesCmd.Flag("http.config.file", "HTTP client configuration file for promtool to connect to Prometheus.").PlaceHolder("<filename>").ExistingFileVar(&httpConfigFilePath)
importRulesCmd.Flag("url", "The URL for the Prometheus API with the data where the rule will be backfilled from.").Default("http://localhost:9090").URLVar(&serverURL)
importRulesStart := importRulesCmd.Flag("start", "The time to start backfilling the new rule from. Must be a RFC3339 formatted date or Unix timestamp. Required.").
Required().String()
importRulesEnd := importRulesCmd.Flag("end", "If an end time is provided, all recording rules in the rule files provided will be backfilled to the end time. Default will backfill up to 3 hours ago. Must be a RFC3339 formatted date or Unix timestamp.").String()
Expand All @@ -224,6 +233,22 @@ func main() {
p = &promqlPrinter{}
}

if httpConfigFilePath != "" {
if serverURL != nil && serverURL.User.Username() != "" {
kingpin.Fatalf("Cannot set base auth in the server URL and use a http.config.file at the same time")
}
var err error
httpConfig, _, err := config_util.LoadHTTPConfigFile(httpConfigFilePath)
if err != nil {
kingpin.Fatalf("Failed to load HTTP config file: %v", err)
}

httpRoundTripper, err = promconfig.NewRoundTripperFromConfig(*httpConfig, "promtool", config_util.WithUserAgent("promtool/"+version.Version))
if err != nil {
kingpin.Fatalf("Failed to create a new HTTP round tripper: %v", err)
}
}

var noDefaultScrapePort bool
for _, f := range *featureList {
opts := strings.Split(f, ",")
Expand Down Expand Up @@ -258,13 +283,13 @@ func main() {
os.Exit(CheckMetrics(*checkMetricsExtended))

case queryInstantCmd.FullCommand():
os.Exit(QueryInstant(*queryInstantServer, *queryInstantExpr, *queryInstantTime, p))
os.Exit(QueryInstant(serverURL, httpRoundTripper, *queryInstantExpr, *queryInstantTime, p))

case queryRangeCmd.FullCommand():
os.Exit(QueryRange(*queryRangeServer, *queryRangeHeaders, *queryRangeExpr, *queryRangeBegin, *queryRangeEnd, *queryRangeStep, p))
os.Exit(QueryRange(serverURL, httpRoundTripper, *queryRangeHeaders, *queryRangeExpr, *queryRangeBegin, *queryRangeEnd, *queryRangeStep, p))

case querySeriesCmd.FullCommand():
os.Exit(QuerySeries(*querySeriesServer, *querySeriesMatch, *querySeriesBegin, *querySeriesEnd, p))
os.Exit(QuerySeries(serverURL, httpRoundTripper, *querySeriesMatch, *querySeriesBegin, *querySeriesEnd, p))

case debugPprofCmd.FullCommand():
os.Exit(debugPprof(*debugPprofServer))
Expand All @@ -276,7 +301,7 @@ func main() {
os.Exit(debugAll(*debugAllServer))

case queryLabelsCmd.FullCommand():
os.Exit(QueryLabels(*queryLabelsServer, *queryLabelsMatch, *queryLabelsName, *queryLabelsBegin, *queryLabelsEnd, p))
os.Exit(QueryLabels(serverURL, httpRoundTripper, *queryLabelsMatch, *queryLabelsName, *queryLabelsBegin, *queryLabelsEnd, p))

case testRulesCmd.FullCommand():
os.Exit(RulesUnitTest(
Expand All @@ -303,7 +328,7 @@ func main() {
os.Exit(backfillOpenMetrics(*importFilePath, *importDBPath, *importHumanReadable, *importQuiet, *maxBlockDuration))

case importRulesCmd.FullCommand():
os.Exit(checkErr(importRules(*importRulesURL, *importRulesStart, *importRulesEnd, *importRulesOutputDir, *importRulesEvalInterval, *maxBlockDuration, *importRulesFiles...)))
os.Exit(checkErr(importRules(serverURL, *importRulesStart, *importRulesEnd, *importRulesOutputDir, *importRulesEvalInterval, *maxBlockDuration, *importRulesFiles...)))
}
}

Expand Down Expand Up @@ -795,12 +820,13 @@ func checkMetricsExtended(r io.Reader) ([]metricStat, int, error) {
}

// QueryInstant performs an instant query against a Prometheus server.
func QueryInstant(url *url.URL, query, evalTime string, p printer) int {
func QueryInstant(url *url.URL, roundTripper http.RoundTripper, query, evalTime string, p printer) int {
if url.Scheme == "" {
url.Scheme = "http"
}
config := api.Config{
Address: url.String(),
Address: url.String(),
RoundTripper: roundTripper,
}

// Create new client.
Expand Down Expand Up @@ -835,20 +861,21 @@ func QueryInstant(url *url.URL, query, evalTime string, p printer) int {
}

// QueryRange performs a range query against a Prometheus server.
func QueryRange(url *url.URL, headers map[string]string, query, start, end string, step time.Duration, p printer) int {
func QueryRange(url *url.URL, roundTripper http.RoundTripper, headers map[string]string, query, start, end string, step time.Duration, p printer) int {
if url.Scheme == "" {
url.Scheme = "http"
}
config := api.Config{
Address: url.String(),
Address: url.String(),
RoundTripper: roundTripper,
}

if len(headers) > 0 {
config.RoundTripper = promhttp.RoundTripperFunc(func(req *http.Request) (*http.Response, error) {
for key, value := range headers {
req.Header.Add(key, value)
}
return http.DefaultTransport.RoundTrip(req)
return roundTripper.RoundTrip(req)
})
}

Expand Down Expand Up @@ -908,12 +935,13 @@ func QueryRange(url *url.URL, headers map[string]string, query, start, end strin
}

// QuerySeries queries for a series against a Prometheus server.
func QuerySeries(url *url.URL, matchers []string, start, end string, p printer) int {
func QuerySeries(url *url.URL, roundTripper http.RoundTripper, matchers []string, start, end string, p printer) int {
if url.Scheme == "" {
url.Scheme = "http"
}
config := api.Config{
Address: url.String(),
Address: url.String(),
RoundTripper: roundTripper,
}

// Create new client.
Expand Down Expand Up @@ -944,12 +972,13 @@ func QuerySeries(url *url.URL, matchers []string, start, end string, p printer)
}

// QueryLabels queries for label values against a Prometheus server.
func QueryLabels(url *url.URL, matchers []string, name, start, end string, p printer) int {
func QueryLabels(url *url.URL, roundTripper http.RoundTripper, matchers []string, name, start, end string, p printer) int {
if url.Scheme == "" {
url.Scheme = "http"
}
config := api.Config{
Address: url.String(),
Address: url.String(),
RoundTripper: roundTripper,
}

// Create new client.
Expand Down
6 changes: 3 additions & 3 deletions cmd/promtool/main_test.go
Expand Up @@ -56,14 +56,14 @@ func TestQueryRange(t *testing.T) {
require.Equal(t, nil, err)

p := &promqlPrinter{}
exitCode := QueryRange(urlObject, map[string]string{}, "up", "0", "300", 0, p)
exitCode := QueryRange(urlObject, http.DefaultTransport, map[string]string{}, "up", "0", "300", 0, p)
require.Equal(t, "/api/v1/query_range", getRequest().URL.Path)
form := getRequest().Form
require.Equal(t, "up", form.Get("query"))
require.Equal(t, "1", form.Get("step"))
require.Equal(t, 0, exitCode)

exitCode = QueryRange(urlObject, map[string]string{}, "up", "0", "300", 10*time.Millisecond, p)
exitCode = QueryRange(urlObject, http.DefaultTransport, map[string]string{}, "up", "0", "300", 10*time.Millisecond, p)
require.Equal(t, "/api/v1/query_range", getRequest().URL.Path)
form = getRequest().Form
require.Equal(t, "up", form.Get("query"))
Expand All @@ -79,7 +79,7 @@ func TestQueryInstant(t *testing.T) {
require.Equal(t, nil, err)

p := &promqlPrinter{}
exitCode := QueryInstant(urlObject, "up", "300", p)
exitCode := QueryInstant(urlObject, http.DefaultTransport, "up", "300", p)
require.Equal(t, "/api/v1/query", getRequest().URL.Path)
form := getRequest().Form
require.Equal(t, "up", form.Get("query"))
Expand Down

0 comments on commit 3ebe4b4

Please sign in to comment.