1// Copyright (c) The Thanos Authors.
2// Licensed under the Apache License 2.0.
3
4package alert
5
6import (
7	"net"
8	"net/url"
9	"strconv"
10	"strings"
11	"time"
12
13	"github.com/pkg/errors"
14	"github.com/prometheus/common/model"
15	"gopkg.in/yaml.v2"
16
17	"github.com/thanos-io/thanos/pkg/discovery/dns"
18	http_util "github.com/thanos-io/thanos/pkg/http"
19)
20
21type AlertingConfig struct {
22	Alertmanagers []AlertmanagerConfig `yaml:"alertmanagers"`
23}
24
25// AlertmanagerConfig represents a client to a cluster of Alertmanager endpoints.
26type AlertmanagerConfig struct {
27	HTTPClientConfig http_util.ClientConfig    `yaml:"http_config"`
28	EndpointsConfig  http_util.EndpointsConfig `yaml:",inline"`
29	Timeout          model.Duration            `yaml:"timeout"`
30	APIVersion       APIVersion                `yaml:"api_version"`
31}
32
33// APIVersion represents the API version of the Alertmanager endpoint.
34type APIVersion string
35
36const (
37	APIv1 APIVersion = "v1"
38	APIv2 APIVersion = "v2"
39)
40
41var supportedAPIVersions = []APIVersion{
42	APIv1, APIv2,
43}
44
45// UnmarshalYAML implements the yaml.Unmarshaler interface.
46func (v *APIVersion) UnmarshalYAML(unmarshal func(interface{}) error) error {
47	var s string
48	if err := unmarshal(&s); err != nil {
49		return errors.Wrap(err, "invalid Alertmanager API version")
50	}
51
52	for _, ver := range supportedAPIVersions {
53		if APIVersion(s) == ver {
54			*v = ver
55			return nil
56		}
57	}
58	return errors.Errorf("expected Alertmanager API version to be one of %v but got %q", supportedAPIVersions, s)
59}
60
61func DefaultAlertmanagerConfig() AlertmanagerConfig {
62	return AlertmanagerConfig{
63		EndpointsConfig: http_util.EndpointsConfig{
64			Scheme:          "http",
65			StaticAddresses: []string{},
66			FileSDConfigs:   []http_util.FileSDConfig{},
67		},
68		Timeout:    model.Duration(time.Second * 10),
69		APIVersion: APIv1,
70	}
71}
72
73// UnmarshalYAML implements the yaml.Unmarshaler interface.
74func (c *AlertmanagerConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
75	*c = DefaultAlertmanagerConfig()
76	type plain AlertmanagerConfig
77	return unmarshal((*plain)(c))
78}
79
80// LoadAlertingConfig loads a list of AlertmanagerConfig from YAML data.
81func LoadAlertingConfig(confYaml []byte) (AlertingConfig, error) {
82	var cfg AlertingConfig
83	if err := yaml.UnmarshalStrict(confYaml, &cfg); err != nil {
84		return cfg, err
85	}
86	return cfg, nil
87}
88
89// BuildAlertmanagerConfig initializes and returns an Alertmanager client configuration from a static address.
90func BuildAlertmanagerConfig(address string, timeout time.Duration) (AlertmanagerConfig, error) {
91	parsed, err := url.Parse(address)
92	if err != nil {
93		return AlertmanagerConfig{}, err
94	}
95
96	scheme := parsed.Scheme
97	host := parsed.Host
98	for _, qType := range []dns.QType{dns.A, dns.SRV, dns.SRVNoA} {
99		prefix := string(qType) + "+"
100		if strings.HasPrefix(strings.ToLower(scheme), prefix) {
101			// Scheme is of the form "<dns type>+<http scheme>".
102			scheme = strings.TrimPrefix(scheme, prefix)
103			host = prefix + parsed.Host
104			if qType == dns.A {
105				if _, _, err := net.SplitHostPort(parsed.Host); err != nil {
106					// The host port could be missing. Append the defaultAlertmanagerPort.
107					host = host + ":" + strconv.Itoa(defaultAlertmanagerPort)
108				}
109			}
110			break
111		}
112	}
113	var basicAuth http_util.BasicAuth
114	if parsed.User != nil && parsed.User.String() != "" {
115		basicAuth.Username = parsed.User.Username()
116		pw, _ := parsed.User.Password()
117		basicAuth.Password = pw
118	}
119
120	return AlertmanagerConfig{
121		HTTPClientConfig: http_util.ClientConfig{
122			BasicAuth: basicAuth,
123		},
124		EndpointsConfig: http_util.EndpointsConfig{
125			PathPrefix:      parsed.Path,
126			Scheme:          scheme,
127			StaticAddresses: []string{host},
128		},
129		Timeout:    model.Duration(timeout),
130		APIVersion: APIv1,
131	}, nil
132}
133