Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PostgreSQL user creation logic was implemented #542

Merged
merged 2 commits into from
Sep 15, 2023

Conversation

DoodgeMatvey
Copy link
Contributor

Closes #428

@DoodgeMatvey DoodgeMatvey added the enhancement New feature or request label Aug 31, 2023
@DoodgeMatvey DoodgeMatvey self-assigned this Aug 31, 2023
@DoodgeMatvey DoodgeMatvey force-pushed the issue-134-create-user branch 2 times, most recently from 7b03af4 to 0fc2d03 Compare August 31, 2023 16:35
Comment on lines 147 to 148
event = models.Created
u.Status.ClustersEvents[namespacedName] = event
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can omit to assign to event var and just do u.Status.ClustersEvents[namespacedName] = models.Created

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, fixed

}

l.Info("User has been created", "username", newUsername)
fmt.Println("userName and ns:", u.Name, namespacedName)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please delete this log

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

Name: parts[1],
Namespace: parts[0],
}
err := r.Get(context.TODO(), userSecretNamespacedName, defaultUserSecret)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe you should you the context that is passed into your Reconcile method?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is reconcile, not controller

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But this func is used in reconcile func, so you just can pass it's context in createUser func trought signature

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, fixed in all such cases

createUserQuery := fmt.Sprintf(`CREATE USER "%s" WITH PASSWORD '%s'`, newUserName, newPassword)
_, err = conn.Exec(context.Background(), createUserQuery)
if err != nil {
return fmt.Errorf("cannot execute creation user query in postgresql err, err: %w", err)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cannot execute creation user query in postgresql err, err -> cannot execute creation user query in postgresql, err: %w

Copy link
Contributor Author

@DoodgeMatvey DoodgeMatvey Sep 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed

Comment on lines 261 to 260
createUserQuery := fmt.Sprintf(`CREATE USER "%s" WITH PASSWORD '%s'`, newUserName, newPassword)
_, err = conn.Exec(context.Background(), createUserQuery)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

conn.Exec requires query and optinal arguments. I think you should omit to create the whole query by yourself. Just do

createUserQuery := `CREATE USER '$1' WITH PASSWORD '$2'`
_, err = conn.Exec(ctx, createUserQuery, newUserName, newPassword)

I hope pgx driver also validates passed arguments to avoid SQL injections, which are possible in your case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We cannot do this because it is important to have " " and ' ' in query

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uhm, I didn't get what you meant. I gave the example with those '' in the query.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean that example that you provided won't work. In the comment above, I described why

Copy link
Collaborator

@worryg0d worryg0d Sep 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see. You have to use the quotes for the SQL.
If you put those " " and ' ' quotes to the example of mine, does the problem still occur? Because it looks a bit strange, so I'm asking about it.
Edited example:

createUserQuery := `CREATE USER "$1" WITH PASSWORD '$2'`

Copy link
Contributor Author

@DoodgeMatvey DoodgeMatvey Sep 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I already tested your solution and it does not work. That's why I wrote this query in fmt.Sprintf

APIVersion: models.ClusterresourcesV1beta1APIVersion,
},
ObjectMeta: ctrl.ObjectMeta{
Name: fmt.Sprintf("%s-%s-%s", models.ClusterNetworkFirewallRulePrefix, clusterID, nodeName),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use the previously created var firewallRuleName

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

Comment on lines 301 to 307
Status: clusterresourcesv1beta1.ClusterNetworkFirewallRuleStatus{
FirewallRuleStatus: clusterresourcesv1beta1.FirewallRuleStatus{
ID: "",
DeferredReason: "",
Status: "",
},
},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I'm not wrong there is no any pointer value, so you can omit this Status initialization

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

return false, err
}

return true, err
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return true, err -> return true, nil

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed


if len(username) == 0 || len(password) == 0 {
return "", "", models.ErrMissingSecretKeys
}

return username[:len(username)-1], password[:len(password)-1], nil
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How can we handle the case if the customer will encode creds without removing the last character?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added validation for such things, If the user adds an indent, we will handle it and remove it


for namespacedName, event := range u.Status.ClustersEvents {
if event == models.CreatingEvent {
l.Info("Creating user", "user", u, "namespacedName", namespacedName)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

namespacedName -> userSecretNamespacedName or secretNamespacedName

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactored, changed this to ClusterID and add new field in status DefaultSecretNamespacedName

@@ -622,3 +601,22 @@ func (pgs *PgStatus) DCsFromInstAPI(iDCs []*models.PGDataCentre) (dcs []*DataCen
}
return
}

func GetDefaultPgUserSecret(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can implement it as reconciler method, wdyt?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because it is a separate function that does not use postgresql cluster fields and gets the name and namespace for the secret. So I think we can leave this as just a function and not a method.

@DoodgeMatvey DoodgeMatvey force-pushed the issue-134-create-user branch 2 times, most recently from 7b4b068 to 625feaf Compare September 11, 2023 11:38
// The keys of the map represent the cluster IDs, while the corresponding values represent the events themselves.
ClustersEvents map[string]string `json:"clustersEvents,omitempty"`
DefaultSecretNamespacedName string `json:"defaultSecretNamespacedName,omitempty"`
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can store NamespacedName as struct wdyt?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, we can, changed

}

clusterName := defaultUserSecret.Labels[models.ControlledByLabel]
clusterID := defaultUserSecret.Labels[models.ClusterIDLabel]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can pass ClusterID trought func signature when you iterate by cluster events

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

}

defaultUserSecret := &k8sCore.Secret{}
userSecretNamespacedName := types.NamespacedName{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

userSecretNamespacedName -> defaultUserSecretNamespacedName

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

renamed

if err != nil {
if k8sErrors.IsNotFound(err) {
return fmt.Errorf("cannot get default PostgreSQL user secret, username: %s , err: %w", parts[1], err)
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can use userName as userSecretNamespacedName.Name instead of parts[1] for more readability. If you agree fix in all similar cases

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

exposeServiceList, err := exposeservice.GetExposeService(r.Client, clusterName, parts[0])
if err != nil {
return fmt.Errorf("cannot list expose services for cluster: %s, err: %w", parts[0], err)
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess parts[0] is a secret namespace not a cluster name(also we can use here already created clusterName variable)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic changed, refactored

return err
}

secret, err := v1beta1.GetDefaultPgUserSecret(context.TODO(), c.Name, c.Namespace, r.Client)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why you dont use ctx that we have in func signature?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

u.Status.ClustersEvents = make(map[string]string)
}

u.Status.DefaultSecretNamespacedName = defaultSecretNamespacedName.String()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will it work on multiple cluster?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice catch, fixed and tested

ClusterID: clusterID,
Type: models.PgAppType,
},
Network: fmt.Sprintf("%s/%s", nodeAddress, "32"),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can just do

fmt.Sprintf("%s/32", nodeAddress)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It also can be, but I prefer my variant to move it in const in future

u.Status.ClustersEvents = make(map[string]string)
}

u.Status.DefaultSecretNamespacedName = defaultSecretNamespacedName.String()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need the reference to the default user in the PostgreSQLUserController to create a new one. But what if there is more than 1 cluster reference to the given user resource?
I think it would be better if we save the reference in another map like map[ClusterID]DefaultSecretNamespacedName. Or modify ClusterEvents to something like that:

type ClusterEvent struct {
    Event string `json:"event,omitempty"`
    DefaultSecretRef types.NamespacedName `json:"defautSecretRef,omitempty"`
}

type PostgreSQLUserStatus struct {
    ClusterEvents map[ClusterID]ClusterEvent `json:"clusterEvents,omitempty"
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice catch, fixed and tested

Comment on lines 19 to 33
import (
"context"
"errors"
"fmt"
"github.com/jackc/pgx/v5"
k8sCore "k8s.io/api/core/v1"
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/tools/record"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/log"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sort imports please

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed


l.Error(err, "Cannot get PostgreSQL user", "user", u.Spec)
r.EventRecorder.Eventf(c, models.Warning, models.CreationFailed,
"Cannot get PostgreSQL user. user reference: %v", uRef)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cannot get PostgreSQL user. user reference -> Cannot get PostgreSQL user. User reference

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

@@ -89,6 +89,7 @@ type PgSpec struct {
ClusterConfigurations map[string]string `json:"clusterConfigurations,omitempty"`
Description string `json:"description,omitempty"`
SynchronousModeStrict bool `json:"synchronousModeStrict,omitempty"`
UserRefs []*UserReference `json:"userRef,omitempty"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

json:userRefs

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

Comment on lines +43 to +46
type NamespacedName struct {
Namespace string `json:"namespace"`
Name string `json:"name"`
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there is no need to create this struct, please just use the existing types.NamespacedName.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we need to create ours because types.NamespacedName does not have json representation

Comment on lines 112 to 121
newUsername, newPassword, err := getUserCreds(s)
if err != nil {
l.Error(err, "Cannot get the PostgreSQL user credentials from the secret",
"secret name", s.Name,
"secret namespace", s.Namespace)
r.EventRecorder.Eventf(u, models.Warning, models.CreatingEvent,
"Cannot get the PostgreSQL user credentials from the secret. Reason: %v", err)

return models.ReconcileRequeue, nil
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move this block of code before adding finalizers

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and name vars just username and password

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved this block before finalazers, we have default username and password and new password. We need to clearly distinguish them for better readability

Comment on lines 151 to 160
err = r.Status().Patch(ctx, u, patch)
if err != nil {
l.Error(err, "Cannot patch PostgreSQL user status")
r.EventRecorder.Eventf(u, models.Warning, models.PatchFailed,
"Resource patch is failed. Reason: %v", err)

return models.ReconcileRequeue, nil
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please make messages more detailed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added details

@DoodgeMatvey DoodgeMatvey merged commit 0753266 into main Sep 15, 2023
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

issue-134, Implement user creation flow for PostgreSQL users
5 participants