diff --git a/CHANGELOG.md b/CHANGELOG.md index d257e794d7f..1cadbe7ff48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,6 +60,7 @@ * [BUGFIX] Querier: fixed panic when querying exemplars and using `-distributor.shard-by-all-labels=false`. #4473 * [BUGFIX] Querier: honor querier minT,maxT if `nil` SelectHints are passed to Select(). #4413 * [BUGFIX] Compactor: fixed panic while collecting Prometheus metrics. #4483 +* [BUGFIX] AlertManager: remove stale template files. #4495 ## 1.10.0 / 2021-08-03 diff --git a/pkg/alertmanager/multitenant.go b/pkg/alertmanager/multitenant.go index d42b5a8e551..498c8724be9 100644 --- a/pkg/alertmanager/multitenant.go +++ b/pkg/alertmanager/multitenant.go @@ -825,14 +825,25 @@ func (am *MultitenantAlertmanager) setConfig(cfg alertspb.AlertConfigDesc) error var userAmConfig *amconfig.Config var err error var hasTemplateChanges bool + var userTemplateDir = filepath.Join(am.getTenantDirectory(cfg.User), templatesDir) + var pathsToRemove = make(map[string]struct{}) + + // List existing files to keep track the ones to be removed + if oldTemplateFiles, err := ioutil.ReadDir(userTemplateDir); err == nil { + for _, file := range oldTemplateFiles { + pathsToRemove[filepath.Join(userTemplateDir, file.Name())] = struct{}{} + } + } for _, tmpl := range cfg.Templates { - templateFilepath, err := safeTemplateFilepath(filepath.Join(am.getTenantDirectory(cfg.User), templatesDir), tmpl.Filename) + templateFilePath, err := safeTemplateFilepath(userTemplateDir, tmpl.Filename) if err != nil { return err } - hasChanged, err := storeTemplateFile(templateFilepath, tmpl.Body) + // Removing from pathsToRemove map the files that still exists in the config + delete(pathsToRemove, templateFilePath) + hasChanged, err := storeTemplateFile(templateFilePath, tmpl.Body) if err != nil { return err } @@ -842,6 +853,14 @@ func (am *MultitenantAlertmanager) setConfig(cfg alertspb.AlertConfigDesc) error } } + for pathToRemove := range pathsToRemove { + err := os.Remove(pathToRemove) + if err != nil { + level.Warn(am.logger).Log("msg", "failed to remove file", "file", pathToRemove, "err", err) + } + hasTemplateChanges = true + } + level.Debug(am.logger).Log("msg", "setting config", "user", cfg.User) am.alertmanagersMtx.Lock() diff --git a/pkg/alertmanager/multitenant_test.go b/pkg/alertmanager/multitenant_test.go index ba1ddc7d964..7e835dcd2c3 100644 --- a/pkg/alertmanager/multitenant_test.go +++ b/pkg/alertmanager/multitenant_test.go @@ -319,6 +319,23 @@ templates: cortex_alertmanager_config_last_reload_successful{user="user2"} 1 cortex_alertmanager_config_last_reload_successful{user="user3"} 1 `), "cortex_alertmanager_config_last_reload_successful")) + + // Removed template files should be cleaned up + user3Cfg.Templates = []*alertspb.TemplateDesc{ + { + Filename: "first.tpl", + Body: `{{ define "t1" }}Template 1 ... {{end}}`, + }, + } + + require.NoError(t, store.SetAlertConfig(ctx, user3Cfg)) + + err = am.loadAndSyncConfigs(context.Background(), reasonPeriodic) + require.NoError(t, err) + + require.True(t, dirExists(t, user3Dir)) + require.True(t, fileExists(t, filepath.Join(user3Dir, templatesDir, "first.tpl"))) + require.False(t, fileExists(t, filepath.Join(user3Dir, templatesDir, "second.tpl"))) } func TestMultitenantAlertmanager_FirewallShouldBlockHTTPBasedReceiversWhenEnabled(t *testing.T) {