Skip to content

Commit

Permalink
fix: correct reconcile loop and mongo db creation error
Browse files Browse the repository at this point in the history
  • Loading branch information
anbraten committed Feb 1, 2022
1 parent 0a173f3 commit d8ba7d6
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 94 deletions.
11 changes: 6 additions & 5 deletions adapters/mongo_adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,14 @@ func (adapter mongoAdapter) HasDatabase(ctx context.Context, database string) (b
return contains(databaseNames, database), err
}

func (adapter mongoAdapter) CreateDatabase(ctx context.Context, name string) error {
adapter.client.Database(name)
return nil
func (adapter mongoAdapter) CreateDatabase(ctx context.Context, database string) error {
// create dummy data as mongo only creates databases if they contain something
_, err := adapter.client.Database(database).Collection("__internal-placeholder__").InsertOne(ctx, bson.D{{Key: "empty", Value: true}})
return err
}

func (adapter mongoAdapter) DeleteDatabase(ctx context.Context, name string) error {
return adapter.client.Database(name).Drop(ctx)
func (adapter mongoAdapter) DeleteDatabase(ctx context.Context, database string) error {
return adapter.client.Database(database).Drop(ctx)
}

func (adapter mongoAdapter) HasDatabaseUserWithAccess(ctx context.Context, database string, username string) (bool, error) {
Expand Down
141 changes: 52 additions & 89 deletions controllers/database_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const databaseFinalizer = "finalizer.database.anbraten.github.io"
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.7.2/pkg/reconcile
func (r *DatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := r.Log.WithValues("database", req.NamespacedName)
log.Info("Reconciling database")

// Fetch the Database instance
database := &anbratengithubiov1alpha1.Database{}
Expand All @@ -54,58 +55,22 @@ func (r *DatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c

db, err := r.getDatabaseConnection(ctx, database.Spec.Type)
if err != nil {
log.Error(err, "Failed to detect and open database connection")
log.Error(err, "Failed to detect or open database connection")
return ctrl.Result{}, err
}

defer db.Close(ctx)

log.Info("Connected to database server")

hasDatabase, err := db.HasDatabase(ctx, database.Spec.Database)
if err != nil {
log.Error(err, "Couldn't check if database exists")
return ctrl.Result{}, err
}

if !hasDatabase {
log.Info("Create new database: '" + database.Spec.Database + "'")

err = db.CreateDatabase(ctx, database.Spec.Database)
if err != nil {
log.Error(err, "Can't create database")
return ctrl.Result{}, err
}
}

hasDatabaseUserWithAccess, err := db.HasDatabaseUserWithAccess(ctx, database.Spec.Database, database.Spec.Username)
if err != nil {
log.Error(err, "Can't check if user has access to database")
return ctrl.Result{}, err
}

if !hasDatabaseUserWithAccess {
log.Info("Create new user '" + database.Spec.Username + "' with access to the database '" + database.Spec.Database + "'")

err = db.CreateDatabaseUser(ctx, database.Spec.Database, database.Spec.Username, database.Spec.Password)
if err != nil {
log.Error(err, "Can't create database user with access to database")
return ctrl.Result{}, err
}
}

log.Info("Created database and user with full access to it")

// Check if the Database instance is marked to be deleted, which is
// indicated by the deletion timestamp being set.
isDatabaseMarkedToBeDeleted := database.GetDeletionTimestamp() != nil
if isDatabaseMarkedToBeDeleted {
if contains(database.GetFinalizers(), databaseFinalizer) {
if controllerutil.ContainsFinalizer(database, databaseFinalizer) {
// Run finalization logic for databaseFinalizer. If the
// finalization logic fails, don't remove the finalizer so
// that we can retry during the next reconciliation.
if err := r.finalizeDatabase(ctx, log, database); err != nil {
log.Error(err, "Can't create finalizer for database")
if err := r.finalizeDatabase(ctx, log, db, database); err != nil {
log.Error(err, "Can't remove database and user")
return ctrl.Result{}, err
}

Expand All @@ -114,19 +79,52 @@ func (r *DatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c
controllerutil.RemoveFinalizer(database, databaseFinalizer)
err := r.Update(ctx, database)
if err != nil {
log.Error(err, "Can't remove finalizers of database")
return ctrl.Result{}, err
}
}
return ctrl.Result{}, nil
}

// Add finalizer for this CR
if !contains(database.GetFinalizers(), databaseFinalizer) {
if err := r.addFinalizer(ctx, log, database); err != nil {
log.Error(err, "Can't add finalizer to this custom-resource (database)")
// Add finalizer for this CR if necessary
if !controllerutil.ContainsFinalizer(database, databaseFinalizer) {
controllerutil.AddFinalizer(database, databaseFinalizer)
err = r.Update(ctx, database)
if err != nil {
return ctrl.Result{}, err
}
}

// Create database if necessary
hasDatabase, err := db.HasDatabase(ctx, database.Spec.Database)
if err != nil {
log.Error(err, "Couldn't check if database '", database.Spec.Database, "' exists")
return ctrl.Result{}, err
} else if !hasDatabase {
log.Info("Creating new database '", database.Spec.Database, "'")

err = db.CreateDatabase(ctx, database.Spec.Database)
if err != nil {
log.Error(err, "Can't create database")
return ctrl.Result{}, err
}

log.Info("Created database '", database.Spec.Database, "'")
}

// Create database user with full access if necessary
hasDatabaseUserWithAccess, err := db.HasDatabaseUserWithAccess(ctx, database.Spec.Database, database.Spec.Username)
if err != nil {
log.Error(err, "Can't check if user has access to database")
return ctrl.Result{}, err
} else if !hasDatabaseUserWithAccess {
log.Info("Creating new user '" + database.Spec.Username + "' and granting access to database '" + database.Spec.Database + "'")

err = db.CreateDatabaseUser(ctx, database.Spec.Database, database.Spec.Username, database.Spec.Password)
if err != nil {
log.Error(err, "Can't create database user with access to database")
return ctrl.Result{}, err
}
log.Info("Created user '" + database.Spec.Username + "' and granted access to database '" + database.Spec.Database + "'")
}

return ctrl.Result{}, nil
Expand Down Expand Up @@ -168,73 +166,38 @@ func (r *DatabaseReconciler) getDatabaseConnection(ctx context.Context, database
return nil, errors.NewBadRequest("Database type not supported")
}

func (r *DatabaseReconciler) finalizeDatabase(ctx context.Context, log logr.Logger, database *anbratengithubiov1alpha1.Database) error {
db, err := r.getDatabaseConnection(ctx, database.Spec.Type)
if err != nil {
return err
}

defer db.Close(ctx)

func (r *DatabaseReconciler) finalizeDatabase(ctx context.Context, log logr.Logger, db adapters.DatabaseAdapter, database *anbratengithubiov1alpha1.Database) error {
// remove database user and database access if it exists
hasDatabaseUserWithAccess, err := db.HasDatabaseUserWithAccess(ctx, database.Spec.Database, database.Spec.Username)
if err != nil {
return err
}

if hasDatabaseUserWithAccess {
log.Info("Remove user '" + database.Spec.Username + "' and its access to the database '" + database.Spec.Database + "'")
} else if hasDatabaseUserWithAccess {
log.Info("Removing user '" + database.Spec.Username + "' and revoking access to the database '" + database.Spec.Database + "'")

err = db.DeleteDatabaseUser(ctx, database.Spec.Database, database.Spec.Username)
if err != nil {
return err
}
log.Info("Removed database user '" + database.Spec.Username + "' and revoked access to the database '" + database.Spec.Database + "'")
}

// remove database if it exists
hasDatabase, err := db.HasDatabase(ctx, database.Spec.Database)
if err != nil {
return err
}

if hasDatabase {
log.Info("Remove database: '" + database.Spec.Database + "'")
} else if hasDatabase {
log.Info("Removing database '" + database.Spec.Database + "'")

err = db.DeleteDatabase(ctx, database.Spec.Database)
if err != nil {
return err
}
log.Info("Removed database '" + database.Spec.Database + "'")
}

err = db.DeleteDatabase(ctx, database.Spec.Database)
if err != nil {
return err
}

log.Info("Database: '" + database.Spec.Database + "' and user: '" + database.Spec.Username + "' removed")
return nil
}

func (r *DatabaseReconciler) addFinalizer(ctx context.Context, log logr.Logger, m *anbratengithubiov1alpha1.Database) error {
log.Info("Adding Finalizer for the database")
controllerutil.AddFinalizer(m, databaseFinalizer)

// Update CR
err := r.Update(ctx, m)
if err != nil {
log.Error(err, "Failed to update database with finalizer")
return err
}
return nil
}

func contains(list []string, s string) bool {
for _, v := range list {
if v == s {
return true
}
}
return false
}

// SetupWithManager sets up the controller with the Manager.
func (r *DatabaseReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
Expand Down

0 comments on commit d8ba7d6

Please sign in to comment.