From 4edfd161c3bb45bf3c0eb85372cf3c9c72b4d8f7 Mon Sep 17 00:00:00 2001 From: Gavin Mogan Date: Wed, 21 Feb 2024 22:45:23 -0800 Subject: [PATCH] Add URIs to connection information Some applications like to have connection strings which isn't really possible to build with the current implementation, so take in the cluster connection string, and replace the user information with the user --- controllers/credentials_secret.go | 26 ++++++++++++++++--- controllers/databaseuser_controller.go | 14 +++++++--- .../databaseuserreference_controller.go | 14 +++++++--- 3 files changed, 45 insertions(+), 9 deletions(-) diff --git a/controllers/credentials_secret.go b/controllers/credentials_secret.go index 4c663272..684d152d 100644 --- a/controllers/credentials_secret.go +++ b/controllers/credentials_secret.go @@ -1,9 +1,11 @@ package controllers import ( + "fmt" "github.com/digitalocean/godo" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "net/url" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -32,8 +34,8 @@ func credentialsSecretForDefaultDBUser(owner client.Object, db *godo.Database) * return secret } -func credentialsSecretForDBUser(owner client.Object, user *godo.DatabaseUser) *corev1.Secret { - return &corev1.Secret{ +func credentialsSecretForDBUser(db *godo.Database, owner client.Object, user *godo.DatabaseUser) (*corev1.Secret, error) { + secret := &corev1.Secret{ TypeMeta: metav1.TypeMeta{ APIVersion: "v1", Kind: "Secret", @@ -45,7 +47,25 @@ func credentialsSecretForDBUser(owner client.Object, user *godo.DatabaseUser) *c StringData: map[string]string{ "username": user.Name, "password": user.Password, - // TODO(awg): Construct uri and private_uri from DB info. }, } + if db.Connection != nil { + dbUri, err := url.Parse(db.Connection.URI) + if err != nil { + return nil, fmt.Errorf("unable to parse connection uri: %s", err) + } + dbUri.User = url.UserPassword(user.Name, user.Password) + secret.StringData["uri"] = dbUri.String() + } + + if db.PrivateConnection != nil { + dbPrivateUri, err := url.Parse(db.PrivateConnection.URI) + if err != nil { + return nil, fmt.Errorf("unable to parse private connection uri: %s", err) + } + dbPrivateUri.User = url.UserPassword(user.Name, user.Password) + secret.StringData["private_uri"] = dbPrivateUri.String() + } + + return secret, nil } diff --git a/controllers/databaseuser_controller.go b/controllers/databaseuser_controller.go index dae3f34a..2c4adbf4 100644 --- a/controllers/databaseuser_controller.go +++ b/controllers/databaseuser_controller.go @@ -167,6 +167,11 @@ func (r *DatabaseUserReconciler) reconcileDBUser(ctx context.Context, clusterUUI "user_name", user.Spec.Username, ) + db, resp, err := r.GodoClient.Databases.Get(ctx, clusterUUID) + if err != nil { + return ctrl.Result{}, fmt.Errorf("checking for existing DB Cluster: %v", err) + } + // The validating webhook checks that the user doesn't already exist, so we // assume that if we find it to exist now we created it. If the user was // created between validation passing and getting here, we could assume @@ -193,7 +198,7 @@ func (r *DatabaseUserReconciler) reconcileDBUser(ctx context.Context, clusterUUI controllerutil.AddFinalizer(user, finalizerName) user.Status.Role = dbUser.Role - err = r.ensureOwnedObjects(ctx, user, dbUser) + err = r.ensureOwnedObjects(ctx, db, user, dbUser) if err != nil { ll.Error(err, "unable to ensure user-related objects") return ctrl.Result{}, fmt.Errorf("ensuring user-related objects: %v", err) @@ -202,7 +207,7 @@ func (r *DatabaseUserReconciler) reconcileDBUser(ctx context.Context, clusterUUI return ctrl.Result{RequeueAfter: 5 * time.Minute}, nil } -func (r *DatabaseUserReconciler) ensureOwnedObjects(ctx context.Context, user *v1alpha1.DatabaseUser, dbUser *godo.DatabaseUser) error { +func (r *DatabaseUserReconciler) ensureOwnedObjects(ctx context.Context, db *godo.Database, user *v1alpha1.DatabaseUser, dbUser *godo.DatabaseUser) error { // For some database engines the password is not returned when fetching a // user, only on initial creation. Avoid creating or updating the user // credentials secret if the password is empty, so we don't clear the @@ -211,7 +216,10 @@ func (r *DatabaseUserReconciler) ensureOwnedObjects(ctx context.Context, user *v return nil } - obj := credentialsSecretForDBUser(user, dbUser) + obj, err := credentialsSecretForDBUser(db, user, dbUser) + if err != nil { + return fmt.Errorf("creating secrett: %s", err) + } controllerutil.SetControllerReference(user, obj, r.Scheme) if err := r.Patch(ctx, obj, client.Apply, client.ForceOwnership, client.FieldOwner("do-operator")); err != nil { return fmt.Errorf("applying object %s: %s", client.ObjectKeyFromObject(obj), err) diff --git a/controllers/databaseuserreference_controller.go b/controllers/databaseuserreference_controller.go index 03224c7e..da05d679 100644 --- a/controllers/databaseuserreference_controller.go +++ b/controllers/databaseuserreference_controller.go @@ -135,6 +135,11 @@ func (r *DatabaseUserReferenceReconciler) reconcileDBUserReference(ctx context.C "user_name", userRef.Spec.Username, ) + db, _, err := r.GodoClient.Databases.Get(ctx, clusterUUID) + if err != nil { + return ctrl.Result{}, fmt.Errorf("looking up DB cluster: %v", err) + } + // The validating webhook checks that the user exists, so normally this // should work. However, the user could have been deleted in which case // we'll fail and back off in case it gets re-created. @@ -145,7 +150,7 @@ func (r *DatabaseUserReferenceReconciler) reconcileDBUserReference(ctx context.C userRef.Status.Role = dbUser.Role - err = r.ensureOwnedObjects(ctx, userRef, dbUser) + err = r.ensureOwnedObjects(ctx, db, userRef, dbUser) if err != nil { ll.Error(err, "unable to ensure user-related objects") return ctrl.Result{}, fmt.Errorf("ensuring user-related objects: %v", err) @@ -154,8 +159,11 @@ func (r *DatabaseUserReferenceReconciler) reconcileDBUserReference(ctx context.C return ctrl.Result{RequeueAfter: 5 * time.Minute}, nil } -func (r *DatabaseUserReferenceReconciler) ensureOwnedObjects(ctx context.Context, userRef *v1alpha1.DatabaseUserReference, dbUser *godo.DatabaseUser) error { - obj := credentialsSecretForDBUser(userRef, dbUser) +func (r *DatabaseUserReferenceReconciler) ensureOwnedObjects(ctx context.Context, db *godo.Database, userRef *v1alpha1.DatabaseUserReference, dbUser *godo.DatabaseUser) error { + obj, err := credentialsSecretForDBUser(db, userRef, dbUser) + if err != nil { + return fmt.Errorf("creating secrett: %s", err) + } controllerutil.SetControllerReference(userRef, obj, r.Scheme) if err := r.Patch(ctx, obj, client.Apply, client.ForceOwnership, client.FieldOwner("do-operator")); err != nil { return fmt.Errorf("applying object %s: %s", client.ObjectKeyFromObject(obj), err)