Skip to content
This repository has been archived by the owner on Jul 22, 2020. It is now read-only.

Add support for proxying user connection to Alertmanager #202

Merged
merged 8 commits into from
Jan 9, 2018
3 changes: 2 additions & 1 deletion internal/alertmanager/dedup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import (
func init() {
log.SetLevel(log.ErrorLevel)
for i, uri := range mock.ListAllMockURIs() {
alertmanager.NewAlertmanager(fmt.Sprintf("dedup-mock-%d", i), uri, time.Second)
name := fmt.Sprintf("dedup-mock-%d", i)
alertmanager.NewAlertmanager(name, uri, alertmanager.WithRequestTimeout(time.Second))
}
}

Expand Down
59 changes: 36 additions & 23 deletions internal/alertmanager/upstream.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,15 @@ import (
log "github.com/sirupsen/logrus"
)

// Option allows to pass functional options to NewAlertmanager()
type Option func(am *Alertmanager)

var (
upstreams = map[string]*Alertmanager{}
)

func newAlertmanager(name, uri string, timeout time.Duration, proxyRequests bool) error {
// NewAlertmanager creates a new Alertmanager instance
func NewAlertmanager(name, uri string, opts ...Option) error {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I'd argue this should be AddAlertmanager because while you do allocate you don't actually get the Alertmanager instance back.

For testing reasons (you're creating the Alertmanger manually in test) perhaps it should, and leave it up to the consumer to add it to the upstreams slice/type. I'll accept this as a separate PR though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'm refactoring this

if _, found := upstreams[name]; found {
return fmt.Errorf("Alertmanager upstream '%s' already exist", name)
}
Expand All @@ -25,16 +29,15 @@ func newAlertmanager(name, uri string, timeout time.Duration, proxyRequests bool
}
}

upstreams[name] = &Alertmanager{
URI: uri,
Timeout: timeout,
Name: name,
ProxyRequests: proxyRequests,
lock: sync.RWMutex{},
alertGroups: []models.AlertGroup{},
silences: map[string]models.Silence{},
colors: models.LabelsColorMap{},
autocomplete: []models.Autocomplete{},
am := &Alertmanager{
URI: uri,
Timeout: time.Second * 10,
Name: name,
lock: sync.RWMutex{},
alertGroups: []models.AlertGroup{},
silences: map[string]models.Silence{},
colors: models.LabelsColorMap{},
autocomplete: []models.Autocomplete{},
metrics: alertmanagerMetrics{
errors: map[string]float64{
labelValueErrorsAlerts: 0,
Expand All @@ -43,21 +46,15 @@ func newAlertmanager(name, uri string, timeout time.Duration, proxyRequests bool
},
}

log.Infof("[%s] Configured Alertmanager source at %s", name, uri)
for _, opt := range opts {
opt(am)
}

return nil
}
upstreams[name] = am

// NewAlertmanager creates a new Alertmanager instance, unsee clients will talk
// to directly to it without unsee proxying any request
func NewAlertmanager(name, uri string, timeout time.Duration) error {
return newAlertmanager(name, uri, timeout, false)
}
log.Infof("[%s] Configured Alertmanager source at %s", name, uri)

// NewProxiedAlertmanager creates a new proxied Alertmanager instance, unsee
// clients will talk to it via unsee
func NewProxiedAlertmanager(name, uri string, timeout time.Duration) error {
return newAlertmanager(name, uri, timeout, false)
return nil
}

// GetAlertmanagers returns a list of all defined Alertmanager instances
Expand All @@ -78,3 +75,19 @@ func GetAlertmanagerByName(name string) *Alertmanager {
}
return nil
}

// WithProxy option can be passed to NewAlertmanager in order to enable request
// proxying for unsee clients
func WithProxy(proxied bool) Option {
return func(am *Alertmanager) {
am.ProxyRequests = proxied
}
}

// WithRequestTimeout option can be passed to NewAlertmanager in order to set
// a custom timeout for Alertmanager upstream requests
func WithRequestTimeout(timeout time.Duration) Option {
return func(am *Alertmanager) {
am.Timeout = timeout

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason not to change the struct member name as well? Any reason to set a request timeout rather than using a context.WithTimeout?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. None, renamed
  2. I know about context but didn't really use it for anything so lack any experience and obvious patterns I could use it for

}
}
2 changes: 1 addition & 1 deletion internal/filters/filter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,7 @@ var tests = []filterTest{
func TestFilters(t *testing.T) {
log.SetLevel(log.ErrorLevel)

err := alertmanager.NewAlertmanager("test", "http://localhost", time.Second)
err := alertmanager.NewAlertmanager("test", "http://localhost", alertmanager.WithRequestTimeout(time.Second))
am := alertmanager.GetAlertmanagerByName("test")
if err != nil {
t.Error(err)
Expand Down
6 changes: 1 addition & 5 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,7 @@ func setupRouter(router *gin.Engine) {
func setupUpstreams() {
for _, s := range config.Config.Alertmanager.Servers {
var err error
if s.Proxy {
err = alertmanager.NewProxiedAlertmanager(s.Name, s.URI, s.Timeout)
} else {
err = alertmanager.NewAlertmanager(s.Name, s.URI, s.Timeout)
}
err = alertmanager.NewAlertmanager(s.Name, s.URI, alertmanager.WithRequestTimeout(s.Timeout), alertmanager.WithProxy(s.Proxy))
if err != nil {
log.Fatalf("Failed to configure Alertmanager '%s' with URI '%s': %s", s.Name, s.URI, err)
}
Expand Down