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 }