Skip to content

Commit 821630b

Browse files
gotjoshpracucci
andauthored
Fixes the Alertmanager panicking when no external URL is provided (#3017)
* Validate alertmanager.web.external-url Signed-off-by: gotjosh <[email protected]> * Improve http error messages when acessing an inactive/non-existent AM Signed-off-by: gotjosh <[email protected]> * Update the changelog. Signed-off-by: gotjosh <[email protected]> * Fix integration tests Signed-off-by: gotjosh <[email protected]> * Update CHANGELOG.md Co-authored-by: Marco Pracucci <[email protected]> Signed-off-by: gotjosh <[email protected]> * Update pkg/alertmanager/multitenant.go Co-authored-by: Marco Pracucci <[email protected]> Signed-off-by: gotjosh <[email protected]> * Update pkg/alertmanager/multitenant.go Co-authored-by: Marco Pracucci <[email protected]> Signed-off-by: gotjosh <[email protected]> * Address review comments Signed-off-by: gotjosh <[email protected]> Co-authored-by: Marco Pracucci <[email protected]>
1 parent 981b147 commit 821630b

File tree

5 files changed

+80
-2
lines changed

5 files changed

+80
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
* [BUGFIX] Querier: query /series from ingesters regardless the `-querier.query-ingesters-within` setting. #3035
2323
* [BUGFIX] Experimental blocks storage: Ingester is less likely to hit gRPC message size limit when streaming data to queriers. #3015
2424
* [BUGFIX] Fix configuration for TLS server validation, TLS skip verify was hardcoded to true for all TLS configurations and prevented validation of server certificates. #3030
25+
* [BUGFIX] Fixes the Alertmanager panicking when no `-alertmanager.web.external-url` is provided. #3017
2526

2627
## 1.3.0 / 2020-08-21
2728

integration/alertmanager_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,5 +106,5 @@ func TestAlertmanagerStoreAPI(t *testing.T) {
106106
cfg, err = c.GetAlertmanagerConfig(context.Background())
107107
require.Error(t, err)
108108
require.Nil(t, cfg)
109-
require.EqualError(t, err, e2ecortex.ErrNotFound.Error())
109+
require.EqualError(t, err, "not found")
110110
}

integration/e2ecortex/client.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,10 @@ func (c *Client) GetAlertmanagerConfig(ctx context.Context) (*alertConfig.Config
212212
return nil, ErrNotFound
213213
}
214214

215+
if resp.StatusCode/100 != 2 {
216+
return nil, fmt.Errorf("getting config failed with status %d and error %v", resp.StatusCode, string(body))
217+
}
218+
215219
var ss *ServerStatus
216220
err = json.Unmarshal(body, &ss)
217221
if err != nil {

pkg/alertmanager/multitenant.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,10 @@ func NewMultitenantAlertmanager(cfg *MultitenantAlertmanagerConfig, logger log.L
175175
return nil, fmt.Errorf("unable to create Alertmanager data directory %q: %s", cfg.DataDir, err)
176176
}
177177

178+
if cfg.ExternalURL.URL == nil {
179+
return nil, fmt.Errorf("unable to create Alertmanager because the external URL has not been configured")
180+
}
181+
178182
var fallbackConfig []byte
179183
if cfg.FallbackConfigFile != "" {
180184
fallbackConfig, err = ioutil.ReadFile(cfg.FallbackConfigFile)
@@ -460,9 +464,10 @@ func (am *MultitenantAlertmanager) ServeHTTP(w http.ResponseWriter, req *http.Re
460464
am.alertmanagersMtx.Unlock()
461465

462466
if !ok || !userAM.IsActive() {
463-
http.Error(w, "no Alertmanager for this user ID", http.StatusNotFound)
467+
http.Error(w, "the Alertmanager is not configured", http.StatusNotFound)
464468
return
465469
}
470+
466471
userAM.mux.ServeHTTP(w, req)
467472
}
468473

pkg/alertmanager/multitenant_test.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"context"
88
"fmt"
99
"io/ioutil"
10+
"net/http/httptest"
1011
"os"
1112
"testing"
1213

@@ -15,6 +16,7 @@ import (
1516
"github.com/prometheus/client_golang/prometheus/testutil"
1617
"github.com/stretchr/testify/assert"
1718
"github.com/stretchr/testify/require"
19+
"github.com/weaveworks/common/user"
1820

1921
"github.com/cortexproject/cortex/pkg/alertmanager/alerts"
2022
"github.com/cortexproject/cortex/pkg/util/flagext"
@@ -177,3 +179,69 @@ func TestLoadAllConfigs(t *testing.T) {
177179
cortex_alertmanager_config_invalid{user="user3"} 0
178180
`), "cortex_alertmanager_config_invalid"))
179181
}
182+
183+
func TestAlertmanager_NoExternalURL(t *testing.T) {
184+
tempDir, err := ioutil.TempDir(os.TempDir(), "alertmanager")
185+
require.NoError(t, err)
186+
defer os.RemoveAll(tempDir)
187+
188+
// Create the Multitenant Alertmanager.
189+
reg := prometheus.NewPedanticRegistry()
190+
_, err = NewMultitenantAlertmanager(&MultitenantAlertmanagerConfig{
191+
DataDir: tempDir,
192+
}, log.NewNopLogger(), reg)
193+
194+
require.EqualError(t, err, "unable to create Alertmanager because the external URL has not been configured")
195+
}
196+
197+
func TestAlertmanager_ServeHTTP(t *testing.T) {
198+
mockStore := &mockAlertStore{
199+
configs: map[string]alerts.AlertConfigDesc{},
200+
}
201+
202+
externalURL := flagext.URLValue{}
203+
err := externalURL.Set("http://localhost:8080/alertmanager")
204+
require.NoError(t, err)
205+
206+
tempDir, err := ioutil.TempDir(os.TempDir(), "alertmanager")
207+
require.NoError(t, err)
208+
defer os.RemoveAll(tempDir)
209+
210+
// Create the Multitenant Alertmanager.
211+
reg := prometheus.NewPedanticRegistry()
212+
am := createMultitenantAlertmanager(&MultitenantAlertmanagerConfig{
213+
ExternalURL: externalURL,
214+
DataDir: tempDir,
215+
}, nil, nil, mockStore, log.NewNopLogger(), reg)
216+
217+
// Request when no user configuration is present.
218+
req := httptest.NewRequest("GET", externalURL.String(), nil)
219+
req.Header.Add(user.OrgIDHeaderName, "user1")
220+
w := httptest.NewRecorder()
221+
222+
am.ServeHTTP(w, req)
223+
224+
resp := w.Result()
225+
body, _ := ioutil.ReadAll(resp.Body)
226+
require.Equal(t, "the Alertmanager is not configured\n", string(body))
227+
228+
// Create a configuration for the user in storage.
229+
mockStore.configs["user1"] = alerts.AlertConfigDesc{
230+
User: "user1",
231+
RawConfig: simpleConfigTwo,
232+
Templates: []*alerts.TemplateDesc{},
233+
}
234+
235+
// Make the alertmanager pick it up, then pause it.
236+
err = am.updateConfigs()
237+
require.NoError(t, err)
238+
am.alertmanagers["user1"].Pause()
239+
240+
// Request when user configuration is paused.
241+
w = httptest.NewRecorder()
242+
am.ServeHTTP(w, req)
243+
244+
resp = w.Result()
245+
body, _ = ioutil.ReadAll(resp.Body)
246+
require.Equal(t, "the Alertmanager is not configured\n", string(body))
247+
}

0 commit comments

Comments
 (0)