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  }