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

Remove secret from MariaDBDatabase and use MariaDBAccount instead #179

Merged

Conversation

zzzeek
Copy link
Contributor

@zzzeek zzzeek commented Jan 11, 2024

this change proposes an initial step to transition to the use of MariaDBAccount to manage DB username/password combinations, rather than using MariaDBDatabase as is the case right now.

Downstream services make use of the Database{} API as an abstraction layer on top of MariaDBDatabase in order to ensure their database exists. They call upon 1. mariadbv1.NewDatabase to create the object and then 2. CreateOrPatchDB to perform the work to make this database available.

Prior to this PR, CreateOrPatchDB creates a MariaDBDatabase CR; MariaDBDatabase does both CREATE DATABASE as well as CREATE USER. MariaDBDatabase includes a secret parameter that is combined with the name of the database itself as a hardcoded username to create a database plus a login. the login cannot be changed independently of the database itself. Even though Database has a separate username parameter Database.databaseUser, this parameter is currently ignored.

The change made to this PR is that CreateOrPatchDB creates a MariaDBDatabase CR and a MariaDBAccount CR, making use of the Database.databaseUser parameter to create both a database and a username/password that is not hardcoded and can vary independently of the database. Appropriate finalizers are applied to both the MariaDBDatabase and MariaDBAccount CRs from the downstream controller.

Towards this change, the secret parameter is deprecated inMariaDBDatabase, defaulting to non-present; MariaDBDatabase no longer creates a username/password by itself in the new database unless "secret" is present. By doing so, MariaDBAccount is free to create database accounts where the username matches the name of the database, which is currently required by downstream controllers that all establish Database.databaseName andDatabase.databaseUser using the same name.

The "secret" parameter continues to be supported by MariaDBDatabase in an optional way so that downstream operators built on previous versions of the mariadb-operator will still continue to function, since they are still generating MariaDBDatabase objects with "secrets" and they are not generating MariaDBAccount objects. Once all downstream operators are built against this version of the code, where the Database construct creates a separate MariaDBAccount, then the "secret" parameter can be fully removed from MariaDBDatabase and the logic to generate and drop mariadb accounts can be removed from database.go.

These steps are intended only to integrate MariaDBAccount and independent username/passwords into the existing flow, without changing the flow itself, as this would be a breaking change in downstream controllers. The downstream controllers right now pass an explicit "databaseuser" and "secret" into Database, not the other way around. There is also no mechanism for downstream controllers to respond to changes in database username / password. this can't be implemented until MariaDBAccount is integrated as the controllers would need to be watching and consuming their corresponding MariaDBAccount objects directly.

Introduction of an independent "account / password generation service" would be done in a subsequent step. Subsequent steps would be:

  1. downstream operators will build against this version of mariadb-operator or greater.
  2. "secret" logic is removed from MariaDBDatabase.
  3. downstream operators would need to be modified to become responsive to changes in MariaDBAccount CRs so that they can rewrite their configs and re-deploy pods when the MariaDBAccount they rely upon is to be replaced with a new one.
  4. a new flow would need to be introduced that can generate as well as rotate username/passwords which can be deployed using the MariaDBAccount CR. The nature of this flow is TBD.
  5. downstream operators would no longer have any kind of "database username" or "database secret" in their own CRs - this would be created dynamically by an account rotation service.

@openshift-ci openshift-ci bot requested review from abays and dciabrin January 11, 2024 22:14
@zzzeek zzzeek requested a review from gibizer January 11, 2024 22:15
@zzzeek zzzeek marked this pull request as draft January 11, 2024 22:15
@zzzeek
Copy link
Contributor Author

zzzeek commented Jan 11, 2024

/ok-to-test

@@ -157,11 +162,28 @@ func (d *Database) CreateOrPatchDBByName(
},
Spec: MariaDBDatabaseSpec{
// the DB name must not change, therefore specify it outside the mutuate function
Name: d.databaseName,
Name: d.databaseName,
DefaultCharacterSet: "utf8",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

suggestions welcome how to set these defaults for the MariaDBDatabaseSpec

Copy link
Contributor

Choose a reason for hiding this comment

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

Unfortunately golang and json defaulting is not working nicely together. When you instantiate MariaDBDatabaseSpec struct here, golang will fill all the undefined fields of the struct with the golang default value of the field type. For string it is "". Then when you send this struct to the json serializer (when send it to the k8s API) the json serializer sees the "" value and puts that into the json payload. Then when that is deserialized by the API from json to golang, it will see that the field is specified so no defaulting is applied.

You can work around this by telling the json serializer not to emit the field to the json payload if the value of the field is the golang default value of the field type. The keyword is omitempty

The following def will consider "" as a value that means no value is provided:

	// +kubebuilder:default=utf8
	// Default character set for this database
	DefaultCharacterSet string `json:"defaultCharacterSet,omitempty"`

If the empty string "" is a valid field value for the CR then you can do an additional step to keep "" valid. You can make the field as a string pointer in golang. This way "" is a valid value and nil will be used to signal unspecified value instead:

	// +kubebuilder:default=utf8
	// Default character set for this database
	DefaultCharacterSet *string `json:"defaultCharacterSet,omitempty"`

@zzzeek zzzeek force-pushed the remove_secret_from_db branch from 7a4fceb to 86c0781 Compare January 12, 2024 15:17
@zzzeek
Copy link
Contributor Author

zzzeek commented Jan 13, 2024

/retest

api/v1beta1/mariadbdatabase_types.go Show resolved Hide resolved
controllers/mariadbdatabase_controller.go Show resolved Hide resolved
controllers/mariadbdatabase_controller.go Show resolved Hide resolved
controllers/mariadbdatabase_controller.go Show resolved Hide resolved
api/v1beta1/mariadbdatabase_funcs.go Show resolved Hide resolved
api/v1beta1/mariadbdatabase_funcs.go Show resolved Hide resolved
@@ -157,11 +162,28 @@ func (d *Database) CreateOrPatchDBByName(
},
Spec: MariaDBDatabaseSpec{
// the DB name must not change, therefore specify it outside the mutuate function
Name: d.databaseName,
Name: d.databaseName,
DefaultCharacterSet: "utf8",
Copy link
Contributor

Choose a reason for hiding this comment

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

Unfortunately golang and json defaulting is not working nicely together. When you instantiate MariaDBDatabaseSpec struct here, golang will fill all the undefined fields of the struct with the golang default value of the field type. For string it is "". Then when you send this struct to the json serializer (when send it to the k8s API) the json serializer sees the "" value and puts that into the json payload. Then when that is deserialized by the API from json to golang, it will see that the field is specified so no defaulting is applied.

You can work around this by telling the json serializer not to emit the field to the json payload if the value of the field is the golang default value of the field type. The keyword is omitempty

The following def will consider "" as a value that means no value is provided:

	// +kubebuilder:default=utf8
	// Default character set for this database
	DefaultCharacterSet string `json:"defaultCharacterSet,omitempty"`

If the empty string "" is a valid field value for the CR then you can do an additional step to keep "" valid. You can make the field as a string pointer in golang. This way "" is a valid value and nil will be used to signal unspecified value instead:

	// +kubebuilder:default=utf8
	// Default character set for this database
	DefaultCharacterSet *string `json:"defaultCharacterSet,omitempty"`

api/v1beta1/mariadbdatabase_funcs.go Outdated Show resolved Hide resolved
@zzzeek
Copy link
Contributor Author

zzzeek commented Jan 15, 2024

/retest

@zzzeek zzzeek force-pushed the remove_secret_from_db branch from 86c0781 to d5c1598 Compare January 15, 2024 22:59
by adding conditions, we allow the mariadbaccount to start its
work before the mariadbdatabase is fully completed.

this also brings MariaDBDatabase into the same condition style
as other controllers in play.
@zzzeek zzzeek force-pushed the remove_secret_from_db branch from d5c1598 to a1b8156 Compare January 16, 2024 14:09
@zzzeek zzzeek requested a review from SeanMooney January 16, 2024 14:44
@gibizer
Copy link
Contributor

gibizer commented Jan 18, 2024

Excellent PR summary message @zzzeek . It explained lot of my questions about the intentions of this and the next steps.

@zzzeek zzzeek marked this pull request as ready for review January 19, 2024 15:31
@openshift-ci openshift-ci bot requested a review from stuggi January 19, 2024 15:31
@zzzeek
Copy link
Contributor Author

zzzeek commented Jan 19, 2024

still looks like CI issues

error: build error: Failed to push image: trying to reuse blob sha256:07a64a71e01156f8f99039bc246149925c6d1480d3957de78510bbec6ec68f7a at destination: pinging container registry quay.rdoproject.org: Get "https://quay.rdoproject.org/v2/": net/http: TLS handshake timeout

@zzzeek
Copy link
Contributor Author

zzzeek commented Jan 22, 2024

/retest

1 similar comment
@zzzeek
Copy link
Contributor Author

zzzeek commented Jan 22, 2024

/retest

@zzzeek
Copy link
Contributor Author

zzzeek commented Jan 22, 2024

I just realized why this isn't building. will have to pull back the changes to mariadbdatabase and have it work "both" ways for cross-compatibility

This replaces the MariaDBDatabase.secret attribute
with the use of MariaDBAccount instead.  The MariaDBDatabase.secret
attribute becomes optional and deprecated.  Once all
consuming operators have moved to the new Database API,
MariaDBDatabase.secret can be removed entirely.

also applies an interim fix to allow the encoding/collation to be
present in the Database API, otherwise the database pod would fail.

Amends MariaDBAccount to return from delete if the owning Galera
resource is already deleted.
@zzzeek zzzeek force-pushed the remove_secret_from_db branch from a1b8156 to 4d759dd Compare January 22, 2024 21:06
@zzzeek zzzeek requested review from gibizer and SeanMooney January 22, 2024 21:11
@zzzeek
Copy link
Contributor Author

zzzeek commented Jan 22, 2024

I had to re-introduce "secret" to MariaDBDatabase since in the build, the downstream operators still build against the older version of the operator. this complicates the MariaDBDatabase operator since "secret" is now optional on that end.

@gibizer
Copy link
Contributor

gibizer commented Jan 23, 2024

I had to re-introduce "secret" to MariaDBDatabase since in the build, the downstream operators still build against the older version of the operator. this complicates the MariaDBDatabase operator since "secret" is now optional on that end.

It is fine by me.
I think we are close to the inevitable point of breaking the service operators and just go and fix them (I know it is a lot of work)

@zzzeek
Copy link
Contributor Author

zzzeek commented Jan 25, 2024

@dciabrin this should be ready to push

@dciabrin
Copy link
Contributor

/approve

Copy link
Contributor

openshift-ci bot commented Jan 25, 2024

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: dciabrin, gibizer, zzzeek

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-merge-bot openshift-merge-bot bot merged commit 331c633 into openstack-k8s-operators:main Jan 25, 2024
6 checks passed
@zzzeek zzzeek deleted the remove_secret_from_db branch January 25, 2024 20:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants