Skip to content

Commit ee33b36

Browse files
authored
Added S3 SSE support to alertmanager storage (#3906)
Signed-off-by: Marco Pracucci <[email protected]>
1 parent 68d537b commit ee33b36

File tree

5 files changed

+66
-18
lines changed

5 files changed

+66
-18
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
* [CHANGE] Alertmanager: the `DELETE /api/v1/alerts` is now idempotent. No error is returned if the alertmanager config doesn't exist. #3888
1313
* [FEATURE] Experimental Ruler Storage: Add a separate set of configuration options to configure the ruler storage backend under the `-ruler-storage.` flag prefix. All blocks storage bucket clients and the config service are currently supported. Clients using this implementation will only be enabled if the existing `-ruler.storage` flags are left unset. #3805 #3864
1414
* [FEATURE] Experimental Alertmanager Storage: Add a separate set of configuration options to configure the alertmanager storage backend under the `-alertmanager-storage.` flag prefix. All blocks storage bucket clients and the config service are currently supported. Clients using this implementation will only be enabled if the existing `-alertmanager.storage` flags are left unset. #3888
15-
* [FEATURE] Adds support to S3 server-side encryption using KMS. The S3 server-side encryption config can be overridden on a per-tenant basis for the blocks storage and ruler. Deprecated `-<prefix>.s3.sse-encryption`, you should use the following CLI flags that have been added. #3651 #3810 #3811 #3870 #3886
15+
* [FEATURE] Adds support to S3 server-side encryption using KMS. The S3 server-side encryption config can be overridden on a per-tenant basis for the blocks storage, ruler and alertmanager. Deprecated `-<prefix>.s3.sse-encryption`, you should use the following CLI flags that have been added. #3651 #3810 #3811 #3870 #3886 #3906
1616
- `-<prefix>.s3.sse.type`
1717
- `-<prefix>.s3.sse.kms-key-id`
1818
- `-<prefix>.s3.sse.kms-encryption-context`

docs/guides/encryption-at-rest.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,13 @@ The [chunks storage](../chunks-storage/_index.md) S3 server-side encryption can
4646
4747
The ruler S3 server-side encryption can be configured similarly to the blocks storage. The per-tenant overrides are supported when using the storage backend configurable the `-ruler-storage.` flag prefix (or their respective YAML config options).
4848

49+
### Alertmanager
50+
51+
The alertmanager S3 server-side encryption can be configured similarly to the blocks storage. The per-tenant overrides are supported when using the storage backend configurable the `-alertmanager-storage.` flag prefix (or their respective YAML config options).
52+
4953
### Per-tenant config overrides
5054

51-
The S3 client used by the blocks storage and ruler supports S3 SSE config overrides on a per-tenant basis, using the [runtime configuration file](../configuration/arguments.md#runtime-configuration-file).
55+
The S3 client used by the blocks storage, ruler and alertmanager supports S3 SSE config overrides on a per-tenant basis, using the [runtime configuration file](../configuration/arguments.md#runtime-configuration-file).
5256
The following settings can ben overridden for each tenant:
5357

5458
- **`s3_sse_type`**<br />

docs/guides/encryption-at-rest.template

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,13 @@ The [chunks storage](../chunks-storage/_index.md) S3 server-side encryption can
2828

2929
The ruler S3 server-side encryption can be configured similarly to the blocks storage. The per-tenant overrides are supported when using the storage backend configurable the `-ruler-storage.` flag prefix (or their respective YAML config options).
3030

31+
### Alertmanager
32+
33+
The alertmanager S3 server-side encryption can be configured similarly to the blocks storage. The per-tenant overrides are supported when using the storage backend configurable the `-alertmanager-storage.` flag prefix (or their respective YAML config options).
34+
3135
### Per-tenant config overrides
3236

33-
The S3 client used by the blocks storage and ruler supports S3 SSE config overrides on a per-tenant basis, using the [runtime configuration file](../configuration/arguments.md#runtime-configuration-file).
37+
The S3 client used by the blocks storage, ruler and alertmanager supports S3 SSE config overrides on a per-tenant basis, using the [runtime configuration file](../configuration/arguments.md#runtime-configuration-file).
3438
The following settings can ben overridden for each tenant:
3539

3640
- **`s3_sse_type`**<br />

pkg/alertmanager/alertstore/bucketclient/bucket_client.go

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,20 +96,22 @@ func (s *BucketAlertStore) SetAlertConfig(ctx context.Context, cfg alertspb.Aler
9696
return err
9797
}
9898

99-
return s.bucket.Upload(ctx, cfg.User, bytes.NewBuffer(cfgBytes))
99+
return s.getUserBucket(cfg.User).Upload(ctx, cfg.User, bytes.NewBuffer(cfgBytes))
100100
}
101101

102102
// DeleteAlertConfig implements alertstore.AlertStore.
103103
func (s *BucketAlertStore) DeleteAlertConfig(ctx context.Context, userID string) error {
104-
err := s.bucket.Delete(ctx, userID)
105-
if s.bucket.IsObjNotFoundErr(err) {
104+
userBkt := s.getUserBucket(userID)
105+
106+
err := userBkt.Delete(ctx, userID)
107+
if userBkt.IsObjNotFoundErr(err) {
106108
return nil
107109
}
108110
return err
109111
}
110112

111-
func (s *BucketAlertStore) getAlertConfig(ctx context.Context, key string) (alertspb.AlertConfigDesc, error) {
112-
readCloser, err := s.bucket.Get(ctx, key)
113+
func (s *BucketAlertStore) getAlertConfig(ctx context.Context, userID string) (alertspb.AlertConfigDesc, error) {
114+
readCloser, err := s.getUserBucket(userID).Get(ctx, userID)
113115
if err != nil {
114116
return alertspb.AlertConfigDesc{}, err
115117
}
@@ -129,3 +131,8 @@ func (s *BucketAlertStore) getAlertConfig(ctx context.Context, key string) (aler
129131

130132
return config, nil
131133
}
134+
135+
func (s *BucketAlertStore) getUserBucket(userID string) objstore.Bucket {
136+
// Inject server-side encryption based on the tenant config.
137+
return bucket.NewSSEBucketClient(userID, s.bucket, s.cfgProvider)
138+
}

pkg/alertmanager/alertstore/store_test.go

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package alertstore
22

33
import (
44
"context"
5+
"errors"
56
"testing"
67

78
"github.com/go-kit/kit/log"
@@ -16,7 +17,7 @@ import (
1617
)
1718

1819
func TestAlertStore_ListAllUsers(t *testing.T) {
19-
runForEachAlertStore(t, func(t *testing.T, store AlertStore) {
20+
runForEachAlertStore(t, func(t *testing.T, store AlertStore, client interface{}) {
2021
ctx := context.Background()
2122
user1Cfg := alertspb.AlertConfigDesc{User: "user-1", RawConfig: "content-1"}
2223
user2Cfg := alertspb.AlertConfigDesc{User: "user-2", RawConfig: "content-2"}
@@ -41,7 +42,7 @@ func TestAlertStore_ListAllUsers(t *testing.T) {
4142
}
4243

4344
func TestAlertStore_SetAndGetAlertConfig(t *testing.T) {
44-
runForEachAlertStore(t, func(t *testing.T, store AlertStore) {
45+
runForEachAlertStore(t, func(t *testing.T, store AlertStore, client interface{}) {
4546
ctx := context.Background()
4647
user1Cfg := alertspb.AlertConfigDesc{User: "user-1", RawConfig: "content-1"}
4748
user2Cfg := alertspb.AlertConfigDesc{User: "user-2", RawConfig: "content-2"}
@@ -64,12 +65,22 @@ func TestAlertStore_SetAndGetAlertConfig(t *testing.T) {
6465
config, err = store.GetAlertConfig(ctx, "user-2")
6566
require.NoError(t, err)
6667
assert.Equal(t, user2Cfg, config)
68+
69+
// Ensure the config is stored at the expected location. Without this check
70+
// we have no guarantee that the objects are stored at the expected location.
71+
exists, err := objectExists(client, "alerts/user-1")
72+
require.NoError(t, err)
73+
assert.True(t, exists)
74+
75+
exists, err = objectExists(client, "alerts/user-2")
76+
require.NoError(t, err)
77+
assert.True(t, exists)
6778
}
6879
})
6980
}
7081

7182
func TestStore_GetAlertConfigs(t *testing.T) {
72-
runForEachAlertStore(t, func(t *testing.T, store AlertStore) {
83+
runForEachAlertStore(t, func(t *testing.T, store AlertStore, client interface{}) {
7384
ctx := context.Background()
7485
user1Cfg := alertspb.AlertConfigDesc{User: "user-1", RawConfig: "content-1"}
7586
user2Cfg := alertspb.AlertConfigDesc{User: "user-2", RawConfig: "content-2"}
@@ -105,7 +116,7 @@ func TestStore_GetAlertConfigs(t *testing.T) {
105116
}
106117

107118
func TestAlertStore_DeleteAlertConfig(t *testing.T) {
108-
runForEachAlertStore(t, func(t *testing.T, store AlertStore) {
119+
runForEachAlertStore(t, func(t *testing.T, store AlertStore, client interface{}) {
109120
ctx := context.Background()
110121
user1Cfg := alertspb.AlertConfigDesc{User: "user-1", RawConfig: "content-1"}
111122
user2Cfg := alertspb.AlertConfigDesc{User: "user-2", RawConfig: "content-2"}
@@ -139,21 +150,43 @@ func TestAlertStore_DeleteAlertConfig(t *testing.T) {
139150
})
140151
}
141152

142-
func runForEachAlertStore(t *testing.T, testFn func(t *testing.T, store AlertStore)) {
153+
func runForEachAlertStore(t *testing.T, testFn func(t *testing.T, store AlertStore, client interface{})) {
143154
legacyClient := chunk.NewMockStorage()
144155
legacyStore := objectclient.NewAlertStore(legacyClient, log.NewNopLogger())
145156

146157
bucketClient := objstore.NewInMemBucket()
147158
bucketStore := bucketclient.NewBucketAlertStore(bucketClient, nil, log.NewNopLogger())
148159

149-
stores := map[string]AlertStore{
150-
"legacy": legacyStore,
151-
"bucket": bucketStore,
160+
stores := map[string]struct {
161+
store AlertStore
162+
client interface{}
163+
}{
164+
"legacy": {store: legacyStore, client: legacyClient},
165+
"bucket": {store: bucketStore, client: bucketClient},
152166
}
153167

154-
for name, store := range stores {
168+
for name, data := range stores {
155169
t.Run(name, func(t *testing.T) {
156-
testFn(t, store)
170+
testFn(t, data.store, data.client)
157171
})
158172
}
159173
}
174+
175+
func objectExists(bucketClient interface{}, key string) (bool, error) {
176+
if typed, ok := bucketClient.(*chunk.MockStorage); ok {
177+
_, err := typed.GetObject(context.Background(), key)
178+
if errors.Is(err, chunk.ErrStorageObjectNotFound) {
179+
return false, nil
180+
}
181+
if err == nil {
182+
return true, nil
183+
}
184+
return false, err
185+
}
186+
187+
if typed, ok := bucketClient.(*objstore.InMemBucket); ok {
188+
return typed.Exists(context.Background(), key)
189+
}
190+
191+
panic("unexpected bucket client")
192+
}

0 commit comments

Comments
 (0)