Skip to content

Commit ee6d518

Browse files
committed
Postgresql garbage collector implementation.
1 parent bfa9fd2 commit ee6d518

File tree

7 files changed

+167
-51
lines changed

7 files changed

+167
-51
lines changed

api/v1beta1/status.go

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,22 @@ func (s *CredentialStatus) SetCredentialsStatus(code StatusCode, message string)
4848
}
4949

5050
type DatabaseStatus struct {
51-
Status StatusCode `json:"status"`
52-
Message string `json:"message"`
53-
Name string `json:"name"`
54-
Host string `json:"host"`
51+
Status StatusCode `json:"status"`
52+
Message string `json:"message"`
53+
Name string `json:"name"`
54+
Host string `json:"host"`
55+
RootUsername string `json:"rootUsername"`
56+
RootAuthenticationDatabase string `json:"rootAuthDatabase"`
57+
RootSecretLookup StatusRootSecretLookup `json:"rootSecretLookup"`
5558
}
5659

57-
func (s *DatabaseStatus) SetDatabaseStatus(code StatusCode, message string, name string, host string) {
60+
type StatusRootSecretLookup struct {
61+
Name string `json:"name"`
62+
Namespace string `json:"namespace"`
63+
Field string `json:"field"`
64+
}
65+
66+
func (s *DatabaseStatus) SetDatabaseStatus(code StatusCode, message string, name string, host string) *DatabaseStatus {
5867
s.Status = code
5968
s.Message = message
6069
if name != "" {
@@ -63,4 +72,24 @@ func (s *DatabaseStatus) SetDatabaseStatus(code StatusCode, message string, name
6372
if host != "" {
6473
s.Host = host
6574
}
75+
return s
76+
}
77+
78+
func (s *DatabaseStatus) WithUsername(rootUsername string) *DatabaseStatus {
79+
s.RootUsername = rootUsername
80+
return s
81+
}
82+
83+
func (s *DatabaseStatus) WithAuthDatabase(rootAuthenticationDatabase string) *DatabaseStatus {
84+
s.RootAuthenticationDatabase = rootAuthenticationDatabase
85+
return s
86+
}
87+
88+
func (s *DatabaseStatus) WithRootSecretLookup(name string, namespace string, field string) *DatabaseStatus {
89+
s.RootSecretLookup = StatusRootSecretLookup{
90+
Name: name,
91+
Namespace: namespace,
92+
Field: field,
93+
}
94+
return s
6695
}

config/crd/bases/infra.doodle.com_mongodbs.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,12 +120,32 @@ spec:
120120
type: string
121121
name:
122122
type: string
123+
rootAuthDatabase:
124+
type: string
125+
rootSecretLookup:
126+
properties:
127+
field:
128+
type: string
129+
name:
130+
type: string
131+
namespace:
132+
type: string
133+
required:
134+
- field
135+
- name
136+
- namespace
137+
type: object
138+
rootUsername:
139+
type: string
123140
status:
124141
type: string
125142
required:
126143
- host
127144
- message
128145
- name
146+
- rootAuthDatabase
147+
- rootSecretLookup
148+
- rootUsername
129149
- status
130150
type: object
131151
lastUpdateTime:

config/crd/bases/infra.doodle.com_postgresqls.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,12 +120,32 @@ spec:
120120
type: string
121121
name:
122122
type: string
123+
rootAuthDatabase:
124+
type: string
125+
rootSecretLookup:
126+
properties:
127+
field:
128+
type: string
129+
name:
130+
type: string
131+
namespace:
132+
type: string
133+
required:
134+
- field
135+
- name
136+
- namespace
137+
type: object
138+
rootUsername:
139+
type: string
123140
status:
124141
type: string
125142
required:
126143
- host
127144
- message
128145
- name
146+
- rootAuthDatabase
147+
- rootSecretLookup
148+
- rootUsername
129149
- status
130150
type: object
131151
lastUpdateTime:
File renamed without changes.

controllers/mongodb_controller.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,6 @@ func (r *MongoDBReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
5959
return ctrl.Result{}, errors.Wrap(err, "unable to fetch Mongodb")
6060
}
6161

62-
log.Info("updating Mongodb status...")
63-
6462
s := make(infrav1beta1.CredentialsStatus, 0)
6563
mongodb.Status.CredentialsStatus = s
6664

controllers/postgresql_controller.go

Lines changed: 23 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ func (r *PostgreSQLReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error)
4848

4949
// common controller functions
5050
cw := NewControllerWrapper(*r, &ctx)
51+
// garbage collector
52+
gc := NewPostgreSQLGarbageCollector(r, cw, &log)
5153

5254
// get postgresql resource by namespaced name
5355
var postgresql infrav1beta1.PostgreSQL
@@ -67,6 +69,11 @@ func (r *PostgreSQLReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error)
6769
return r.updateAndReturn(&ctx, &postgresql, &log)
6870
}
6971

72+
// Garbage Collection. If errors occur, log and proceed with reconciliation.
73+
if err := gc.Clean(&postgresql); err != nil {
74+
log.Info("Error while cleaning garbage", "error", err)
75+
}
76+
7077
// get root database password from k8s secret
7178
rootPassword, err := cw.GetRootPassword(postgresql.Spec.RootSecretLookup.Name, postgresql.Spec.RootSecretLookup.Namespace, postgresql.Spec.RootSecretLookup.Field)
7279
if err != nil {
@@ -75,63 +82,41 @@ func (r *PostgreSQLReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error)
7582
return r.updateAndReturn(&ctx, &postgresql, &log)
7683
}
7784

78-
// - garbage collection
79-
// if host is changed, drop credentials for old host. Keep database & data for now, until we figure out what we'll do with it
80-
// TODO refactor to have a general garbage collector mechanism, instead of code throughout controller
81-
if postgresql.Spec.HostName != postgresql.Status.DatabaseStatus.Host {
82-
// we might not be able to login with these old credentials anymore
83-
postgreSQLServerOld, err := r.ServerCache.Get(postgresql.Status.DatabaseStatus.Host, postgresql.Spec.RootUsername, rootPassword, postgresql.Spec.RootAuthenticationDatabase)
84-
if err != nil {
85-
// ignore the error for now
86-
} else {
87-
postgresql.Status.CredentialsStatus.ForEach(func(status *infrav1beta1.CredentialStatus) {
88-
_ = postgreSQLServerOld.DropUser(status.Username, postgresql.Status.DatabaseStatus.Name)
89-
})
90-
}
91-
}
92-
9385
// postgresql connection to spec host, cached
9486
postgreSQLServer, err := r.ServerCache.Get(postgresql.Spec.HostName, postgresql.Spec.RootUsername, rootPassword, postgresql.Spec.RootAuthenticationDatabase)
9587
if err != nil {
96-
postgresql.Status.DatabaseStatus.SetDatabaseStatus(infrav1beta1.Unavailable, err.Error(), "", postgresql.Spec.HostName)
88+
postgresql.Status.DatabaseStatus.SetDatabaseStatus(infrav1beta1.Unavailable, err.Error(), "", postgresql.Spec.HostName).
89+
WithUsername(postgresql.Spec.RootUsername).
90+
WithAuthDatabase(postgresql.Spec.RootAuthenticationDatabase).
91+
WithRootSecretLookup(postgresql.Spec.RootSecretLookup.Name, postgresql.Spec.RootSecretLookup.Namespace, postgresql.Spec.RootSecretLookup.Field)
9792
postgresql.Status.CredentialsStatus = make(infrav1beta1.CredentialsStatus, 0)
9893
return r.updateAndReturn(&ctx, &postgresql, &log)
9994
}
10095
// vault connection, cached
10196
vault, err := r.VaultCache.Get(postgresql.Spec.RootSecretLookup.Name)
10297
if err != nil {
103-
postgresql.Status.DatabaseStatus.SetDatabaseStatus(infrav1beta1.Unavailable, err.Error(), "", postgresql.Spec.HostName)
98+
postgresql.Status.DatabaseStatus.SetDatabaseStatus(infrav1beta1.Unavailable, err.Error(), "", postgresql.Spec.HostName).
99+
WithUsername(postgresql.Spec.RootUsername).
100+
WithAuthDatabase(postgresql.Spec.RootAuthenticationDatabase).
101+
WithRootSecretLookup(postgresql.Spec.RootSecretLookup.Name, postgresql.Spec.RootSecretLookup.Namespace, postgresql.Spec.RootSecretLookup.Field)
104102
postgresql.Status.CredentialsStatus = make(infrav1beta1.CredentialsStatus, 0)
105103
return r.updateAndReturn(&ctx, &postgresql, &log)
106104
}
107105

108-
// - garbage collection
109-
// TODO for now, disallow database renaming
110-
if postgresql.Spec.DatabaseName != postgresql.Status.DatabaseStatus.Name && postgresql.Status.DatabaseStatus.Name != "" {
111-
postgresql.Status.DatabaseStatus.SetDatabaseStatus(infrav1beta1.Unavailable, "Cannot change the name of the database.", "", postgresql.Spec.HostName)
112-
postgresql.Status.CredentialsStatus = make(infrav1beta1.CredentialsStatus, 0)
113-
r.ServerCache.Remove(postgresql.Spec.HostName)
114-
return r.updateAndReturn(&ctx, &postgresql, &log)
115-
}
116-
117106
// Setup database
118107
if err := postgreSQLServer.CreateDatabaseIfNotExists(postgresql.Spec.DatabaseName); err != nil {
119-
postgresql.Status.DatabaseStatus.SetDatabaseStatus(infrav1beta1.Unavailable, err.Error(), "", postgresql.Spec.HostName)
108+
postgresql.Status.DatabaseStatus.SetDatabaseStatus(infrav1beta1.Unavailable, err.Error(), "", postgresql.Spec.HostName).
109+
WithUsername(postgresql.Spec.RootUsername).
110+
WithAuthDatabase(postgresql.Spec.RootAuthenticationDatabase).
111+
WithRootSecretLookup(postgresql.Spec.RootSecretLookup.Name, postgresql.Spec.RootSecretLookup.Namespace, postgresql.Spec.RootSecretLookup.Field)
120112
postgresql.Status.CredentialsStatus = make(infrav1beta1.CredentialsStatus, 0)
121113
r.ServerCache.Remove(postgresql.Spec.HostName)
122114
return r.updateAndReturn(&ctx, &postgresql, &log)
123115
}
124-
postgresql.Status.DatabaseStatus.SetDatabaseStatus(infrav1beta1.Available, "Database up.", postgresql.Spec.DatabaseName, postgresql.Spec.HostName)
125-
126-
// - garbage collection
127-
// Delete all credentials if they exist, and are no longer required by spec
128-
if postgresql.Spec.Credentials == nil {
129-
postgresql.Status.CredentialsStatus.ForEach(func(status *infrav1beta1.CredentialStatus) {
130-
_ = postgreSQLServer.DropUser(status.Username, postgresql.Status.DatabaseStatus.Name)
131-
})
132-
postgresql.Status.CredentialsStatus = make([]*infrav1beta1.CredentialStatus, 0)
133-
return r.updateAndReturn(&ctx, &postgresql, &log)
134-
}
116+
postgresql.Status.DatabaseStatus.SetDatabaseStatus(infrav1beta1.Available, "Database up.", postgresql.Spec.DatabaseName, postgresql.Spec.HostName).
117+
WithUsername(postgresql.Spec.RootUsername).
118+
WithAuthDatabase(postgresql.Spec.RootAuthenticationDatabase).
119+
WithRootSecretLookup(postgresql.Spec.RootSecretLookup.Name, postgresql.Spec.RootSecretLookup.Namespace, postgresql.Spec.RootSecretLookup.Field)
135120

136121
// setup credentials as per spec
137122
for _, credential := range postgresql.Spec.Credentials {
@@ -155,12 +140,6 @@ func (r *PostgreSQLReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error)
155140
}
156141
}
157142

158-
// - garbage collection
159-
// remove all statuses for credentials that are no longer required by spec, and delete users in database
160-
postgresql.RemoveUnneededCredentialsStatus().ForEach(func(status *infrav1beta1.CredentialStatus) {
161-
_ = postgreSQLServer.DropUser(status.Username, postgresql.Status.DatabaseStatus.Name)
162-
})
163-
164143
return r.updateAndReturn(&ctx, &postgresql, &log)
165144
}
166145

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package controllers
2+
3+
import (
4+
infrav1beta1 "github.com/doodlescheduling/kubedb/api/v1beta1"
5+
postgresqlAPI "github.com/doodlescheduling/kubedb/common/db/postgresql"
6+
"github.com/go-logr/logr"
7+
)
8+
9+
type PostgreSQLGarbageCollector struct {
10+
r *PostgreSQLReconciler
11+
cw *ControllerWrapper
12+
log *logr.Logger
13+
}
14+
15+
func NewPostgreSQLGarbageCollector(r *PostgreSQLReconciler, cw *ControllerWrapper, log *logr.Logger) *PostgreSQLGarbageCollector {
16+
return &PostgreSQLGarbageCollector{
17+
r: r,
18+
cw: cw,
19+
log: log,
20+
}
21+
}
22+
23+
/* For now, Garbage Collector does not drop databases, because we haven't decided if we want to delete all data.
24+
Possible avenue to proceed is to have a separate option flag/struct (to be set in Spec), that will force deletion of all garbage, including data.
25+
*/
26+
func (g *PostgreSQLGarbageCollector) Clean(postgresql *infrav1beta1.PostgreSQL) error {
27+
rootPassword, err := g.cw.GetRootPassword(postgresql.Status.DatabaseStatus.RootSecretLookup.Name, postgresql.Status.DatabaseStatus.RootSecretLookup.Namespace, postgresql.Status.DatabaseStatus.RootSecretLookup.Field)
28+
if err != nil {
29+
// no point in proceeding. In future could also try with Spec credential
30+
return err
31+
}
32+
postgreSQLServer, err := g.r.ServerCache.Get(postgresql.Status.DatabaseStatus.Host, postgresql.Status.DatabaseStatus.RootUsername, rootPassword, postgresql.Status.DatabaseStatus.RootAuthenticationDatabase)
33+
if err != nil {
34+
// no point in proceeding. In future could also try with Spec credential
35+
return err
36+
}
37+
// if an error happens, just collect it and try to clean as much as possible. Return it at the end. This is a pattern in for most of this Garbage Collector.
38+
var errToReturn error
39+
errToReturn = g.HandleHostOrDatabaseChange(postgreSQLServer, postgresql, rootPassword)
40+
errToReturn = g.HandleUnneededCredentials(postgresql, postgreSQLServer)
41+
return errToReturn
42+
}
43+
44+
// if host or database changed in spec, try to clean on old host/database
45+
func (g *PostgreSQLGarbageCollector) HandleHostOrDatabaseChange(postgreSQLServer *postgresqlAPI.PostgreSQLServer, postgresql *infrav1beta1.PostgreSQL, rootPassword string) error {
46+
var errToReturn error
47+
if (postgresql.Status.DatabaseStatus.Host != "" && postgresql.Spec.HostName != postgresql.Status.DatabaseStatus.Host) ||
48+
(postgresql.Status.DatabaseStatus.Name != "" && postgresql.Spec.DatabaseName != postgresql.Status.DatabaseStatus.Name) {
49+
postgresql.Status.CredentialsStatus.ForEach(func(status *infrav1beta1.CredentialStatus) {
50+
err := postgreSQLServer.DropUser(status.Username, postgresql.Status.DatabaseStatus.Name)
51+
if err != nil {
52+
errToReturn = err
53+
} else {
54+
(*g.log).Info("Deleted user on database", "user", status.Username, "database", postgresql.Status.DatabaseStatus.Name)
55+
}
56+
})
57+
58+
}
59+
return errToReturn
60+
}
61+
62+
func (g *PostgreSQLGarbageCollector) HandleUnneededCredentials(postgresql *infrav1beta1.PostgreSQL, postgreSQLServer *postgresqlAPI.PostgreSQLServer) error {
63+
var errToReturn error
64+
// - garbage collection
65+
// remove all statuses for credentials that are no longer required by spec, and delete users in database
66+
postgresql.RemoveUnneededCredentialsStatus().ForEach(func(status *infrav1beta1.CredentialStatus) {
67+
errToReturn = postgreSQLServer.DropUser(status.Username, postgresql.Status.DatabaseStatus.Name)
68+
})
69+
return errToReturn
70+
}

0 commit comments

Comments
 (0)