Skip to content

Commit 3951432

Browse files
committed
Add endpoints to deactivate and restore config
- call deactivate on instance deletion
1 parent aca2c72 commit 3951432

File tree

7 files changed

+190
-4
lines changed

7 files changed

+190
-4
lines changed

pkg/configs/api/api.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ func (a *API) RegisterRoutes(r *mux.Router) {
6161
{"get_alertmanager_config", "GET", "/api/prom/configs/alertmanager", a.getConfig},
6262
{"set_alertmanager_config", "POST", "/api/prom/configs/alertmanager", a.setConfig},
6363
{"validate_alertmanager_config", "POST", "/api/prom/configs/alertmanager/validate", a.validateAlertmanagerConfig},
64+
{"deactivate_config", "DELETE", "/api/prom/configs/deactivate", a.deactivateConfig},
65+
{"restore_config", "POST", "/api/prom/configs/restore", a.restoreConfig},
6466
// Internal APIs.
6567
{"private_get_rules", "GET", "/private/api/prom/configs/rules", a.getConfigs},
6668
{"private_get_alertmanager_config", "GET", "/private/api/prom/configs/alertmanager", a.getConfigs},
@@ -217,3 +219,58 @@ func (a *API) getConfigs(w http.ResponseWriter, r *http.Request) {
217219
return
218220
}
219221
}
222+
223+
func (a *API) deactivateConfig(w http.ResponseWriter, r *http.Request) {
224+
userID, _, err := user.ExtractOrgIDFromHTTPRequest(r)
225+
if err != nil {
226+
http.Error(w, err.Error(), http.StatusUnauthorized)
227+
return
228+
}
229+
logger := util.WithContext(r.Context(), util.Logger)
230+
231+
fmt.Printf("deactivateConfigs org ID %s\n", userID)
232+
233+
if _, err := a.db.GetConfig(userID); err != nil {
234+
if err == sql.ErrNoRows {
235+
http.Error(w, "No configuration", http.StatusNotFound)
236+
return
237+
}
238+
level.Error(logger).Log("msg", "error getting config", "err", err)
239+
http.Error(w, err.Error(), http.StatusInternalServerError)
240+
return
241+
}
242+
243+
if err := a.db.DeactivateConfig(userID); err != nil {
244+
level.Error(logger).Log("msg", "error deactivating configs", "err", err)
245+
http.Error(w, err.Error(), http.StatusInternalServerError)
246+
return
247+
}
248+
w.WriteHeader(http.StatusAccepted)
249+
}
250+
251+
func (a *API) restoreConfig(w http.ResponseWriter, r *http.Request) {
252+
userID, _, err := user.ExtractOrgIDFromHTTPRequest(r)
253+
if err != nil {
254+
http.Error(w, err.Error(), http.StatusUnauthorized)
255+
return
256+
}
257+
logger := util.WithContext(r.Context(), util.Logger)
258+
fmt.Printf("restore org ID %s\n", userID)
259+
260+
if _, err := a.db.GetConfig(userID); err != nil {
261+
if err == sql.ErrNoRows {
262+
http.Error(w, "No configuration", http.StatusNotFound)
263+
return
264+
}
265+
level.Error(logger).Log("msg", "error getting config", "err", err)
266+
http.Error(w, err.Error(), http.StatusInternalServerError)
267+
return
268+
}
269+
270+
if err := a.db.RestoreConfig(userID); err != nil {
271+
level.Error(logger).Log("msg", "error restoring config", "err", err)
272+
http.Error(w, err.Error(), http.StatusInternalServerError)
273+
return
274+
}
275+
w.WriteHeader(http.StatusCreated)
276+
}

pkg/configs/configs.go

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

33
import (
44
"fmt"
5+
"time"
56

67
"github.com/prometheus/prometheus/promql"
78
"github.com/prometheus/prometheus/rules"
@@ -26,8 +27,9 @@ type Config struct {
2627
// _version_ of a configuration a unique ID and guarantees that later versions
2728
// have greater IDs.
2829
type View struct {
29-
ID ID `json:"id"`
30-
Config Config `json:"config"`
30+
ID ID `json:"id"`
31+
Config Config `json:"config"`
32+
DeletedAt time.Time
3133
}
3234

3335
// GetVersionedRulesConfig specializes the view to just the rules config.

pkg/configs/db/db.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ type DB interface {
4848
GetAllConfigs() (map[string]configs.View, error)
4949
GetConfigs(since configs.ID) (map[string]configs.View, error)
5050

51+
DeactivateConfig(userID string) error
52+
RestoreConfig(userID string) error
53+
5154
Close() error
5255
}
5356

pkg/configs/db/memory/memory.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package memory
22

33
import (
44
"database/sql"
5+
"time"
56

67
"github.com/weaveworks/cortex/pkg/configs"
78
)
@@ -52,6 +53,29 @@ func (d *DB) GetConfigs(since configs.ID) (map[string]configs.View, error) {
5253
return cfgs, nil
5354
}
5455

56+
// SetDeletedAtConfig sets deletedAt for last configuration for a user.
57+
func (d *DB) SetDeletedAtConfig(userID string, deletedAt time.Time) error {
58+
cv, err := d.GetConfig(userID)
59+
if err != nil {
60+
return err
61+
}
62+
cv.DeletedAt = deletedAt
63+
cv.ID = configs.ID(d.id)
64+
d.cfgs[userID] = cv
65+
d.id++
66+
return nil
67+
}
68+
69+
// DeactivateConfig deactivates configuration for a user by creating new configuration with DeletedAt set to now
70+
func (d *DB) DeactivateConfig(userID string) error {
71+
return d.SetDeletedAtConfig(userID, time.Now())
72+
}
73+
74+
// RestoreConfig restores deactivated configuration for a user by creating new configuration with empty DeletedAt
75+
func (d *DB) RestoreConfig(userID string) error {
76+
return d.SetDeletedAtConfig(userID, time.Time{})
77+
}
78+
5579
// Close finishes using the db. Noop.
5680
func (d *DB) Close() error {
5781
return nil

pkg/configs/db/postgres/postgres.go

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@ package postgres
33
import (
44
"database/sql"
55
"encoding/json"
6-
"errors"
6+
"time"
77

88
"github.com/Masterminds/squirrel"
99
"github.com/go-kit/kit/log/level"
1010
_ "github.com/lib/pq" // Import the postgres sql driver
1111
_ "github.com/mattes/migrate/driver/postgres" // Import the postgres migrations driver
1212
"github.com/mattes/migrate/migrate"
13+
"github.com/pkg/errors"
1314
"github.com/weaveworks/cortex/pkg/configs"
1415
"github.com/weaveworks/cortex/pkg/util"
1516
)
@@ -19,6 +20,8 @@ const (
1920
// schema so this isn't needed.
2021
entityType = "org"
2122
subsystem = "cortex"
23+
// timeout waiting for database connection to be established
24+
dbTimeout = 5 * time.Minute
2225
)
2326

2427
var (
@@ -42,8 +45,32 @@ type dbProxy interface {
4245
Prepare(query string) (*sql.Stmt, error)
4346
}
4447

48+
// dbWait waits for database connection to be established
49+
func dbWait(db *sql.DB) error {
50+
deadline := time.Now().Add(dbTimeout)
51+
var err error
52+
for tries := 0; time.Now().Before(deadline); tries++ {
53+
err = db.Ping()
54+
if err == nil {
55+
return nil
56+
}
57+
level.Warn(util.Logger).Log("msg", "db connection not established, retrying...", "error", err)
58+
time.Sleep(time.Second << uint(tries))
59+
}
60+
return errors.Wrapf(err, "db connection not established after %s", dbTimeout)
61+
}
62+
4563
// New creates a new postgres DB
4664
func New(uri, migrationsDir string) (DB, error) {
65+
db, err := sql.Open("postgres", uri)
66+
if err != nil {
67+
return DB{}, errors.Wrap(err, "cannot open postgres db")
68+
}
69+
70+
if err := dbWait(db); err != nil {
71+
return DB{}, errors.Wrap(err, "cannot establish db connection")
72+
}
73+
4774
if migrationsDir != "" {
4875
level.Info(util.Logger).Log("msg", "running database migrations...")
4976
if errs, ok := migrate.UpSync(uri, migrationsDir); !ok {
@@ -53,7 +80,7 @@ func New(uri, migrationsDir string) (DB, error) {
5380
return DB{}, errors.New("database migrations failed")
5481
}
5582
}
56-
db, err := sql.Open("postgres", uri)
83+
5784
return DB{
5885
dbProxy: db,
5986
StatementBuilderType: statementBuilder(db),
@@ -225,6 +252,57 @@ func (d DB) GetRulesConfigs(since configs.ID) (map[string]configs.VersionedRules
225252
})
226253
}
227254

255+
// GetLastConfig gets a last configuration (active or deleted).
256+
func (d DB) GetLastConfig(userID string) (configs.View, error) {
257+
var cfgView configs.View
258+
var cfgBytes []byte
259+
err := d.Select("id", "config").
260+
From("configs").
261+
Where(squirrel.Eq{"owner_id": userID}).
262+
OrderBy("id DESC").
263+
Limit(1).
264+
QueryRow().Scan(&cfgView.ID, &cfgBytes)
265+
if err != nil {
266+
return cfgView, err
267+
}
268+
err = json.Unmarshal(cfgBytes, &cfgView.Config)
269+
log.Infof("get last config bytes: %s", cfgBytes)
270+
return cfgView, err
271+
}
272+
273+
// SetDeletedAtConfig sets a deletedAt for configuration.
274+
func (d DB) SetDeletedAtConfig(userID string, deletedAt time.Time, cfg configs.Config) error {
275+
cfgBytes, err := json.Marshal(cfg)
276+
if err != nil {
277+
return err
278+
}
279+
280+
log.Infof("set deletedA to %s for %s", deletedAt, cfgBytes)
281+
_, err = d.Insert("configs").
282+
Columns("owner_id", "owner_type", "subsystem", "deleted_at", "config").
283+
Values(userID, entityType, subsystem, deletedAt, cfgBytes).
284+
Exec()
285+
return err
286+
}
287+
288+
// DeactivateConfig deactivates a configuration.
289+
func (d DB) DeactivateConfig(userID string) error {
290+
cfg, err := d.GetLastConfig(userID)
291+
if err != nil {
292+
return err
293+
}
294+
return d.SetDeletedAtConfig(userID, time.Now(), cfg.Config)
295+
}
296+
297+
// RestoreConfig restores deactivated configuration.
298+
func (d DB) RestoreConfig(userID string) error {
299+
cfg, err := d.GetLastConfig(userID)
300+
if err != nil {
301+
return err
302+
}
303+
return d.SetDeletedAtConfig(userID, time.Time{}, cfg.Config)
304+
}
305+
228306
// Transaction runs the given function in a postgres transaction. If fn returns
229307
// an error the txn will be rolled back.
230308
func (d DB) Transaction(f func(DB) error) error {

pkg/configs/db/timed.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,18 @@ func (t timed) GetConfigs(since configs.ID) (cfgs map[string]configs.View, err e
6969
return
7070
}
7171

72+
func (t timed) DeactivateConfig(userID string) (err error) {
73+
return t.timeRequest("DeactivateConfig", func(_ context.Context) error {
74+
return t.d.DeactivateConfig(userID)
75+
})
76+
}
77+
78+
func (t timed) RestoreConfig(userID string) (err error) {
79+
return t.timeRequest("RestoreConfig", func(_ context.Context) error {
80+
return t.d.RestoreConfig(userID)
81+
})
82+
}
83+
7284
func (t timed) Close() error {
7385
return t.timeRequest("Close", func(_ context.Context) error {
7486
return t.d.Close()

pkg/configs/db/traced.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,16 @@ func (t traced) GetConfigs(since configs.ID) (cfgs map[string]configs.View, err
3737
return t.d.GetConfigs(since)
3838
}
3939

40+
func (t traced) DeactivateConfig(userID string) (err error) {
41+
defer func() { t.trace("DeactivateConfig", userID, err) }()
42+
return t.d.DeactivateConfig(userID)
43+
}
44+
45+
func (t traced) RestoreConfig(userID string) (err error) {
46+
defer func() { t.trace("RestoreConfig", userID, err) }()
47+
return t.d.RestoreConfig(userID)
48+
}
49+
4050
func (t traced) Close() (err error) {
4151
defer func() { t.trace("Close", err) }()
4252
return t.d.Close()

0 commit comments

Comments
 (0)