Extended the support for running scripts as a part of Compute Service. Now scripts can be run as root. Also, credentials are optional and moved to RunScriptOptions.

TODO next: tests to verify non-root execution of the scripts
This commit is contained in:
Alex Yarmula 2010-04-14 15:00:26 -07:00
parent 882bf5f651
commit 5ff12c4a73
4 changed files with 74 additions and 43 deletions

View File

@ -158,23 +158,22 @@ public interface ComputeService {
/**
* Runs the script without any additional options
*
* @see #runScriptOnNodesWithTag(String, org.jclouds.domain.Credentials,
* @see #runScriptOnNodesWithTag(String,
* byte[], org.jclouds.compute.options.RunScriptOptions)
*/
Map<String, ExecResponse> runScriptOnNodesWithTag(String tag, Credentials credentials,
Map<String, ExecResponse> runScriptOnNodesWithTag(String tag,
byte[] runScript);
/**
* Run the script on all nodes with the specific tag.
*
* @param tag tag to look up the nodes
* @param credentials credentials to use (same for all nodes)
* @param runScript script to run in byte format. If the script is a string, use
* {@link String#getBytes()} to retrieve the bytes
* @param options nullable options to how to run the script
* @return map with node identifiers and corresponding responses
*/
Map<String, ExecResponse> runScriptOnNodesWithTag(String tag, Credentials credentials,
Map<String, ExecResponse> runScriptOnNodesWithTag(String tag,
byte[] runScript, RunScriptOptions options);
}

View File

@ -323,26 +323,24 @@ public class BaseComputeService implements ComputeService {
}
/**
* @see #runScriptOnNodesWithTag(String, org.jclouds.domain.Credentials, byte[],
* @see #runScriptOnNodesWithTag(String, byte[],
* org.jclouds.compute.options.RunScriptOptions)
*/
public Map<String, ExecResponse> runScriptOnNodesWithTag(String tag, Credentials credentials,
public Map<String, ExecResponse> runScriptOnNodesWithTag(String tag,
byte[] runScript) {
return runScriptOnNodesWithTag(tag, credentials, runScript, RunScriptOptions.NONE);
return runScriptOnNodesWithTag(tag, runScript, RunScriptOptions.NONE);
}
/**
* Run the script on all nodes with the specific tag.
*
* @param tag tag to look up the nodes
* @param credentials nullable credentials to use (same for all nodes).
* @param runScript script to run in byte format. If the script is a string, use
* {@link String#getBytes()} to retrieve the bytes
* @param options nullable options to how to run the script
* @param options nullable options to how to run the script, whether to override credentials
* @return map with node identifiers and corresponding responses
*/
public Map<String, ExecResponse> runScriptOnNodesWithTag(String tag, @Nullable Credentials credentials,
byte[] runScript, @Nullable RunScriptOptions options) {
public Map<String, ExecResponse> runScriptOnNodesWithTag(String tag, byte[] runScript, @Nullable RunScriptOptions options) {
checkNotEmpty(tag, "Tag must be provided");
checkNotNull(runScript,
"The script (represented by bytes array - use \"script\".getBytes() must be provided");
@ -354,18 +352,19 @@ public class BaseComputeService implements ComputeService {
for(NodeMetadata node : nodes.values()) {
if(NodeState.RUNNING != node.getState()) continue; //make sure the node is active
if(options.isOverrideCredentials()) {
if(options.getOverrideCredentials() != null) {
//override the credentials with provided to this method
checkNotNull(credentials, "If the credentials need to be overridden, they can't be null");
node = ComputeUtils.installNewCredentials(node, credentials);
node = ComputeUtils.installNewCredentials(node, options.getOverrideCredentials());
} else {
//don't override
checkNotNull(node.getCredentials(), "If the default credentials need to be used, they can't be null");
}
//todo: execute script as root if required
ComputeUtils.SshCallable<?> callable = utils.runScriptOnNode(node, "computeserv.sh", runScript);
ComputeUtils.SshCallable<?> callable;
if(options.isRunAsRoot())
callable = utils.runScriptOnNode(node, "computeserv.sh", runScript);
else
callable = utils.runScriptOnNodeAsDefaultUser(node, "computeserv.sh", runScript);
Map<ComputeUtils.SshCallable<?>, ?> scriptRunResults = utils.runCallablesOnNode(node,
Sets.newHashSet(callable),

View File

@ -18,6 +18,8 @@
*/
package org.jclouds.compute.options;
import org.jclouds.domain.Credentials;
/**
* Enables additional options for running a script.
*
@ -30,16 +32,16 @@ public class RunScriptOptions {
* <ul>
* <li>override the credentials with ones supplied in
* call to {@link org.jclouds.compute.ComputeService#runScriptOnNodesWithTag}</li>
* <li>do not run the script as root (run with current privileges)</li>
* <li>run the script as root (versus running with current privileges)</li>
* </ul>
*/
public static final RunScriptOptions NONE = new RunScriptOptions();
private boolean overrideCredentials = true;
private boolean runAsRoot = false;
private Credentials overridingCredentials;
private boolean runAsRoot = true;
private void overrideCredentials(boolean overrideCredentials) {
this.overrideCredentials = overrideCredentials;
private void withOverridingCredentials(Credentials overridingCredentials) {
this.overridingCredentials = overridingCredentials;
}
private void runAsRoot(boolean runAsRoot) {
@ -52,13 +54,13 @@ public class RunScriptOptions {
* By default, true.
* @return value
*/
public boolean isOverrideCredentials() {
return overrideCredentials;
public Credentials getOverrideCredentials() {
return overridingCredentials;
}
/**
* Whether to run the script as root (run with current privileges).
* By default, false.
* Whether to run the script as root (or run with current privileges).
* By default, true.
* @return value
*/
public boolean isRunAsRoot() {
@ -68,9 +70,9 @@ public class RunScriptOptions {
public static class Builder {
private RunScriptOptions options;
public Builder overrideCredentials(boolean value) {
public Builder overrideCredentials(Credentials credentials) {
if(options == null) options = new RunScriptOptions();
options.overrideCredentials(value);
options.withOverridingCredentials(credentials);
return this;
}

View File

@ -130,6 +130,10 @@ public class ComputeUtils {
return new RunScriptOnNode(runScriptNotRunning, node, scriptName, script);
}
public RunScriptOnNode runScriptOnNodeAsDefaultUser(NodeMetadata node, String scriptName, byte[] script) {
return new RunScriptOnNode(runScriptNotRunning, node, scriptName, script, false);
}
public Map<SshCallable<?>, ?> runCallablesOnNode(NodeMetadata node, Iterable<? extends SshCallable<?>> parallel,
@Nullable SshCallable<?> last) {
checkState(this.sshFactory != null, "runScript requested, but no SshModule configured");
@ -210,6 +214,7 @@ public class ComputeUtils {
private final NodeMetadata node;
private final String scriptName;
private final byte[] script;
private final boolean runAsRoot;
private Logger logger = Logger.NULL;
RunScriptOnNode(@Named("NOT_RUNNING") Predicate<SshClient> runScriptNotRunning,
@ -221,6 +226,19 @@ public class ComputeUtils {
.<String, String> of(), Iterables.toArray(Splitter.on("\n").split(
new String(checkNotNull(script, "script"))), String.class)).build(OsFamily.UNIX)
.getBytes();
this.runAsRoot = true;
}
RunScriptOnNode(@Named("NOT_RUNNING") Predicate<SshClient> runScriptNotRunning,
NodeMetadata node, String scriptName, byte[] script, boolean runAsRoot) {
this.runScriptNotRunning = runScriptNotRunning;
this.node = checkNotNull(node, "node");
this.scriptName = checkNotNull(scriptName, "scriptName");
this.script = new InitBuilder("runscript", "/tmp", "/tmp", ImmutableMap
.<String, String> of(), Iterables.toArray(Splitter.on("\n").split(
new String(checkNotNull(script, "script"))), String.class)).build(OsFamily.UNIX)
.getBytes();
this.runAsRoot = runAsRoot;
}
@Override
@ -228,21 +246,9 @@ public class ComputeUtils {
ssh.put(scriptName, new ByteArrayInputStream(script));
ExecResponse returnVal = ssh.exec("chmod 755 " + scriptName);
returnVal = ssh.exec("./" + scriptName + " init");
if (node.getCredentials().account.equals("root")) {
logger.debug(">> running %s as %s@%s", scriptName, node.getCredentials().account,
Iterables.get(node.getPublicAddresses(), 0).getHostAddress());
returnVal = ssh.exec("./" + scriptName + " start");
} else if (isKeyAuth(node)) {
logger.debug(">> running sudo %s as %s@%s", scriptName, node.getCredentials().account,
Iterables.get(node.getPublicAddresses(), 0).getHostAddress());
returnVal = ssh.exec("sudo ./" + scriptName + " start");
} else {
logger.debug(">> running sudo -S %s as %s@%s", scriptName,
node.getCredentials().account, Iterables.get(node.getPublicAddresses(), 0)
.getHostAddress());
returnVal = ssh.exec(String.format("echo %s|sudo -S ./%s", node.getCredentials().key,
scriptName + " start"));
}
if(runAsRoot) returnVal = runScriptAsRoot();
else returnVal = runScriptAsDefaultUser();
runScriptNotRunning.apply(ssh);
logger.debug("<< complete(%d)", returnVal.getExitCode());
return returnVal;
@ -253,6 +259,31 @@ public class ComputeUtils {
this.logger = checkNotNull(logger, "logger");
this.ssh = checkNotNull(ssh, "ssh");
}
private ExecResponse runScriptAsRoot() {
if (node.getCredentials().account.equals("root")) {
logger.debug(">> running %s as %s@%s", scriptName, node.getCredentials().account,
Iterables.get(node.getPublicAddresses(), 0).getHostAddress());
return ssh.exec("./" + scriptName + " start");
} else if (isKeyAuth(node)) {
logger.debug(">> running sudo %s as %s@%s", scriptName, node.getCredentials().account,
Iterables.get(node.getPublicAddresses(), 0).getHostAddress());
return ssh.exec("sudo ./" + scriptName + " start");
} else {
logger.debug(">> running sudo -S %s as %s@%s", scriptName,
node.getCredentials().account, Iterables.get(node.getPublicAddresses(), 0)
.getHostAddress());
return ssh.exec(String.format("echo %s|sudo -S ./%s", node.getCredentials().key,
scriptName + " start"));
}
}
private ExecResponse runScriptAsDefaultUser() {
logger.debug(">> running script %s as %s@%s", scriptName,
node.getCredentials().account, Iterables.get(node.getPublicAddresses(), 0)
.getHostAddress());
return ssh.exec(String.format("./%s", scriptName + " start"));
}
}
public static class InstallRSAPrivateKey implements SshCallable<ExecResponse> {