Skip to content

Commit de2ef5e

Browse files
authored
Cheery pick fix for issue #4888 (#4891)
* Cheery pick fix for issue #4888 Signed-off-by: Alvin Lin <[email protected]>
1 parent 223a127 commit de2ef5e

File tree

6 files changed

+123
-8
lines changed

6 files changed

+123
-8
lines changed

CHANGELOG.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Changelog
22

3-
## master / unreleased
4-
3+
## 1.13.1 2022-10-03
4+
[BUGFIX] AlertManager: fixed issue introduced by #4495 where templates files were being deleted when using alertmanager local store. #4890
55

66
## 1.13.0 2022-07-14
77
* [CHANGE] Changed default for `-ingester.min-ready-duration` from 1 minute to 15 seconds. #4539

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.13.0
1+
1.13.1

docs/chunks-storage/running-chunks-storage-with-cassandra.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,12 +111,12 @@ storage:
111111
```
112112

113113
The latest tag is not published for the Cortex docker image. Visit quay.io/repository/cortexproject/cortex
114-
to find the latest stable version tag and use it in the command below (currently it is `v1.11.0`).
114+
to find the latest stable version tag and use it in the command below (currently it is `v1.13.1`).
115115

116116
Run Cortex using the latest stable version:
117117

118118
```
119-
docker run -d --name=cortex -v $(pwd)/single-process-config.yaml:/etc/single-process-config.yaml -p 9009:9009 quay.io/cortexproject/cortex:v1.11.0 -config.file=/etc/single-process-config.yaml
119+
docker run -d --name=cortex -v $(pwd)/single-process-config.yaml:/etc/single-process-config.yaml -p 9009:9009 quay.io/cortexproject/cortex:v1.13.1 -config.file=/etc/single-process-config.yaml
120120
```
121121
In case you prefer to run the master version, please follow this [documentation](./chunks-storage-getting-started.md) on how to build Cortex from source.
122122

pkg/alertmanager/alertstore/local/store.go

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ import (
1515
)
1616

1717
const (
18-
Name = "local"
18+
Name = "local"
19+
templatesDir = "templates"
1920
)
2021

2122
var (
@@ -149,9 +150,42 @@ func (f *Store) reloadConfigs() (map[string]alertspb.AlertConfigDesc, error) {
149150
// The file name must correspond to the user tenant ID
150151
user := strings.TrimSuffix(info.Name(), ext)
151152

153+
// Load template files
154+
userTemplateDir := filepath.Join(f.cfg.Path, user, templatesDir)
155+
var templates []*alertspb.TemplateDesc
156+
157+
if _, e := os.Stat(userTemplateDir); e == nil {
158+
err = filepath.Walk(userTemplateDir, func(templatePath string, info os.FileInfo, err error) error {
159+
if err != nil {
160+
return errors.Wrapf(err, "unable to walk file path at %s", templatePath)
161+
}
162+
// Ignore files that are directories
163+
if info.IsDir() {
164+
return nil
165+
}
166+
content, err := os.ReadFile(templatePath)
167+
if err != nil {
168+
return errors.Wrapf(err, "unable to read alertmanager templates %s", templatePath)
169+
}
170+
171+
templates = append(templates, &alertspb.TemplateDesc{
172+
Body: string(content),
173+
Filename: info.Name(),
174+
})
175+
return nil
176+
})
177+
178+
if err != nil {
179+
return errors.Wrapf(err, "unable to list alertmanager templates: %s", userTemplateDir)
180+
}
181+
} else if !os.IsNotExist(e) {
182+
return errors.Wrapf(e, "unable to read alertmanager templates %s", path)
183+
}
184+
152185
configs[user] = alertspb.AlertConfigDesc{
153186
User: user,
154187
RawConfig: string(content),
188+
Templates: templates,
155189
}
156190
return nil
157191
})

pkg/alertmanager/alertstore/local/store_test.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,17 +82,22 @@ func TestStore_GetAlertConfigs(t *testing.T) {
8282
// The storage contains some configs.
8383
{
8484
user1Cfg := prepareAlertmanagerConfig("user-1")
85-
require.NoError(t, ioutil.WriteFile(filepath.Join(storeDir, "user-1.yaml"), []byte(user1Cfg), os.ModePerm))
85+
user1Dir, user1TemplateDir := prepareUserDir(t, storeDir, true, "user-1")
86+
require.NoError(t, os.WriteFile(filepath.Join(user1Dir, "user-1.yaml"), []byte(user1Cfg), os.ModePerm))
87+
88+
require.NoError(t, os.WriteFile(filepath.Join(user1TemplateDir, "template.tpl"), []byte("testTemplate"), os.ModePerm))
8689

8790
configs, err := store.GetAlertConfigs(ctx, []string{"user-1", "user-2"})
8891
require.NoError(t, err)
8992
assert.Contains(t, configs, "user-1")
9093
assert.NotContains(t, configs, "user-2")
9194
assert.Equal(t, user1Cfg, configs["user-1"].RawConfig)
95+
assert.Equal(t, "testTemplate", configs["user-1"].Templates[0].Body)
9296

9397
// Add another user config.
9498
user2Cfg := prepareAlertmanagerConfig("user-2")
95-
require.NoError(t, ioutil.WriteFile(filepath.Join(storeDir, "user-2.yaml"), []byte(user2Cfg), os.ModePerm))
99+
user2Dir, _ := prepareUserDir(t, storeDir, false, "user-2")
100+
require.NoError(t, os.WriteFile(filepath.Join(user2Dir, "user-2.yaml"), []byte(user2Cfg), os.ModePerm))
96101

97102
configs, err = store.GetAlertConfigs(ctx, []string{"user-1", "user-2"})
98103
require.NoError(t, err)
@@ -103,6 +108,16 @@ func TestStore_GetAlertConfigs(t *testing.T) {
103108
}
104109
}
105110

111+
func prepareUserDir(t *testing.T, storeDir string, createTemplateDir bool, user string) (userDir string, templateDir string) {
112+
userDir = filepath.Join(storeDir, user)
113+
templateDir = filepath.Join(userDir, templatesDir)
114+
require.NoError(t, os.MkdirAll(userDir, os.ModePerm))
115+
if createTemplateDir {
116+
require.NoError(t, os.MkdirAll(templateDir, os.ModePerm))
117+
}
118+
return
119+
}
120+
106121
func prepareLocalStore(t *testing.T) (store *Store, storeDir string) {
107122
var err error
108123

pkg/alertmanager/multitenant_test.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"os"
1515
"path/filepath"
1616
"regexp"
17+
"sort"
1718
"strings"
1819
"sync"
1920
"testing"
@@ -40,6 +41,7 @@ import (
4041
"github.com/cortexproject/cortex/pkg/alertmanager/alertspb"
4142
"github.com/cortexproject/cortex/pkg/alertmanager/alertstore"
4243
"github.com/cortexproject/cortex/pkg/alertmanager/alertstore/bucketclient"
44+
"github.com/cortexproject/cortex/pkg/alertmanager/alertstore/local"
4345
"github.com/cortexproject/cortex/pkg/ring"
4446
"github.com/cortexproject/cortex/pkg/ring/kv/consul"
4547
"github.com/cortexproject/cortex/pkg/storage/bucket"
@@ -166,6 +168,49 @@ func TestMultitenantAlertmanagerConfig_Validate(t *testing.T) {
166168
}
167169
}
168170

171+
func TestMultitenantAlertmanager_loadAndSyncConfigsLocalStorage(t *testing.T) {
172+
storeDir := t.TempDir()
173+
store, _ := local.NewStore(local.StoreConfig{Path: storeDir})
174+
config := `global:
175+
resolve_timeout: 1m
176+
smtp_require_tls: false
177+
178+
route:
179+
receiver: 'email'
180+
181+
receivers:
182+
- name: 'email'
183+
email_configs:
184+
185+
186+
smarthost: smtp:2525
187+
`
188+
user1Dir, user1TemplateDir := prepareUserDir(t, storeDir, "user-1")
189+
user2Dir, _ := prepareUserDir(t, storeDir, "user-2")
190+
require.NoError(t, os.WriteFile(filepath.Join(user1Dir, "user-1.yaml"), []byte(config), os.ModePerm))
191+
require.NoError(t, os.WriteFile(filepath.Join(user2Dir, "user-2.yaml"), []byte(config), os.ModePerm))
192+
require.NoError(t, os.WriteFile(filepath.Join(user1TemplateDir, "template.tpl"), []byte("testTemplate"), os.ModePerm))
193+
194+
originalFiles, err := listFiles(storeDir)
195+
require.NoError(t, err)
196+
require.Equal(t, 3, len(originalFiles))
197+
198+
cfg := mockAlertmanagerConfig(t)
199+
cfg.DataDir = storeDir
200+
reg := prometheus.NewPedanticRegistry()
201+
am, err := createMultitenantAlertmanager(cfg, nil, nil, store, nil, nil, log.NewNopLogger(), reg)
202+
require.NoError(t, err)
203+
for i := 0; i < 5; i++ {
204+
err = am.loadAndSyncConfigs(context.Background(), reasonPeriodic)
205+
require.NoError(t, err)
206+
require.Len(t, am.alertmanagers, 2)
207+
files, err := listFiles(storeDir)
208+
require.NoError(t, err)
209+
// Verify if the files were not deleted
210+
require.Equal(t, originalFiles, files)
211+
}
212+
}
213+
169214
func TestMultitenantAlertmanager_loadAndSyncConfigs(t *testing.T) {
170215
ctx := context.Background()
171216

@@ -1892,6 +1937,27 @@ func prepareInMemoryAlertStore() alertstore.AlertStore {
18921937
return bucketclient.NewBucketAlertStore(objstore.NewInMemBucket(), nil, log.NewNopLogger())
18931938
}
18941939

1940+
func prepareUserDir(t *testing.T, storeDir string, user string) (userDir string, templateDir string) {
1941+
userDir = filepath.Join(storeDir, user)
1942+
templateDir = filepath.Join(userDir, templatesDir)
1943+
require.NoError(t, os.MkdirAll(userDir, os.ModePerm))
1944+
require.NoError(t, os.MkdirAll(templateDir, os.ModePerm))
1945+
return
1946+
}
1947+
1948+
func listFiles(dir string) ([]string, error) {
1949+
var r []string
1950+
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
1951+
if !info.IsDir() {
1952+
r = append(r, path)
1953+
}
1954+
return nil
1955+
})
1956+
sort.Strings(r)
1957+
1958+
return r, err
1959+
}
1960+
18951961
func TestSafeTemplateFilepath(t *testing.T) {
18961962
tests := map[string]struct {
18971963
dir string

0 commit comments

Comments
 (0)