blob: 8389c2e09631eeda4f139d158f61b67c1aa99e76 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.brooklyn.entity.database.postgresql;
import static java.lang.String.format;
import static org.apache.brooklyn.util.ssh.BashCommands.alternativesGroup;
import static org.apache.brooklyn.util.ssh.BashCommands.chainGroup;
import static org.apache.brooklyn.util.ssh.BashCommands.dontRequireTtyForSudo;
import static org.apache.brooklyn.util.ssh.BashCommands.executeCommandThenAsUserTeeOutputToFile;
import static org.apache.brooklyn.util.ssh.BashCommands.fail;
import static org.apache.brooklyn.util.ssh.BashCommands.ifExecutableElse0;
import static org.apache.brooklyn.util.ssh.BashCommands.ifExecutableElse1;
import static org.apache.brooklyn.util.ssh.BashCommands.installPackage;
import static org.apache.brooklyn.util.ssh.BashCommands.sudo;
import static org.apache.brooklyn.util.ssh.BashCommands.sudoAsUser;
import static org.apache.brooklyn.util.ssh.BashCommands.warn;
import static org.apache.brooklyn.util.ssh.BashCommands.commandToDownloadUrlAs;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidParameterException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
import org.apache.brooklyn.api.location.OsDetails;
import org.apache.brooklyn.core.effector.ssh.SshEffectorTasks;
import org.apache.brooklyn.core.entity.Attributes;
import org.apache.brooklyn.core.sensor.BasicAttributeSensorAndConfigKey;
import org.apache.brooklyn.entity.database.DatastoreMixins;
import org.apache.brooklyn.entity.software.base.AbstractSoftwareProcessSshDriver;
import org.apache.brooklyn.entity.software.base.SoftwareProcess;
import org.apache.brooklyn.location.ssh.SshMachineLocation;
import org.apache.brooklyn.util.collections.MutableList;
import org.apache.brooklyn.util.collections.MutableMap;
import org.apache.brooklyn.util.core.task.DynamicTasks;
import org.apache.brooklyn.util.core.task.ssh.SshTasks;
import org.apache.brooklyn.util.core.task.ssh.SshTasks.OnFailingTask;
import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper;
import org.apache.brooklyn.util.exceptions.Exceptions;
import org.apache.brooklyn.util.net.Urls;
import org.apache.brooklyn.util.os.Os;
import org.apache.brooklyn.util.stream.Streams;
import org.apache.brooklyn.util.text.Identifiers;
import org.apache.brooklyn.util.text.StringEscapes;
import org.apache.brooklyn.util.text.StringFunctions;
import org.apache.brooklyn.util.text.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Charsets;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.io.Files;
/**
* The SSH implementation of the {@link PostgreSqlDriver}.
*/
public class PostgreSqlSshDriver extends AbstractSoftwareProcessSshDriver implements PostgreSqlDriver {
public static final Logger log = LoggerFactory.getLogger(PostgreSqlSshDriver.class);
public PostgreSqlSshDriver(PostgreSqlNodeImpl entity, SshMachineLocation machine) {
super(entity, machine);
entity.sensors().set(Attributes.LOG_FILE_LOCATION, getLogFile());
}
/*
* TODO this is much messier than we would like because postgres runs as user postgres,
* meaning the dirs must be RW by that user, and accessible (thus all parent paths),
* which may rule out putting it in a location used by the default user.
* Two irritating things:
* * currently we sometimes make up a different onbox base dir;
* * currently we put files to /tmp for staging
* Could investigate if it really needs to run as user postgres;
* could also see whether default user can be added to group postgres,
* and the run dir (and all parents) made accessible to group postgres.
*/
@Override
public void install() {
String version = getEntity().getConfig(SoftwareProcess.SUGGESTED_VERSION); //10-10-2
String majorMinorVersion = version.substring(0, version.lastIndexOf("-")); //10-10
String shortVersion = majorMinorVersion.substring(0, majorMinorVersion.lastIndexOf("-")); //10
String altTarget = "/opt/brooklyn/postgres/";
String altInstallDir = Urls.mergePaths(altTarget, "install/"+majorMinorVersion);
Iterable<String> pgctlLocations = ImmutableList.of(
altInstallDir+"/bin",
"/usr/pgsql-"+shortVersion+"/bin",
"/usr/lib/postgresql/"+majorMinorVersion+"/bin/",
"/opt/local/lib/postgresql"+shortVersion+"/bin/",
"/usr/pgsql-"+majorMinorVersion+"/bin",
"/usr/local/bin/",
"/usr/bin/",
"/bin/");
DynamicTasks.queueIfPossible(SshTasks.dontRequireTtyForSudo(getMachine(),
// sudo is absolutely required here, in customize we set user to postgres
OnFailingTask.FAIL)).orSubmitAndBlock();
DynamicTasks.waitForLast();
// Check whether we can find a usable pg_ctl, and if not install one
MutableList<String> findOrInstall = MutableList.<String>of()
.append("which pg_ctl")
.appendAll(Iterables.transform(pgctlLocations, StringFunctions.formatter("test -x %s/pg_ctl")))
.append(installPackage(ImmutableMap.of(
"yum", "postgresql"+shortVersion+" postgresql"+shortVersion+"-server postgresql"+shortVersion+"-contrib postgresql"+shortVersion+"-libs" ,
"apt", "postgresql-"+majorMinorVersion,
"port", "postgresql"+shortVersion+" postgresql"+shortVersion+"-server"
), null))
// due to impl of installPackage, it will not come to the line below I don't think
.append(warn(format("WARNING: failed to find or install postgresql %s binaries", majorMinorVersion)));
// Link to correct binaries folder (different versions of pg_ctl and psql don't always play well together)
MutableList<String> linkFromHere = MutableList.<String>of()
.append(ifExecutableElse1("pg_ctl", chainGroup(
"PG_EXECUTABLE=`which pg_ctl`",
"PG_DIR=`dirname $PG_EXECUTABLE`",
"echo 'found pg_ctl in '$PG_DIR' on path so linking PG bin/ to that dir'",
"ln -s $PG_DIR bin")))
.appendAll(Iterables.transform(pgctlLocations, givenDirIfFileExistsInItLinkToDir("pg_ctl", "bin")))
.append(fail(format("WARNING: failed to find postgresql %s binaries for pg_ctl, may already have another version installed; aborting", majorMinorVersion), 9));
newScript(INSTALLING)
.body.append(
dontRequireTtyForSudo(),
ifExecutableElse0("yum", getYumRepository(version, majorMinorVersion, shortVersion)),
ifExecutableElse0("apt-get", getAptRepository()),
"rm -f bin", // if left over from previous incomplete/failed install (not sure why that keeps happening!)
alternativesGroup(findOrInstall),
alternativesGroup(linkFromHere))
.failOnNonZeroResultCode()
.queue();
// check that the proposed install dir is one that user postgres can access
// TODO if command above fails then the following `getUnchecked` blocks forever
if (DynamicTasks.queue(SshEffectorTasks.ssh(sudoAsUser("postgres", "ls "+getInstallDir())).allowingNonZeroExitCode()
.summary("check postgres user can access install dir")).asTask().getUnchecked()!=0) {
log.info("Postgres install dir "+getInstallDir()+" for "+getEntity()+" is not accessible to user 'postgres'; " + "using "+altInstallDir+" instead");
String newRunDir = Urls.mergePaths(altTarget, "apps", getEntity().getApplication().getId(), getEntity().getId());
if (DynamicTasks.queue(SshEffectorTasks.ssh("ls "+altInstallDir+"/pg_ctl").allowingNonZeroExitCode()
.summary("check whether "+altInstallDir+" is set up")).asTask().getUnchecked()==0) {
// alt target already exists with binary; nothing to do for install
} else {
DynamicTasks.queue(SshEffectorTasks.ssh(
"mkdir -p "+altInstallDir,
"rm -rf '"+altInstallDir+"'",
"mv "+getInstallDir()+" "+altInstallDir,
"rm -rf '"+getInstallDir()+"'",
"ln -s "+altInstallDir+" "+getInstallDir(),
"mkdir -p " + newRunDir,
"chown -R postgres:postgres "+altTarget).runAsRoot().requiringExitCodeZero()
.summary("move install dir from user to postgres owned space"));
}
DynamicTasks.waitForLast();
setInstallDir(altInstallDir);
setRunDir(newRunDir);
}
}
private String getYumRepository(String version, String majorMinorVersion, String shortVersion) {
// postgres becomes available if you add the repos using an RPM such as
// http://yum.postgresql.org/9.3/redhat/rhel-6-i386/pgdg-centos93-9.3-1.noarch.rpm
// fedora, rhel, sl, and centos supported for RPM's
OsDetails osDetails = getMachine().getMachineDetails().getOsDetails();
String arch = osDetails.getArch();
String osMajorVersion = osDetails.getVersion();
String osName = osDetails.getName();
log.debug("postgres detecting yum information for "+getEntity()+" at "+getMachine()+": "+osName+", "+osMajorVersion+", "+arch);
if (osName==null) osName = ""; else osName = osName.toLowerCase();
if (osName.equals("ubuntu")) return "echo skipping yum repo setup as this is not an rpm environment";
if (osName.equals("rhel") || osName.contains("red hat")) osName = "redhat";
else if (osName.equals("centos")) osName = "centos";
else if (osName.equals("sl") || osName.startsWith("scientific")) osName = "sl";
else if (osName.equals("fedora")) osName = "fedora";
else {
log.debug("insufficient OS family information '"+osName+"' for "+getMachine()+" when installing "+getEntity()+" (yum repos); treating as centos");
osName = "centos";
}
if (Strings.isBlank(arch)) {
log.warn("Insuffient architecture information '"+arch+"' for "+getMachine()+"when installing "+getEntity()+"; treating as x86_64");
arch = "x86_64";
}
if (Strings.isBlank(osMajorVersion)) {
if (osName.equals("fedora")) osMajorVersion = "20";
else osMajorVersion = "7";
log.warn("Insuffient OS version information '"+getMachine().getOsDetails().getVersion()+"' for "+getMachine()+"when installing "+getEntity()+" (yum repos); treating as "+osMajorVersion);
} else {
if (osMajorVersion.indexOf(".")>0)
osMajorVersion = osMajorVersion.substring(0, osMajorVersion.indexOf('.'));
}
return chainGroup(
sudo(commandToDownloadUrlAs(
format("https://download.postgresql.org/pub/repos/yum/%s/redhat/rhel-%s-%s/pgdg-centos%s.noarch.rpm", shortVersion, osMajorVersion, arch, version),
"pgrepo.rpm")),
sudo("rpm -Uvh pgrepo.rpm")
);
}
private String getAptRepository() {
return chainGroup(
sudo("apt-key adv --fetch-keys http://apt.postgresql.org/pub/repos/apt/ACCC4CF8.asc"),
"echo \"deb http://apt.postgresql.org/pub/repos/apt/ $(sudo lsb_release --codename --short)-pgdg main\" | sudo tee -a /etc/apt/sources.list.d/postgresql.list"
);
}
private static Function<String, String> givenDirIfFileExistsInItLinkToDir(final String filename, final String linkToMake) {
return new Function<String, String>() {
@Override
public String apply(@Nullable String dir) {
return ifExecutableElse1(Urls.mergePaths(dir, filename),
chainGroup("echo 'found "+filename+" in "+dir+" so linking to it in "+linkToMake+"'", "ln -s "+dir+" "+linkToMake));
}
};
}
@Override
public void customize() {
// Some OSes start postgres during package installation
DynamicTasks.queue(SshEffectorTasks.ssh(sudoAsUser("postgres", "/etc/init.d/postgresql stop")).allowingNonZeroExitCode()).get();
newScript(CUSTOMIZING)
.body.append(
sudo("mkdir -p " + getDataDir()),
sudo("chown postgres:postgres " + getDataDir()),
sudo("chmod 700 " + getDataDir()),
sudo("touch " + getLogFile()),
sudo("chown postgres:postgres " + getLogFile()),
sudo("touch " + getPidFile()),
sudo("chown postgres:postgres " + getPidFile()),
alternativesGroup(
chainGroup(format("test -e %s", getInstallDir() + "/bin/initdb"),
sudoAsUser("postgres", getInstallDir() + "/bin/initdb -D " + getDataDir())),
callPgctl("initdb", true)))
.failOnNonZeroResultCode()
.execute();
String configUrl = getEntity().getConfig(PostgreSqlNode.CONFIGURATION_FILE_URL);
if (Strings.isBlank(configUrl)) {
// http://wiki.postgresql.org/wiki/Tuning_Your_PostgreSQL_Server
// If the same setting is listed multiple times, the last one wins.
DynamicTasks.queue(SshEffectorTasks.ssh(
executeCommandThenAsUserTeeOutputToFile(
chainGroup(
"echo \"listen_addresses = '*'\"",
"echo \"port = " + getEntity().getPostgreSqlPort() + "\"",
"echo \"max_connections = " + getEntity().getMaxConnections() + "\"",
"echo \"shared_buffers = " + getEntity().getSharedMemory() + "\"",
"echo \"external_pid_file = '" + getPidFile() + "'\""),
"postgres", getDataDir() + "/postgresql.conf")));
} else {
String contents = processTemplate(configUrl);
DynamicTasks.queue(
SshEffectorTasks.put("/tmp/postgresql.conf").contents(contents),
SshEffectorTasks.ssh(sudoAsUser("postgres", "cp /tmp/postgresql.conf " + getDataDir() + "/postgresql.conf")));
}
String authConfigUrl = getEntity().getConfig(PostgreSqlNode.AUTHENTICATION_CONFIGURATION_FILE_URL);
if (Strings.isBlank(authConfigUrl)) {
DynamicTasks.queue(SshEffectorTasks.ssh(
// TODO give users control which hosts can connect and the authentication mechanism
executeCommandThenAsUserTeeOutputToFile("echo \"host all all 0.0.0.0/0 md5\"", "postgres", getDataDir() + "/pg_hba.conf")));
} else {
String contents = processTemplate(authConfigUrl);
DynamicTasks.queue(
SshEffectorTasks.put("/tmp/pg_hba.conf").contents(contents),
SshEffectorTasks.ssh(sudoAsUser("postgres", "cp /tmp/pg_hba.conf " + getDataDir() + "/pg_hba.conf")));
}
// Wait for commands to complete before running the creation script
DynamicTasks.waitForLast();
if(Boolean.TRUE.equals(entity.getConfig(PostgreSqlNode.INITIALIZE_DB))){
initializeNewDatabase();
}
// Capture log file contents if there is an error configuring the database
try {
executeDatabaseCreationScript();
} catch (RuntimeException r) {
logTailOfPostgresLog();
throw Exceptions.propagate(r);
}
// Try establishing an external connection. If you get a "Connection refused...accepting TCP/IP connections
// on port 5432?" error then the port is probably closed. Check that the firewall allows external TCP/IP
// connections (netstat -nap). You can open a port with lokkit or by configuring the iptables.
}
private void initializeNewDatabase() {
String createUserCommand = String.format(
"\"CREATE USER %s WITH PASSWORD '%s'; \"",
StringEscapes.escapeSql(getUsername()),
StringEscapes.escapeSql(getUserPassword())
);
String createDatabaseCommand = String.format(
"\"CREATE DATABASE %s OWNER %s\"",
StringEscapes.escapeSql(getDatabaseName()),
StringEscapes.escapeSql(getUsername()));
String createRolesAdditionalCommand = "";
if (entity.getConfig(PostgreSqlNode.ROLES) != null && !entity.getConfig(PostgreSqlNode.ROLES).isEmpty()) {
String createRolesQuery = buildCreateRolesQuery();
createRolesAdditionalCommand =
sudoAsUser("postgres", getInstallDir() + "/bin/psql -p " + entity.getAttribute(PostgreSqlNode.POSTGRESQL_PORT) +
" --command="+ createRolesQuery);
}
newScript("initializing user and database")
.body.append(
"cd " + getInstallDir(),
callPgctl("start", true),
sudoAsUser("postgres", getInstallDir() + "/bin/psql -p " + entity.getAttribute(PostgreSqlNode.POSTGRESQL_PORT) +
" --command="+ createUserCommand),
sudoAsUser("postgres", getInstallDir() + "/bin/psql -p " + entity.getAttribute(PostgreSqlNode.POSTGRESQL_PORT) +
" --command="+ createDatabaseCommand),
createRolesAdditionalCommand,
callPgctl("stop", true))
.failOnNonZeroResultCode().execute();
}
@VisibleForTesting
protected String buildCreateRolesQuery() {
Map<String, Map<String, ?>> roles = entity.getConfig(PostgreSqlNode.ROLES);
StringBuilder builder = new StringBuilder("\"");
for (Map.Entry<String, ? extends Map<String, ?>> entry : roles.entrySet()) {
String roleName = entry.getKey();
Map<String, ?> roleConfig = entry.getValue();
if (roleConfig == null) roleConfig = ImmutableMap.of();
if (Strings.isBlank(roleName)) {
throw new NullPointerException("Role name must not be blank, but got "+roles);
}
validateInput(roleName, "role name '"+roleName+"'");
builder.append(String.format("CREATE ROLE %s", roleName));
if (roleConfig != null && roleConfig.containsKey(PostgreSqlNode.ROLE_PROPERTIES_KEY)) {
Object rawProps = roleConfig.get("properties");
String props = validateInput((String) rawProps, "role '"+roleName+"' property "+rawProps);;
builder.append(String.format(" WITH %s; ", props));
} else {
builder.append("; ");
}
if (roleConfig.containsKey(PostgreSqlNode.ROLE_PRIVILEGES_KEY)) {
List<String> privileges = toListOfStrings(roleConfig.get(PostgreSqlNode.ROLE_PRIVILEGES_KEY));
for (Object rawPrivilege : privileges) {
String privilege = validateInput((String) rawPrivilege, "role '"+roleName+"' privilege "+rawPrivilege);
builder.append(String.format("GRANT %s TO %s; ", privilege, roleName));
}
}
Set<String> otherConfig = Sets.difference(roleConfig.keySet(),
ImmutableSet.of(PostgreSqlNode.ROLE_PROPERTIES_KEY, PostgreSqlNode.ROLE_PRIVILEGES_KEY));
if (!otherConfig.isEmpty()) {
throw new IllegalArgumentException("Invalid configuration for role "+roleName+", got "+roles);
}
}
builder.append("\"");
return builder.toString();
}
private String validateInput(String input, String errContext) {
String regex = "[A-Za-z_,\\s]+";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(input);
if (Strings.isBlank(input)) {
throw new NullPointerException("Must be non-blank for "+errContext);
}
if (matcher.find() && matcher.start() == 0 && matcher.end() == input.length()) {
return input;
}
throw new InvalidParameterException("Query input seems to be insecure. Make sure you pass a valid value for "+errContext);
}
// TODO Duplicate of JcloudsLocation.toListOfStrings
private static List<String> toListOfStrings(Object v) {
List<String> result = Lists.newArrayList();
if (v instanceof Iterable) {
for (Object o : (Iterable<?>)v) {
result.add(o.toString());
}
} else if (v instanceof Object[]) {
for (int i = 0; i < ((Object[])v).length; i++) {
result.add(((Object[])v)[i].toString());
}
} else if (v instanceof String) {
result.add((String) v);
} else {
throw new IllegalArgumentException("Invalid type for List<String>: "+v+" of type "+v.getClass());
}
return result;
}
private String getConfigOrDefault(BasicAttributeSensorAndConfigKey<String> key, String def) {
String val = entity.getConfig(key);
if (Strings.isEmpty(val)) {
val = entity.sensors().get(key);
if (Strings.isEmpty(val)) {
val = def;
log.debug(entity + " has no config specified for " + key + "; using default `" + def + "`");
entity.sensors().set(key, val);
}
}
return val;
}
protected String getDatabaseName() {
return getConfigOrDefault(PostgreSqlNode.DATABASE, PostgreSqlNode.DEFAULT_DB_NAME);
}
protected String getUsername(){
return getConfigOrDefault(PostgreSqlNode.USERNAME, PostgreSqlNode.DEFAULT_USERNAME);
}
protected String getUserPassword() {
return getConfigOrDefault(PostgreSqlNode.PASSWORD, Identifiers.makeRandomPassword(8));
}
protected void executeDatabaseCreationScript() {
if (copyDatabaseCreationScript()) {
newScript("running postgres creation script")
.body.append(
"cd " + getInstallDir(),
callPgctl("start", true),
sudoAsUser("postgres", getInstallDir() + "/bin/psql -p " + entity.getAttribute(PostgreSqlNode.POSTGRESQL_PORT) + " --file " + getRunDir() + "/creation-script.sql"),
callPgctl("stop", true))
.failOnNonZeroResultCode()
.execute();
}
}
private boolean installFile(InputStream contents, String destName) {
String uid = Identifiers.makeRandomId(8);
// TODO currently put in /tmp for staging, since run dir may not be accessible to ssh user
getMachine().copyTo(contents, "/tmp/"+destName+"_"+uid);
DynamicTasks.queueIfPossible(SshEffectorTasks.ssh(
"cd "+getRunDir(),
"mv /tmp/"+destName+"_"+uid+" "+destName,
"chown postgres:postgres "+destName,
"chmod 644 "+destName)
.runAsRoot().requiringExitCodeZero())
.orSubmitAndBlock(getEntity()).andWaitForSuccess();
return true;
}
private boolean copyDatabaseCreationScript() {
InputStream creationScript = DatastoreMixins.getDatabaseCreationScript(entity);
if (creationScript==null)
return false;
return installFile(creationScript, "creation-script.sql");
}
public String getDataDir() {
return getRunDir() + "/data";
}
public String getLogFile() {
return getRunDir() + "/postgresql.log";
}
public String getPidFile() {
return getRunDir() + "/postgresql.pid";
}
/** @deprecated since 0.7.0 renamed {@link #logTailOfPostgresLog()} */
@Deprecated
public void copyLogFileContents() { logTailOfPostgresLog(); }
public void logTailOfPostgresLog() {
try {
File file = Os.newTempFile("postgresql-"+getEntity().getId(), "log");
int result = getMachine().copyFrom(getLogFile(), file.getAbsolutePath());
if (result != 0) throw new IllegalStateException("Could not access log file " + getLogFile());
log.info("Saving {} contents as {}", getLogFile(), file);
Streams.logStreamTail(log, "postgresql.log", Streams.byteArrayOfString(Files.toString(file, Charsets.UTF_8)), 1024);
file.delete();
} catch (IOException ioe) {
log.debug("Error reading copied log file: {}", ioe);
}
}
protected String callPgctl(String command, boolean waitForIt) {
return sudoAsUser("postgres", getInstallDir() + "/bin/pg_ctl -D " + getDataDir() +
" -l " + getLogFile() + (waitForIt ? " -w " : " ") + command);
}
@Override
public void launch() {
log.info(String.format("Starting entity %s at %s", this, getLocation()));
newScript(MutableMap.of("usePidFile", false), LAUNCHING)
.body.append(callPgctl("start", false))
.execute();
}
@Override
public boolean isRunning() {
return newScript(MutableMap.of("usePidFile", getPidFile()), CHECK_RUNNING)
.body.append(getStatusCmd())
.execute() == 0;
}
@Override
public void stop() {
newScript(MutableMap.of("usePidFile", false), STOPPING)
.body.append(callPgctl((entity.getConfig(PostgreSqlNode.DISCONNECT_ON_STOP) ? "-m immediate " : "") + "stop", false))
.failOnNonZeroResultCode()
.execute();
newScript(MutableMap.of("usePidFile", getPidFile(), "processOwner", "postgres"), STOPPING).execute();
}
@Override
public PostgreSqlNodeImpl getEntity() {
return (PostgreSqlNodeImpl) super.getEntity();
}
@Override
public String getStatusCmd() {
return callPgctl("status", false);
}
@Override
public ProcessTaskWrapper<Integer> executeScriptAsync(String commands) {
String filename = "postgresql-commands-"+Identifiers.makeRandomId(8);
installFile(Streams.newInputStreamWithContents(commands), filename);
return executeScriptFromInstalledFileAsync(filename);
}
public ProcessTaskWrapper<Integer> executeScriptFromInstalledFileAsync(String filenameAlreadyInstalledAtServer) {
return DynamicTasks.queue(
SshEffectorTasks.ssh(
"cd "+getRunDir(),
sudoAsUser("postgres", getInstallDir() + "/bin/psql -p " + entity.getAttribute(PostgreSqlNode.POSTGRESQL_PORT) + " -v ON_ERROR_STOP=1 --file " + filenameAlreadyInstalledAtServer))
.requiringExitCodeZero()
.summary("executing datastore script "+filenameAlreadyInstalledAtServer));
}
}