Merge pull request #2499 from roidelapluie/update-common

Update common
This commit is contained in:
Julien Pivotto 2021-03-02 06:35:58 +01:00 committed by GitHub
commit b54f77998d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 168 additions and 42 deletions

View File

@ -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,

View File

@ -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 {

View 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

View File

@ -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
View File

@ -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
View File

@ -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=

View File

@ -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()
}

View File

@ -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
View File

@ -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