diff --git a/internal/alertmanager/dedup_test.go b/internal/alertmanager/dedup_test.go index 1d7e6443..59e6484b 100644 --- a/internal/alertmanager/dedup_test.go +++ b/internal/alertmanager/dedup_test.go @@ -17,7 +17,10 @@ func init() { log.SetLevel(log.ErrorLevel) for i, uri := range mock.ListAllMockURIs() { name := fmt.Sprintf("dedup-mock-%d", i) - am := alertmanager.NewAlertmanager(name, uri, alertmanager.WithRequestTimeout(time.Second)) + am, err := alertmanager.NewAlertmanager(name, uri, alertmanager.WithRequestTimeout(time.Second)) + if err != nil { + log.Fatal(err) + } alertmanager.RegisterAlertmanager(am) } } diff --git a/internal/alertmanager/models.go b/internal/alertmanager/models.go index ce539911..9bf41f6a 100644 --- a/internal/alertmanager/models.go +++ b/internal/alertmanager/models.go @@ -1,6 +1,7 @@ package alertmanager import ( + "encoding/json" "fmt" "path" "sort" @@ -34,6 +35,8 @@ type Alertmanager struct { Name string `json:"name"` // whenever this instance should be proxied ProxyRequests bool + // transport instances are specific to URI scheme we collect from + transport transport.Transport // lock protects data access while updating lock sync.RWMutex // fields for storing pulled data @@ -56,9 +59,19 @@ func (am *Alertmanager) detectVersion() string { return defaultVersion } ver := alertmanagerVersion{} - err = transport.ReadJSON(url, am.RequestTimeout, &ver) + + // read raw body from the source + source, err := am.transport.Read(url) + defer source.Close() + if err != nil { + log.Errorf("[%s] %s request failed: %s", am.Name, url, err) + return defaultVersion + } + + // decode body as JSON + err = json.NewDecoder(source).Decode(&ver) if err != nil { - log.Errorf("[%s] %s request failed: %s", am.Name, url, err.Error()) + log.Errorf("[%s] %s failed to decode as JSON: %s", am.Name, url, err) return defaultVersion } @@ -91,8 +104,24 @@ func (am *Alertmanager) pullSilences(version string) error { return err } + // generate full URL to collect silences from + url, err := mapper.AbsoluteURL(am.URI) + if err != nil { + log.Errorf("[%s] Failed to generate silences endpoint URL: %s", am.Name, err) + return err + } + start := time.Now() - silences, err := mapper.GetSilences(am.URI, am.RequestTimeout) + // read raw body from the source + source, err := am.transport.Read(url) + defer source.Close() + if err != nil { + log.Errorf("[%s] %s request failed: %s", am.Name, url, err) + return err + } + + // decode body text + silences, err := mapper.Decode(source) if err != nil { return err } @@ -134,8 +163,24 @@ func (am *Alertmanager) pullAlerts(version string) error { return err } + // generate full URL to collect alerts from + url, err := mapper.AbsoluteURL(am.URI) + if err != nil { + log.Errorf("[%s] Failed to generate alerts endpoint URL: %s", am.Name, err) + return err + } + start := time.Now() - groups, err := mapper.GetAlerts(am.URI, am.RequestTimeout) + // read raw body from the source + source, err := am.transport.Read(url) + defer source.Close() + if err != nil { + log.Errorf("[%s] %s request failed: %s", am.Name, url, err) + return err + } + + // decode body text + groups, err := mapper.Decode(source) if err != nil { return err } diff --git a/internal/alertmanager/upstream.go b/internal/alertmanager/upstream.go index 285b973c..a6701881 100644 --- a/internal/alertmanager/upstream.go +++ b/internal/alertmanager/upstream.go @@ -6,6 +6,7 @@ import ( "time" "github.com/cloudflare/unsee/internal/models" + "github.com/cloudflare/unsee/internal/transport" log "github.com/sirupsen/logrus" ) @@ -18,7 +19,7 @@ var ( ) // NewAlertmanager creates a new Alertmanager instance -func NewAlertmanager(name, uri string, opts ...Option) *Alertmanager { +func NewAlertmanager(name, uri string, opts ...Option) (*Alertmanager, error) { am := &Alertmanager{ URI: uri, RequestTimeout: time.Second * 10, @@ -40,7 +41,13 @@ func NewAlertmanager(name, uri string, opts ...Option) *Alertmanager { opt(am) } - return am + var err error + am.transport, err = transport.NewTransport(am.URI, am.RequestTimeout) + if err != nil { + return am, err + } + + return am, nil } // RegisterAlertmanager will add an Alertmanager instance to the list of diff --git a/internal/alertmanager/version.go b/internal/alertmanager/version.go index 0ff58e26..0fd021d6 100644 --- a/internal/alertmanager/version.go +++ b/internal/alertmanager/version.go @@ -1,6 +1,7 @@ package alertmanager import ( + "encoding/json" "time" "github.com/cloudflare/unsee/internal/transport" @@ -30,7 +31,15 @@ func GetVersion(uri string, timeout time.Duration) string { return defaultVersion } ver := alertmanagerVersion{} - err = transport.ReadJSON(url, timeout, &ver) + + t, err := transport.NewTransport(uri, timeout) + if err != nil { + log.Errorf("Unable to get the version information from %s", url) + return defaultVersion + } + + source, err := t.Read(url) + err = json.NewDecoder(source).Decode(&ver) if err != nil { log.Errorf("%s request failed: %s", url, err.Error()) return defaultVersion diff --git a/internal/filters/filter_test.go b/internal/filters/filter_test.go index c329afb2..3b7f8547 100644 --- a/internal/filters/filter_test.go +++ b/internal/filters/filter_test.go @@ -485,7 +485,10 @@ var tests = []filterTest{ func TestFilters(t *testing.T) { log.SetLevel(log.ErrorLevel) - am := alertmanager.NewAlertmanager("test", "http://localhost", alertmanager.WithRequestTimeout(time.Second)) + am, err := alertmanager.NewAlertmanager("test", "http://localhost", alertmanager.WithRequestTimeout(time.Second)) + if err != nil { + t.Error(err) + } for _, ft := range tests { alert := models.Alert(ft.Alert) if &ft.Silence != nil { diff --git a/main.go b/main.go index b517c54d..50f8fc2c 100644 --- a/main.go +++ b/main.go @@ -60,10 +60,13 @@ func setupRouter(router *gin.Engine) { func setupUpstreams() { for _, s := range config.Config.Alertmanager.Servers { - am := alertmanager.NewAlertmanager(s.Name, s.URI, alertmanager.WithRequestTimeout(s.Timeout), alertmanager.WithProxy(s.Proxy)) - err := alertmanager.RegisterAlertmanager(am) + am, 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) + log.Fatalf("Failed to create Alertmanager '%s' with URI '%s': %s", s.Name, s.URI, err) + } + err = alertmanager.RegisterAlertmanager(am) + if err != nil { + log.Fatalf("Failed to register Alertmanager '%s' with URI '%s': %s", s.Name, s.URI, err) } } } diff --git a/proxy_test.go b/proxy_test.go index 18ed8c9f..330bcc27 100644 --- a/proxy_test.go +++ b/proxy_test.go @@ -90,12 +90,15 @@ var proxyTests = []proxyTest{ func TestProxy(t *testing.T) { r := ginTestEngine() - am := alertmanager.NewAlertmanager( + am, err := alertmanager.NewAlertmanager( "dummy", "http://localhost:9093", alertmanager.WithRequestTimeout(time.Second*5), alertmanager.WithProxy(true), ) + if err != nil { + t.Error(err) + } setupRouterProxyHandlers(r, am) httpmock.Activate()