mirror of
https://github.com/prometheus/alertmanager
synced 2025-01-11 16:29:30 +00:00
commit
b54f77998d
@ -486,9 +486,10 @@ func checkTimeInterval(r *Route, timeIntervals map[string]struct{}) error {
|
||||
|
||||
// DefaultGlobalConfig returns GlobalConfig with default values.
|
||||
func DefaultGlobalConfig() GlobalConfig {
|
||||
var defaultHTTPConfig = commoncfg.DefaultHTTPClientConfig
|
||||
return GlobalConfig{
|
||||
ResolveTimeout: model.Duration(5 * time.Minute),
|
||||
HTTPConfig: &commoncfg.HTTPClientConfig{},
|
||||
HTTPConfig: &defaultHTTPConfig,
|
||||
|
||||
SMTPHello: "localhost",
|
||||
SMTPRequireTLS: true,
|
||||
|
@ -675,7 +675,9 @@ func TestEmptyFieldsAndRegex(t *testing.T) {
|
||||
var expectedConf = Config{
|
||||
|
||||
Global: &GlobalConfig{
|
||||
HTTPConfig: &commoncfg.HTTPClientConfig{},
|
||||
HTTPConfig: &commoncfg.HTTPClientConfig{
|
||||
FollowRedirects: true,
|
||||
},
|
||||
ResolveTimeout: model.Duration(5 * time.Minute),
|
||||
SMTPSmarthost: HostPort{Host: "localhost", Port: "25"},
|
||||
SMTPFrom: "alertmanager@example.org",
|
||||
@ -755,6 +757,21 @@ func TestEmptyFieldsAndRegex(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGlobalAndLocalHTTPConfig(t *testing.T) {
|
||||
config, err := LoadFile("testdata/conf.http-config.good.yml")
|
||||
if err != nil {
|
||||
t.Fatalf("Error parsing %s: %s", "testdata/conf-http-config.good.yml", err)
|
||||
}
|
||||
|
||||
if config.Global.HTTPConfig.FollowRedirects {
|
||||
t.Fatalf("global HTTP config should not follow redirects")
|
||||
}
|
||||
|
||||
if !config.Receivers[0].SlackConfigs[0].HTTPConfig.FollowRedirects {
|
||||
t.Fatalf("global HTTP config should follow redirects")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSMTPHello(t *testing.T) {
|
||||
c, err := LoadFile("testdata/conf.good.yml")
|
||||
if err != nil {
|
||||
|
11
config/testdata/conf.http-config.good.yml
vendored
Normal file
11
config/testdata/conf.http-config.good.yml
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
global:
|
||||
slack_api_url: 'https://slack.com/webhook'
|
||||
http_config:
|
||||
follow_redirects: false
|
||||
route:
|
||||
receiver: team-X-slack
|
||||
receivers:
|
||||
- name: 'team-X-slack'
|
||||
slack_configs:
|
||||
- http_config:
|
||||
proxy_url: foo
|
@ -319,8 +319,7 @@ A `http_config` allows configuring the HTTP client that the receiver uses to
|
||||
communicate with HTTP-based API services.
|
||||
|
||||
```yaml
|
||||
# Note that `basic_auth`, `bearer_token` and `bearer_token_file` options are
|
||||
# mutually exclusive.
|
||||
# Note that `basic_auth` and `authorization` options are mutually exclusive.
|
||||
|
||||
# Sets the `Authorization` header with the configured username and password.
|
||||
# password and password_file are mutually exclusive.
|
||||
@ -329,18 +328,26 @@ basic_auth:
|
||||
[ password: <secret> ]
|
||||
[ password_file: <string> ]
|
||||
|
||||
# Sets the `Authorization` header with the configured bearer token.
|
||||
[ bearer_token: <secret> ]
|
||||
# Optional the `Authorization` header configuration.
|
||||
authorization:
|
||||
# Sets the authentication type.
|
||||
[ type: <string> | default: Bearer ]
|
||||
# Sets the credentials. It is mutually exclusive with
|
||||
# `credentials_file`.
|
||||
[ credentials: <secret> ]
|
||||
# Sets the credentials with the credentials read from the configured file.
|
||||
# It is mutually exclusive with `credentials`.
|
||||
[ credentials_file: <filename> ]
|
||||
|
||||
# Sets the `Authorization` header with the bearer token read from the configured file.
|
||||
[ bearer_token_file: <filepath> ]
|
||||
# Optional proxy URL.
|
||||
[ proxy_url: <string> ]
|
||||
|
||||
# Configure whether HTTP requests follow HTTP 3xx redirects.
|
||||
[ follow_redirects: <bool> | default = true ]
|
||||
|
||||
# Configures the TLS settings.
|
||||
tls_config:
|
||||
[ <tls_config> ]
|
||||
|
||||
# Optional proxy URL.
|
||||
[ proxy_url: <string> ]
|
||||
```
|
||||
|
||||
## `<tls_config>`
|
||||
|
2
go.mod
2
go.mod
@ -22,7 +22,7 @@ require (
|
||||
github.com/oklog/ulid v1.3.1
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/prometheus/client_golang v1.7.1
|
||||
github.com/prometheus/common v0.15.0
|
||||
github.com/prometheus/common v0.18.0
|
||||
github.com/prometheus/exporter-toolkit v0.5.0
|
||||
github.com/rs/cors v1.7.0
|
||||
github.com/satori/go.uuid v1.2.0
|
||||
|
2
go.sum
2
go.sum
@ -427,6 +427,8 @@ github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lN
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/common v0.15.0 h1:4fgOnadei3EZvgRwxJ7RMpG1k1pOZth5Pc13tyspaKM=
|
||||
github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
|
||||
github.com/prometheus/common v0.18.0 h1:WCVKW7aL6LEe1uryfI9dnEc2ZqNB1Fn0ok930v0iL1Y=
|
||||
github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
|
||||
github.com/prometheus/exporter-toolkit v0.5.0 h1:GwrxhCviqOl8Mm0vKqkh7Xy54m+FPlHEJacFs48M3gY=
|
||||
github.com/prometheus/exporter-toolkit v0.5.0/go.mod h1:OCkM4805mmisBhLmVFw858QYi3v0wKdY6/UxrT0pZVg=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
|
134
vendor/github.com/prometheus/common/config/http_config.go
generated
vendored
134
vendor/github.com/prometheus/common/config/http_config.go
generated
vendored
@ -33,6 +33,11 @@ import (
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
// DefaultHTTPClientConfig is the default HTTP client configuration.
|
||||
var DefaultHTTPClientConfig = HTTPClientConfig{
|
||||
FollowRedirects: true,
|
||||
}
|
||||
|
||||
type closeIdler interface {
|
||||
CloseIdleConnections()
|
||||
}
|
||||
@ -52,6 +57,21 @@ func (a *BasicAuth) SetDirectory(dir string) {
|
||||
a.PasswordFile = JoinDir(dir, a.PasswordFile)
|
||||
}
|
||||
|
||||
// Authorization contains HTTP authorization credentials.
|
||||
type Authorization struct {
|
||||
Type string `yaml:"type,omitempty"`
|
||||
Credentials Secret `yaml:"credentials,omitempty"`
|
||||
CredentialsFile string `yaml:"credentials_file,omitempty"`
|
||||
}
|
||||
|
||||
// SetDirectory joins any relative file paths with dir.
|
||||
func (a *Authorization) SetDirectory(dir string) {
|
||||
if a == nil {
|
||||
return
|
||||
}
|
||||
a.CredentialsFile = JoinDir(dir, a.CredentialsFile)
|
||||
}
|
||||
|
||||
// URL is a custom URL type that allows validation at configuration load time.
|
||||
type URL struct {
|
||||
*url.URL
|
||||
@ -84,14 +104,22 @@ func (u URL) MarshalYAML() (interface{}, error) {
|
||||
type HTTPClientConfig struct {
|
||||
// The HTTP basic authentication credentials for the targets.
|
||||
BasicAuth *BasicAuth `yaml:"basic_auth,omitempty"`
|
||||
// The bearer token for the targets.
|
||||
// The HTTP authorization credentials for the targets.
|
||||
Authorization *Authorization `yaml:"authorization,omitempty"`
|
||||
// The bearer token for the targets. Deprecated in favour of
|
||||
// Authorization.Credentials.
|
||||
BearerToken Secret `yaml:"bearer_token,omitempty"`
|
||||
// The bearer token file for the targets.
|
||||
// The bearer token file for the targets. Deprecated in favour of
|
||||
// Authorization.CredentialsFile.
|
||||
BearerTokenFile string `yaml:"bearer_token_file,omitempty"`
|
||||
// HTTP proxy server to use to connect to the targets.
|
||||
ProxyURL URL `yaml:"proxy_url,omitempty"`
|
||||
// TLSConfig to use to connect to the targets.
|
||||
TLSConfig TLSConfig `yaml:"tls_config,omitempty"`
|
||||
// FollowRedirects specifies whether the client should follow HTTP 3xx redirects.
|
||||
// The omitempty flag is not set, because it would be hidden from the
|
||||
// marshalled configuration when set to false.
|
||||
FollowRedirects bool `yaml:"follow_redirects"`
|
||||
}
|
||||
|
||||
// SetDirectory joins any relative file paths with dir.
|
||||
@ -101,12 +129,14 @@ func (c *HTTPClientConfig) SetDirectory(dir string) {
|
||||
}
|
||||
c.TLSConfig.SetDirectory(dir)
|
||||
c.BasicAuth.SetDirectory(dir)
|
||||
c.Authorization.SetDirectory(dir)
|
||||
c.BearerTokenFile = JoinDir(dir, c.BearerTokenFile)
|
||||
}
|
||||
|
||||
// Validate validates the HTTPClientConfig to check only one of BearerToken,
|
||||
// BasicAuth and BearerTokenFile is configured.
|
||||
func (c *HTTPClientConfig) Validate() error {
|
||||
// Backwards compatibility with the bearer_token field.
|
||||
if len(c.BearerToken) > 0 && len(c.BearerTokenFile) > 0 {
|
||||
return fmt.Errorf("at most one of bearer_token & bearer_token_file must be configured")
|
||||
}
|
||||
@ -116,12 +146,42 @@ func (c *HTTPClientConfig) Validate() error {
|
||||
if c.BasicAuth != nil && (string(c.BasicAuth.Password) != "" && c.BasicAuth.PasswordFile != "") {
|
||||
return fmt.Errorf("at most one of basic_auth password & password_file must be configured")
|
||||
}
|
||||
if c.Authorization != nil {
|
||||
if len(c.BearerToken) > 0 || len(c.BearerTokenFile) > 0 {
|
||||
return fmt.Errorf("authorization is not compatible with bearer_token & bearer_token_file")
|
||||
}
|
||||
if string(c.Authorization.Credentials) != "" && c.Authorization.CredentialsFile != "" {
|
||||
return fmt.Errorf("at most one of authorization credentials & credentials_file must be configured")
|
||||
}
|
||||
c.Authorization.Type = strings.TrimSpace(c.Authorization.Type)
|
||||
if len(c.Authorization.Type) == 0 {
|
||||
c.Authorization.Type = "Bearer"
|
||||
}
|
||||
if strings.ToLower(c.Authorization.Type) == "basic" {
|
||||
return fmt.Errorf(`authorization type cannot be set to "basic", use "basic_auth" instead`)
|
||||
}
|
||||
if c.BasicAuth != nil {
|
||||
return fmt.Errorf("at most one of basic_auth & authorization must be configured")
|
||||
}
|
||||
} else {
|
||||
if len(c.BearerToken) > 0 {
|
||||
c.Authorization = &Authorization{Credentials: c.BearerToken}
|
||||
c.Authorization.Type = "Bearer"
|
||||
c.BearerToken = ""
|
||||
}
|
||||
if len(c.BearerTokenFile) > 0 {
|
||||
c.Authorization = &Authorization{CredentialsFile: c.BearerTokenFile}
|
||||
c.Authorization.Type = "Bearer"
|
||||
c.BearerTokenFile = ""
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface
|
||||
func (c *HTTPClientConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
type plain HTTPClientConfig
|
||||
*c = DefaultHTTPClientConfig
|
||||
if err := unmarshal((*plain)(c)); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -146,7 +206,13 @@ func NewClientFromConfig(cfg HTTPClientConfig, name string, disableKeepAlives, e
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newClient(rt), nil
|
||||
client := newClient(rt)
|
||||
if !cfg.FollowRedirects {
|
||||
client.CheckRedirect = func(*http.Request, []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
}
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// NewRoundTripperFromConfig returns a new HTTP RoundTripper configured for the
|
||||
@ -186,12 +252,19 @@ func NewRoundTripperFromConfig(cfg HTTPClientConfig, name string, disableKeepAli
|
||||
}
|
||||
}
|
||||
|
||||
// If a bearer token is provided, create a round tripper that will set the
|
||||
// If a authorization_credentials is provided, create a round tripper that will set the
|
||||
// Authorization header correctly on each request.
|
||||
if cfg.Authorization != nil && len(cfg.Authorization.Credentials) > 0 {
|
||||
rt = NewAuthorizationCredentialsRoundTripper(cfg.Authorization.Type, cfg.Authorization.Credentials, rt)
|
||||
} else if cfg.Authorization != nil && len(cfg.Authorization.CredentialsFile) > 0 {
|
||||
rt = NewAuthorizationCredentialsFileRoundTripper(cfg.Authorization.Type, cfg.Authorization.CredentialsFile, rt)
|
||||
}
|
||||
// Backwards compatibility, be nice with importers who would not have
|
||||
// called Validate().
|
||||
if len(cfg.BearerToken) > 0 {
|
||||
rt = NewBearerAuthRoundTripper(cfg.BearerToken, rt)
|
||||
rt = NewAuthorizationCredentialsRoundTripper("Bearer", cfg.BearerToken, rt)
|
||||
} else if len(cfg.BearerTokenFile) > 0 {
|
||||
rt = NewBearerAuthFileRoundTripper(cfg.BearerTokenFile, rt)
|
||||
rt = NewAuthorizationCredentialsFileRoundTripper("Bearer", cfg.BearerTokenFile, rt)
|
||||
}
|
||||
|
||||
if cfg.BasicAuth != nil {
|
||||
@ -214,58 +287,61 @@ func NewRoundTripperFromConfig(cfg HTTPClientConfig, name string, disableKeepAli
|
||||
return newTLSRoundTripper(tlsConfig, cfg.TLSConfig.CAFile, newRT)
|
||||
}
|
||||
|
||||
type bearerAuthRoundTripper struct {
|
||||
bearerToken Secret
|
||||
rt http.RoundTripper
|
||||
type authorizationCredentialsRoundTripper struct {
|
||||
authType string
|
||||
authCredentials Secret
|
||||
rt http.RoundTripper
|
||||
}
|
||||
|
||||
// NewBearerAuthRoundTripper adds the provided bearer token to a request unless the authorization
|
||||
// header has already been set.
|
||||
func NewBearerAuthRoundTripper(token Secret, rt http.RoundTripper) http.RoundTripper {
|
||||
return &bearerAuthRoundTripper{token, rt}
|
||||
// NewAuthorizationCredentialsRoundTripper adds the provided credentials to a
|
||||
// request unless the authorization header has already been set.
|
||||
func NewAuthorizationCredentialsRoundTripper(authType string, authCredentials Secret, rt http.RoundTripper) http.RoundTripper {
|
||||
return &authorizationCredentialsRoundTripper{authType, authCredentials, rt}
|
||||
}
|
||||
|
||||
func (rt *bearerAuthRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
func (rt *authorizationCredentialsRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
if len(req.Header.Get("Authorization")) == 0 {
|
||||
req = cloneRequest(req)
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", string(rt.bearerToken)))
|
||||
req.Header.Set("Authorization", fmt.Sprintf("%s %s", rt.authType, string(rt.authCredentials)))
|
||||
}
|
||||
return rt.rt.RoundTrip(req)
|
||||
}
|
||||
|
||||
func (rt *bearerAuthRoundTripper) CloseIdleConnections() {
|
||||
func (rt *authorizationCredentialsRoundTripper) CloseIdleConnections() {
|
||||
if ci, ok := rt.rt.(closeIdler); ok {
|
||||
ci.CloseIdleConnections()
|
||||
}
|
||||
}
|
||||
|
||||
type bearerAuthFileRoundTripper struct {
|
||||
bearerFile string
|
||||
rt http.RoundTripper
|
||||
type authorizationCredentialsFileRoundTripper struct {
|
||||
authType string
|
||||
authCredentialsFile string
|
||||
rt http.RoundTripper
|
||||
}
|
||||
|
||||
// NewBearerAuthFileRoundTripper adds the bearer token read from the provided file to a request unless
|
||||
// the authorization header has already been set. This file is read for every request.
|
||||
func NewBearerAuthFileRoundTripper(bearerFile string, rt http.RoundTripper) http.RoundTripper {
|
||||
return &bearerAuthFileRoundTripper{bearerFile, rt}
|
||||
// NewAuthorizationCredentialsFileRoundTripper adds the authorization
|
||||
// credentials read from the provided file to a request unless the authorization
|
||||
// header has already been set. This file is read for every request.
|
||||
func NewAuthorizationCredentialsFileRoundTripper(authType, authCredentialsFile string, rt http.RoundTripper) http.RoundTripper {
|
||||
return &authorizationCredentialsFileRoundTripper{authType, authCredentialsFile, rt}
|
||||
}
|
||||
|
||||
func (rt *bearerAuthFileRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
func (rt *authorizationCredentialsFileRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
if len(req.Header.Get("Authorization")) == 0 {
|
||||
b, err := ioutil.ReadFile(rt.bearerFile)
|
||||
b, err := ioutil.ReadFile(rt.authCredentialsFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to read bearer token file %s: %s", rt.bearerFile, err)
|
||||
return nil, fmt.Errorf("unable to read authorization credentials file %s: %s", rt.authCredentialsFile, err)
|
||||
}
|
||||
bearerToken := strings.TrimSpace(string(b))
|
||||
authCredentials := strings.TrimSpace(string(b))
|
||||
|
||||
req = cloneRequest(req)
|
||||
req.Header.Set("Authorization", "Bearer "+bearerToken)
|
||||
req.Header.Set("Authorization", fmt.Sprintf("%s %s", rt.authType, authCredentials))
|
||||
}
|
||||
|
||||
return rt.rt.RoundTrip(req)
|
||||
}
|
||||
|
||||
func (rt *bearerAuthFileRoundTripper) CloseIdleConnections() {
|
||||
func (rt *authorizationCredentialsFileRoundTripper) CloseIdleConnections() {
|
||||
if ci, ok := rt.rt.(closeIdler); ok {
|
||||
ci.CloseIdleConnections()
|
||||
}
|
||||
|
12
vendor/github.com/prometheus/common/model/time.go
generated
vendored
12
vendor/github.com/prometheus/common/model/time.go
generated
vendored
@ -254,6 +254,18 @@ func (d Duration) String() string {
|
||||
return r
|
||||
}
|
||||
|
||||
// MarshalText implements the encoding.TextMarshaler interface.
|
||||
func (d *Duration) MarshalText() ([]byte, error) {
|
||||
return []byte(d.String()), nil
|
||||
}
|
||||
|
||||
// UnmarshalText implements the encoding.TextUnmarshaler interface.
|
||||
func (d *Duration) UnmarshalText(text []byte) error {
|
||||
var err error
|
||||
*d, err = ParseDuration(string(text))
|
||||
return err
|
||||
}
|
||||
|
||||
// MarshalYAML implements the yaml.Marshaler interface.
|
||||
func (d Duration) MarshalYAML() (interface{}, error) {
|
||||
return d.String(), nil
|
||||
|
2
vendor/modules.txt
vendored
2
vendor/modules.txt
vendored
@ -167,7 +167,7 @@ github.com/prometheus/client_golang/prometheus/internal
|
||||
github.com/prometheus/client_golang/prometheus/promhttp
|
||||
# github.com/prometheus/client_model v0.2.0
|
||||
github.com/prometheus/client_model/go
|
||||
# github.com/prometheus/common v0.15.0
|
||||
# github.com/prometheus/common v0.18.0
|
||||
## explicit
|
||||
github.com/prometheus/common/config
|
||||
github.com/prometheus/common/expfmt
|
||||
|
Loading…
Reference in New Issue
Block a user