Skip to content

Commit

Permalink
initialize Alertmanagers on-demand
Browse files Browse the repository at this point in the history
  • Loading branch information
santihernandezc committed Jan 27, 2025
1 parent 91a8ea0 commit fa2f2d9
Showing 1 changed file with 58 additions and 1 deletion.
59 changes: 58 additions & 1 deletion pkg/alertmanager/multitenant.go
Original file line number Diff line number Diff line change
Expand Up @@ -739,7 +739,7 @@ func (am *MultitenantAlertmanager) computeConfig(cfgs alertspb.AlertConfigDescs)
if !ok {
return amConfig{}, false, nil
}
fmt.Printf("%s is receiving alerts!", cfgs.Mimir.User)
level.Debug(am.logger).Log("msg", "user has no usable config but is receiving alerts, starting Alertmanager", "user", cfgs.Mimir.User)
}

cfg := amConfig{
Expand Down Expand Up @@ -1028,6 +1028,16 @@ func (am *MultitenantAlertmanager) serveRequest(w http.ResponseWriter, req *http
am.lolMtx.Lock()
am.receivingAlerts[userID] = struct{}{}
am.lolMtx.Unlock()
userAM, err = am.startAlertmanager(req.Context(), userID)
if err != nil {
level.Error(am.logger).Log("msg", "unable to initialize the Alertmanager", "user", userID, "err", err)
http.Error(w, "Failed to initialize the Alertmanager", http.StatusInternalServerError)
return
}

level.Debug(am.logger).Log("msg", "alerts received, Alertmanager initialized", "user", userID, "err", err)
userAM.mux.ServeHTTP(w, req)
return
}

if am.fallbackConfig != "" {
Expand All @@ -1050,6 +1060,53 @@ func (am *MultitenantAlertmanager) serveRequest(w http.ResponseWriter, req *http
http.Error(w, "the Alertmanager is not configured", http.StatusPreconditionFailed)
}

func (am *MultitenantAlertmanager) startAlertmanager(ctx context.Context, userID string) (*Alertmanager, error) {
if !am.isUserOwned(userID) {
return nil, errors.Wrap(errNotUploadingFallback, "user not owned by this instance")
}

cfg, err := am.store.GetAlertConfig(ctx, userID)
if err != nil {
if !errors.Is(err, alertspb.ErrNotFound) {
return nil, errors.Wrap(err, "failed to check for existing configuration")
}

level.Warn(am.logger).Log("msg", "no configuration exists for user; uploading fallback configuration", "user", userID)

// Upload an empty config so that the Alertmanager is not de-activated in the next poll.
cfgDesc := alertspb.ToProto("", nil, userID)
err = am.store.SetAlertConfig(ctx, cfgDesc)
if err != nil {
return nil, err
}

// Calling setConfig with an empty configuration will use the fallback config.
amConfig := amConfig{
AlertConfigDesc: cfgDesc,
tmplExternalURL: am.cfg.ExternalURL.URL,
}
err = am.setConfig(amConfig)
if err != nil {
return nil, err
}

am.alertmanagersMtx.Lock()
defer am.alertmanagersMtx.Unlock()
return am.alertmanagers[userID], nil
}

amConfig := amConfig{
AlertConfigDesc: cfg,
tmplExternalURL: am.cfg.ExternalURL.URL,
}
if err := am.setConfig(amConfig); err != nil {
return nil, err
}
am.alertmanagersMtx.Lock()
defer am.alertmanagersMtx.Unlock()
return am.alertmanagers[userID], nil
}

func (am *MultitenantAlertmanager) alertmanagerFromFallbackConfig(ctx context.Context, userID string) (*Alertmanager, error) {
// Make sure we never create fallback instances for a user not owned by this instance.
// This check is not strictly necessary as the configuration polling loop will deactivate
Expand Down

0 comments on commit fa2f2d9

Please sign in to comment.