Skip to content

Commit

Permalink
*: factorize HTTP client code into separate package (#1913)
Browse files Browse the repository at this point in the history
Signed-off-by: Simon Pasquier <[email protected]>
  • Loading branch information
simonpasquier authored and squat committed Dec 19, 2019
1 parent f5b7bac commit b32108b
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 84 deletions.
85 changes: 5 additions & 80 deletions pkg/alert/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package alert

import (
"context"
"fmt"
"io"
"net"
"net/http"
Expand All @@ -15,15 +14,14 @@ import (

"github.com/go-kit/kit/log"
"github.com/pkg/errors"
config_util "github.com/prometheus/common/config"
"github.com/prometheus/common/model"
"github.com/prometheus/common/version"
"github.com/prometheus/prometheus/discovery/file"
"github.com/prometheus/prometheus/discovery/targetgroup"
"gopkg.in/yaml.v2"

"github.com/thanos-io/thanos/pkg/discovery/cache"
"github.com/thanos-io/thanos/pkg/discovery/dns"
http_util "github.com/thanos-io/thanos/pkg/http"
"github.com/thanos-io/thanos/pkg/runutil"
)

Expand All @@ -33,8 +31,6 @@ const (
contentTypeJSON = "application/json"
)

var userAgent = fmt.Sprintf("Thanos/%s", version.Version)

type AlertingConfig struct {
Alertmanagers []AlertmanagerConfig `yaml:"alertmanagers"`
}
Expand All @@ -43,7 +39,7 @@ type AlertingConfig struct {
// TODO(simonpasquier): add support for API version (v1 or v2).
type AlertmanagerConfig struct {
// HTTP client configuration.
HTTPClientConfig HTTPClientConfig `yaml:"http_config"`
HTTPClientConfig http_util.ClientConfig `yaml:"http_config"`

// List of addresses with DNS prefixes.
StaticAddresses []string `yaml:"static_configs"`
Expand All @@ -60,72 +56,6 @@ type AlertmanagerConfig struct {
Timeout model.Duration `yaml:"timeout"`
}

type HTTPClientConfig struct {
// The HTTP basic authentication credentials for the targets.
BasicAuth BasicAuth `yaml:"basic_auth"`
// The bearer token for the targets.
BearerToken string `yaml:"bearer_token"`
// The bearer token file for the targets.
BearerTokenFile string `yaml:"bearer_token_file"`
// HTTP proxy server to use to connect to the targets.
ProxyURL string `yaml:"proxy_url"`
// TLSConfig to use to connect to the targets.
TLSConfig TLSConfig `yaml:"tls_config"`
}

type TLSConfig struct {
// The CA cert to use for the targets.
CAFile string `yaml:"ca_file"`
// The client cert file for the targets.
CertFile string `yaml:"cert_file"`
// The client key file for the targets.
KeyFile string `yaml:"key_file"`
// Used to verify the hostname for the targets.
ServerName string `yaml:"server_name"`
// Disable target certificate validation.
InsecureSkipVerify bool `yaml:"insecure_skip_verify"`
}

type BasicAuth struct {
Username string `yaml:"username"`
Password string `yaml:"password"`
PasswordFile string `yaml:"password_file"`
}

func (b BasicAuth) IsZero() bool {
return b.Username == "" && b.Password == "" && b.PasswordFile == ""
}

func (c HTTPClientConfig) convert() (config_util.HTTPClientConfig, error) {
httpClientConfig := config_util.HTTPClientConfig{
BearerToken: config_util.Secret(c.BearerToken),
BearerTokenFile: c.BearerTokenFile,
TLSConfig: config_util.TLSConfig{
CAFile: c.TLSConfig.CAFile,
CertFile: c.TLSConfig.CertFile,
KeyFile: c.TLSConfig.KeyFile,
ServerName: c.TLSConfig.ServerName,
InsecureSkipVerify: c.TLSConfig.InsecureSkipVerify,
},
}
if c.ProxyURL != "" {
var proxy config_util.URL
err := yaml.Unmarshal([]byte(c.ProxyURL), &proxy)
if err != nil {
return httpClientConfig, err
}
httpClientConfig.ProxyURL = proxy
}
if !c.BasicAuth.IsZero() {
httpClientConfig.BasicAuth = &config_util.BasicAuth{
Username: c.BasicAuth.Username,
Password: config_util.Secret(c.BasicAuth.Password),
PasswordFile: c.BasicAuth.PasswordFile,
}
}
return httpClientConfig, httpClientConfig.Validate()
}

type FileSDConfig struct {
Files []string `yaml:"files"`
RefreshInterval model.Duration `yaml:"refresh_interval"`
Expand Down Expand Up @@ -187,11 +117,7 @@ func NewAlertmanager(logger log.Logger, cfg AlertmanagerConfig, provider Address
logger = log.NewNopLogger()
}

httpClientConfig, err := cfg.HTTPClientConfig.convert()
if err != nil {
return nil, err
}
client, err := config_util.NewClientFromConfig(httpClientConfig, "alertmanager", false)
client, err := http_util.NewClient(cfg.HTTPClientConfig, "alertmanager")
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -250,7 +176,7 @@ func BuildAlertmanagerConfig(logger log.Logger, address string, timeout time.Dur
break
}
}
var basicAuth BasicAuth
var basicAuth http_util.BasicAuth
if parsed.User != nil && parsed.User.String() != "" {
basicAuth.Username = parsed.User.Username()
pw, _ := parsed.User.Password()
Expand All @@ -262,7 +188,7 @@ func BuildAlertmanagerConfig(logger log.Logger, address string, timeout time.Dur
Scheme: scheme,
StaticAddresses: []string{host},
Timeout: model.Duration(timeout),
HTTPClientConfig: HTTPClientConfig{
HTTPClientConfig: http_util.ClientConfig{
BasicAuth: basicAuth,
},
}, nil
Expand Down Expand Up @@ -293,7 +219,6 @@ func (a *Alertmanager) Do(ctx context.Context, u *url.URL, r io.Reader) error {
defer cancel()
req = req.WithContext(ctx)
req.Header.Set("Content-Type", contentTypeJSON)
req.Header.Set("User-Agent", userAgent)

resp, err := a.client.Do(req)
if err != nil {
Expand Down
5 changes: 3 additions & 2 deletions pkg/alert/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"testing"
"time"

"github.com/thanos-io/thanos/pkg/http"
"github.com/thanos-io/thanos/pkg/testutil"
)

Expand Down Expand Up @@ -60,8 +61,8 @@ func TestBuildAlertmanagerConfiguration(t *testing.T) {
{
address: "http://user:pass@localhost:9093",
expected: AlertmanagerConfig{
HTTPClientConfig: HTTPClientConfig{
BasicAuth: BasicAuth{
HTTPClientConfig: http.ClientConfig{
BasicAuth: http.BasicAuth{
Username: "user",
Password: "pass",
},
Expand Down
116 changes: 116 additions & 0 deletions pkg/http/http.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Package http is a wrapper around github.com/prometheus/common/config.
package http

import (
"fmt"
"net/http"

config_util "github.com/prometheus/common/config"
"github.com/prometheus/common/version"
"gopkg.in/yaml.v2"
)

// ClientConfig configures an HTTP client.
type ClientConfig struct {
// The HTTP basic authentication credentials for the targets.
BasicAuth BasicAuth `yaml:"basic_auth"`
// The bearer token for the targets.
BearerToken string `yaml:"bearer_token"`
// The bearer token file for the targets.
BearerTokenFile string `yaml:"bearer_token_file"`
// HTTP proxy server to use to connect to the targets.
ProxyURL string `yaml:"proxy_url"`
// TLSConfig to use to connect to the targets.
TLSConfig TLSConfig `yaml:"tls_config"`
}

// TLSConfig configures TLS connections.
type TLSConfig struct {
// The CA cert to use for the targets.
CAFile string `yaml:"ca_file"`
// The client cert file for the targets.
CertFile string `yaml:"cert_file"`
// The client key file for the targets.
KeyFile string `yaml:"key_file"`
// Used to verify the hostname for the targets.
ServerName string `yaml:"server_name"`
// Disable target certificate validation.
InsecureSkipVerify bool `yaml:"insecure_skip_verify"`
}

// BasicAuth configures basic authentication for HTTP clients.
type BasicAuth struct {
Username string `yaml:"username"`
Password string `yaml:"password"`
PasswordFile string `yaml:"password_file"`
}

// IsZero returns false if basic authentication isn't enabled.
func (b BasicAuth) IsZero() bool {
return b.Username == "" && b.Password == "" && b.PasswordFile == ""
}

// NewClient returns a new HTTP client.
func NewClient(cfg ClientConfig, name string) (*http.Client, error) {
httpClientConfig := config_util.HTTPClientConfig{
BearerToken: config_util.Secret(cfg.BearerToken),
BearerTokenFile: cfg.BearerTokenFile,
TLSConfig: config_util.TLSConfig{
CAFile: cfg.TLSConfig.CAFile,
CertFile: cfg.TLSConfig.CertFile,
KeyFile: cfg.TLSConfig.KeyFile,
ServerName: cfg.TLSConfig.ServerName,
InsecureSkipVerify: cfg.TLSConfig.InsecureSkipVerify,
},
}
if cfg.ProxyURL != "" {
var proxy config_util.URL
err := yaml.Unmarshal([]byte(cfg.ProxyURL), &proxy)
if err != nil {
return nil, err
}
httpClientConfig.ProxyURL = proxy
}
if !cfg.BasicAuth.IsZero() {
httpClientConfig.BasicAuth = &config_util.BasicAuth{
Username: cfg.BasicAuth.Username,
Password: config_util.Secret(cfg.BasicAuth.Password),
PasswordFile: cfg.BasicAuth.PasswordFile,
}
}
if err := httpClientConfig.Validate(); err != nil {
return nil, err
}

client, err := config_util.NewClientFromConfig(httpClientConfig, name, false)
if err != nil {
return nil, err
}
client.Transport = &userAgentRoundTripper{name: userAgent, rt: client.Transport}
return client, nil
}

var userAgent = fmt.Sprintf("Thanos/%s", version.Version)

type userAgentRoundTripper struct {
name string
rt http.RoundTripper
}

// RoundTrip implements the http.RoundTripper interface.
func (u userAgentRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) {
if r.UserAgent() == "" {
// The specification of http.RoundTripper says that it shouldn't mutate
// the request so make a copy of req.Header since this is all that is
// modified.
r2 := new(http.Request)
*r2 = *r
r2.Header = make(http.Header)
for k, s := range r.Header {
r2.Header[k] = s
}
r2.Header.Set("User-Agent", u.name)
r = r2
}
return u.rt.RoundTrip(r)
}
5 changes: 3 additions & 2 deletions test/e2e/rule_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
yaml "gopkg.in/yaml.v2"

"github.com/thanos-io/thanos/pkg/alert"
http_util "github.com/thanos-io/thanos/pkg/http"
"github.com/thanos-io/thanos/pkg/promclient"
rapi "github.com/thanos-io/thanos/pkg/rule/api"
"github.com/thanos-io/thanos/pkg/runutil"
Expand Down Expand Up @@ -200,8 +201,8 @@ func TestRuleAlertmanagerHTTPClient(t *testing.T) {
PathPrefix: "/prefix/",
},
alert.AlertmanagerConfig{
HTTPClientConfig: alert.HTTPClientConfig{
TLSConfig: alert.TLSConfig{
HTTPClientConfig: http_util.ClientConfig{
TLSConfig: http_util.TLSConfig{
CAFile: caFile,
},
BearerToken: "secret",
Expand Down

0 comments on commit b32108b

Please sign in to comment.