From faed8df31dcda3b336cb59acc85b48dcaac3b088 Mon Sep 17 00:00:00 2001 From: Levi Harrison Date: Fri, 11 Jun 2021 18:06:59 -0400 Subject: [PATCH] Enable reading consul token from file (#8926) * Adopted common http client Signed-off-by: Levi Harrison --- config/config_test.go | 13 +++-- discovery/consul/consul.go | 41 +++++++------ discovery/consul/consul_test.go | 89 +++++++++++++++++++++++++++++ docs/configuration/configuration.md | 40 ++++++++++++- 4 files changed, 156 insertions(+), 27 deletions(-) diff --git a/config/config_test.go b/config/config_test.go index 519362e471..fb5947227c 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -332,11 +332,14 @@ var expectedConf = &Config{ Scheme: "https", RefreshInterval: consul.DefaultSDConfig.RefreshInterval, AllowStale: true, - TLSConfig: config.TLSConfig{ - CertFile: filepath.FromSlash("testdata/valid_cert_file"), - KeyFile: filepath.FromSlash("testdata/valid_key_file"), - CAFile: filepath.FromSlash("testdata/valid_ca_file"), - InsecureSkipVerify: false, + HTTPClientConfig: config.HTTPClientConfig{ + TLSConfig: config.TLSConfig{ + CertFile: filepath.FromSlash("testdata/valid_cert_file"), + KeyFile: filepath.FromSlash("testdata/valid_key_file"), + CAFile: filepath.FromSlash("testdata/valid_ca_file"), + InsecureSkipVerify: false, + }, + FollowRedirects: true, }, }, }, diff --git a/discovery/consul/consul.go b/discovery/consul/consul.go index c72d7b1bff..3416d86d60 100644 --- a/discovery/consul/consul.go +++ b/discovery/consul/consul.go @@ -92,11 +92,12 @@ var ( // DefaultSDConfig is the default Consul SD configuration. DefaultSDConfig = SDConfig{ - TagSeparator: ",", - Scheme: "http", - Server: "localhost:8500", - AllowStale: true, - RefreshInterval: model.Duration(30 * time.Second), + TagSeparator: ",", + Scheme: "http", + Server: "localhost:8500", + AllowStale: true, + RefreshInterval: model.Duration(30 * time.Second), + HTTPClientConfig: config.DefaultHTTPClientConfig, } ) @@ -134,7 +135,7 @@ type SDConfig struct { // Desired node metadata. NodeMeta map[string]string `yaml:"node_meta,omitempty"` - TLSConfig config.TLSConfig `yaml:"tls_config,omitempty"` + HTTPClientConfig config.HTTPClientConfig `yaml:",inline"` } // Name returns the name of the Config. @@ -147,7 +148,7 @@ func (c *SDConfig) NewDiscoverer(opts discovery.DiscovererOptions) (discovery.Di // SetDirectory joins any relative file paths with dir. func (c *SDConfig) SetDirectory(dir string) { - c.TLSConfig.SetDirectory(dir) + c.HTTPClientConfig.SetDirectory(dir) } // UnmarshalYAML implements the yaml.Unmarshaler interface. @@ -161,7 +162,19 @@ func (c *SDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { if strings.TrimSpace(c.Server) == "" { return errors.New("consul SD configuration requires a server address") } - return nil + if c.Username != "" || c.Password != "" { + if c.HTTPClientConfig.BasicAuth != nil { + return errors.New("at most one of consul SD configuration username and password and basic auth can be configured") + } + c.HTTPClientConfig.BasicAuth = &config.BasicAuth{ + Username: c.Username, + Password: c.Password, + } + } + if c.Token != "" && (c.HTTPClientConfig.Authorization != nil || c.HTTPClientConfig.OAuth2 != nil) { + return errors.New("at most one of consul SD token, authorization, or oauth2 can be configured") + } + return c.HTTPClientConfig.Validate() } // Discovery retrieves target information from a Consul server @@ -186,13 +199,7 @@ func NewDiscovery(conf *SDConfig, logger log.Logger) (*Discovery, error) { logger = log.NewNopLogger() } - httpConfig := config.HTTPClientConfig{ - TLSConfig: conf.TLSConfig, - FollowRedirects: true, - } - - wrapper, err := config.NewClientFromConfig(httpConfig, "consul_sd", config.WithHTTP2Disabled(), config.WithIdleConnTimeout(2*time.Duration(watchTimeout))) - + wrapper, err := config.NewClientFromConfig(conf.HTTPClientConfig, "consul_sd", config.WithHTTP2Disabled(), config.WithIdleConnTimeout(2*time.Duration(watchTimeout))) if err != nil { return nil, err } @@ -204,10 +211,6 @@ func NewDiscovery(conf *SDConfig, logger log.Logger) (*Discovery, error) { Datacenter: conf.Datacenter, Namespace: conf.Namespace, Token: string(conf.Token), - HttpAuth: &consul.HttpBasicAuth{ - Username: conf.Username, - Password: string(conf.Password), - }, HttpClient: wrapper, } client, err := consul.NewClient(clientConf) diff --git a/discovery/consul/consul_test.go b/discovery/consul/consul_test.go index 9570474d27..15092c3041 100644 --- a/discovery/consul/consul_test.go +++ b/discovery/consul/consul_test.go @@ -22,9 +22,11 @@ import ( "time" "github.com/go-kit/log" + "github.com/prometheus/common/config" "github.com/prometheus/common/model" "github.com/stretchr/testify/require" "go.uber.org/goleak" + "gopkg.in/yaml.v2" "github.com/prometheus/prometheus/discovery/targetgroup" ) @@ -397,3 +399,90 @@ func TestGetDatacenterShouldReturnError(t *testing.T) { require.Equal(t, "", d.clientDatacenter) } } + +func TestUnmarshalConfig(t *testing.T) { + unmarshal := func(d []byte) func(interface{}) error { + return func(o interface{}) error { + return yaml.Unmarshal(d, o) + } + } + + goodConfig := DefaultSDConfig + goodConfig.Username = "123" + goodConfig.Password = "1234" + goodConfig.HTTPClientConfig = config.HTTPClientConfig{ + BasicAuth: &config.BasicAuth{ + Username: "123", + Password: "1234", + }, + FollowRedirects: true, + } + + cases := []struct { + name string + config string + expected SDConfig + errMessage string + }{ + { + name: "good", + config: ` +server: localhost:8500 +username: 123 +password: 1234 +`, + expected: goodConfig, + }, + { + name: "username and password and basic auth configured", + config: ` +server: localhost:8500 +username: 123 +password: 1234 +basic_auth: + username: 12345 + password: 123456 +`, + errMessage: "at most one of consul SD configuration username and password and basic auth can be configured", + }, + { + name: "token and authorization configured", + config: ` +server: localhost:8500 +token: 1234567 +authorization: + credentials: 12345678 +`, + errMessage: "at most one of consul SD token, authorization, or oauth2 can be configured", + }, + { + name: "token and oauth2 configured", + config: ` +server: localhost:8500 +token: 1234567 +oauth2: + client_id: 10 + client_secret: 11 + token_url: http://example.com +`, + errMessage: "at most one of consul SD token, authorization, or oauth2 can be configured", + }, + } + + for _, test := range cases { + t.Run(test.name, func(t *testing.T) { + var config SDConfig + err := config.UnmarshalYAML(unmarshal([]byte(test.config))) + if err != nil { + require.Equalf(t, err.Error(), test.errMessage, "Expected error '%s', got '%v'", test.errMessage, err) + return + } + if test.errMessage != "" { + t.Errorf("Expected error %s, got none", test.errMessage) + return + } + + require.Equal(t, config, test.expected) + }) + } +} diff --git a/docs/configuration/configuration.md b/docs/configuration/configuration.md index 0036132dde..d96f624b9a 100644 --- a/docs/configuration/configuration.md +++ b/docs/configuration/configuration.md @@ -447,12 +447,10 @@ The following meta labels are available on targets during [relabeling](#relabel_ # Namespaces are only supported in Consul Enterprise. [ namespace: ] [ scheme: | default = "http" ] +# The username and password fields are deprecated in favor of the basic_auth configuration. [ username: ] [ password: ] -tls_config: - [ ] - # A list of services for which targets are retrieved. If omitted, all services # are scraped. services: @@ -478,6 +476,42 @@ tags: # The time after which the provided names are refreshed. # On large setup it might be a good idea to increase this value because the catalog will change all the time. [ refresh_interval: | default = 30s ] + +# Authentication information used to authenticate to the consul server. +# Note that `basic_auth`, `authorization` and `oauth2` options are +# mutually exclusive. +# `password` and `password_file` are mutually exclusive. + +# Optional HTTP basic authentication information. +basic_auth: + [ username: ] + [ password: ] + [ password_file: ] + +# Optional `Authorization` header configuration. +authorization: + # Sets the authentication type. + [ type: | default: Bearer ] + # Sets the credentials. It is mutually exclusive with + # `credentials_file`. + [ credentials: ] + # Sets the credentials to the credentials read from the configured file. + # It is mutually exclusive with `credentials`. + [ credentials_file: ] + +# Optional OAuth 2.0 configuration. +oauth2: + [ ] + +# Optional proxy URL. +[ proxy_url: ] + +# Configure whether HTTP requests follow HTTP 3xx redirects. +[ follow_redirects: | default = true ] + +# TLS configuration. +tls_config: + [ ] ``` Note that the IP number and port used to scrape the targets is assembled as