github.com/muhammadn/cortex@v1.9.1-0.20220510110439-46bb7000d03d/pkg/alertmanager/alertstore/store_test.go (about) 1 package alertstore 2 3 import ( 4 "context" 5 "errors" 6 "testing" 7 8 "github.com/go-kit/log" 9 "github.com/prometheus/alertmanager/cluster/clusterpb" 10 "github.com/stretchr/testify/assert" 11 "github.com/stretchr/testify/require" 12 "github.com/thanos-io/thanos/pkg/objstore" 13 14 "github.com/cortexproject/cortex/pkg/alertmanager/alertspb" 15 "github.com/cortexproject/cortex/pkg/alertmanager/alertstore/bucketclient" 16 "github.com/cortexproject/cortex/pkg/alertmanager/alertstore/objectclient" 17 "github.com/cortexproject/cortex/pkg/chunk" 18 ) 19 20 func TestAlertStore_ListAllUsers(t *testing.T) { 21 runForEachAlertStore(t, func(t *testing.T, store AlertStore, client interface{}) { 22 ctx := context.Background() 23 user1Cfg := alertspb.AlertConfigDesc{User: "user-1", RawConfig: "content-1"} 24 user2Cfg := alertspb.AlertConfigDesc{User: "user-2", RawConfig: "content-2"} 25 26 // The storage is empty. 27 { 28 users, err := store.ListAllUsers(ctx) 29 require.NoError(t, err) 30 assert.Empty(t, users) 31 } 32 33 // The storage contains users. 34 { 35 require.NoError(t, store.SetAlertConfig(ctx, user1Cfg)) 36 require.NoError(t, store.SetAlertConfig(ctx, user2Cfg)) 37 38 users, err := store.ListAllUsers(ctx) 39 require.NoError(t, err) 40 assert.ElementsMatch(t, []string{"user-1", "user-2"}, users) 41 } 42 }) 43 } 44 45 func TestAlertStore_SetAndGetAlertConfig(t *testing.T) { 46 runForEachAlertStore(t, func(t *testing.T, store AlertStore, client interface{}) { 47 ctx := context.Background() 48 user1Cfg := alertspb.AlertConfigDesc{User: "user-1", RawConfig: "content-1"} 49 user2Cfg := alertspb.AlertConfigDesc{User: "user-2", RawConfig: "content-2"} 50 51 // The user has no config. 52 { 53 _, err := store.GetAlertConfig(ctx, "user-1") 54 assert.Equal(t, alertspb.ErrNotFound, err) 55 } 56 57 // The user has a config 58 { 59 require.NoError(t, store.SetAlertConfig(ctx, user1Cfg)) 60 require.NoError(t, store.SetAlertConfig(ctx, user2Cfg)) 61 62 config, err := store.GetAlertConfig(ctx, "user-1") 63 require.NoError(t, err) 64 assert.Equal(t, user1Cfg, config) 65 66 config, err = store.GetAlertConfig(ctx, "user-2") 67 require.NoError(t, err) 68 assert.Equal(t, user2Cfg, config) 69 70 // Ensure the config is stored at the expected location. Without this check 71 // we have no guarantee that the objects are stored at the expected location. 72 exists, err := objectExists(client, "alerts/user-1") 73 require.NoError(t, err) 74 assert.True(t, exists) 75 76 exists, err = objectExists(client, "alerts/user-2") 77 require.NoError(t, err) 78 assert.True(t, exists) 79 } 80 }) 81 } 82 83 func TestStore_GetAlertConfigs(t *testing.T) { 84 runForEachAlertStore(t, func(t *testing.T, store AlertStore, client interface{}) { 85 ctx := context.Background() 86 user1Cfg := alertspb.AlertConfigDesc{User: "user-1", RawConfig: "content-1"} 87 user2Cfg := alertspb.AlertConfigDesc{User: "user-2", RawConfig: "content-2"} 88 89 // The storage is empty. 90 { 91 configs, err := store.GetAlertConfigs(ctx, []string{"user-1", "user-2"}) 92 require.NoError(t, err) 93 assert.Empty(t, configs) 94 } 95 96 // The storage contains some configs. 97 { 98 require.NoError(t, store.SetAlertConfig(ctx, user1Cfg)) 99 100 configs, err := store.GetAlertConfigs(ctx, []string{"user-1", "user-2"}) 101 require.NoError(t, err) 102 assert.Contains(t, configs, "user-1") 103 assert.NotContains(t, configs, "user-2") 104 assert.Equal(t, user1Cfg, configs["user-1"]) 105 106 // Add another user config. 107 require.NoError(t, store.SetAlertConfig(ctx, user2Cfg)) 108 109 configs, err = store.GetAlertConfigs(ctx, []string{"user-1", "user-2"}) 110 require.NoError(t, err) 111 assert.Contains(t, configs, "user-1") 112 assert.Contains(t, configs, "user-2") 113 assert.Equal(t, user1Cfg, configs["user-1"]) 114 assert.Equal(t, user2Cfg, configs["user-2"]) 115 } 116 }) 117 } 118 119 func TestAlertStore_DeleteAlertConfig(t *testing.T) { 120 runForEachAlertStore(t, func(t *testing.T, store AlertStore, client interface{}) { 121 ctx := context.Background() 122 user1Cfg := alertspb.AlertConfigDesc{User: "user-1", RawConfig: "content-1"} 123 user2Cfg := alertspb.AlertConfigDesc{User: "user-2", RawConfig: "content-2"} 124 125 // Upload the config for 2 users. 126 require.NoError(t, store.SetAlertConfig(ctx, user1Cfg)) 127 require.NoError(t, store.SetAlertConfig(ctx, user2Cfg)) 128 129 // Ensure the config has been correctly uploaded. 130 config, err := store.GetAlertConfig(ctx, "user-1") 131 require.NoError(t, err) 132 assert.Equal(t, user1Cfg, config) 133 134 config, err = store.GetAlertConfig(ctx, "user-2") 135 require.NoError(t, err) 136 assert.Equal(t, user2Cfg, config) 137 138 // Delete the config for user-1. 139 require.NoError(t, store.DeleteAlertConfig(ctx, "user-1")) 140 141 // Ensure the correct config has been deleted. 142 _, err = store.GetAlertConfig(ctx, "user-1") 143 assert.Equal(t, alertspb.ErrNotFound, err) 144 145 config, err = store.GetAlertConfig(ctx, "user-2") 146 require.NoError(t, err) 147 assert.Equal(t, user2Cfg, config) 148 149 // Delete again (should be idempotent). 150 require.NoError(t, store.DeleteAlertConfig(ctx, "user-1")) 151 }) 152 } 153 154 func runForEachAlertStore(t *testing.T, testFn func(t *testing.T, store AlertStore, client interface{})) { 155 legacyClient := chunk.NewMockStorage() 156 legacyStore := objectclient.NewAlertStore(legacyClient, log.NewNopLogger()) 157 158 bucketClient := objstore.NewInMemBucket() 159 bucketStore := bucketclient.NewBucketAlertStore(bucketClient, nil, log.NewNopLogger()) 160 161 stores := map[string]struct { 162 store AlertStore 163 client interface{} 164 }{ 165 "legacy": {store: legacyStore, client: legacyClient}, 166 "bucket": {store: bucketStore, client: bucketClient}, 167 } 168 169 for name, data := range stores { 170 t.Run(name, func(t *testing.T) { 171 testFn(t, data.store, data.client) 172 }) 173 } 174 } 175 176 func objectExists(bucketClient interface{}, key string) (bool, error) { 177 if typed, ok := bucketClient.(*chunk.MockStorage); ok { 178 _, err := typed.GetObject(context.Background(), key) 179 if errors.Is(err, chunk.ErrStorageObjectNotFound) { 180 return false, nil 181 } 182 if err == nil { 183 return true, nil 184 } 185 return false, err 186 } 187 188 if typed, ok := bucketClient.(*objstore.InMemBucket); ok { 189 return typed.Exists(context.Background(), key) 190 } 191 192 panic("unexpected bucket client") 193 } 194 195 func makeTestFullState(content string) alertspb.FullStateDesc { 196 return alertspb.FullStateDesc{ 197 State: &clusterpb.FullState{ 198 Parts: []clusterpb.Part{ 199 { 200 Key: "key", 201 Data: []byte(content), 202 }, 203 }, 204 }, 205 } 206 } 207 208 func TestBucketAlertStore_GetSetDeleteFullState(t *testing.T) { 209 bucket := objstore.NewInMemBucket() 210 store := bucketclient.NewBucketAlertStore(bucket, nil, log.NewNopLogger()) 211 ctx := context.Background() 212 213 state1 := makeTestFullState("one") 214 state2 := makeTestFullState("two") 215 216 // The storage is empty. 217 { 218 _, err := store.GetFullState(ctx, "user-1") 219 assert.Equal(t, alertspb.ErrNotFound, err) 220 221 _, err = store.GetFullState(ctx, "user-2") 222 assert.Equal(t, alertspb.ErrNotFound, err) 223 224 users, err := store.ListUsersWithFullState(ctx) 225 assert.NoError(t, err) 226 assert.ElementsMatch(t, []string{}, users) 227 } 228 229 // The storage contains users. 230 { 231 require.NoError(t, store.SetFullState(ctx, "user-1", state1)) 232 require.NoError(t, store.SetFullState(ctx, "user-2", state2)) 233 234 res, err := store.GetFullState(ctx, "user-1") 235 require.NoError(t, err) 236 assert.Equal(t, state1, res) 237 238 res, err = store.GetFullState(ctx, "user-2") 239 require.NoError(t, err) 240 assert.Equal(t, state2, res) 241 242 // Ensure the config is stored at the expected location. Without this check 243 // we have no guarantee that the objects are stored at the expected location. 244 exists, err := bucket.Exists(ctx, "alertmanager/user-1/fullstate") 245 require.NoError(t, err) 246 assert.True(t, exists) 247 248 exists, err = bucket.Exists(ctx, "alertmanager/user-2/fullstate") 249 require.NoError(t, err) 250 assert.True(t, exists) 251 252 users, err := store.ListUsersWithFullState(ctx) 253 assert.NoError(t, err) 254 assert.ElementsMatch(t, []string{"user-1", "user-2"}, users) 255 } 256 257 // The storage has had user-1 deleted. 258 { 259 require.NoError(t, store.DeleteFullState(ctx, "user-1")) 260 261 // Ensure the correct entry has been deleted. 262 _, err := store.GetFullState(ctx, "user-1") 263 assert.Equal(t, alertspb.ErrNotFound, err) 264 265 res, err := store.GetFullState(ctx, "user-2") 266 require.NoError(t, err) 267 assert.Equal(t, state2, res) 268 269 users, err := store.ListUsersWithFullState(ctx) 270 assert.NoError(t, err) 271 assert.ElementsMatch(t, []string{"user-2"}, users) 272 273 // Delete again (should be idempotent). 274 require.NoError(t, store.DeleteFullState(ctx, "user-1")) 275 } 276 }