cmd/prometheus: cleanup
This commit is contained in:
parent
34ab7a885a
commit
867b8d108f
|
@ -1,215 +0,0 @@
|
|||
// Copyright 2015 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/asaskevich/govalidator"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/common/model"
|
||||
"github.com/prometheus/prometheus/config"
|
||||
"github.com/prometheus/prometheus/notifier"
|
||||
"github.com/prometheus/prometheus/promql"
|
||||
"github.com/prometheus/prometheus/storage/tsdb"
|
||||
"github.com/prometheus/prometheus/web"
|
||||
)
|
||||
|
||||
// cfg contains immutable configuration parameters for a running Prometheus
|
||||
// server. It is populated by its flag set.
|
||||
var cfg = struct {
|
||||
printVersion bool
|
||||
configFile string
|
||||
|
||||
localStoragePath string
|
||||
localStorageEngine string
|
||||
notifier notifier.Options
|
||||
notifierTimeout model.Duration
|
||||
queryEngine promql.EngineOptions
|
||||
web web.Options
|
||||
tsdb tsdb.Options
|
||||
lookbackDelta model.Duration
|
||||
webTimeout model.Duration
|
||||
queryTimeout model.Duration
|
||||
|
||||
alertmanagerURLs stringset
|
||||
prometheusURL string
|
||||
|
||||
logFormat string
|
||||
logLevel string
|
||||
}{
|
||||
// The defaults for model.Duration flag parsing.
|
||||
notifierTimeout: model.Duration(10 * time.Second),
|
||||
tsdb: tsdb.Options{
|
||||
MinBlockDuration: model.Duration(2 * time.Hour),
|
||||
Retention: model.Duration(15 * 24 * time.Hour),
|
||||
},
|
||||
lookbackDelta: model.Duration(5 * time.Minute),
|
||||
webTimeout: model.Duration(30 * time.Second),
|
||||
queryTimeout: model.Duration(2 * time.Minute),
|
||||
|
||||
alertmanagerURLs: stringset{},
|
||||
notifier: notifier.Options{
|
||||
Registerer: prometheus.DefaultRegisterer,
|
||||
},
|
||||
}
|
||||
|
||||
func validate() error {
|
||||
if err := parsePrometheusURL(); err != nil {
|
||||
return errors.Wrapf(err, "parse external URL %q", cfg.prometheusURL)
|
||||
}
|
||||
|
||||
cfg.web.ReadTimeout = time.Duration(cfg.webTimeout)
|
||||
// Default -web.route-prefix to path of -web.external-url.
|
||||
if cfg.web.RoutePrefix == "" {
|
||||
cfg.web.RoutePrefix = cfg.web.ExternalURL.Path
|
||||
}
|
||||
// RoutePrefix must always be at least '/'.
|
||||
cfg.web.RoutePrefix = "/" + strings.Trim(cfg.web.RoutePrefix, "/")
|
||||
|
||||
for u := range cfg.alertmanagerURLs {
|
||||
if err := validateAlertmanagerURL(u); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.tsdb.MaxBlockDuration == 0 {
|
||||
cfg.tsdb.MaxBlockDuration = cfg.tsdb.Retention / 10
|
||||
}
|
||||
|
||||
promql.LookbackDelta = time.Duration(cfg.lookbackDelta)
|
||||
|
||||
cfg.queryEngine.Timeout = time.Duration(cfg.queryTimeout)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parsePrometheusURL() error {
|
||||
fmt.Println("promurl", cfg.prometheusURL)
|
||||
if cfg.prometheusURL == "" {
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("listenaddr", cfg.web.ListenAddress)
|
||||
_, port, err := net.SplitHostPort(cfg.web.ListenAddress)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfg.prometheusURL = fmt.Sprintf("http://%s:%s/", hostname, port)
|
||||
}
|
||||
|
||||
if ok := govalidator.IsURL(cfg.prometheusURL); !ok {
|
||||
return fmt.Errorf("invalid Prometheus URL: %s", cfg.prometheusURL)
|
||||
}
|
||||
|
||||
promURL, err := url.Parse(cfg.prometheusURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfg.web.ExternalURL = promURL
|
||||
|
||||
ppref := strings.TrimRight(cfg.web.ExternalURL.Path, "/")
|
||||
if ppref != "" && !strings.HasPrefix(ppref, "/") {
|
||||
ppref = "/" + ppref
|
||||
}
|
||||
cfg.web.ExternalURL.Path = ppref
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateAlertmanagerURL(u string) error {
|
||||
if u == "" {
|
||||
return nil
|
||||
}
|
||||
if ok := govalidator.IsURL(u); !ok {
|
||||
return fmt.Errorf("invalid Alertmanager URL: %s", u)
|
||||
}
|
||||
url, err := url.Parse(u)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if url.Scheme == "" {
|
||||
return fmt.Errorf("missing scheme in Alertmanager URL: %s", u)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseAlertmanagerURLToConfig(us string) (*config.AlertmanagerConfig, error) {
|
||||
u, err := url.Parse(us)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
acfg := &config.AlertmanagerConfig{
|
||||
Scheme: u.Scheme,
|
||||
PathPrefix: u.Path,
|
||||
Timeout: time.Duration(cfg.notifierTimeout),
|
||||
ServiceDiscoveryConfig: config.ServiceDiscoveryConfig{
|
||||
StaticConfigs: []*config.TargetGroup{
|
||||
{
|
||||
Targets: []model.LabelSet{
|
||||
{
|
||||
model.AddressLabel: model.LabelValue(u.Host),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if u.User != nil {
|
||||
acfg.HTTPClientConfig = config.HTTPClientConfig{
|
||||
BasicAuth: &config.BasicAuth{
|
||||
Username: u.User.Username(),
|
||||
},
|
||||
}
|
||||
|
||||
if password, isSet := u.User.Password(); isSet {
|
||||
acfg.HTTPClientConfig.BasicAuth.Password = config.Secret(password)
|
||||
}
|
||||
}
|
||||
|
||||
return acfg, nil
|
||||
}
|
||||
|
||||
type stringset map[string]struct{}
|
||||
|
||||
func (ss stringset) Set(s string) error {
|
||||
for _, v := range strings.Split(s, ",") {
|
||||
v = strings.TrimSpace(v)
|
||||
if v != "" {
|
||||
ss[v] = struct{}{}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ss stringset) String() string {
|
||||
return strings.Join(ss.slice(), ",")
|
||||
}
|
||||
|
||||
func (ss stringset) slice() []string {
|
||||
slice := make([]string, 0, len(ss))
|
||||
for k := range ss {
|
||||
slice = append(slice, k)
|
||||
}
|
||||
sort.Strings(slice)
|
||||
return slice
|
||||
}
|
|
@ -16,15 +16,21 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
_ "net/http/pprof" // Comment this line to disable pprof endpoint.
|
||||
"net/url"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/asaskevich/govalidator"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/common/log"
|
||||
"github.com/prometheus/common/model"
|
||||
"github.com/prometheus/common/version"
|
||||
"golang.org/x/net/context"
|
||||
"gopkg.in/alecthomas/kingpin.v2"
|
||||
|
@ -38,7 +44,48 @@ import (
|
|||
"github.com/prometheus/prometheus/web"
|
||||
)
|
||||
|
||||
var (
|
||||
configSuccess = prometheus.NewGauge(prometheus.GaugeOpts{
|
||||
Namespace: "prometheus",
|
||||
Name: "config_last_reload_successful",
|
||||
Help: "Whether the last configuration reload attempt was successful.",
|
||||
})
|
||||
configSuccessTime = prometheus.NewGauge(prometheus.GaugeOpts{
|
||||
Namespace: "prometheus",
|
||||
Name: "config_last_reload_success_timestamp_seconds",
|
||||
Help: "Timestamp of the last successful configuration reload.",
|
||||
})
|
||||
)
|
||||
|
||||
func init() {
|
||||
prometheus.MustRegister(version.NewCollector("prometheus"))
|
||||
}
|
||||
|
||||
func main() {
|
||||
cfg := struct {
|
||||
printVersion bool
|
||||
configFile string
|
||||
|
||||
localStoragePath string
|
||||
notifier notifier.Options
|
||||
notifierTimeout model.Duration
|
||||
queryEngine promql.EngineOptions
|
||||
web web.Options
|
||||
tsdb tsdb.Options
|
||||
lookbackDelta model.Duration
|
||||
webTimeout model.Duration
|
||||
queryTimeout model.Duration
|
||||
|
||||
prometheusURL string
|
||||
|
||||
logFormat string
|
||||
logLevel string
|
||||
}{
|
||||
notifier: notifier.Options{
|
||||
Registerer: prometheus.DefaultRegisterer,
|
||||
},
|
||||
}
|
||||
|
||||
a := kingpin.New(filepath.Base(os.Args[0]), "The Prometheus monitoring server")
|
||||
|
||||
a.Version(version.Print("prometheus"))
|
||||
|
@ -103,58 +150,49 @@ func main() {
|
|||
Default("10000").IntVar(&cfg.notifier.QueueCapacity)
|
||||
|
||||
a.Flag("alertmanager.timeout", "Timeout for sending alerts to Alertmanager").
|
||||
SetValue(&cfg.notifierTimeout)
|
||||
Default("10s").SetValue(&cfg.notifierTimeout)
|
||||
|
||||
a.Flag("query.lookback-delta", "The delta difference allowed for retrieving metrics during expression evaluations.").
|
||||
Default("5m").SetValue(&cfg.lookbackDelta)
|
||||
|
||||
a.Flag("query.timeout", "Maximum time a query may take before being aborted.").
|
||||
SetValue(&cfg.queryTimeout)
|
||||
Default("2m").SetValue(&cfg.queryTimeout)
|
||||
|
||||
a.Flag("query.max-concurrency", "Maximum number of queries executed concurrently.").
|
||||
Default("20").IntVar(&cfg.queryEngine.MaxConcurrentQueries)
|
||||
|
||||
if _, err := a.Parse(os.Args[1:]); err != nil {
|
||||
_, err := a.Parse(os.Args[1:])
|
||||
if err != nil {
|
||||
a.Usage(os.Args[1:])
|
||||
os.Exit(2)
|
||||
}
|
||||
fmt.Printf("a %+v\n", cfg)
|
||||
if err := validate(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "invalid argument: %s\n", err)
|
||||
|
||||
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))
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
os.Exit(Main(a))
|
||||
}
|
||||
cfg.web.ReadTimeout = time.Duration(cfg.webTimeout)
|
||||
// Default -web.route-prefix to path of -web.external-url.
|
||||
if cfg.web.RoutePrefix == "" {
|
||||
cfg.web.RoutePrefix = cfg.web.ExternalURL.Path
|
||||
}
|
||||
// RoutePrefix must always be at least '/'.
|
||||
cfg.web.RoutePrefix = "/" + strings.Trim(cfg.web.RoutePrefix, "/")
|
||||
|
||||
var (
|
||||
configSuccess = prometheus.NewGauge(prometheus.GaugeOpts{
|
||||
Namespace: "prometheus",
|
||||
Name: "config_last_reload_successful",
|
||||
Help: "Whether the last configuration reload attempt was successful.",
|
||||
})
|
||||
configSuccessTime = prometheus.NewGauge(prometheus.GaugeOpts{
|
||||
Namespace: "prometheus",
|
||||
Name: "config_last_reload_success_timestamp_seconds",
|
||||
Help: "Timestamp of the last successful configuration reload.",
|
||||
})
|
||||
)
|
||||
if cfg.tsdb.MaxBlockDuration == 0 {
|
||||
cfg.tsdb.MaxBlockDuration = cfg.tsdb.Retention / 10
|
||||
}
|
||||
|
||||
func init() {
|
||||
prometheus.MustRegister(version.NewCollector("prometheus"))
|
||||
}
|
||||
promql.LookbackDelta = time.Duration(cfg.lookbackDelta)
|
||||
|
||||
cfg.queryEngine.Timeout = time.Duration(cfg.queryTimeout)
|
||||
|
||||
// Main manages the stup and shutdown lifecycle of the entire Prometheus server.
|
||||
func Main(a *kingpin.Application) int {
|
||||
logger := log.NewLogger(os.Stdout)
|
||||
logger.SetLevel(cfg.logLevel)
|
||||
logger.SetFormat(cfg.logFormat)
|
||||
|
||||
if cfg.printVersion {
|
||||
fmt.Fprintln(os.Stdout, version.Print("prometheus"))
|
||||
return 0
|
||||
}
|
||||
|
||||
logger.Infoln("Starting prometheus", version.Info())
|
||||
logger.Infoln("Build context", version.BuildContext())
|
||||
logger.Infoln("Host details", Uname())
|
||||
|
@ -173,7 +211,7 @@ func Main(a *kingpin.Application) int {
|
|||
localStorage, err := tsdb.Open(cfg.localStoragePath, prometheus.DefaultRegisterer, &cfg.tsdb)
|
||||
if err != nil {
|
||||
log.Errorf("Opening storage failed: %s", err)
|
||||
return 1
|
||||
os.Exit(1)
|
||||
}
|
||||
logger.Infoln("tsdb started")
|
||||
|
||||
|
@ -225,7 +263,7 @@ func Main(a *kingpin.Application) int {
|
|||
|
||||
if err := reloadConfig(cfg.configFile, logger, reloadables...); err != nil {
|
||||
logger.Errorf("Error loading config: %s", err)
|
||||
return 1
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Wait for reload or termination signals. Start the handler for SIGHUP as
|
||||
|
@ -294,7 +332,6 @@ func Main(a *kingpin.Application) int {
|
|||
}
|
||||
|
||||
logger.Info("See you next time!")
|
||||
return 0
|
||||
}
|
||||
|
||||
// Reloadable things can change their internal state to match a new config
|
||||
|
@ -331,3 +368,36 @@ func reloadConfig(filename string, logger log.Logger, rls ...Reloadable) (err er
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// computeExternalURL computes a sanitized external URL from a raw input. It infers unset
|
||||
// URL parts from the OS and the given listen address.
|
||||
func computeExternalURL(u, listenAddr string) (*url.URL, error) {
|
||||
if u == "" {
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, port, err := net.SplitHostPort(listenAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u = fmt.Sprintf("http://%s:%s/", hostname, port)
|
||||
}
|
||||
|
||||
if ok := govalidator.IsURL(u); !ok {
|
||||
return nil, fmt.Errorf("invalid external URL %q", u)
|
||||
}
|
||||
|
||||
eu, err := url.Parse(u)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ppref := strings.TrimRight(eu.Path, "/")
|
||||
if ppref != "" && !strings.HasPrefix(ppref, "/") {
|
||||
ppref = "/" + ppref
|
||||
}
|
||||
eu.Path = ppref
|
||||
|
||||
return eu, nil
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2015 The Prometheus Authors
|
||||
// Copyright 2017 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
@ -15,40 +15,31 @@ package main
|
|||
|
||||
import "testing"
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
func TestComputeExternalURL(t *testing.T) {
|
||||
tests := []struct {
|
||||
input []string
|
||||
valid bool
|
||||
extURL string
|
||||
valid bool
|
||||
}{
|
||||
{
|
||||
input: []string{},
|
||||
valid: true,
|
||||
extURL: "",
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
input: []string{"--web.external-url", ""},
|
||||
valid: true,
|
||||
extURL: "http://proxy.com/prometheus",
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
input: []string{"--web.external-url", "http://proxy.com/prometheus"},
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
input: []string{"--web.external-url", "'https://url/prometheus'"},
|
||||
valid: false,
|
||||
extURL: "https:/proxy.com/prometheus",
|
||||
valid: false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
// reset "immutable" config
|
||||
cfg.prometheusURL = ""
|
||||
cfg.alertmanagerURLs = stringset{}
|
||||
|
||||
newRootCmd() // To register the flags and flagset.
|
||||
|
||||
err := parse(test.input)
|
||||
r, err := computeExternalURL(test.extURL, "0.0.0.0:9090")
|
||||
if test.valid && err != nil {
|
||||
t.Errorf("%d. expected input to be valid, got %s", i, err)
|
||||
} else if !test.valid && err == nil {
|
||||
t.Logf("%+v", r)
|
||||
t.Errorf("%d. expected input to be invalid", i)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue