github.com/muhammadn/cortex@v1.9.1-0.20220510110439-46bb7000d03d/pkg/alertmanager/alertstore/bucketclient/bucket_client.go (about) 1 package bucketclient 2 3 import ( 4 "bytes" 5 "context" 6 "io/ioutil" 7 "strings" 8 "sync" 9 10 "github.com/go-kit/log" 11 "github.com/gogo/protobuf/proto" 12 "github.com/grafana/dskit/concurrency" 13 "github.com/grafana/dskit/runutil" 14 "github.com/pkg/errors" 15 "github.com/thanos-io/thanos/pkg/objstore" 16 17 "github.com/cortexproject/cortex/pkg/alertmanager/alertspb" 18 "github.com/cortexproject/cortex/pkg/storage/bucket" 19 ) 20 21 const ( 22 // The bucket prefix under which all tenants alertmanager configs are stored. 23 // Note that objects stored under this prefix follow the pattern: 24 // alerts/<user-id> 25 alertsPrefix = "alerts" 26 27 // The bucket prefix under which other alertmanager state is stored. 28 // Note that objects stored under this prefix follow the pattern: 29 // alertmanager/<user-id>/<object> 30 alertmanagerPrefix = "alertmanager" 31 32 // The name of alertmanager full state objects (notification log + silences). 33 fullStateName = "fullstate" 34 35 // How many users to load concurrently. 36 fetchConcurrency = 16 37 ) 38 39 // BucketAlertStore is used to support the AlertStore interface against an object storage backend. It is implemented 40 // using the Thanos objstore.Bucket interface 41 type BucketAlertStore struct { 42 alertsBucket objstore.Bucket 43 amBucket objstore.Bucket 44 cfgProvider bucket.TenantConfigProvider 45 logger log.Logger 46 } 47 48 func NewBucketAlertStore(bkt objstore.Bucket, cfgProvider bucket.TenantConfigProvider, logger log.Logger) *BucketAlertStore { 49 return &BucketAlertStore{ 50 alertsBucket: bucket.NewPrefixedBucketClient(bkt, alertsPrefix), 51 amBucket: bucket.NewPrefixedBucketClient(bkt, alertmanagerPrefix), 52 cfgProvider: cfgProvider, 53 logger: logger, 54 } 55 } 56 57 // ListAllUsers implements alertstore.AlertStore. 58 func (s *BucketAlertStore) ListAllUsers(ctx context.Context) ([]string, error) { 59 var userIDs []string 60 61 err := s.alertsBucket.Iter(ctx, "", func(key string) error { 62 userIDs = append(userIDs, key) 63 return nil 64 }) 65 66 return userIDs, err 67 } 68 69 // GetAlertConfigs implements alertstore.AlertStore. 70 func (s *BucketAlertStore) GetAlertConfigs(ctx context.Context, userIDs []string) (map[string]alertspb.AlertConfigDesc, error) { 71 var ( 72 cfgsMx = sync.Mutex{} 73 cfgs = make(map[string]alertspb.AlertConfigDesc, len(userIDs)) 74 ) 75 76 err := concurrency.ForEach(ctx, concurrency.CreateJobsFromStrings(userIDs), fetchConcurrency, func(ctx context.Context, job interface{}) error { 77 userID := job.(string) 78 79 cfg, err := s.getAlertConfig(ctx, userID) 80 if s.alertsBucket.IsObjNotFoundErr(err) { 81 return nil 82 } else if err != nil { 83 return errors.Wrapf(err, "failed to fetch alertmanager config for user %s", userID) 84 } 85 86 cfgsMx.Lock() 87 cfgs[userID] = cfg 88 cfgsMx.Unlock() 89 90 return nil 91 }) 92 93 return cfgs, err 94 } 95 96 // GetAlertConfig implements alertstore.AlertStore. 97 func (s *BucketAlertStore) GetAlertConfig(ctx context.Context, userID string) (alertspb.AlertConfigDesc, error) { 98 cfg, err := s.getAlertConfig(ctx, userID) 99 if s.alertsBucket.IsObjNotFoundErr(err) { 100 return cfg, alertspb.ErrNotFound 101 } 102 103 return cfg, err 104 } 105 106 // SetAlertConfig implements alertstore.AlertStore. 107 func (s *BucketAlertStore) SetAlertConfig(ctx context.Context, cfg alertspb.AlertConfigDesc) error { 108 cfgBytes, err := cfg.Marshal() 109 if err != nil { 110 return err 111 } 112 113 return s.getUserBucket(cfg.User).Upload(ctx, cfg.User, bytes.NewBuffer(cfgBytes)) 114 } 115 116 // DeleteAlertConfig implements alertstore.AlertStore. 117 func (s *BucketAlertStore) DeleteAlertConfig(ctx context.Context, userID string) error { 118 userBkt := s.getUserBucket(userID) 119 120 err := userBkt.Delete(ctx, userID) 121 if userBkt.IsObjNotFoundErr(err) { 122 return nil 123 } 124 return err 125 } 126 127 // ListUsersWithFullState implements alertstore.AlertStore. 128 func (s *BucketAlertStore) ListUsersWithFullState(ctx context.Context) ([]string, error) { 129 var userIDs []string 130 131 err := s.amBucket.Iter(ctx, "", func(key string) error { 132 userIDs = append(userIDs, strings.TrimRight(key, "/")) 133 return nil 134 }) 135 136 return userIDs, err 137 } 138 139 // GetFullState implements alertstore.AlertStore. 140 func (s *BucketAlertStore) GetFullState(ctx context.Context, userID string) (alertspb.FullStateDesc, error) { 141 bkt := s.getAlertmanagerUserBucket(userID) 142 fs := alertspb.FullStateDesc{} 143 144 err := s.get(ctx, bkt, fullStateName, &fs) 145 if s.amBucket.IsObjNotFoundErr(err) { 146 return fs, alertspb.ErrNotFound 147 } 148 149 return fs, err 150 } 151 152 // SetFullState implements alertstore.AlertStore. 153 func (s *BucketAlertStore) SetFullState(ctx context.Context, userID string, fs alertspb.FullStateDesc) error { 154 bkt := s.getAlertmanagerUserBucket(userID) 155 156 fsBytes, err := fs.Marshal() 157 if err != nil { 158 return err 159 } 160 161 return bkt.Upload(ctx, fullStateName, bytes.NewBuffer(fsBytes)) 162 } 163 164 // DeleteFullState implements alertstore.AlertStore. 165 func (s *BucketAlertStore) DeleteFullState(ctx context.Context, userID string) error { 166 userBkt := s.getAlertmanagerUserBucket(userID) 167 168 err := userBkt.Delete(ctx, fullStateName) 169 if userBkt.IsObjNotFoundErr(err) { 170 return nil 171 } 172 return err 173 } 174 175 func (s *BucketAlertStore) getAlertConfig(ctx context.Context, userID string) (alertspb.AlertConfigDesc, error) { 176 config := alertspb.AlertConfigDesc{} 177 err := s.get(ctx, s.getUserBucket(userID), userID, &config) 178 return config, err 179 } 180 181 func (s *BucketAlertStore) get(ctx context.Context, bkt objstore.Bucket, name string, msg proto.Message) error { 182 readCloser, err := bkt.Get(ctx, name) 183 if err != nil { 184 return err 185 } 186 187 defer runutil.CloseWithLogOnErr(s.logger, readCloser, "close bucket reader") 188 189 buf, err := ioutil.ReadAll(readCloser) 190 if err != nil { 191 return errors.Wrapf(err, "failed to read alertmanager config for user %s", name) 192 } 193 194 err = proto.Unmarshal(buf, msg) 195 if err != nil { 196 return errors.Wrapf(err, "failed to deserialize alertmanager config for user %s", name) 197 } 198 199 return nil 200 } 201 202 func (s *BucketAlertStore) getUserBucket(userID string) objstore.Bucket { 203 // Inject server-side encryption based on the tenant config. 204 return bucket.NewSSEBucketClient(userID, s.alertsBucket, s.cfgProvider) 205 } 206 207 func (s *BucketAlertStore) getAlertmanagerUserBucket(userID string) objstore.Bucket { 208 return bucket.NewUserBucketClient(userID, s.amBucket, s.cfgProvider).WithExpectedErrs(s.amBucket.IsObjNotFoundErr) 209 }