diff --git a/config/default/manager_auth_proxy_patch.yaml b/config/default/manager_auth_proxy_patch.yaml index 70c3437..ccf2f22 100644 --- a/config/default/manager_auth_proxy_patch.yaml +++ b/config/default/manager_auth_proxy_patch.yaml @@ -15,7 +15,7 @@ spec: capabilities: drop: - "ALL" - image: gcr.io/kubebuilder/kube-rbac-proxy:v0.15.0 + image: quay.io/brancz/kube-rbac-proxy:v0.18.2 args: - "--secure-listen-address=0.0.0.0:8443" - "--upstream=http://127.0.0.1:8080/" diff --git a/config/samples/crd_v1alpha1_databaserequest_postgres-superuser.yaml b/config/samples/crd_v1alpha1_databaserequest_postgres-superuser.yaml new file mode 100644 index 0000000..0fb47a5 --- /dev/null +++ b/config/samples/crd_v1alpha1_databaserequest_postgres-superuser.yaml @@ -0,0 +1,14 @@ +apiVersion: crd.lagoon.sh/v1alpha1 +kind: DatabaseRequest +metadata: + labels: + app.kubernetes.io/name: databaserequest + app.kubernetes.io/instance: databaserequest-postgres-superuser-sample + app.kubernetes.io/part-of: dbaas-controller + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/created-by: dbaas-controller + name: databaserequest-postgres-superuser-sample +spec: + name: first-postgres-superuser-db + selector: psql-superuser + type: postgres diff --git a/config/samples/crd_v1alpha1_relationaldatabaseprovider_postgres-superuser.yaml b/config/samples/crd_v1alpha1_relationaldatabaseprovider_postgres-superuser.yaml new file mode 100644 index 0000000..cfc54e3 --- /dev/null +++ b/config/samples/crd_v1alpha1_relationaldatabaseprovider_postgres-superuser.yaml @@ -0,0 +1,19 @@ +apiVersion: crd.lagoon.sh/v1alpha1 +kind: RelationalDatabaseProvider +metadata: + labels: + app.kubernetes.io/name: dbaas-controller + app.kubernetes.io/managed-by: kustomize + name: relationaldatabaseprovider-postgres-superuser-sample +spec: + type: postgres + selector: psql-superuser + connections: + - name: primary-test-postgres-connection + hostname: postgres-service.postgres + passwordSecretRef: + name: postgres-superuser-secret + namespace: postgres + port: 5432 + username: postgres + enabled: true diff --git a/config/samples/crd_v1alpha1_relationaldatabaseprovider_postgres.yaml b/config/samples/crd_v1alpha1_relationaldatabaseprovider_postgres.yaml index 534bac2..78c533b 100644 --- a/config/samples/crd_v1alpha1_relationaldatabaseprovider_postgres.yaml +++ b/config/samples/crd_v1alpha1_relationaldatabaseprovider_postgres.yaml @@ -15,5 +15,5 @@ spec: name: postgres-secret namespace: postgres port: 5432 - username: postgres + username: root enabled: true diff --git a/internal/controller/databaserequest_controller.go b/internal/controller/databaserequest_controller.go index dd29fd1..6bfa38c 100644 --- a/internal/controller/databaserequest_controller.go +++ b/internal/controller/databaserequest_controller.go @@ -751,6 +751,7 @@ func (r *DatabaseRequestReconciler) relationalDatabaseOperation( databaseRequest.Name, databaseRequest.Namespace, databaseRequest.Spec.Type, + conn.username, ) if err != nil { return fmt.Errorf("%s db operation %s failed: %w", databaseRequest.Spec.Type, operation, err) diff --git a/internal/controller/relationaldatabaseprovider_controller.go b/internal/controller/relationaldatabaseprovider_controller.go index 7e0a4c1..d9d48e8 100644 --- a/internal/controller/relationaldatabaseprovider_controller.go +++ b/internal/controller/relationaldatabaseprovider_controller.go @@ -342,6 +342,9 @@ func (rc *reldbConn) getDSN(useDatabase bool) string { ) if useDatabase { dsn += fmt.Sprintf(" dbname=%s", rc.name) + } else { + // Fallback to default `postgres` database. + dsn += fmt.Sprintf(" dbname=%s", "postgres") } } return dsn diff --git a/internal/database/database.go b/internal/database/database.go index d3bfa88..04b4111 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -62,7 +62,7 @@ type RelationalDatabaseInterface interface { // It also creates a user and grants the user permissions on the database. // This function is idempotent and can be called multiple times without side effects. // returns the database name, username, and password - CreateDatabase(ctx context.Context, dsn, name, namespace, dbType string) (RelationalDatabaseInfo, error) + CreateDatabase(ctx context.Context, dsn, name, namespace, dbType string, providerUsername string) (RelationalDatabaseInfo, error) // DropDatabase drops a database in the MySQL or PostgreSQL database if it exists. // This function is idempotent and can be called multiple times without side effects. @@ -260,6 +260,7 @@ func (ri *RelationalDatabaseImpl) CreateDatabase( ctx context.Context, dsn, name, namespace string, dbType string, + providerUsername string, ) (RelationalDatabaseInfo, error) { log.FromContext(ctx).Info("Creating database", "dbType", dbType) db, err := ri.GetConnection(ctx, dsn, dbType) @@ -308,15 +309,6 @@ func (ri *RelationalDatabaseImpl) CreateDatabase( if err != nil { return info, fmt.Errorf("create database failed to get %s database info: %w", dbType, err) } - // Create the database - if _, err := db.Exec(fmt.Sprintf("CREATE DATABASE \"%s\"", info.Dbname)); err != nil { - if pqErr, ok := err.(*pq.Error); !ok || ok && pqErr.Code != "42P04" { - // either the error is not a pq.Error or it is a pq.Error but not a duplicate_database error - // 42P04 is the error code for duplicate_database - return info, fmt.Errorf( - "create %s database error in creating the database `%s`: %w", dbType, info.Dbname, err) - } - } // Check if user exists and create or update the user var userExists int @@ -337,20 +329,20 @@ func (ri *RelationalDatabaseImpl) CreateDatabase( } } - // Change database owner - if _, err := db.Exec( - fmt.Sprintf("ALTER DATABASE \"%s\" OWNER TO \"%s\";", info.Dbname, info.Username), - ); err != nil { + // Give dbaas-operator access to the User + if _, err := db.Exec(fmt.Sprintf("GRANT \"%s\" TO \"%s\";", info.Username, providerUsername)); err != nil { return info, fmt.Errorf( - "create %s database error in change owner of database `%s`: %w", dbType, info.Dbname, err) + "create %s database error in grant user access to `%s`: %w", dbType, info.Username, err) } - // Grant privileges - if _, err := db.Exec( - fmt.Sprintf("GRANT ALL PRIVILEGES ON DATABASE \"%s\" TO \"%s\"", info.Dbname, info.Username), - ); err != nil { - return info, fmt.Errorf( - "create %s database error in grant privileges in database `%s`: %w", dbType, info.Dbname, err) + // Create the database + if _, err := db.Exec(fmt.Sprintf("CREATE DATABASE \"%s\" OWNER \"%s\"", info.Dbname, info.Username)); err != nil { + if pqErr, ok := err.(*pq.Error); !ok || ok && pqErr.Code != "42P04" { + // either the error is not a pq.Error or it is a pq.Error but not a duplicate_database error + // 42P04 is the error code for duplicate_database + return info, fmt.Errorf( + "create %s database error in creating the database `%s`: %w", dbType, info.Dbname, err) + } } default: return RelationalDatabaseInfo{}, fmt.Errorf( diff --git a/internal/database/mock.go b/internal/database/mock.go index 939e654..5e8be2a 100644 --- a/internal/database/mock.go +++ b/internal/database/mock.go @@ -27,7 +27,7 @@ func (mi *RelationalDatabaseMock) Initialize(ctx context.Context, dsn string, ki // CreateDatabase creates a database in the relational database if it does not exist. func (mi *RelationalDatabaseMock) CreateDatabase( - ctx context.Context, dsn, name, namespace, kind string) (RelationalDatabaseInfo, error) { + ctx context.Context, dsn, name, namespace, kind string, username string) (RelationalDatabaseInfo, error) { return RelationalDatabaseInfo{Username: "user", Password: "pass", Dbname: "db"}, nil } diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index bed2ead..dfce88b 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -60,7 +60,7 @@ var _ = Describe("controller", Ordered, func() { utils.UninstallCertManager() By("removing the RelationalDatabaseProvider resource") - for _, name := range []string{"mysql", "mysql-scope", "postgres", "mongodb"} { + for _, name := range []string{"mysql", "mysql-scope", "postgres", "postgres-superuser", "mongodb"} { cmd := exec.Command( utils.Kubectl(), "patch", @@ -77,7 +77,7 @@ var _ = Describe("controller", Ordered, func() { _, _ = utils.Run(cmd) } By("removing the DatabaseRequest resource") - for _, name := range []string{"mysql", "mysql-scope", "postgres", "seed"} { + for _, name := range []string{"mysql", "mysql-scope", "postgres", "postgres-superuser", "seed"} { cmd := exec.Command( utils.Kubectl(), "patch", @@ -104,7 +104,7 @@ var _ = Describe("controller", Ordered, func() { utils.UninstallMongoDB() By("removing service and secret") - for _, name := range []string{"mysql", "mysql-scope", "postgres", "mongodb"} { + for _, name := range []string{"mysql", "mysql-scope", "postgres", "postgres-superuser", "mongodb"} { cmd = exec.Command( utils.Kubectl(), "delete", "service", "-n", "default", "-l", "app.kubernetes.io/instance=databaserequest-"+name+"-sample") _, _ = utils.Run(cmd) @@ -178,7 +178,7 @@ var _ = Describe("controller", Ordered, func() { EventuallyWithOffset(1, verifyControllerUp, time.Minute, time.Second).Should(Succeed()) By("validating that all database providers and database requests are working") - for _, name := range []string{"mysql", "mysql-scope", "postgres", "seed"} { + for _, name := range []string{"mysql", "mysql-scope", "postgres", "postgres-superuser", "seed"} { if name != "seed" { By("creating a RelationalDatabaseProvider resource") cmd = exec.Command( diff --git a/test/e2e/testdata/postgres.yaml b/test/e2e/testdata/postgres.yaml index 375442b..4e4855d 100644 --- a/test/e2e/testdata/postgres.yaml +++ b/test/e2e/testdata/postgres.yaml @@ -4,6 +4,18 @@ metadata: name: postgres --- apiVersion: v1 +kind: ConfigMap +metadata: + name: postgres + namespace: postgres + labels: + app: postgres +data: + create-root-user.sql: | + CREATE USER "root" WITH ENCRYPTED PASSWORD 'e2e-root-password' CREATEDB CREATEROLE; + ALTER DATABASE "postgres" OWNER TO "root"; +--- +apiVersion: v1 kind: Pod metadata: name: postgres @@ -13,13 +25,21 @@ metadata: spec: containers: - name: postgres - image: postgres:13 + image: postgres:15 env: - name: POSTGRES_PASSWORD value: "e2e-postgres-password" ports: - containerPort: 5432 name: postgres + volumeMounts: + - name: entrypoints + mountPath: /docker-entrypoint-initdb.d + volumes: + - name: entrypoints + configMap: + name: postgres + defaultMode: 0555 --- apiVersion: v1 kind: Service @@ -40,5 +60,14 @@ metadata: name: postgres-secret namespace: postgres type: Opaque -data: - password: ZTJlLXBvc3RncmVzLXBhc3N3b3Jk \ No newline at end of file +stringData: + password: e2e-root-password +--- +apiVersion: v1 +kind: Secret +metadata: + name: postgres-superuser-secret + namespace: postgres +type: Opaque +stringData: + password: e2e-postgres-password