diff --git a/builder/src/main/java/cz/xtf/builder/db/AbstractDatabase.java b/builder/src/main/java/cz/xtf/builder/db/AbstractDatabase.java index 7246282f..7d88e8a4 100644 --- a/builder/src/main/java/cz/xtf/builder/db/AbstractDatabase.java +++ b/builder/src/main/java/cz/xtf/builder/db/AbstractDatabase.java @@ -1,7 +1,10 @@ package cz.xtf.builder.db; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.function.Supplier; import cz.xtf.builder.builders.ApplicationBuilder; import cz.xtf.builder.builders.DeploymentConfigBuilder; @@ -11,6 +14,9 @@ import cz.xtf.builder.builders.pod.PersistentVolumeClaim; public abstract class AbstractDatabase extends DefaultStatefulAuxiliary { + private static final String DEFAULT_USERNAME = "testuser"; + private static final String DEFAULT_PASSWORD = "testpwd"; + private static final String DEFAULT_DATABASE_NAME = "testdb"; protected final String username; protected final String password; protected final String dbName; @@ -26,12 +32,49 @@ public abstract class AbstractDatabase extends DefaultStatefulAuxiliary { protected boolean withReadinessProbe; protected boolean withStartupProbe; + private Supplier deploymentConfigName; + private Supplier envVarPrefix; + + public void setDeploymentConfigName(Supplier deploymentConfigName) { + this.deploymentConfigName = deploymentConfigName; + } + + public void setEnvVarPrefix(Supplier envVarPrefix) { + this.envVarPrefix = envVarPrefix; + } + + public AbstractDatabase( + String symbolicName, + String dataDir, + PersistentVolumeClaim pvc, + String username, + String password, + String dbName, + boolean configureEnvironment, + boolean withLivenessProbe, + boolean withReadinessProbe, + boolean withStartupProbe, + Supplier deploymentConfigName, + Supplier envVarPrefix) { + super(symbolicName, dataDir, pvc); + this.symbolicName = symbolicName; + this.username = (username == null || username.isEmpty()) ? DEFAULT_USERNAME : username; + this.password = (password == null || password.isEmpty()) ? DEFAULT_PASSWORD : password; + this.dbName = (dbName == null || dbName.isEmpty()) ? DEFAULT_DATABASE_NAME : dbName; + this.withLivenessProbe = withLivenessProbe; + this.withReadinessProbe = withReadinessProbe; + this.withStartupProbe = withStartupProbe; + this.configureEnvironment = configureEnvironment; + this.deploymentConfigName = deploymentConfigName; + this.envVarPrefix = envVarPrefix; + } + public AbstractDatabase(String symbolicName, String dataDir) { - this("testuser", "testpwd", "testdb", symbolicName, dataDir); + this(DEFAULT_USERNAME, DEFAULT_PASSWORD, DEFAULT_DATABASE_NAME, symbolicName, dataDir); } public AbstractDatabase(String symbolicName, String dataDir, boolean withLivenessProbe, boolean withReadinessProbe) { - this("testuser", "testpwd", "testdb", symbolicName, dataDir); + this(DEFAULT_USERNAME, DEFAULT_PASSWORD, DEFAULT_DATABASE_NAME, symbolicName, dataDir); this.withLivenessProbe = withLivenessProbe; this.withReadinessProbe = withReadinessProbe; @@ -54,12 +97,13 @@ public AbstractDatabase(String symbolicName, String dataDir, boolean withLivenes } public AbstractDatabase(String symbolicName, String dataDir, PersistentVolumeClaim pvc) { - this("testuser", "testpwd", "testdb", symbolicName, dataDir, pvc); + this(DEFAULT_USERNAME, DEFAULT_PASSWORD, DEFAULT_DATABASE_NAME, symbolicName, dataDir, pvc); } public AbstractDatabase(String symbolicName, String dataDir, PersistentVolumeClaim pvc, boolean withLivenessProbe, boolean withReadinessProbe) { - this("testuser", "testpwd", "testdb", symbolicName, dataDir, pvc, withLivenessProbe, withReadinessProbe); + this(DEFAULT_USERNAME, DEFAULT_PASSWORD, DEFAULT_DATABASE_NAME, symbolicName, dataDir, pvc, withLivenessProbe, + withReadinessProbe); } public AbstractDatabase(String username, String password, String dbName, String symbolicName, String dataDir, @@ -73,7 +117,7 @@ public AbstractDatabase(String username, String password, String dbName, String public AbstractDatabase(String symbolicName, String dataDir, PersistentVolumeClaim pvc, boolean withLivenessProbe, boolean withReadinessProbe, boolean withStartupProbe) { - this("testuser", "testpwd", "testdb", symbolicName, dataDir, pvc); + this(DEFAULT_USERNAME, DEFAULT_PASSWORD, DEFAULT_DATABASE_NAME, symbolicName, dataDir, pvc); this.withLivenessProbe = withLivenessProbe; this.withReadinessProbe = withReadinessProbe; @@ -148,7 +192,18 @@ public Map getImageVariables() { return vars; } + public List getImageArgs() { + return Collections.emptyList(); + } + + public String getServiceAccount() { + return null; + } + public String getDeploymentConfigName() { + if (deploymentConfigName != null) { + return deploymentConfigName.get(); + } if (openShiftName == null) { openShiftName = dbName.toLowerCase() + "-" + getSymbolicName().toLowerCase(); } @@ -156,6 +211,9 @@ public String getDeploymentConfigName() { } public String getEnvVarPrefix() { + if (envVarPrefix != null) { + return envVarPrefix.get(); + } return dbName.toUpperCase() + "_" + getSymbolicName().toUpperCase(); } @@ -183,11 +241,19 @@ public DeploymentConfigBuilder configureDeployment(ApplicationBuilder appBuilder public DeploymentConfigBuilder configureDeployment(ApplicationBuilder appBuilder, boolean synchronous) { final DeploymentConfigBuilder builder = appBuilder.deploymentConfig(getDeploymentConfigName()); - builder.podTemplate().container().fromImage(getImageName()).envVars(getImageVariables()).port(getPort()); + ContainerBuilder containerBuilder = builder.podTemplate().container().fromImage(getImageName()) + .envVars(getImageVariables()) + .port(getPort()); + if (getImageArgs() != null) { + getImageArgs().forEach(containerBuilder::args); + } if (synchronous) { builder.onConfigurationChange(); builder.synchronousDeployment(); } + if (getServiceAccount() != null) { + builder.podTemplate().addServiceAccount(getServiceAccount()); + } configureContainer(builder.podTemplate().container()); diff --git a/builder/src/main/java/cz/xtf/builder/db/AbstractSQLDatabase.java b/builder/src/main/java/cz/xtf/builder/db/AbstractSQLDatabase.java index 600bde93..048b9405 100644 --- a/builder/src/main/java/cz/xtf/builder/db/AbstractSQLDatabase.java +++ b/builder/src/main/java/cz/xtf/builder/db/AbstractSQLDatabase.java @@ -4,6 +4,7 @@ import java.sql.Connection; import java.sql.SQLException; import java.util.function.Consumer; +import java.util.function.Supplier; import org.apache.commons.io.IOUtils; @@ -12,6 +13,34 @@ public abstract class AbstractSQLDatabase extends AbstractDatabase implements SQLExecutor { + public AbstractSQLDatabase( + String symbolicName, + String dataDir, + PersistentVolumeClaim pvc, + String username, + String password, + String dbName, + boolean configureEnvironment, + boolean withLivenessProbe, + boolean withReadinessProbe, + boolean withStartupProbe, + Supplier deploymentConfigName, + Supplier envVarPrefix) { + super( + symbolicName, + dataDir, + pvc, + username, + password, + dbName, + configureEnvironment, + withLivenessProbe, + withReadinessProbe, + withStartupProbe, + deploymentConfigName, + envVarPrefix); + } + public AbstractSQLDatabase(String symbolicName, String dataDir, boolean withLivenessProbe, boolean withReadinessProbe) { super(symbolicName, dataDir, withLivenessProbe, withReadinessProbe); } diff --git a/builder/src/main/java/cz/xtf/builder/db/OfficialPostgreSQL.java b/builder/src/main/java/cz/xtf/builder/db/OfficialPostgreSQL.java new file mode 100644 index 00000000..7e06e936 --- /dev/null +++ b/builder/src/main/java/cz/xtf/builder/db/OfficialPostgreSQL.java @@ -0,0 +1,269 @@ +package cz.xtf.builder.db; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; + +import cz.xtf.builder.builders.pod.PersistentVolumeClaim; +import cz.xtf.core.image.Image; + +public class OfficialPostgreSQL extends AbstractSQLDatabase { + private static final String DEFAULT_SYMBOLIC_NAME = "POSTGRESQL"; + + // data directory for the Official Docker PostgreSQL image + private static final String OFFICIAL_IMAGE_DATA_DIR = "/var/lib/postgresql/data"; + private static final String OFFICIAL_IMAGE_PGDATA_DIR = "/var/lib/postgresql/data/pgdata"; + + // env variables names for the Official Docker PostgreSQL image + private static final String OFFICIAL_IMAGE_POSTGRESQL_USER_ENV_VAR = "POSTGRES_USER"; + private static final String OFFICIAL_IMAGE_POSTGRESQL_DATABASE_ENV_VAR = "POSTGRES_DB"; + private static final String OFFICIAL_IMAGE_POSTGRES_PASSWORD_ENV_VAR = "POSTGRES_PASSWORD"; + + // default env variables for for the Official Docker PostgreSQL image + private static final Map OFFICIAL_IMAGE_DEFAULT_VARS = new HashMap() { + { + // Temporary workaround for https://github.com/sclorg/postgresql-container/issues/297 + // Increase the "set_passwords.sh" timeout from the default 60s to 300s to give the + // PostgreSQL server chance properly to start under high OCP cluster load + put("PGCTLTIMEOUT", "300"); + } + }; + + // default command arguments for the Official Docker PostgreSQL image + private static final List OFFICIAL_IMAGE_DEFAULT_ARGS = new ArrayList() { + { + add("-c"); + add("shared_buffers=16MB"); + add("-c"); + add("max_connections=100"); + add("-c"); + add("max_prepared_transactions=90"); + } + }; + + private String postgresqlUserEnvVar; + private String postgresqlDatabaseEnvVar; + private Map vars; + private List args; + private String dataDir; + private String serviceAccount; + private String pgData; + + private OfficialPostgreSQL(OfficialPostgreSQLBuilder builder) { + super( + (builder.symbolicName == null || builder.symbolicName.isEmpty()) + ? DEFAULT_SYMBOLIC_NAME + : builder.symbolicName, + (builder.dataDir == null || builder.dataDir.isEmpty()) + ? OFFICIAL_IMAGE_DATA_DIR + : builder.dataDir, + builder.pvc, + builder.username, + builder.password, + builder.dbName, + builder.configureEnvironment, + builder.withLivenessProbe, + builder.withReadinessProbe, + builder.withStartupProbe, + builder.deploymentConfigName, + builder.envVarPrefix); + postgresqlUserEnvVar = OFFICIAL_IMAGE_POSTGRESQL_USER_ENV_VAR; + postgresqlDatabaseEnvVar = OFFICIAL_IMAGE_POSTGRESQL_DATABASE_ENV_VAR; + this.vars = builder.vars; + if (this.vars == null) { + this.vars = OFFICIAL_IMAGE_DEFAULT_VARS; + } + this.args = builder.args; + if (this.args == null) { + this.args = OFFICIAL_IMAGE_DEFAULT_ARGS; + } + this.dataDir = (builder.dataDir == null || builder.dataDir.isEmpty()) + ? OFFICIAL_IMAGE_DATA_DIR + : builder.dataDir; + this.serviceAccount = builder.serviceAccount; + this.pgData = (builder.pgData == null || builder.pgData.isEmpty()) + ? OFFICIAL_IMAGE_PGDATA_DIR + : builder.pgData; + } + + public void setPostgresqlUserEnvVar(String postgresqlUserEnvVar) { + this.postgresqlUserEnvVar = postgresqlUserEnvVar; + } + + public void setPostgresqlDatabaseEnvVar(String postgresqlDatabaseEnvVar) { + this.postgresqlDatabaseEnvVar = postgresqlDatabaseEnvVar; + } + + public void setVars(Map vars) { + this.vars = vars; + } + + public void setArgs(List args) { + this.args = args; + } + + @Override + public String getImageName() { + return Image.resolve("postgresql").getUrl(); + } + + @Override + public int getPort() { + return 5432; + } + + protected ProbeSettings getProbeSettings() { + return new ProbeSettings(300, + String.valueOf(getPort()), + 5, + String.format( + "psql -h 127.0.0.1 -U $%s -q -d $%s -c 'SELECT 1'", + postgresqlUserEnvVar, + postgresqlDatabaseEnvVar), + 5, + String.format( + "psql -h 127.0.0.1 -U $%s -q -d $%s -c 'SELECT 1'", + postgresqlUserEnvVar, + postgresqlDatabaseEnvVar), + 10, + 10); + } + + @Override + public String toString() { + return "OfficialPostgreSQL"; + } + + @Override + protected String getJDBCConnectionStringPattern() { + return "jdbc:postgresql://%s:%s/%s"; + } + + @Override + public Map getImageVariables() { + Map vars; + vars = new HashMap<>(); + vars.put(OFFICIAL_IMAGE_POSTGRESQL_USER_ENV_VAR, getUsername()); + vars.put(OFFICIAL_IMAGE_POSTGRES_PASSWORD_ENV_VAR, getPassword()); + vars.put(OFFICIAL_IMAGE_POSTGRESQL_DATABASE_ENV_VAR, getDbName()); + vars.put("PGDATA", this.pgData); + vars.putAll(this.vars); + return vars; + } + + @Override + public List getImageArgs() { + return args; + } + + @Override + public String getServiceAccount() { + return serviceAccount; + } + + public static class OfficialPostgreSQLBuilder { + private String symbolicName; + private String dataDir; + private PersistentVolumeClaim pvc; + private String username; + private String password; + private String dbName; + private boolean configureEnvironment = true; + private boolean withLivenessProbe; + private boolean withReadinessProbe; + private boolean withStartupProbe; + private Map vars; + private List args; + private Supplier deploymentConfigName; + private Supplier envVarPrefix; + private String serviceAccount; + private String pgData; + + public OfficialPostgreSQLBuilder withArgs(List args) { + this.args = args; + return this; + } + + public OfficialPostgreSQLBuilder withConfigureEnvironment(boolean configureEnvironment) { + this.configureEnvironment = configureEnvironment; + return this; + } + + public OfficialPostgreSQLBuilder withDataDir(String dataDir) { + this.dataDir = dataDir; + return this; + } + + public OfficialPostgreSQLBuilder withDbName(String dbName) { + this.dbName = dbName; + return this; + } + + public OfficialPostgreSQLBuilder withDeploymentConfigName(Supplier deploymentConfigName) { + this.deploymentConfigName = deploymentConfigName; + return this; + } + + public OfficialPostgreSQLBuilder withEnvVarPrefix(Supplier envVarPrefix) { + this.envVarPrefix = envVarPrefix; + return this; + } + + public OfficialPostgreSQLBuilder withPassword(String password) { + this.password = password; + return this; + } + + public OfficialPostgreSQLBuilder withPvc(PersistentVolumeClaim pvc) { + this.pvc = pvc; + return this; + } + + public OfficialPostgreSQLBuilder withSymbolicName(String symbolicName) { + this.symbolicName = symbolicName; + return this; + } + + public OfficialPostgreSQLBuilder withUsername(String username) { + this.username = username; + return this; + } + + public OfficialPostgreSQLBuilder withVars(Map vars) { + this.vars = vars; + return this; + } + + public OfficialPostgreSQLBuilder withWithLivenessProbe(boolean withLivenessProbe) { + this.withLivenessProbe = withLivenessProbe; + return this; + } + + public OfficialPostgreSQLBuilder withWithReadinessProbe(boolean withReadinessProbe) { + this.withReadinessProbe = withReadinessProbe; + return this; + } + + public OfficialPostgreSQLBuilder withWithStartupProbe(boolean withStartupProbe) { + this.withStartupProbe = withStartupProbe; + return this; + } + + public OfficialPostgreSQLBuilder withServiceAccount(String serviceAccount) { + this.serviceAccount = serviceAccount; + return this; + } + + public OfficialPostgreSQLBuilder withPgData(String pgData) { + this.pgData = pgData; + return this; + } + + public OfficialPostgreSQL build() { + OfficialPostgreSQL postgreSQL = new OfficialPostgreSQL(this); + return postgreSQL; + } + } +} diff --git a/builder/src/main/java/cz/xtf/builder/db/PostgreSQL.java b/builder/src/main/java/cz/xtf/builder/db/PostgreSQL.java index 8116c828..89bd354a 100644 --- a/builder/src/main/java/cz/xtf/builder/db/PostgreSQL.java +++ b/builder/src/main/java/cz/xtf/builder/db/PostgreSQL.java @@ -5,6 +5,10 @@ import cz.xtf.builder.builders.pod.PersistentVolumeClaim; import cz.xtf.core.image.Image; +/** + * @deprecated superseded by {@link RedHatPostgreSQL} + */ +@Deprecated public class PostgreSQL extends AbstractSQLDatabase { public PostgreSQL() { diff --git a/builder/src/main/java/cz/xtf/builder/db/RedHatPostgreSQL.java b/builder/src/main/java/cz/xtf/builder/db/RedHatPostgreSQL.java new file mode 100644 index 00000000..49b0c4c6 --- /dev/null +++ b/builder/src/main/java/cz/xtf/builder/db/RedHatPostgreSQL.java @@ -0,0 +1,221 @@ +package cz.xtf.builder.db; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; + +import cz.xtf.builder.builders.pod.PersistentVolumeClaim; +import cz.xtf.core.image.Image; + +public class RedHatPostgreSQL extends AbstractSQLDatabase { + + public static final String DEFAULT_SYMBOLIC_NAME = "POSTGRESQL"; + private static final String DEFAULT_DATA_DIR = "/var/lib/pgsql/data"; + + // default env variables for the Red Hat image + private static final Map DEFAULT_VARS = new HashMap() { + { + put("POSTGRESQL_MAX_CONNECTIONS", "100"); + put("POSTGRESQL_SHARED_BUFFERS", "16MB"); + put("POSTGRESQL_MAX_PREPARED_TRANSACTIONS", "90"); + // Temporary workaround for https://github.com/sclorg/postgresql-container/issues/297 + // Increase the "set_passwords.sh" timeout from the default 60s to 300s to give the + // PostgreSQL server chance properly to start under high OCP cluster load + put("PGCTLTIMEOUT", "300"); + } + }; + + // env variables names for the Red Hat image + private static final String DEFAULT_POSTGRESQL_USER_ENV_VAR = "POSTGRESQL_USER"; + private static final String DEFAULT_POSTGRESQL_DATABASE_ENV_VAR = "POSTGRESQL_DATABASE"; + + private String postgresqlUserEnvVar; + private String postgresqlDatabaseEnvVar; + private Map vars; + private List args; + + public RedHatPostgreSQL(RedHatPostgreSQLBuilder builder) { + super( + (builder.symbolicName == null || builder.symbolicName.isEmpty()) + ? DEFAULT_SYMBOLIC_NAME + : builder.symbolicName, + (builder.dataDir == null || builder.dataDir.isEmpty()) + ? DEFAULT_DATA_DIR + : builder.dataDir, + builder.pvc, + builder.username, + builder.password, + builder.dbName, + builder.configureEnvironment, + builder.withLivenessProbe, + builder.withReadinessProbe, + builder.withStartupProbe, + builder.deploymentConfigName, + builder.envVarPrefix); + postgresqlUserEnvVar = DEFAULT_POSTGRESQL_USER_ENV_VAR; + postgresqlDatabaseEnvVar = DEFAULT_POSTGRESQL_DATABASE_ENV_VAR; + this.vars = builder.vars; + if (this.vars == null) { + this.vars = DEFAULT_VARS; + } + this.args = builder.args; + } + + public void setPostgresqlUserEnvVar(String postgresqlUserEnvVar) { + this.postgresqlUserEnvVar = postgresqlUserEnvVar; + } + + public void setPostgresqlDatabaseEnvVar(String postgresqlDatabaseEnvVar) { + this.postgresqlDatabaseEnvVar = postgresqlDatabaseEnvVar; + } + + public void setVars(Map vars) { + this.vars = vars; + } + + public void setArgs(List args) { + this.args = args; + } + + @Override + public List getImageArgs() { + return args; + } + + @Override + public String getImageName() { + return Image.resolve("postgresql").getUrl(); + } + + @Override + public int getPort() { + return 5432; + } + + protected ProbeSettings getProbeSettings() { + return new ProbeSettings(300, + String.valueOf(getPort()), + 5, + "psql -h 127.0.0.1 -U $POSTGRESQL_USER -q -d $POSTGRESQL_DATABASE -c 'SELECT 1'", + 5, + "psql -h 127.0.0.1 -U $POSTGRESQL_USER -q -d $POSTGRESQL_DATABASE -c 'SELECT 1'", + 10, + 10); + } + + @Override + public String toString() { + return "RedHatPostgreSQL"; + } + + @Override + protected String getJDBCConnectionStringPattern() { + return "jdbc:postgresql://%s:%s/%s"; + } + + @Override + public Map getImageVariables() { + Map vars = super.getImageVariables(); + vars.put("POSTGRESQL_MAX_CONNECTIONS", "100"); + vars.put("POSTGRESQL_SHARED_BUFFERS", "16MB"); + vars.put("POSTGRESQL_MAX_PREPARED_TRANSACTIONS", "90"); + // Temporary workaround for https://github.com/sclorg/postgresql-container/issues/297 + // Increase the "set_passwords.sh" timeout from the default 60s to 300s to give the + // RedHatPostgreSQL server chance properly to start under high OCP cluster load + vars.put("PGCTLTIMEOUT", "300"); + return vars; + } + + public static class RedHatPostgreSQLBuilder { + private String symbolicName; + private String dataDir; + private PersistentVolumeClaim pvc; + private String username; + private String password; + private String dbName; + private boolean configureEnvironment = true; + private boolean withLivenessProbe; + private boolean withReadinessProbe; + private boolean withStartupProbe; + private Map vars; + private List args; + private Supplier deploymentConfigName; + private Supplier envVarPrefix; + + public RedHatPostgreSQLBuilder withArgs(List args) { + this.args = args; + return this; + } + + public RedHatPostgreSQLBuilder withConfigureEnvironment(boolean configureEnvironment) { + this.configureEnvironment = configureEnvironment; + return this; + } + + public RedHatPostgreSQLBuilder withDataDir(String dataDir) { + this.dataDir = dataDir; + return this; + } + + public RedHatPostgreSQLBuilder withDbName(String dbName) { + this.dbName = dbName; + return this; + } + + public RedHatPostgreSQLBuilder withDeploymentConfigName(Supplier deploymentConfigName) { + this.deploymentConfigName = deploymentConfigName; + return this; + } + + public RedHatPostgreSQLBuilder withEnvVarPrefix(Supplier envVarPrefix) { + this.envVarPrefix = envVarPrefix; + return this; + } + + public RedHatPostgreSQLBuilder withPassword(String password) { + this.password = password; + return this; + } + + public RedHatPostgreSQLBuilder withPvc(PersistentVolumeClaim pvc) { + this.pvc = pvc; + return this; + } + + public RedHatPostgreSQLBuilder withSymbolicName(String symbolicName) { + this.symbolicName = symbolicName; + return this; + } + + public RedHatPostgreSQLBuilder withUsername(String username) { + this.username = username; + return this; + } + + public RedHatPostgreSQLBuilder withVars(Map vars) { + this.vars = vars; + return this; + } + + public RedHatPostgreSQLBuilder withWithLivenessProbe(boolean withLivenessProbe) { + this.withLivenessProbe = withLivenessProbe; + return this; + } + + public RedHatPostgreSQLBuilder withWithReadinessProbe(boolean withReadinessProbe) { + this.withReadinessProbe = withReadinessProbe; + return this; + } + + public RedHatPostgreSQLBuilder withWithStartupProbe(boolean withStartupProbe) { + this.withStartupProbe = withStartupProbe; + return this; + } + + public RedHatPostgreSQL build() { + RedHatPostgreSQL postgreSQL = new RedHatPostgreSQL(this); + return postgreSQL; + } + } +}