-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
packaging alertmanager sets and add a tests.
Signed-off-by: johncming <[email protected]>
- Loading branch information
Showing
4 changed files
with
235 additions
and
187 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,8 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"net/url" | ||
"testing" | ||
|
||
"github.com/pkg/errors" | ||
"github.com/thanos-io/thanos/pkg/discovery/dns" | ||
"github.com/thanos-io/thanos/pkg/testutil" | ||
) | ||
|
||
|
@@ -49,97 +45,3 @@ func Test_parseFlagLabels(t *testing.T) { | |
testutil.Equals(t, err != nil, td.expectErr) | ||
} | ||
} | ||
|
||
func TestRule_AlertmanagerResolveWithoutPort(t *testing.T) { | ||
mockResolver := mockResolver{ | ||
resultIPs: map[string][]string{ | ||
"alertmanager.com:9093": {"1.1.1.1:9300"}, | ||
}, | ||
} | ||
am := alertmanagerSet{resolver: mockResolver, addrs: []string{"dns+http://alertmanager.com"}} | ||
|
||
ctx := context.TODO() | ||
err := am.update(ctx) | ||
testutil.Ok(t, err) | ||
|
||
expected := []*url.URL{ | ||
{ | ||
Scheme: "http", | ||
Host: "1.1.1.1:9300", | ||
}, | ||
} | ||
gotURLs := am.get() | ||
testutil.Equals(t, expected, gotURLs) | ||
} | ||
|
||
func TestRule_AlertmanagerResolveWithPort(t *testing.T) { | ||
mockResolver := mockResolver{ | ||
resultIPs: map[string][]string{ | ||
"alertmanager.com:19093": {"1.1.1.1:9300"}, | ||
}, | ||
} | ||
am := alertmanagerSet{resolver: mockResolver, addrs: []string{"dns+http://alertmanager.com:19093"}} | ||
|
||
ctx := context.TODO() | ||
err := am.update(ctx) | ||
testutil.Ok(t, err) | ||
|
||
expected := []*url.URL{ | ||
{ | ||
Scheme: "http", | ||
Host: "1.1.1.1:9300", | ||
}, | ||
} | ||
gotURLs := am.get() | ||
testutil.Equals(t, expected, gotURLs) | ||
} | ||
|
||
type mockResolver struct { | ||
resultIPs map[string][]string | ||
err error | ||
} | ||
|
||
func (m mockResolver) Resolve(ctx context.Context, name string, qtype dns.QType) ([]string, error) { | ||
if m.err != nil { | ||
return nil, m.err | ||
} | ||
if res, ok := m.resultIPs[name]; ok { | ||
return res, nil | ||
} | ||
return nil, errors.Errorf("mockResolver not found response for name: %s", name) | ||
} | ||
|
||
func Test_ParseAlertmanagerAddress(t *testing.T) { | ||
var tData = []struct { | ||
address string | ||
expectQueryType dns.QType | ||
expectUrl *url.URL | ||
expectError error | ||
}{ | ||
{ | ||
address: "http://user:[email protected]:3289", | ||
expectQueryType: dns.QType(""), | ||
expectUrl: &url.URL{Host: "foo.bar:3289", Scheme: "http", User: url.UserPassword("user", "pass+word")}, | ||
expectError: nil, | ||
}, | ||
{ | ||
address: "dnssrvnoa+http://user:[email protected]:3289", | ||
expectQueryType: dns.QType("dnssrvnoa"), | ||
expectUrl: &url.URL{Host: "foo.bar:3289", Scheme: "http", User: url.UserPassword("user", "pass+word")}, | ||
expectError: nil, | ||
}, | ||
{ | ||
address: "foo+bar+http://foo.bar:3289", | ||
expectQueryType: dns.QType("foo+bar"), | ||
expectUrl: &url.URL{Host: "foo.bar:3289", Scheme: "http"}, | ||
expectError: nil, | ||
}, | ||
} | ||
|
||
for _, d := range tData { | ||
q, u, e := parseAlertmanagerAddress(d.address) | ||
testutil.Equals(t, d.expectError, e) | ||
testutil.Equals(t, d.expectUrl, u) | ||
testutil.Equals(t, d.expectQueryType, q) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
package alertmanager | ||
|
||
import ( | ||
"context" | ||
"net" | ||
"net/url" | ||
"strconv" | ||
"strings" | ||
"sync" | ||
|
||
"github.com/go-kit/kit/log" | ||
"github.com/pkg/errors" | ||
"github.com/thanos-io/thanos/pkg/discovery/dns" | ||
) | ||
|
||
const ( | ||
defaultAlertmanagerPort = 9093 | ||
) | ||
|
||
// Alertmanager replica URLs to push firing alerts. Ruler claims success if | ||
// push to at least one alertmanager from discovered succeeds. The scheme | ||
//should not be empty e.g `http` might be used. The scheme may be prefixed | ||
//with 'dns+' or 'dnssrv+' to detect Alertmanager IPs through respective | ||
//DNS lookups. The port defaults to 9093 or the SRV record's value. | ||
//The URL path is used as a prefix for the regular Alertmanager API path. | ||
type AlertManager interface { | ||
// Gets the address of the configured alertmanager | ||
Get() []*url.URL | ||
|
||
// Update and parse the raw url | ||
Update(ctx context.Context) error | ||
} | ||
|
||
type alertmanagerSet struct { | ||
resolver dns.Resolver | ||
addrs []string | ||
mtx sync.Mutex | ||
current []*url.URL | ||
} | ||
|
||
func NewAlertmanagerSet(logger log.Logger, addrs []string, dnsSDResolver dns.ResolverType) *alertmanagerSet { | ||
return &alertmanagerSet{ | ||
resolver: dns.NewResolver(dnsSDResolver.ToResolver(logger)), | ||
addrs: addrs, | ||
} | ||
} | ||
|
||
// Gets the address of the configured alertmanager | ||
func (s *alertmanagerSet) Get() []*url.URL { | ||
s.mtx.Lock() | ||
defer s.mtx.Unlock() | ||
|
||
return s.current | ||
} | ||
|
||
// Update and parse the raw url | ||
func (s *alertmanagerSet) Update(ctx context.Context) error { | ||
var result []*url.URL | ||
for _, addr := range s.addrs { | ||
var ( | ||
qtype dns.QType | ||
resolvedHosts []string | ||
) | ||
|
||
qtype, u, err := parseAlertmanagerAddress(addr) | ||
if err != nil { | ||
return errors.Wrapf(err, "parse URL %q", addr) | ||
} | ||
|
||
// Get only the host and resolve it if needed. | ||
host := u.Host | ||
if qtype != "" { | ||
if qtype == dns.A { | ||
_, _, err = net.SplitHostPort(host) | ||
if err != nil { | ||
// The host could be missing a port. Append the defaultAlertmanagerPort. | ||
host = host + ":" + strconv.Itoa(defaultAlertmanagerPort) | ||
} | ||
} | ||
resolvedHosts, err = s.resolver.Resolve(ctx, host, qtype) | ||
if err != nil { | ||
return errors.Wrap(err, "alertmanager resolve") | ||
} | ||
} else { | ||
resolvedHosts = []string{host} | ||
} | ||
|
||
for _, host := range resolvedHosts { | ||
result = append(result, &url.URL{ | ||
Scheme: u.Scheme, | ||
Host: host, | ||
Path: u.Path, | ||
User: u.User, | ||
}) | ||
} | ||
} | ||
|
||
s.mtx.Lock() | ||
s.current = result | ||
s.mtx.Unlock() | ||
|
||
return nil | ||
} | ||
|
||
func parseAlertmanagerAddress(addr string) (qType dns.QType, parsedUrl *url.URL, err error) { | ||
qType = "" | ||
parsedUrl, err = url.Parse(addr) | ||
if err != nil { | ||
return qType, nil, err | ||
} | ||
|
||
// The Scheme might contain DNS resolver type separated by + so we split it a part. | ||
if schemeParts := strings.Split(parsedUrl.Scheme, "+"); len(schemeParts) > 1 { | ||
parsedUrl.Scheme = schemeParts[len(schemeParts)-1] | ||
qType = dns.QType(strings.Join(schemeParts[:len(schemeParts)-1], "+")) | ||
} | ||
|
||
switch parsedUrl.Scheme { | ||
case "http", "https": | ||
case "": | ||
return "", nil, errors.New("The scheme should not be empty, e.g `http` or `https`") | ||
default: | ||
return "", nil, errors.New("Scheme should be `http` or `https`") | ||
} | ||
|
||
return qType, parsedUrl, err | ||
} |
Oops, something went wrong.