diff --git a/apis/cloud.redhat.com/v1alpha1/clowdenvironment_types.go b/apis/cloud.redhat.com/v1alpha1/clowdenvironment_types.go index c6f7ce013..bf6a0c393 100644 --- a/apis/cloud.redhat.com/v1alpha1/clowdenvironment_types.go +++ b/apis/cloud.redhat.com/v1alpha1/clowdenvironment_types.go @@ -284,7 +284,7 @@ type KafkaConfig struct { } // DatabaseMode details the mode of operation of the Clowder Database Provider -// +kubebuilder:validation:Enum=shared;app-interface;local;none +// +kubebuilder:validation:Enum=single;shared;app-interface;local;none type DatabaseMode string // DatabaseConfig configures the Clowder provider controlling the creation of @@ -292,8 +292,10 @@ type DatabaseMode string type DatabaseConfig struct { // The mode of operation of the Clowder Database Provider. Valid options are: // (*_app-interface_*) where the provider will pass through database credentials - // found in the secret defined by the database name in the ClowdApp, and (*_local_*) - // where the provider will spin up a local instance of the database. + // found in the secret defined by the database name in the ClowdApp, (*_local_*) + // where the provider will spin up a local instance of the database, and (*_single_*) + // that is similar to local but keeps only one database deployment and isolates apps by + // PG schemas. Mode DatabaseMode `json:"mode"` // Indicates where Clowder will fetch the database CA certificate bundle from. Currently only used in diff --git a/config/crd/bases/cloud.redhat.com_clowdenvironments.yaml b/config/crd/bases/cloud.redhat.com_clowdenvironments.yaml index 1f38f3bdd..c8b303ec4 100644 --- a/config/crd/bases/cloud.redhat.com_clowdenvironments.yaml +++ b/config/crd/bases/cloud.redhat.com_clowdenvironments.yaml @@ -81,12 +81,15 @@ spec: type: string mode: description: 'The mode of operation of the Clowder Database - Provider. Valid options are: (*_app-interface_*) where the - provider will pass through database credentials found in - the secret defined by the database name in the ClowdApp, - and (*_local_*) where the provider will spin up a local - instance of the database.' + Provider. Valid options are: (*_app-interface_*) where + the provider will pass through database credentials found + in the secret defined by the database name in the + ClowdApp, (*_local_*) where the provider will spin up a + local instance of the database, and (*_single_*) that is + similar to local but keeps only one database deployment + and isolates apps by PG schemas.' enum: + - single - shared - app-interface - local diff --git a/controllers/cloud.redhat.com/providers/database/localdb.go b/controllers/cloud.redhat.com/providers/database/localdb.go index 598d48dc7..03026893d 100644 --- a/controllers/cloud.redhat.com/providers/database/localdb.go +++ b/controllers/cloud.redhat.com/providers/database/localdb.go @@ -88,7 +88,7 @@ func (db *localDbProvider) Provide(app *crd.ClowdApp) error { dataInit := func() map[string]string { hostname := fmt.Sprintf("%v.%v.svc", nn.Name, nn.Namespace) - port := "5432" + port := provutils.DefaultPGPort username := utils.RandString(16) name := app.Spec.Database.Name @@ -116,7 +116,7 @@ func (db *localDbProvider) Provide(app *crd.ClowdApp) error { if err != nil { return errors.Wrap("couldn't convert to int", err) } - dbCfg.AdminUsername = "postgres" + dbCfg.AdminUsername = provutils.DefaultPGAdminUsername dbCfg.SslMode = "disable" var image string @@ -183,39 +183,23 @@ func (db *localDbProvider) processSharedDB(app *crd.ClowdApp) error { return err } - dbCfg := config.DatabaseConfig{} - dbCfg.SslMode = "disable" - refApp, err := crd.GetAppForDBInSameEnv(db.Ctx, db.Client, app) - if err != nil { return err } - secret := core.Secret{} - + dbCfg := config.DatabaseConfig{} inn := types.NamespacedName{ Name: fmt.Sprintf("%s-db", refApp.Name), Namespace: refApp.Namespace, } // This is a REAL call here, not a cached call as the reconciliation must have been processed - // for the app we depend on. - if err = db.Client.Get(db.Ctx, inn, &secret); err != nil { - return errors.Wrap("Couldn't set/get secret", err) - } - - secMap := make(map[string]string) - - for k, v := range secret.Data { - (secMap)[k] = string(v) - } - - err = dbCfg.Populate(&secMap) + // for the app we depend on, hence the nil for the ident. + err = provutils.ReadDbConfigFromSecret(db.Provider, nil, &dbCfg, inn) if err != nil { - return errors.Wrap("couldn't convert to int", err) + return err } - dbCfg.AdminUsername = "postgres" db.Config.Database = &dbCfg diff --git a/controllers/cloud.redhat.com/providers/database/provider.go b/controllers/cloud.redhat.com/providers/database/provider.go index 7123a2013..b1ec14eaf 100644 --- a/controllers/cloud.redhat.com/providers/database/provider.go +++ b/controllers/cloud.redhat.com/providers/database/provider.go @@ -18,6 +18,8 @@ var imageList map[int32]string func GetDatabase(c *p.Provider) (p.ClowderProvider, error) { dbMode := c.Env.Spec.Providers.Database.Mode switch dbMode { + case "single": + return NewSingleDBProvider(c) case "shared": return NewSharedDBProvider(c) case "local": diff --git a/controllers/cloud.redhat.com/providers/database/shareddb.go b/controllers/cloud.redhat.com/providers/database/shareddb.go index 7e9bab504..52e16e95f 100644 --- a/controllers/cloud.redhat.com/providers/database/shareddb.go +++ b/controllers/cloud.redhat.com/providers/database/shareddb.go @@ -86,7 +86,7 @@ func createVersionedDatabase(p *providers.Provider, version int32) (*config.Data dataInit := func() map[string]string { return map[string]string{ "hostname": fmt.Sprintf("%v.%v.svc", nn.Name, nn.Namespace), - "port": "5432", + "port": provutils.DefaultPGPort, "username": utils.RandString(16), "password": password, "pgPass": pgPassword, @@ -103,7 +103,7 @@ func createVersionedDatabase(p *providers.Provider, version int32) (*config.Data if err != nil { return nil, errors.Wrap("couldn't convert to int", err) } - dbCfg.AdminUsername = "postgres" + dbCfg.AdminUsername = provutils.DefaultPGAdminUsername dbCfg.SslMode = "disable" var image string @@ -243,7 +243,7 @@ func (db *sharedDbProvider) Provide(app *crd.ClowdApp) error { return err } - dbCfg.AdminUsername = "postgres" + dbCfg.AdminUsername = provutils.DefaultPGAdminUsername dbCfg.AdminPassword = string(vSec.Data["pgPass"]) dbCfg.Hostname = string(vSec.Data["hostname"]) dbCfg.Name = app.Spec.Database.Name @@ -252,12 +252,8 @@ func (db *sharedDbProvider) Provide(app *crd.ClowdApp) error { dbCfg.Port = int(port) dbCfg.SslMode = "disable" - host := dbCfg.Hostname - user := dbCfg.AdminUsername - password := dbCfg.AdminPassword dbname := app.Spec.Database.Name - - appSQLConnectionString := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", host, port, user, password, dbname) + appSQLConnectionString := provutils.PGAdminConnectionStr(&dbCfg, dbname) ctx, cancel := context.WithTimeout(db.Ctx, 5*time.Second) defer cancel() @@ -273,8 +269,7 @@ func (db *sharedDbProvider) Provide(app *crd.ClowdApp) error { if pErr != nil { if strings.Contains(pErr.Error(), fmt.Sprintf("database \"%s\" does not exist", app.Spec.Database.Name)) { - envSQLConnectionString := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", host, port, user, password, db.Env.Name) - + envSQLConnectionString := provutils.PGAdminConnectionStr(&dbCfg, db.Env.Name) envDbClient, envErr := sql.Open("postgres", envSQLConnectionString) if envErr != nil { return envErr @@ -307,8 +302,8 @@ func (db *sharedDbProvider) Provide(app *crd.ClowdApp) error { } secret.StringData = map[string]string{ - "hostname": host, - "port": "5432", + "hostname": dbCfg.Hostname, + "port": provutils.DefaultPGPort, "username": dbCfg.Username, "password": dbCfg.Password, "pgPass": dbCfg.AdminPassword, @@ -337,39 +332,24 @@ func (db *sharedDbProvider) processSharedDB(app *crd.ClowdApp) error { return err } - dbCfg := config.DatabaseConfig{} - dbCfg.SslMode = "disable" - refApp, err := crd.GetAppForDBInSameEnv(db.Ctx, db.Client, app) if err != nil { return err } - secret := core.Secret{} - + dbCfg := config.DatabaseConfig{} inn := types.NamespacedName{ Name: fmt.Sprintf("%s-db", refApp.Name), Namespace: refApp.Namespace, } // This is a REAL call here, not a cached call as the reconciliation must have been processed - // for the app we depend on. - if err = db.Client.Get(db.Ctx, inn, &secret); err != nil { - return errors.Wrap("Couldn't set/get secret", err) - } - - secMap := make(map[string]string) - - for k, v := range secret.Data { - (secMap)[k] = string(v) - } - - err = dbCfg.Populate(&secMap) + // for the app we depend on, hence the nil for the ident. + err = provutils.ReadDbConfigFromSecret(db.Provider, nil, &dbCfg, inn) if err != nil { - return errors.Wrap("couldn't convert to int", err) + return err } - dbCfg.AdminUsername = "postgres" db.Config.Database = &dbCfg diff --git a/controllers/cloud.redhat.com/providers/database/single.go b/controllers/cloud.redhat.com/providers/database/single.go new file mode 100644 index 000000000..6a36a4e75 --- /dev/null +++ b/controllers/cloud.redhat.com/providers/database/single.go @@ -0,0 +1,302 @@ +package database + +import ( + "context" + "database/sql" + "fmt" + "time" + + "github.com/lib/pq" + + crd "github.com/RedHatInsights/clowder/apis/cloud.redhat.com/v1alpha1" + "github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/config" + "github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/errors" + obj "github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/object" + "github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/providers" + "github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/providers/sizing" + "github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/providers/sizing/sizingconfig" + provutils "github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/providers/utils" + + apps "k8s.io/api/apps/v1" + core "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + + rc "github.com/RedHatInsights/rhc-osdk-utils/resourceCache" + "github.com/RedHatInsights/rhc-osdk-utils/utils" +) + +// SingleDBDeployment is the ident referring to the single local DB deployment object +var SingleDBDeployment = rc.NewSingleResourceIdent(ProvName, "single_db_deployment", &apps.Deployment{}) + +// SingleDBScret is the ident referring to the single local DB secret object +var SingleDBSecret = rc.NewMultiResourceIdent(ProvName, "single_db_secret", &core.Secret{}) + +// SingleDBPVC is the ident referring to the single local DB PersitentVolumeClaim +var SingleDBPVC = rc.NewSingleResourceIdent(ProvName, "single_db_pvc", &core.PersistentVolumeClaim{}) + +// SingleDBService is the ident referring to the single local DB service object. +var SingleDBService = rc.NewMultiResourceIdent(ProvName, "single_db_service", &core.Service{}) + +func NewSingleDBProvider(p *providers.Provider) (providers.ClowderProvider, error) { + p.Cache.AddPossibleGVKFromIdent( + SingleDBDeployment, + SingleDBSecret, + SingleDBPVC, + SingleDBService, + ) + return &singleDbProvider{Provider: *p}, nil +} + +type singleDbProvider struct { + providers.Provider +} + +func (db *singleDbProvider) DBNamespacedName() types.NamespacedName { + return types.NamespacedName{ + Name: fmt.Sprintf("%s-db-single", db.Env.Name), + Namespace: db.Env.Status.TargetNamespace, + } +} + +func (db *singleDbProvider) EnvProvide() error { + appList, err := db.Env.GetAppsInEnv(db.Ctx, db.Client) + if err != nil { + return err + } + + needsDb := false + for _, app := range appList.Items { + if app.Spec.Database.Name != "" { + needsDb = true + break + } + } + + if needsDb { + if _, err := db.createDatabaseDeployment(); err != nil { + return err + } + if db.Env.Spec.Providers.Database.PVC { + if err := db.createDatabasePVC(); err != nil { + return err + } + } + } + return nil +} + +// Creates a single database deployment locked to one version with a main secret +func (db *singleDbProvider) createDatabaseDeployment() (*config.DatabaseConfig, error) { + nn := db.DBNamespacedName() + + dd := &apps.Deployment{} + ownerrefs := []metav1.OwnerReference{db.Env.MakeOwnerReference()} + dd.ObjectMeta.OwnerReferences = ownerrefs + err := db.Cache.Create(SingleDBDeployment, nn, dd) + if err != nil { + return nil, err + } + + dbName := db.Env.Name + dbCfg, err := db.createOrReadDbConfig(db.Env, nn, dbName, true) + if err != nil { + return nil, err + } + + labels := &map[string]string{ + "sub": "single_db", + } + usePVC := db.Env.Spec.Providers.Database.PVC + provutils.MakeLocalDB(dd, nn, db.Env, labels, dbCfg, provutils.DefaultImageDatabasePG, usePVC, dbName, nil) + if err := db.Cache.Update(SingleDBDeployment, dd); err != nil { + return dbCfg, err + } + + svc := &core.Service{} + svc.ObjectMeta.OwnerReferences = ownerrefs + if err := db.Cache.Create(SingleDBService, nn, svc); err != nil { + return dbCfg, err + } + + provutils.MakeLocalDBService(svc, nn, db.Env, labels) + + if err = db.Cache.Update(SingleDBService, svc); err != nil { + return dbCfg, err + } + + return dbCfg, nil +} + +func (db *singleDbProvider) createDatabasePVC() error { + nn := db.DBNamespacedName() + + pvc := &core.PersistentVolumeClaim{} + if err := db.Cache.Create(SingleDBPVC, nn, pvc); err != nil { + return err + } + + // TODO handle volume capacity + capacity := sizing.GetVolCapacityForSize(sizingconfig.SizeLarge) + provutils.MakeLocalDBPVC(pvc, nn, db.Env, capacity) + if err := db.Cache.Update(SingleDBPVC, pvc); err != nil { + return err + } + return nil +} + +func (db *singleDbProvider) Provide(app *crd.ClowdApp) error { + if app.Spec.Database.SharedDBAppName != "" { + return db.processSharedDB(app) + } else if app.Spec.Database.Name != "" { + return db.provideAppDB(app) + } + return nil +} + +func (db *singleDbProvider) provideAppDB(app *crd.ClowdApp) error { + dbnn := db.DBNamespacedName() + dbCfg := &config.DatabaseConfig{} + + if err := provutils.ReadDbConfigFromSecret(db.Provider, SingleDBSecret, dbCfg, dbnn); err != nil { + return err + } + + // Create database config and secret for the app, + // without the admin credentials. + appnn := types.NamespacedName{ + Name: fmt.Sprintf("%v-db", app.Name), + Namespace: app.Namespace, + } + appDbCfg, err := db.createOrReadDbConfig(app, appnn, app.Name, false) + if err != nil { + return err + } + db.Config.Database = appDbCfg + + // reconcile access + if err := db.reconcileDBAppAccess(dbCfg, appDbCfg); err != nil { + return err + } + + return nil +} + +func (db *singleDbProvider) processSharedDB(app *crd.ClowdApp) error { + refApp, err := crd.GetAppForDBInSameEnv(db.Ctx, db.Client, app) + if err != nil { + return err + } + + nn := types.NamespacedName{ + Name: fmt.Sprintf("%v-db", refApp.Name), + Namespace: refApp.Namespace, + } + dbCfg := &config.DatabaseConfig{} + if err := provutils.ReadDbConfigFromSecret(db.Provider, SingleDBSecret, dbCfg, nn); err != nil { + return errors.Wrap(fmt.Sprintf("sharedDBApp %s", refApp.Name), err) + } + + db.Config.Database = dbCfg + return nil +} + +func (db *singleDbProvider) Hostname() string { + nn := db.DBNamespacedName() + return fmt.Sprintf("%v.%v.svc", nn.Name, nn.Namespace) +} + +func (db *singleDbProvider) createOrReadDbConfig(obj obj.ClowdObject, nn types.NamespacedName, username string, setAdmin bool) (cfg *config.DatabaseConfig, err error) { + cfg = &config.DatabaseConfig{} + password, err := utils.RandPassword(16, provutils.RCharSet) + if err != nil { + return cfg, errors.Wrap("password generate failed", err) + } + + var pgPassword string + if setAdmin { + pgPassword, err = utils.RandPassword(16, provutils.RCharSet) + if err != nil { + return cfg, errors.Wrap("pgPassword generate failed", err) + } + } + + dataInit := func() map[string]string { + return map[string]string{ + "hostname": db.Hostname(), + "port": provutils.DefaultPGPort, + "name": db.Env.Name, + "username": username, + "password": password, + "pgPass": pgPassword, + } + } + + var secMap *map[string]string + secMap, err = providers.MakeOrGetSecret(obj, db.Cache, SingleDBSecret, nn, dataInit) + if err != nil { + return cfg, errors.Wrap("couldn't set/get secret", err) + } + + err = cfg.Populate(secMap) + if err != nil { + return cfg, errors.Wrap("couldn't populate db config from secret", err) + } + if setAdmin { + cfg.AdminUsername = provutils.DefaultPGAdminUsername + } + cfg.SslMode = "disable" + return +} + +func (db *singleDbProvider) reconcileDBAppAccess(envCfg *config.DatabaseConfig, appCfg *config.DatabaseConfig) error { + appSQLConnectionString := provutils.PGAdminConnectionStr(envCfg, envCfg.Name) + + ctx, cancel := context.WithTimeout(db.Ctx, 5*time.Second) + defer cancel() + + dbClient, err := sql.Open("postgres", appSQLConnectionString) + if err != nil { + return errors.Wrap("unable ,to connect to db", err) + } + defer dbClient.Close() + + if err := dbClient.PingContext(ctx); err != nil { + return err + } + + username := appCfg.Username + password := appCfg.Password + rows, err := dbClient.QueryContext(ctx, "SELECT TRUE FROM pg_roles WHERE rolname = $1;", username) + if err != nil { + return errors.Wrap("unable to query for roles", err) + } + + var roleExists = rows.Next() + rows.Close() + + if roleExists { + _, err = dbClient.ExecContext(ctx, + fmt.Sprintf("ALTER ROLE %s WITH LOGIN ENCRYPTED PASSWORD %s;", + pq.QuoteIdentifier(username), pq.QuoteLiteral(password), + )) + } else { + _, err = dbClient.ExecContext(ctx, + fmt.Sprintf("CREATE ROLE %s WITH LOGIN ENCRYPTED PASSWORD %s;", + pq.QuoteIdentifier(username), pq.QuoteLiteral(password), + )) + } + if err != nil { + return errors.Wrap("unable to create/alter role", err) + } + + _, err = dbClient.ExecContext(ctx, + fmt.Sprintf("CREATE SCHEMA IF NOT EXISTS %s AUTHORIZATION %s;", + pq.QuoteIdentifier(username), pq.QuoteIdentifier(username), + )) + if err != nil { + return errors.Wrap("unable to create db schema for the app", err) + } + + return nil +} diff --git a/controllers/cloud.redhat.com/providers/featureflags/localfeatureflags.go b/controllers/cloud.redhat.com/providers/featureflags/localfeatureflags.go index 9f3f872a2..ca2fc659d 100644 --- a/controllers/cloud.redhat.com/providers/featureflags/localfeatureflags.go +++ b/controllers/cloud.redhat.com/providers/featureflags/localfeatureflags.go @@ -115,7 +115,7 @@ func (ff *localFeatureFlagsProvider) EnvProvide() error { return map[string]string{ "hostname": hostname, - "port": "5432", + "port": provutils.DefaultPGPort, "username": username, "password": password, "pgPass": pgPassword, diff --git a/controllers/cloud.redhat.com/providers/utils/utils.go b/controllers/cloud.redhat.com/providers/utils/utils.go index d8816281c..5ba4b5d67 100644 --- a/controllers/cloud.redhat.com/providers/utils/utils.go +++ b/controllers/cloud.redhat.com/providers/utils/utils.go @@ -7,10 +7,12 @@ import ( crd "github.com/RedHatInsights/clowder/apis/cloud.redhat.com/v1alpha1" "github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/clowderconfig" "github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/config" + "github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/errors" obj "github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/object" "github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/providers" "github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/providers/sizing" "github.com/go-logr/logr" + "github.com/lib/pq" apps "k8s.io/api/apps/v1" core "k8s.io/api/core/v1" @@ -18,6 +20,7 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/intstr" + rc "github.com/RedHatInsights/rhc-osdk-utils/resourceCache" "github.com/RedHatInsights/rhc-osdk-utils/utils" ) @@ -33,7 +36,10 @@ var DefaultImageDatabasePG13 = "quay.io/cloudservices/postgresql-rds:13-2318dee" var DefaultImageDatabasePG14 = "quay.io/cloudservices/postgresql-rds:14-2318dee" var DefaultImageDatabasePG15 = "quay.io/cloudservices/postgresql-rds:15-2318dee" var DefaultImageDatabasePG16 = "quay.io/cloudservices/postgresql-rds:16-759c25d" +var DefaultImageDatabasePG = DefaultImageDatabasePG16 var DefaultImageInMemoryDB = "registry.redhat.io/rhel9/redis-6:1-199.1726663404" +var DefaultPGPort = "5432" +var DefaultPGAdminUsername = "postgres" // MakeLocalDB populates the given deployment object with the local DB struct. func MakeLocalDB(dd *apps.Deployment, nn types.NamespacedName, baseResource obj.ClowdObject, extraLabels *map[string]string, cfg *config.DatabaseConfig, image string, usePVC bool, dbName string, res *core.ResourceRequirements) { @@ -173,6 +179,47 @@ func MakeLocalDBPVC(pvc *core.PersistentVolumeClaim, nn types.NamespacedName, ba utils.MakePVC(pvc, nn, providers.Labels{"service": "db", "app": baseResource.GetClowdName()}, capacity, baseResource) } +func PGAdminConnectionStr(cfg *config.DatabaseConfig, dbname string) string { + return fmt.Sprintf( + "host=%s port=%d user=%s password=%s dbname=%s sslmode=%s", + pq.QuoteLiteral(cfg.Hostname), + cfg.Port, + pq.QuoteLiteral(cfg.AdminUsername), + pq.QuoteLiteral(cfg.AdminPassword), + pq.QuoteLiteral(dbname), + pq.QuoteLiteral(cfg.SslMode), + ) +} + +func ReadDbConfigFromSecret(p providers.Provider, resourceIdent rc.ResourceIdent, dbCfg *config.DatabaseConfig, nn types.NamespacedName) error { + secret := &core.Secret{} + + var err error + if resourceIdent != nil { + err = p.Cache.Get(resourceIdent, secret, nn) + } else { + err = fmt.Errorf("no cache") + } + if err != nil { + if err := p.Client.Get(p.Ctx, nn, secret); err != nil { + return errors.Wrap("couldn't get db secret", err) + } + } + + secMap := make(map[string]string) + for k, v := range secret.Data { + (secMap)[k] = string(v) + } + + if err := dbCfg.Populate(&secMap); err != nil { + return errors.Wrap("couldn't convert to int", err) + } + dbCfg.AdminUsername = DefaultPGAdminUsername + dbCfg.SslMode = "disable" + + return nil +} + // GetCaddyImage returns the caddy image to use in a given environment func GetCaddyGatewayImage(env *crd.ClowdEnvironment) string { if env.Spec.Providers.Web.Images.CaddyGateway != "" { diff --git a/controllers/cloud.redhat.com/providers/web/resources_keycloak.go b/controllers/cloud.redhat.com/providers/web/resources_keycloak.go index c528fd96f..fcd7d63a1 100644 --- a/controllers/cloud.redhat.com/providers/web/resources_keycloak.go +++ b/controllers/cloud.redhat.com/providers/web/resources_keycloak.go @@ -82,7 +82,7 @@ func configureKeycloakDB(web *localWebProvider) error { return map[string]string{ "hostname": hostname, - "port": "5432", + "port": provutils.DefaultPGPort, "username": username, "password": password, "pgPass": pgPassword, @@ -273,7 +273,7 @@ func makeKeycloak(o obj.ClowdObject, objMap providers.ObjectMap, _ bool, nodePor }, { Name: "KC_DB_URL_PORT", - Value: "5432", + Value: provutils.DefaultPGPort, }, { Name: "PROXY_ADDRESS_FORWARDING", diff --git a/docs/providers/database.md b/docs/providers/database.md index 626db4b0f..9afe5e8d7 100644 --- a/docs/providers/database.md +++ b/docs/providers/database.md @@ -64,6 +64,17 @@ ClowdEnv Config options available: - `pvc` +#### single + +(*EXPERIMENTAL*) + +In the single mode, the the **Database Provider** will provision a single node +PostgreSQL instance, of latest supported version, for the whole ClowdEnvironment. +Each ClowdApp requesting a database would get a separate user and +a [PostgreSQL schema](https://www.postgresql.org/docs/current/ddl-schemas.html) +with the same name as the user. The user has only access to create objects +only within it's schema. Admin credentials are (currently) not provided. + #### shared In shared mode, the **Database Provider** will provision a single node PostgreSQL