Skip to content

Commit fc53af5

Browse files
committed
Add endpoints to deactivate and restore config
- call deactivate on instance deletion
1 parent 3e7450f commit fc53af5

File tree

6 files changed

+165
-6
lines changed

6 files changed

+165
-6
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/db/db.go

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

33+
DeactivateConfig(userID string) error
34+
RestoreConfig(userID string) error
35+
3336
Close() error
3437
}
3538

pkg/configs/db/memory/memory.go

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@ package memory
22

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

67
"github.com/weaveworks/cortex/pkg/configs"
78
)
89

910
type config struct {
10-
cfg configs.Config
11-
id configs.ID
11+
cfg configs.Config
12+
id configs.ID
13+
deletedAt time.Time
1214
}
1315

1416
func (c config) toView() configs.View {
@@ -32,12 +34,19 @@ func New(_, _ string) (*DB, error) {
3234
}, nil
3335
}
3436

37+
func isActive(c config) bool {
38+
return c.deletedAt.IsZero()
39+
}
40+
3541
// GetConfig gets the user's configuration.
3642
func (d *DB) GetConfig(userID string) (configs.View, error) {
3743
c, ok := d.cfgs[userID]
3844
if !ok {
3945
return configs.View{}, sql.ErrNoRows
4046
}
47+
if !isActive(c) {
48+
return configs.View{}, sql.ErrNoRows
49+
}
4150
return c.toView(), nil
4251
}
4352

@@ -52,7 +61,9 @@ func (d *DB) SetConfig(userID string, cfg configs.Config) error {
5261
func (d *DB) GetAllConfigs() (map[string]configs.View, error) {
5362
cfgs := map[string]configs.View{}
5463
for user, c := range d.cfgs {
55-
cfgs[user] = c.toView()
64+
if isActive(c) {
65+
cfgs[user] = c.toView()
66+
}
5667
}
5768
return cfgs, nil
5869
}
@@ -61,13 +72,33 @@ func (d *DB) GetAllConfigs() (map[string]configs.View, error) {
6172
func (d *DB) GetConfigs(since configs.ID) (map[string]configs.View, error) {
6273
cfgs := map[string]configs.View{}
6374
for user, c := range d.cfgs {
64-
if c.id > since {
75+
if c.id > since && isActive(c) {
6576
cfgs[user] = c.toView()
6677
}
6778
}
6879
return cfgs, nil
6980
}
7081

82+
// DeactivateConfig deactivates configuration for a user.
83+
func (d *DB) DeactivateConfig(userID string) error {
84+
cfg, ok := d.cfgs[userID]
85+
if !ok {
86+
return sql.ErrNoRows
87+
}
88+
cfg.deletedAt = time.Now()
89+
return nil
90+
}
91+
92+
// RestoreConfig restores deactivated configuration for a user.
93+
func (d *DB) RestoreConfig(userID string) error {
94+
cfg, ok := d.cfgs[userID]
95+
if !ok {
96+
return sql.ErrNoRows
97+
}
98+
cfg.deletedAt = time.Time{}
99+
return nil
100+
}
101+
71102
// Close finishes using the db. Noop.
72103
func (d *DB) Close() error {
73104
return nil

pkg/configs/db/postgres/postgres.go

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@ 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"
14+
"github.com/prometheus/common/log"
1315
"github.com/weaveworks/cortex/pkg/configs"
1416
"github.com/weaveworks/cortex/pkg/util"
1517
)
@@ -19,6 +21,8 @@ const (
1921
// schema so this isn't needed.
2022
entityType = "org"
2123
subsystem = "cortex"
24+
// timeout waiting for database connection to be established
25+
dbTimeout = 5 * time.Minute
2226
)
2327

2428
var (
@@ -42,8 +46,32 @@ type dbProxy interface {
4246
Prepare(query string) (*sql.Stmt, error)
4347
}
4448

49+
// dbWait waits for database connection to be established
50+
func dbWait(db *sql.DB) error {
51+
deadline := time.Now().Add(dbTimeout)
52+
var err error
53+
for tries := 0; time.Now().Before(deadline); tries++ {
54+
err = db.Ping()
55+
if err == nil {
56+
return nil
57+
}
58+
log.Warnf("db connection not established, error: %s; retrying...", err)
59+
time.Sleep(time.Second << uint(tries))
60+
}
61+
return errors.Wrapf(err, "db connection not established after %s", dbTimeout)
62+
}
63+
4564
// New creates a new postgres DB
4665
func New(uri, migrationsDir string) (DB, error) {
66+
db, err := sql.Open("postgres", uri)
67+
if err != nil {
68+
return DB{}, errors.Wrap(err, "cannot open postgres db")
69+
}
70+
71+
if err := dbWait(db); err != nil {
72+
return DB{}, errors.Wrap(err, "cannot establish db connection")
73+
}
74+
4775
if migrationsDir != "" {
4876
level.Info(util.Logger).Log("msg", "running database migrations...")
4977
if errs, ok := migrate.UpSync(uri, migrationsDir); !ok {
@@ -53,7 +81,7 @@ func New(uri, migrationsDir string) (DB, error) {
5381
return DB{}, errors.New("database migrations failed")
5482
}
5583
}
56-
db, err := sql.Open("postgres", uri)
84+
5785
return DB{
5886
dbProxy: db,
5987
StatementBuilderType: statementBuilder(db),
@@ -134,6 +162,24 @@ func (d DB) GetConfigs(since configs.ID) (map[string]configs.View, error) {
134162
})
135163
}
136164

165+
// DeactivateConfig deactivates a configuration.
166+
func (d DB) DeactivateConfig(userID string) error {
167+
_, err := d.Exec(
168+
`update configs set deleted_at = $1 where owner_id = lower($2)`,
169+
time.Now(), userID,
170+
)
171+
return err
172+
}
173+
174+
// RestoreConfig restores deactivated configuration.
175+
func (d DB) RestoreConfig(userID string) error {
176+
_, err := d.Exec(
177+
`update configs set deleted_at = null where owner_id = lower($1)`,
178+
userID,
179+
)
180+
return err
181+
}
182+
137183
// Transaction runs the given function in a postgres transaction. If fn returns
138184
// an error the txn will be rolled back.
139185
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)