github.com/muhammadn/cortex@v1.9.1-0.20220510110439-46bb7000d03d/pkg/alertmanager/alertstore/objectclient/store.go (about)

     1  package objectclient
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"io/ioutil"
     7  	"path"
     8  	"strings"
     9  	"sync"
    10  
    11  	"github.com/go-kit/log"
    12  	"github.com/grafana/dskit/concurrency"
    13  	"github.com/grafana/dskit/runutil"
    14  	"github.com/pkg/errors"
    15  
    16  	"github.com/cortexproject/cortex/pkg/alertmanager/alertspb"
    17  	"github.com/cortexproject/cortex/pkg/chunk"
    18  )
    19  
    20  // Object Alert Storage Schema
    21  // =======================
    22  // Object Name: "alerts/<user_id>"
    23  // Storage Format: Encoded AlertConfigDesc
    24  
    25  const (
    26  	// The bucket prefix under which all tenants alertmanager configs are stored.
    27  	alertPrefix = "alerts/"
    28  
    29  	// How many users to load concurrently.
    30  	fetchConcurrency = 16
    31  )
    32  
    33  var (
    34  	errState = errors.New("legacy object alertmanager storage does not support state persistency")
    35  )
    36  
    37  // AlertStore allows cortex alertmanager configs to be stored using an object store backend.
    38  type AlertStore struct {
    39  	client chunk.ObjectClient
    40  	logger log.Logger
    41  }
    42  
    43  // NewAlertStore returns a new AlertStore
    44  func NewAlertStore(client chunk.ObjectClient, logger log.Logger) *AlertStore {
    45  	return &AlertStore{
    46  		client: client,
    47  		logger: logger,
    48  	}
    49  }
    50  
    51  // ListAllUsers implements alertstore.AlertStore.
    52  func (a *AlertStore) ListAllUsers(ctx context.Context) ([]string, error) {
    53  	objs, _, err := a.client.List(ctx, alertPrefix, "")
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  
    58  	userIDs := make([]string, 0, len(objs))
    59  	for _, obj := range objs {
    60  		userID := strings.TrimPrefix(obj.Key, alertPrefix)
    61  		userIDs = append(userIDs, userID)
    62  	}
    63  
    64  	return userIDs, nil
    65  }
    66  
    67  // GetAlertConfigs implements alertstore.AlertStore.
    68  func (a *AlertStore) GetAlertConfigs(ctx context.Context, userIDs []string) (map[string]alertspb.AlertConfigDesc, error) {
    69  	var (
    70  		cfgsMx = sync.Mutex{}
    71  		cfgs   = make(map[string]alertspb.AlertConfigDesc, len(userIDs))
    72  	)
    73  
    74  	err := concurrency.ForEach(ctx, concurrency.CreateJobsFromStrings(userIDs), fetchConcurrency, func(ctx context.Context, job interface{}) error {
    75  		userID := job.(string)
    76  
    77  		cfg, err := a.getAlertConfig(ctx, path.Join(alertPrefix, userID))
    78  		if errors.Is(err, chunk.ErrStorageObjectNotFound) {
    79  			return nil
    80  		} else if err != nil {
    81  			return errors.Wrapf(err, "failed to fetch alertmanager config for user %s", userID)
    82  		}
    83  
    84  		cfgsMx.Lock()
    85  		cfgs[userID] = cfg
    86  		cfgsMx.Unlock()
    87  
    88  		return nil
    89  	})
    90  
    91  	return cfgs, err
    92  }
    93  
    94  func (a *AlertStore) getAlertConfig(ctx context.Context, key string) (alertspb.AlertConfigDesc, error) {
    95  	readCloser, err := a.client.GetObject(ctx, key)
    96  	if err != nil {
    97  		return alertspb.AlertConfigDesc{}, err
    98  	}
    99  
   100  	defer runutil.CloseWithLogOnErr(a.logger, readCloser, "close alert config reader")
   101  
   102  	buf, err := ioutil.ReadAll(readCloser)
   103  	if err != nil {
   104  		return alertspb.AlertConfigDesc{}, errors.Wrapf(err, "failed to read alertmanager config %s", key)
   105  	}
   106  
   107  	config := alertspb.AlertConfigDesc{}
   108  	err = config.Unmarshal(buf)
   109  	if err != nil {
   110  		return alertspb.AlertConfigDesc{}, errors.Wrapf(err, "failed to unmarshal alertmanager config %s", key)
   111  	}
   112  
   113  	return config, nil
   114  }
   115  
   116  // GetAlertConfig implements alertstore.AlertStore.
   117  func (a *AlertStore) GetAlertConfig(ctx context.Context, user string) (alertspb.AlertConfigDesc, error) {
   118  	cfg, err := a.getAlertConfig(ctx, path.Join(alertPrefix, user))
   119  	if err == chunk.ErrStorageObjectNotFound {
   120  		return cfg, alertspb.ErrNotFound
   121  	}
   122  
   123  	return cfg, err
   124  }
   125  
   126  // SetAlertConfig implements alertstore.AlertStore.
   127  func (a *AlertStore) SetAlertConfig(ctx context.Context, cfg alertspb.AlertConfigDesc) error {
   128  	cfgBytes, err := cfg.Marshal()
   129  	if err != nil {
   130  		return err
   131  	}
   132  
   133  	return a.client.PutObject(ctx, path.Join(alertPrefix, cfg.User), bytes.NewReader(cfgBytes))
   134  }
   135  
   136  // DeleteAlertConfig implements alertstore.AlertStore.
   137  func (a *AlertStore) DeleteAlertConfig(ctx context.Context, user string) error {
   138  	err := a.client.DeleteObject(ctx, path.Join(alertPrefix, user))
   139  	if err == chunk.ErrStorageObjectNotFound {
   140  		return nil
   141  	}
   142  	return err
   143  }
   144  
   145  // ListUsersWithFullState implements alertstore.AlertStore.
   146  func (a *AlertStore) ListUsersWithFullState(ctx context.Context) ([]string, error) {
   147  	return nil, errState
   148  }
   149  
   150  // GetFullState implements alertstore.AlertStore.
   151  func (a *AlertStore) GetFullState(ctx context.Context, user string) (alertspb.FullStateDesc, error) {
   152  	return alertspb.FullStateDesc{}, errState
   153  }
   154  
   155  // SetFullState implements alertstore.AlertStore.
   156  func (a *AlertStore) SetFullState(ctx context.Context, user string, cfg alertspb.FullStateDesc) error {
   157  	return errState
   158  }
   159  
   160  // DeleteFullState implements alertstore.AlertStore.
   161  func (a *AlertStore) DeleteFullState(ctx context.Context, user string) error {
   162  	return errState
   163  }