-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
feat(backend): add postgres initialization #9798
Conversation
backend/Dockerfile.persistenceagent
Outdated
go-licenses save ./backend/src/agent/persistence --save_path /tmp/NOTICES | ||
|
||
# RUN go-licenses csv ./backend/src/agent/persistence > /tmp/licenses.csv && \ | ||
# diff /tmp/licenses.csv backend/third_party_licenses/persistence_agent.csv && \ | ||
# go-licenses save ./backend/src/agent/persistence --save_path /tmp/NOTICES |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we save license file to /tmp/NOTICES
twice? What is the reason for this change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for pointing this out. I was temporarily disabling them, because the generated license files apparently changed although I didn't change this part of code. I intend to double check if we need to update all the license files.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should have this sorted out before we can merge the PR.
@@ -44,3 +45,26 @@ func CreateMySQLConfig(user, password string, mysqlServiceHost string, | |||
AllowNativePasswords: true, | |||
} | |||
} | |||
|
|||
func CreatePostgreSQLConfig(user, password, postgresHost, dbName string, postgresPort uint16, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you plan to introduce sslmode for security support in the coming PRs? It is important to make sure we are using secure channel when reading/writing to DB.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be great to improve security, but I wonder if it is necessary, since the DB and the backend are hosted in the same cluster.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This allows scenario like connecting to a managed database service. We can discuss about this in future implementation.
if password != "" { | ||
fmt.Fprintf(&b, "password=%s ", password) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we provide an option for passfile
as well? Reference: https://github.com/google/ml-metadata/blob/master/ml_metadata/proto/metadata_store.proto#L704
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
passfile
sounds like a great way to enhance security. Could you tell me more about it? My guess is this will allow users to pass a file containing the password as part of the deployment config. Maybe this can be added together with other configs users can set.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is right! So the passfile
identify the location path to a password, so you don't need to expose the secret via deployment. https://www.postgresql.org/docs/current/libpq-pgpass.html When passing this parameter to postgresql, the password
field can be omitted.
We can add this configuration in the coming PR, if this makes sense to you.
if postgresHost != "" { | ||
fmt.Fprintf(&b, "host=%s ", postgresHost) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we provide an option for hostaddr
? https://github.com/google/ml-metadata/blob/master/ml_metadata/proto/metadata_store.proto#L690
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if this is necessary, since the host address here is simply for communications inside the cluster. Could you tell me more why this is beneficial? Thank you!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure thing, hostaddr
is helpful when we are connecting to external database, for example: CloudSQL. Yes we can use host
which is handy for Unix-domain communication
, but hostaddr
allows postgresql client to make TCP/IP communication
instead.
Again, we can implement this feature in future PR.
func InitDBClient(initConnectionTimeout time.Duration) *storage.DB { | ||
// Allowed driverName values: | ||
// 1) To use MySQL, use `mysql` | ||
// 2) To use PostgreSQL, use `pgx` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we choose pgx
for postgresql in DBDriverName
flag? Is there an alternative which is easier for people to understand?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1, pgx
feels weird, albeit I saw that's the name of the go package you're using. I think postgres
would be still better here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I used this because we are using the package pgx
as the PostgreSQL driver, and it is needed in the sql.Open()
function to create the correct DB instance. See the example here. I agree it may be quite confusing to people, but I was weighting this against being consistent with the driver name we are using, so I eventually opted for making it clear in the comments to reduce the confusion. WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay I think @Linchin 's point is reasonable, we just need to make sure we document this parameter is being used as a sql driver flag: https://github.com/golang/go/wiki/SQLDrivers.
case "mysql": | ||
textFormat = "longtext not null" | ||
case "pgx": | ||
textFormat = "text" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We might need to define some const
for value like text
and longtext not null
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you help me understand why it is better to use const
values here? Thank you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DB dialects should be stored in a centralized place, so it helps reviewing, modifying and extending. For example: If we integrate with a new DB type in the future, I hope that I can view the existing syntax differences easily in one place, instead of trying to catch those adhoc variables during implementation.
Therefore, my recommendation is to define a const for MYSQL_TEXT_FORMAT
and PGX_TEXT_FORMAT
in a centralized file: https://github.com/kubeflow/pipelines/blob/master/backend/src/apiserver/storage/db.go
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you so much for the thourought explanation! I added the constants in backend/src/apiserver/client/sql.go
instead, because this is a SQL related dependency that client_manager is already using.
common.GetStringConfigWithDefault(postgresUser, ""), | ||
common.GetStringConfigWithDefault(postgresPassword, ""), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to set a default user like mysql does?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, thanks for finding this! I should add a default user here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added
AddForeignKey("RunUUID", "run_details(UUID)", "CASCADE" /* onDelete */, "CASCADE" /* update */) | ||
if response.Error != nil { | ||
glog.Fatalf("Failed to create a foreign key for RunUUID in task table. Error: %s", response.Error) | ||
switch driverName { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think extracting the different behavior of each driver to their own module is helpful. But feel free to defer it to future PRs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. This part was more of a patch work to make it work for now.
backend/Dockerfile.persistenceagent
Outdated
go-licenses save ./backend/src/agent/persistence --save_path /tmp/NOTICES | ||
|
||
# RUN go-licenses csv ./backend/src/agent/persistence > /tmp/licenses.csv && \ | ||
# diff /tmp/licenses.csv backend/third_party_licenses/persistence_agent.csv && \ | ||
# go-licenses save ./backend/src/agent/persistence --save_path /tmp/NOTICES |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should have this sorted out before we can merge the PR.
postgresPort = "DBConfig.PostgreSQLConfig.Port" | ||
postgresUser = "DBConfig.PostgreSQLConfig.User" | ||
postgresPassword = "DBConfig.PostgreSQLConfig.Password" | ||
postgresDBName = "DBConfig.PostgreSQLConfig.DBName" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The newly added Postgres configs seems so generic and completely covered by existing configs for mysql already. Do we need to separate them?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that they are pretty much the same fields here, but I think it is possible that there can be different values for the two types of DB, as well as different configs available, so it's safer to have one set of values for each of them. For example, the default host name and port number for MySQL and PostgreSQL are different.
@@ -12,15 +12,17 @@ | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
|
|||
package main | |||
package clientmanager |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the reason for moving this file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point! I moved it because I added integration test for functions here, but it's impossible to test stuff in "main" from a different package in Golang. Reference: https://stackoverflow.com/questions/53997114/how-to-test-the-main-package-in-golang-from-a-test-package
func InitDBClient(initConnectionTimeout time.Duration) *storage.DB { | ||
// Allowed driverName values: | ||
// 1) To use MySQL, use `mysql` | ||
// 2) To use PostgreSQL, use `pgx` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1, pgx
feels weird, albeit I saw that's the name of the go package you're using. I think postgres
would be still better here.
// Returns the same error, if it's not "already exists" related. | ||
// Otherwise, return nil. | ||
func ignoreAlreadyExistError(err error) error { | ||
if err != nil && strings.Contains(err.Error(), "exists") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method seems very fragile. I imagine there's could be multiple scenarios matching keyword "exists".
By the way, why do we need to ignore this now? Was it an issue for MySQL in the past or does only applies to Postgres?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added this because MySQL simply ignores if the foreign key already exist, but PostgreSQL reports an error. A more robust way I can think of is to use an if/else branch for the two SQLs and handle them separately.
|
||
### Run database tests with PostgreSQL | ||
|
||
To run this test, you need to first deploy the PostgreSQL images on your Kubernetes cluster. For how to deploy, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it in your next step to automate the test? We can rely on manual test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally we should, and also include PostgreSQL set of tests in the test infra.
if driverName == "pgx" && err != nil && strings.Contains(err.Error(), "already exists") { | ||
return nil | ||
} | ||
if driverName == "mysql" && err != nil && strings.Contains(err.Error(), "database exists") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can define consts for each error message keyword already exists
and database exists
in a centralized place: https://github.com/kubeflow/pipelines/blob/master/backend/src/apiserver/storage/db.go
@@ -2,7 +2,8 @@ apiVersion: kustomize.config.k8s.io/v1beta1 | |||
kind: Kustomization | |||
resources: | |||
- pg-deployment.yaml | |||
- pg-pv.yaml |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I shouldn't need a PV resource, it is automatically created by PVC when it is bound to a deployment resource.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure about this. I added the PV file because the PVC had trouble binding due to lack of PV. Maybe I was doing something wrong during my deployment. Could you give me more reference on this? Thank you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can look at the Events
for the PVC to learn more about why is PV not created. Maybe the disk driver has something wrong when provisioning a PV. But we shouldn't create PV resource manually in codebase. Refer to MySQL manifest folder: https://github.com/kubeflow/pipelines/tree/master/manifests/kustomize/third-party/mysql/base
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right about this. I tried a fresh deployment without the PV, and it passed the tests. The previous error was likely caused by something else. I removed the pv declaration.
/test kubeflow-pipeline-e2e-test |
part of #9813 |
/test kubeflow-pipeline-e2e-test |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/lgtm
Great work on this PR, Lingqing!
Waiting for Chen for a final approval.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/lgtm
Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/approve
Thanks!
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: chensun 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 |
I get the following error when the db driver name is configured to "gpx": client_manager.go:428] sql: unknown driver "pgx" (forgotten import?) According to this, it appears to be a missing import which will register the needed driver: https://stackoverflow.com/a/52791919 |
* add postgres initialization * remove load balancer * go mod tidy * update license * license update for viewer * (test) disable controller license check * (test) disable persistence agence licence check * (test) disable scheduled workflow license check * (test) disable cacheserver license check * fix db config location * fix mysql support * test * test * no long set host address * address comments * address comments and enable license check * format * remove extra blank line * update licenses * cache server license * address comments * centralize error message * remove pv in postgres deployment
Description of your changes:
Part 1 of supporting PostgreSQL:
client_manager.go
to its own package and folder for ease of testingChecklist: