mirror of https://github.com/apache/jclouds.git
added statements for extracting targzs and running commands from a http request
This commit is contained in:
parent
0e15e52a70
commit
00361f859b
|
@ -21,18 +21,23 @@ package org.jclouds.aws;
|
||||||
|
|
||||||
import static org.jclouds.compute.BaseComputeServiceLiveTest.buildScript;
|
import static org.jclouds.compute.BaseComputeServiceLiveTest.buildScript;
|
||||||
import static org.jclouds.compute.options.TemplateOptions.Builder.runScript;
|
import static org.jclouds.compute.options.TemplateOptions.Builder.runScript;
|
||||||
import static org.jclouds.compute.util.ComputeServiceUtils.buildCurlsh;
|
import static org.jclouds.compute.util.ComputeServiceUtils.execHttpResponse;
|
||||||
import static org.jclouds.io.Payloads.newStringPayload;
|
import static org.jclouds.compute.util.ComputeServiceUtils.extractTargzIntoDirectory;
|
||||||
|
import static org.jclouds.scriptbuilder.domain.Statements.newStatementList;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
import org.jclouds.aws.ec2.compute.BlobStoreAndComputeServiceLiveTest;
|
import org.jclouds.aws.ec2.compute.BlobStoreAndComputeServiceLiveTest;
|
||||||
|
import org.jclouds.blobstore.BlobStore;
|
||||||
import org.jclouds.compute.RunNodesException;
|
import org.jclouds.compute.RunNodesException;
|
||||||
import org.jclouds.compute.domain.NodeMetadata;
|
import org.jclouds.compute.domain.NodeMetadata;
|
||||||
import org.jclouds.compute.domain.OperatingSystem;
|
import org.jclouds.compute.domain.OperatingSystem;
|
||||||
import org.jclouds.compute.options.TemplateOptions;
|
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
|
import org.jclouds.scriptbuilder.domain.Statement;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* This test helps us understand how we can use the power of blobstores to our favor.
|
||||||
*
|
*
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
|
@ -57,9 +62,9 @@ public class ComputeAndBlobStoreTogetherHappilyLiveTest extends BlobStoreAndComp
|
||||||
* next convert this into something that can be invoked via the commandline. Looking around, it
|
* next convert this into something that can be invoked via the commandline. Looking around, it
|
||||||
* seems like alestic runurl is pretty close. However, it is limited as it only works on requests
|
* seems like alestic runurl is pretty close. However, it is limited as it only works on requests
|
||||||
* that can be fully specified without headers (ex. Amazon S3). Instead, we use a variant
|
* that can be fully specified without headers (ex. Amazon S3). Instead, we use a variant
|
||||||
* (curlsh).
|
* (execHttpResponse).
|
||||||
* <p/>
|
* <p/>
|
||||||
* curlsh simply assembles an http request, headers and all, and passes it to bash
|
* execHttpResponse simply assembles an http request, headers and all, and passes it to bash
|
||||||
* <p/>
|
* <p/>
|
||||||
* With this script ready, any node or nodes will take instructions from the blobstore when it
|
* With this script ready, any node or nodes will take instructions from the blobstore when it
|
||||||
* boots up. we verify this with an assertion.
|
* boots up. we verify this with an assertion.
|
||||||
|
@ -79,15 +84,27 @@ public class ComputeAndBlobStoreTogetherHappilyLiveTest extends BlobStoreAndComp
|
||||||
// configured for amz. Note we are getting temporary access to a private blob.
|
// configured for amz. Note we are getting temporary access to a private blob.
|
||||||
HttpRequest signedRequestOfInstallScript = blobContext.getBlobStore().signRequestForBlob(tag, "openjdk/install");
|
HttpRequest signedRequestOfInstallScript = blobContext.getBlobStore().signRequestForBlob(tag, "openjdk/install");
|
||||||
|
|
||||||
// instruct the bootstrap to pull the script from the blobstore and invoke it directly with bash.
|
// so one of our commands is to execute the contents of the blob above
|
||||||
TemplateOptions runOpenJDKInstallDirectlyFromBlobStore = runScript(newStringPayload(buildCurlsh(signedRequestOfInstallScript)));
|
Statement installOpenJDK = execHttpResponse(signedRequestOfInstallScript);
|
||||||
|
|
||||||
|
// if we want to, we can mix and match batched and ad-hoc commands, such as extracting maven
|
||||||
|
String mavenVersion = "3.0-beta-3";
|
||||||
|
Statement extractMavenIntoUsrLocal = extractTargzIntoDirectory(URI
|
||||||
|
.create("http://mirrors.ibiblio.org/pub/mirrors/apache//maven/binaries/apache-maven-" + mavenVersion
|
||||||
|
+ "-bin.tar.gz"), "/usr/local");
|
||||||
|
|
||||||
|
// have both of these commands occur on boot
|
||||||
|
Statement bootstrapInstructions = newStatementList(installOpenJDK, extractMavenIntoUsrLocal);
|
||||||
|
|
||||||
// now that we have the correct instructions, kick-off the provisioner
|
// now that we have the correct instructions, kick-off the provisioner
|
||||||
Iterable<? extends NodeMetadata> nodes = computeContext.getComputeService().runNodesWithTag(tag, 1,
|
Iterable<? extends NodeMetadata> nodes = computeContext.getComputeService().runNodesWithTag(tag, 2,
|
||||||
runOpenJDKInstallDirectlyFromBlobStore);
|
runScript(bootstrapInstructions));
|
||||||
|
|
||||||
// ensure the bootstrap operated by checking for a known signature of success.
|
// ensure the bootstrap operated by checking for the components we installed at boot time.
|
||||||
assertSshOutputOfCommandContains(nodes, "openjdk -version", "OpenJDK");
|
// Note this test will ensure both nodes are in sync.
|
||||||
|
assertSshOutputOfCommandContains(nodes, "java -version", "OpenJDK");
|
||||||
|
assertSshOutputOfCommandContains(nodes, "/usr/local/apache-maven-" + mavenVersion + "/bin/mvn -version",
|
||||||
|
"Apache Maven " + mavenVersion + "");
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,20 +64,20 @@ public class BlobStoreAndComputeServiceLiveTest {
|
||||||
setupCredentials();
|
setupCredentials();
|
||||||
computeContext = new ComputeServiceContextFactory().createContext(computeServiceProvider, identity, credential,
|
computeContext = new ComputeServiceContextFactory().createContext(computeServiceProvider, identity, credential,
|
||||||
ImmutableSet.of(new Log4JLoggingModule(), new JschSshClientModule()));
|
ImmutableSet.of(new Log4JLoggingModule(), new JschSshClientModule()));
|
||||||
blobContext = new BlobStoreContextFactory().createContext(blobStoreProvider, identity, credential,
|
blobContext = new BlobStoreContextFactory().createContext(blobStoreProvider, identity, credential, ImmutableSet
|
||||||
ImmutableSet.of(new Log4JLoggingModule()));
|
.of(new Log4JLoggingModule()));
|
||||||
blobContext.getAsyncBlobStore().createContainerInLocation(null, tag);
|
blobContext.getAsyncBlobStore().createContainerInLocation(null, tag);
|
||||||
computeContext.getComputeService().destroyNodesMatching(NodePredicates.withTag(tag));
|
computeContext.getComputeService().destroyNodesMatching(NodePredicates.withTag(tag));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void assertSshOutputOfCommandContains(Iterable<? extends NodeMetadata> nodes, String cmd, String expects) {
|
protected void assertSshOutputOfCommandContains(Iterable<? extends NodeMetadata> nodes, String cmd, String expects) {
|
||||||
IPSocket socket = new IPSocket(get(get(nodes, 0).getPublicAddresses(), 0), 22);
|
for (NodeMetadata node : nodes) {
|
||||||
|
IPSocket socket = new IPSocket(get(node.getPublicAddresses(), 0), 22);
|
||||||
|
|
||||||
SshClient ssh = computeContext.utils().sshFactory().create(socket, get(nodes, 0).getCredentials().identity,
|
SshClient ssh = computeContext.utils().sshFactory().create(socket, node.getCredentials().identity,
|
||||||
get(nodes, 0).getCredentials().credential.getBytes());
|
node.getCredentials().credential.getBytes());
|
||||||
try {
|
try {
|
||||||
ssh.connect();
|
ssh.connect();
|
||||||
;
|
|
||||||
ExecResponse exec = ssh.exec(cmd);
|
ExecResponse exec = ssh.exec(cmd);
|
||||||
assert exec.getOutput().indexOf(expects) != -1 || exec.getError().indexOf(expects) != -1 : exec;
|
assert exec.getOutput().indexOf(expects) != -1 || exec.getError().indexOf(expects) != -1 : exec;
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -85,6 +85,7 @@ public class BlobStoreAndComputeServiceLiveTest {
|
||||||
ssh.disconnect();
|
ssh.disconnect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected void uploadBlob(String container, String name, String script) {
|
protected void uploadBlob(String container, String name, String script) {
|
||||||
Blob blob = blobContext.getBlobStore().newBlob(name);
|
Blob blob = blobContext.getBlobStore().newBlob(name);
|
||||||
|
|
|
@ -26,14 +26,19 @@ import static org.jclouds.io.Payloads.newStringPayload;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.jclouds.compute.domain.OperatingSystem;
|
||||||
|
import org.jclouds.compute.predicates.OperatingSystemPredicates;
|
||||||
import org.jclouds.io.Payload;
|
import org.jclouds.io.Payload;
|
||||||
|
import org.jclouds.scriptbuilder.domain.OsFamily;
|
||||||
|
import org.jclouds.scriptbuilder.domain.Statement;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains options supported in the {@code ComputeService#runNodesWithTag}
|
* Contains options supported in the {@code ComputeService#runNodesWithTag} operation. <h2>
|
||||||
* operation. <h2>
|
* Usage</h2> The recommended way to instantiate a TemplateOptions object is to statically import
|
||||||
* Usage</h2> The recommended way to instantiate a TemplateOptions object is to
|
* TemplateOptions.* and invoke a static creation method followed by an instance mutator (if
|
||||||
* statically import TemplateOptions.* and invoke a static creation method
|
* needed):
|
||||||
* followed by an instance mutator (if needed):
|
|
||||||
* <p/>
|
* <p/>
|
||||||
* <code>
|
* <code>
|
||||||
* import static org.jclouds.compute.options.TemplateOptions.Builder.*;
|
* import static org.jclouds.compute.options.TemplateOptions.Builder.*;
|
||||||
|
@ -207,8 +212,8 @@ public class TemplateOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This script will be executed as the root user upon system startup. This
|
* This script will be executed as the root user upon system startup. This script gets a
|
||||||
* script gets a prologue, so no #!/bin/bash required, path set up, etc
|
* prologue, so no #!/bin/bash required, path set up, etc
|
||||||
* <p/>
|
* <p/>
|
||||||
* please use alternative that uses the {@link org.jclouds.io.Payload} object
|
* please use alternative that uses the {@link org.jclouds.io.Payload} object
|
||||||
*
|
*
|
||||||
|
@ -220,19 +225,26 @@ public class TemplateOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This script will be executed as the root user upon system startup. This
|
* This script will be executed as the root user upon system startup. This script gets a
|
||||||
* script gets a prologue, so no #!/bin/bash required, path set up, etc
|
* prologue, so no #!/bin/bash required, path set up, etc
|
||||||
*
|
*
|
||||||
* @see org.jclouds.io.Payloads
|
* @see org.jclouds.io.Payloads
|
||||||
*/
|
*/
|
||||||
public TemplateOptions runScript(Payload script) {
|
public TemplateOptions runScript(Payload script) {
|
||||||
checkArgument(
|
checkNotNull(script, "script");
|
||||||
checkNotNull(checkNotNull(script, "script").getContentLength(), "script.contentLength") <= 16 * 1024,
|
|
||||||
"script cannot be larger than 16kb");
|
|
||||||
this.script = script;
|
this.script = script;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TemplateOptions runScript(Statement script, @Nullable OperatingSystem os) {
|
||||||
|
return runScript(newStringPayload(checkNotNull(script, "script").render(
|
||||||
|
os == null || OperatingSystemPredicates.isUnix().apply(os) ? OsFamily.UNIX : OsFamily.WINDOWS)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public TemplateOptions runScript(Statement script) {
|
||||||
|
return runScript(script, null);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* replaces the rsa ssh key used at login.
|
* replaces the rsa ssh key used at login.
|
||||||
* <p/>
|
* <p/>
|
||||||
|
@ -265,8 +277,8 @@ public class TemplateOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* if true, return when node(s) are NODE_RUNNING, if false, return as soon as
|
* if true, return when node(s) are NODE_RUNNING, if false, return as soon as the server is
|
||||||
* the server is provisioned.
|
* provisioned.
|
||||||
* <p/>
|
* <p/>
|
||||||
* default is true
|
* default is true
|
||||||
*/
|
*/
|
||||||
|
@ -344,8 +356,7 @@ public class TemplateOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* please use alternative that uses the {@link org.jclouds.io.Payload}
|
* please use alternative that uses the {@link org.jclouds.io.Payload} object
|
||||||
* object
|
|
||||||
*
|
*
|
||||||
* @see org.jclouds.io.Payloads
|
* @see org.jclouds.io.Payloads
|
||||||
* @see #runScript(Payload)
|
* @see #runScript(Payload)
|
||||||
|
@ -366,8 +377,16 @@ public class TemplateOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* please use alternative that uses the {@link org.jclouds.io.Payload}
|
* @see TemplateOptions#runScript
|
||||||
* object
|
* @see org.jclouds.io.Payloads
|
||||||
|
*/
|
||||||
|
public static TemplateOptions runScript(Statement script) {
|
||||||
|
TemplateOptions options = new TemplateOptions();
|
||||||
|
return options.runScript(script);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* please use alternative that uses the {@link org.jclouds.io.Payload} object
|
||||||
*
|
*
|
||||||
* @see org.jclouds.io.Payloads
|
* @see org.jclouds.io.Payloads
|
||||||
* @see #installPrivateKey(Payload)
|
* @see #installPrivateKey(Payload)
|
||||||
|
@ -388,8 +407,7 @@ public class TemplateOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* please use alternative that uses the {@link org.jclouds.io.Payload}
|
* please use alternative that uses the {@link org.jclouds.io.Payload} object
|
||||||
* object
|
|
||||||
*
|
*
|
||||||
* @see org.jclouds.io.Payloads
|
* @see org.jclouds.io.Payloads
|
||||||
* @see #authorizePublicKey(Payload)
|
* @see #authorizePublicKey(Payload)
|
||||||
|
@ -420,8 +438,8 @@ public class TemplateOptions {
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "TemplateOptions [inboundPorts=" + Arrays.toString(inboundPorts) + ", privateKey=" + (privateKey != null)
|
return "TemplateOptions [inboundPorts=" + Arrays.toString(inboundPorts) + ", privateKey=" + (privateKey != null)
|
||||||
+ ", publicKey=" + (publicKey != null) + ", runScript=" + (script != null) + ", blockUntilRunning="
|
+ ", publicKey=" + (publicKey != null) + ", runScript=" + (script != null) + ", blockUntilRunning="
|
||||||
+ blockUntilRunning + ", port:seconds=" + port + ":" + seconds + ", metadata/details: " + includeMetadata
|
+ blockUntilRunning + ", port:seconds=" + port + ":" + seconds + ", metadata/details: "
|
||||||
+ "]";
|
+ includeMetadata + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -34,6 +34,33 @@ import com.google.common.collect.ImmutableSet;
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
public class OperatingSystemPredicates {
|
public class OperatingSystemPredicates {
|
||||||
|
/**
|
||||||
|
* evaluates true if the OperatingSystem is unix like
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static Predicate<OperatingSystem> isUnix() {
|
||||||
|
return new Predicate<OperatingSystem>() {
|
||||||
|
@Override
|
||||||
|
public boolean apply(OperatingSystem os) {
|
||||||
|
if (os.getFamily() != null) {
|
||||||
|
switch (os.getFamily()) {
|
||||||
|
case WINDOWS:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (String toMatch : ImmutableSet.of(os.getName(), os.getDescription()))
|
||||||
|
if (toMatch != null && toMatch.toLowerCase().indexOf("windows") != -1)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "isUnix()";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* evaluates true if the OperatingSystem supports the apt installer
|
* evaluates true if the OperatingSystem supports the apt installer
|
||||||
*
|
*
|
||||||
|
|
|
@ -19,6 +19,12 @@
|
||||||
|
|
||||||
package org.jclouds.compute.util;
|
package org.jclouds.compute.util;
|
||||||
|
|
||||||
|
import static com.google.common.base.Throwables.getStackTraceAsString;
|
||||||
|
import static com.google.common.collect.Iterables.filter;
|
||||||
|
import static com.google.common.collect.Iterables.find;
|
||||||
|
import static org.jclouds.scriptbuilder.domain.Statements.pipeHttpResponseToBash;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
import java.util.Formatter;
|
import java.util.Formatter;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
|
@ -38,15 +44,13 @@ import org.jclouds.compute.domain.internal.NodeMetadataImpl;
|
||||||
import org.jclouds.domain.Credentials;
|
import org.jclouds.domain.Credentials;
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
import org.jclouds.logging.Logger;
|
import org.jclouds.logging.Logger;
|
||||||
|
import org.jclouds.scriptbuilder.domain.Statement;
|
||||||
|
import org.jclouds.scriptbuilder.domain.Statements;
|
||||||
import org.jclouds.ssh.SshClient;
|
import org.jclouds.ssh.SshClient;
|
||||||
import org.jclouds.util.Utils;
|
import org.jclouds.util.Utils;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
|
||||||
import com.google.common.base.Joiner;
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.base.Throwables;
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.Iterables;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -60,17 +64,25 @@ public class ComputeServiceUtils {
|
||||||
*
|
*
|
||||||
* @return a shell script that will invoke the http request
|
* @return a shell script that will invoke the http request
|
||||||
*/
|
*/
|
||||||
public static String buildCurlsh(HttpRequest request) {
|
public static Statement execHttpResponse(HttpRequest request) {
|
||||||
String headers = Joiner.on(' ').join(
|
return pipeHttpResponseToBash(request.getMethod(), request.getEndpoint(), request.getHeaders());
|
||||||
Iterables.transform(request.getHeaders().entries(), new Function<Entry<String, String>, String>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String apply(Entry<String, String> from) {
|
|
||||||
return String.format("-H \"%s: %s\"", from.getKey(), from.getValue());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}));
|
public static Statement execHttpResponse(URI location) {
|
||||||
return String.format("curl -s --retry 20 %s %s |bash\n", headers, request.getEndpoint().toASCIIString());
|
return execHttpResponse(new HttpRequest("GET", location));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* build a shell script that invokes the contents of the http request in bash.
|
||||||
|
*
|
||||||
|
* @return a shell script that will invoke the http request
|
||||||
|
*/
|
||||||
|
public static Statement extractTargzIntoDirectory(HttpRequest targz, String directory) {
|
||||||
|
return Statements.extractTargzIntoDirectory(targz.getMethod(), targz.getEndpoint(), targz.getHeaders(), directory);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Statement extractTargzIntoDirectory(URI targz, String directory) {
|
||||||
|
return extractTargzIntoDirectory(new HttpRequest("GET", targz), directory);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String parseTagFromName(String from) {
|
public static String parseTagFromName(String from) {
|
||||||
|
@ -113,11 +125,11 @@ public class ComputeServiceUtils {
|
||||||
if (NAME_VERSION_MAP.containsKey(family)) {
|
if (NAME_VERSION_MAP.containsKey(family)) {
|
||||||
CONTAINS_SUBSTRING contains = new CONTAINS_SUBSTRING(in.replace('-', '.'));
|
CONTAINS_SUBSTRING contains = new CONTAINS_SUBSTRING(in.replace('-', '.'));
|
||||||
try {
|
try {
|
||||||
String key = Iterables.find(NAME_VERSION_MAP.get(family).keySet(), contains);
|
String key = find(NAME_VERSION_MAP.get(family).keySet(), contains);
|
||||||
return NAME_VERSION_MAP.get(family).get(key);
|
return NAME_VERSION_MAP.get(family).get(key);
|
||||||
} catch (NoSuchElementException e) {
|
} catch (NoSuchElementException e) {
|
||||||
try {
|
try {
|
||||||
return Iterables.find(NAME_VERSION_MAP.get(family).values(), contains);
|
return find(NAME_VERSION_MAP.get(family).values(), contains);
|
||||||
} catch (NoSuchElementException e1) {
|
} catch (NoSuchElementException e1) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,7 +152,7 @@ public class ComputeServiceUtils {
|
||||||
int index = 1;
|
int index = 1;
|
||||||
for (Entry<?, Exception> errorMessage : executionExceptions.entrySet()) {
|
for (Entry<?, Exception> errorMessage : executionExceptions.entrySet()) {
|
||||||
fmt.format("%s) %s on %s:%n%s%n%n", index++, errorMessage.getValue().getClass().getSimpleName(), errorMessage
|
fmt.format("%s) %s on %s:%n%s%n%n", index++, errorMessage.getValue().getClass().getSimpleName(), errorMessage
|
||||||
.getKey(), Throwables.getStackTraceAsString(errorMessage.getValue()));
|
.getKey(), getStackTraceAsString(errorMessage.getValue()));
|
||||||
}
|
}
|
||||||
return fmt.format("%s error[s]", executionExceptions.size()).toString();
|
return fmt.format("%s error[s]", executionExceptions.size()).toString();
|
||||||
}
|
}
|
||||||
|
@ -150,14 +162,14 @@ public class ComputeServiceUtils {
|
||||||
int index = 1;
|
int index = 1;
|
||||||
for (Entry<? extends NodeMetadata, ? extends Throwable> errorMessage : failedNodes.entrySet()) {
|
for (Entry<? extends NodeMetadata, ? extends Throwable> errorMessage : failedNodes.entrySet()) {
|
||||||
fmt.format("%s) %s on node %s:%n%s%n%n", index++, errorMessage.getValue().getClass().getSimpleName(),
|
fmt.format("%s) %s on node %s:%n%s%n%n", index++, errorMessage.getValue().getClass().getSimpleName(),
|
||||||
errorMessage.getKey().getId(), Throwables.getStackTraceAsString(errorMessage.getValue()));
|
errorMessage.getKey().getId(), getStackTraceAsString(errorMessage.getValue()));
|
||||||
}
|
}
|
||||||
return fmt.format("%s error[s]", failedNodes.size()).toString();
|
return fmt.format("%s error[s]", failedNodes.size()).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Iterable<? extends ComputeMetadata> filterByName(Iterable<? extends ComputeMetadata> nodes,
|
public static Iterable<? extends ComputeMetadata> filterByName(Iterable<? extends ComputeMetadata> nodes,
|
||||||
final String name) {
|
final String name) {
|
||||||
return Iterables.filter(nodes, new Predicate<ComputeMetadata>() {
|
return filter(nodes, new Predicate<ComputeMetadata>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(ComputeMetadata input) {
|
public boolean apply(ComputeMetadata input) {
|
||||||
return input.getName().equalsIgnoreCase(name);
|
return input.getName().equalsIgnoreCase(name);
|
||||||
|
|
|
@ -24,6 +24,7 @@ import static org.testng.Assert.assertEquals;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
|
import org.jclouds.scriptbuilder.domain.OsFamily;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMultimap;
|
import com.google.common.collect.ImmutableMultimap;
|
||||||
|
@ -38,14 +39,26 @@ import com.google.common.collect.ImmutableMultimap;
|
||||||
public class ComputeServiceUtilsTest {
|
public class ComputeServiceUtilsTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCurlSh() {
|
public void testExecHttpResponse() {
|
||||||
HttpRequest request = new HttpRequest("GET", URI.create("https://adriancolehappy.s3.amazonaws.com/java/install"),
|
HttpRequest request = new HttpRequest("GET", URI.create("https://adriancolehappy.s3.amazonaws.com/java/install"),
|
||||||
ImmutableMultimap.of("Host", "adriancolehappy.s3.amazonaws.com", "Date",
|
ImmutableMultimap.of("Host", "adriancolehappy.s3.amazonaws.com", "Date",
|
||||||
"Sun, 12 Sep 2010 08:25:19 GMT", "Authorization", "AWS 0ASHDJAS82:JASHFDA="));
|
"Sun, 12 Sep 2010 08:25:19 GMT", "Authorization", "AWS 0ASHDJAS82:JASHFDA="));
|
||||||
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
ComputeServiceUtils.buildCurlsh(request),
|
ComputeServiceUtils.execHttpResponse(request).render(OsFamily.UNIX),
|
||||||
"curl -s --retry 20 -H \"Host: adriancolehappy.s3.amazonaws.com\" -H \"Date: Sun, 12 Sep 2010 08:25:19 GMT\" -H \"Authorization: AWS 0ASHDJAS82:JASHFDA=\" https://adriancolehappy.s3.amazonaws.com/java/install |bash\n");
|
"curl -X GET -s --retry 20 -H \"Host: adriancolehappy.s3.amazonaws.com\" -H \"Date: Sun, 12 Sep 2010 08:25:19 GMT\" -H \"Authorization: AWS 0ASHDJAS82:JASHFDA=\" https://adriancolehappy.s3.amazonaws.com/java/install |(bash)\n");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTarxzpHttpResponse() {
|
||||||
|
HttpRequest request = new HttpRequest("GET", URI.create("https://adriancolehappy.s3.amazonaws.com/java/install"),
|
||||||
|
ImmutableMultimap.of("Host", "adriancolehappy.s3.amazonaws.com", "Date",
|
||||||
|
"Sun, 12 Sep 2010 08:25:19 GMT", "Authorization", "AWS 0ASHDJAS82:JASHFDA="));
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
ComputeServiceUtils.extractTargzIntoDirectory(request, "/stage/").render(OsFamily.UNIX),
|
||||||
|
"curl -X GET -s --retry 20 -H \"Host: adriancolehappy.s3.amazonaws.com\" -H \"Date: Sun, 12 Sep 2010 08:25:19 GMT\" -H \"Authorization: AWS 0ASHDJAS82:JASHFDA=\" https://adriancolehappy.s3.amazonaws.com/java/install |(mkdir -p /stage/ &&cd /stage/ &&tar -xpzf -)\n");
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||||
|
*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed 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.jclouds.scriptbuilder.domain;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
import com.google.common.base.Joiner;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.google.common.collect.Multimap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pipes the content of the http response to a shell command that accepts input from stdin
|
||||||
|
*
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
public class PipeHttpResponseTo extends InterpretableStatement {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param toExec
|
||||||
|
* what to invoke
|
||||||
|
* @param method
|
||||||
|
* http method: ex GET
|
||||||
|
* @param endpoint
|
||||||
|
* uri corresponding to the request
|
||||||
|
* @param headers
|
||||||
|
* request headers to send
|
||||||
|
*/
|
||||||
|
public PipeHttpResponseTo(Statement toExec, String method, URI endpoint, Multimap<String, String> headers) {
|
||||||
|
super(String.format("curl -X %s -s --retry 20 %s %s |(%s)\n", method, Joiner.on(' ').join(
|
||||||
|
Iterables.transform(headers.entries(), new Function<Entry<String, String>, String>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String apply(Entry<String, String> from) {
|
||||||
|
return String.format("-H \"%s: %s\"", from.getKey(), from.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
})), endpoint.toASCIIString(), toExec.render(OsFamily.UNIX)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String render(OsFamily family) {
|
||||||
|
checkArgument(family != OsFamily.WINDOWS, "windows not supported");
|
||||||
|
return super.render(family);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||||
|
*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed 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.jclouds.scriptbuilder.domain;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
import com.google.common.collect.Multimap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pipes the content of the http response to bash
|
||||||
|
*
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
public class PipeHttpResponseToBash extends PipeHttpResponseTo {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param method
|
||||||
|
* http method: ex GET
|
||||||
|
* @param endpoint
|
||||||
|
* uri corresponding to the request
|
||||||
|
* @param headers
|
||||||
|
* request headers to send
|
||||||
|
*/
|
||||||
|
public PipeHttpResponseToBash(String method, URI endpoint, Multimap<String, String> headers) {
|
||||||
|
super(Statements.interpret("bash"), method, endpoint, headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||||
|
*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed 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.jclouds.scriptbuilder.domain;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
import com.google.common.collect.Multimap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pipes the content of the http response to tar -xpzf
|
||||||
|
*
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
public class PipeHttpResponseToTarxpzfIntoDirectory extends PipeHttpResponseTo {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param method
|
||||||
|
* http method: ex GET
|
||||||
|
* @param endpoint
|
||||||
|
* uri corresponding to the request
|
||||||
|
* @param headers
|
||||||
|
* request headers to send
|
||||||
|
*/
|
||||||
|
public PipeHttpResponseToTarxpzfIntoDirectory(String method, URI endpoint, Multimap<String, String> headers,
|
||||||
|
String directory) {
|
||||||
|
super(Statements.interpret(String.format("{md} %s &&{cd} %s &&tar -xpzf -", directory, directory)), method,
|
||||||
|
endpoint, headers);
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,8 +19,11 @@
|
||||||
|
|
||||||
package org.jclouds.scriptbuilder.domain;
|
package org.jclouds.scriptbuilder.domain;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.google.common.collect.Multimap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Statements used in shell scripts.
|
* Statements used in shell scripts.
|
||||||
*
|
*
|
||||||
|
@ -97,7 +100,7 @@ public class Statements {
|
||||||
*
|
*
|
||||||
* @see ShellToken
|
* @see ShellToken
|
||||||
*/
|
*/
|
||||||
public static Statement interpret(String ... portableStatements) {
|
public static Statement interpret(String... portableStatements) {
|
||||||
return new InterpretableStatement(portableStatements);
|
return new InterpretableStatement(portableStatements);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,4 +110,33 @@ public class Statements {
|
||||||
public static Statement exec(String portableStatement) {
|
public static Statement exec(String portableStatement) {
|
||||||
return interpret(portableStatement + "{lf}");
|
return interpret(portableStatement + "{lf}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* untar, ungzip the data received from the request parameters.
|
||||||
|
*
|
||||||
|
* @param method
|
||||||
|
* http method: ex GET
|
||||||
|
* @param endpoint
|
||||||
|
* uri corresponding to the request
|
||||||
|
* @param headers
|
||||||
|
* request headers to send
|
||||||
|
*/
|
||||||
|
public static Statement extractTargzIntoDirectory(String method, URI endpoint, Multimap<String, String> headers,
|
||||||
|
String directory) {
|
||||||
|
return new PipeHttpResponseToTarxpzfIntoDirectory( method, endpoint, headers,directory);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* exec the data received from the request parameters.
|
||||||
|
*
|
||||||
|
* @param method
|
||||||
|
* http method: ex GET
|
||||||
|
* @param endpoint
|
||||||
|
* uri corresponding to the request
|
||||||
|
* @param headers
|
||||||
|
* request headers to send
|
||||||
|
*/
|
||||||
|
public static Statement pipeHttpResponseToBash(String method, URI endpoint, Multimap<String, String> headers) {
|
||||||
|
return new PipeHttpResponseToBash(method, endpoint, headers);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||||
|
*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed 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.jclouds.scriptbuilder.domain;
|
||||||
|
|
||||||
|
import static org.testng.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMultimap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
@Test(groups = "unit", testName = "scriptbuilder.PipeHttpResponseToTest")
|
||||||
|
public class PipeHttpResponseToTest {
|
||||||
|
PipeHttpResponseTo bash = new PipeHttpResponseTo(Statements.interpret("bash"), "GET", URI
|
||||||
|
.create("https://adriancolehappy.s3.amazonaws.com/java/install"), ImmutableMultimap.of("Host",
|
||||||
|
"adriancolehappy.s3.amazonaws.com", "Date", "Sun, 12 Sep 2010 08:25:19 GMT", "Authorization",
|
||||||
|
"AWS 0ASHDJAS82:JASHFDA="));
|
||||||
|
|
||||||
|
PipeHttpResponseToBash bash2 = new PipeHttpResponseToBash("GET", URI
|
||||||
|
.create("https://adriancolehappy.s3.amazonaws.com/java/install"), ImmutableMultimap.of("Host",
|
||||||
|
"adriancolehappy.s3.amazonaws.com", "Date", "Sun, 12 Sep 2010 08:25:19 GMT", "Authorization",
|
||||||
|
"AWS 0ASHDJAS82:JASHFDA="));
|
||||||
|
|
||||||
|
public void testPipeHttpResponseToBashUNIX() {
|
||||||
|
assertEquals(
|
||||||
|
bash.render(OsFamily.UNIX),
|
||||||
|
"curl -X GET -s --retry 20 -H \"Host: adriancolehappy.s3.amazonaws.com\" -H \"Date: Sun, 12 Sep 2010 08:25:19 GMT\" -H \"Authorization: AWS 0ASHDJAS82:JASHFDA=\" https://adriancolehappy.s3.amazonaws.com/java/install |(bash)\n");
|
||||||
|
assertEquals(bash2.render(OsFamily.UNIX), bash.render(OsFamily.UNIX));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||||
|
public void testPipeHttpResponseToBashWINDOWS() {
|
||||||
|
bash.render(OsFamily.WINDOWS);
|
||||||
|
}
|
||||||
|
|
||||||
|
PipeHttpResponseTo untar = new PipeHttpResponseTo(Statements
|
||||||
|
.interpret("{md} {root}stage{fs} &&{cd} {root}stage{fs} &&tar -xpzf -"), "GET", URI
|
||||||
|
.create("https://adriancolehappy.s3.amazonaws.com/java/install"), ImmutableMultimap.of("Host",
|
||||||
|
"adriancolehappy.s3.amazonaws.com", "Date", "Sun, 12 Sep 2010 08:25:19 GMT", "Authorization",
|
||||||
|
"AWS 0ASHDJAS82:JASHFDA="));
|
||||||
|
|
||||||
|
PipeHttpResponseTo untar2 = new PipeHttpResponseToTarxpzfIntoDirectory( "GET", URI
|
||||||
|
.create("https://adriancolehappy.s3.amazonaws.com/java/install"), ImmutableMultimap.of("Host",
|
||||||
|
"adriancolehappy.s3.amazonaws.com", "Date", "Sun, 12 Sep 2010 08:25:19 GMT", "Authorization",
|
||||||
|
"AWS 0ASHDJAS82:JASHFDA="),"{root}stage{fs}");
|
||||||
|
|
||||||
|
public void testPipeHttpResponseToUntarUNIX() {
|
||||||
|
assertEquals(
|
||||||
|
untar.render(OsFamily.UNIX),
|
||||||
|
"curl -X GET -s --retry 20 -H \"Host: adriancolehappy.s3.amazonaws.com\" -H \"Date: Sun, 12 Sep 2010 08:25:19 GMT\" -H \"Authorization: AWS 0ASHDJAS82:JASHFDA=\" https://adriancolehappy.s3.amazonaws.com/java/install |(mkdir -p /stage/ &&cd /stage/ &&tar -xpzf -)\n");
|
||||||
|
assertEquals(untar.render(OsFamily.UNIX), untar2.render(OsFamily.UNIX));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue