diff --git a/adapters/mongo_adapter.go b/adapters/mongo_adapter.go index 45120a6..8e4c2af 100644 --- a/adapters/mongo_adapter.go +++ b/adapters/mongo_adapter.go @@ -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) { diff --git a/controllers/database_controller.go b/controllers/database_controller.go index 36bb7d0..753b832 100644 --- a/controllers/database_controller.go +++ b/controllers/database_controller.go @@ -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{} @@ -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 } @@ -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 @@ -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).