diff --git a/aws/core/src/test/java/org/jclouds/aws/ComputeAndBlobStoreTogetherHappilyLiveTest.java b/aws/core/src/test/java/org/jclouds/aws/ComputeAndBlobStoreTogetherHappilyLiveTest.java index fd941c6456..9d7b37602e 100644 --- a/aws/core/src/test/java/org/jclouds/aws/ComputeAndBlobStoreTogetherHappilyLiveTest.java +++ b/aws/core/src/test/java/org/jclouds/aws/ComputeAndBlobStoreTogetherHappilyLiveTest.java @@ -21,18 +21,23 @@ package org.jclouds.aws; import static org.jclouds.compute.BaseComputeServiceLiveTest.buildScript; import static org.jclouds.compute.options.TemplateOptions.Builder.runScript; -import static org.jclouds.compute.util.ComputeServiceUtils.buildCurlsh; -import static org.jclouds.io.Payloads.newStringPayload; +import static org.jclouds.compute.util.ComputeServiceUtils.execHttpResponse; +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.blobstore.BlobStore; import org.jclouds.compute.RunNodesException; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.OperatingSystem; -import org.jclouds.compute.options.TemplateOptions; import org.jclouds.http.HttpRequest; +import org.jclouds.scriptbuilder.domain.Statement; import org.testng.annotations.Test; /** + * This test helps us understand how we can use the power of blobstores to our favor. * * @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 * 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 - * (curlsh). + * (execHttpResponse). *

- * 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 *

* With this script ready, any node or nodes will take instructions from the blobstore when it * 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. HttpRequest signedRequestOfInstallScript = blobContext.getBlobStore().signRequestForBlob(tag, "openjdk/install"); - // instruct the bootstrap to pull the script from the blobstore and invoke it directly with bash. - TemplateOptions runOpenJDKInstallDirectlyFromBlobStore = runScript(newStringPayload(buildCurlsh(signedRequestOfInstallScript))); + // so one of our commands is to execute the contents of the blob above + 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 - Iterable nodes = computeContext.getComputeService().runNodesWithTag(tag, 1, - runOpenJDKInstallDirectlyFromBlobStore); + Iterable nodes = computeContext.getComputeService().runNodesWithTag(tag, 2, + runScript(bootstrapInstructions)); - // ensure the bootstrap operated by checking for a known signature of success. - assertSshOutputOfCommandContains(nodes, "openjdk -version", "OpenJDK"); + // ensure the bootstrap operated by checking for the components we installed at boot time. + // 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 + ""); } } diff --git a/aws/core/src/test/java/org/jclouds/aws/ec2/compute/BlobStoreAndComputeServiceLiveTest.java b/aws/core/src/test/java/org/jclouds/aws/ec2/compute/BlobStoreAndComputeServiceLiveTest.java index e10b353170..4febc492b6 100644 --- a/aws/core/src/test/java/org/jclouds/aws/ec2/compute/BlobStoreAndComputeServiceLiveTest.java +++ b/aws/core/src/test/java/org/jclouds/aws/ec2/compute/BlobStoreAndComputeServiceLiveTest.java @@ -64,25 +64,26 @@ public class BlobStoreAndComputeServiceLiveTest { setupCredentials(); computeContext = new ComputeServiceContextFactory().createContext(computeServiceProvider, identity, credential, ImmutableSet.of(new Log4JLoggingModule(), new JschSshClientModule())); - blobContext = new BlobStoreContextFactory().createContext(blobStoreProvider, identity, credential, - ImmutableSet.of(new Log4JLoggingModule())); + blobContext = new BlobStoreContextFactory().createContext(blobStoreProvider, identity, credential, ImmutableSet + .of(new Log4JLoggingModule())); blobContext.getAsyncBlobStore().createContainerInLocation(null, tag); computeContext.getComputeService().destroyNodesMatching(NodePredicates.withTag(tag)); } protected void assertSshOutputOfCommandContains(Iterable 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, - get(nodes, 0).getCredentials().credential.getBytes()); - try { - ssh.connect(); - ; - ExecResponse exec = ssh.exec(cmd); - assert exec.getOutput().indexOf(expects) != -1 || exec.getError().indexOf(expects) != -1 : exec; - } finally { - if (ssh != null) - ssh.disconnect(); + SshClient ssh = computeContext.utils().sshFactory().create(socket, node.getCredentials().identity, + node.getCredentials().credential.getBytes()); + try { + ssh.connect(); + ExecResponse exec = ssh.exec(cmd); + assert exec.getOutput().indexOf(expects) != -1 || exec.getError().indexOf(expects) != -1 : exec; + } finally { + if (ssh != null) + ssh.disconnect(); + } } } diff --git a/compute/src/main/java/org/jclouds/compute/options/TemplateOptions.java b/compute/src/main/java/org/jclouds/compute/options/TemplateOptions.java index 93a22f6ee5..2f75bbb3d8 100644 --- a/compute/src/main/java/org/jclouds/compute/options/TemplateOptions.java +++ b/compute/src/main/java/org/jclouds/compute/options/TemplateOptions.java @@ -26,14 +26,19 @@ import static org.jclouds.io.Payloads.newStringPayload; 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.scriptbuilder.domain.OsFamily; +import org.jclouds.scriptbuilder.domain.Statement; /** - * Contains options supported in the {@code ComputeService#runNodesWithTag} - * operation.

- * Usage

The recommended way to instantiate a TemplateOptions object is to - * statically import TemplateOptions.* and invoke a static creation method - * followed by an instance mutator (if needed): + * Contains options supported in the {@code ComputeService#runNodesWithTag} operation.

+ * Usage

The recommended way to instantiate a TemplateOptions object is to statically import + * TemplateOptions.* and invoke a static creation method followed by an instance mutator (if + * needed): *

* * 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 - * script gets a prologue, so no #!/bin/bash required, path set up, etc + * This script will be executed as the root user upon system startup. This script gets a + * prologue, so no #!/bin/bash required, path set up, etc *

* 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 - * script gets a prologue, so no #!/bin/bash required, path set up, etc + * This script will be executed as the root user upon system startup. This script gets a + * prologue, so no #!/bin/bash required, path set up, etc * * @see org.jclouds.io.Payloads */ public TemplateOptions runScript(Payload script) { - checkArgument( - checkNotNull(checkNotNull(script, "script").getContentLength(), "script.contentLength") <= 16 * 1024, - "script cannot be larger than 16kb"); + checkNotNull(script, "script"); this.script = script; 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. *

@@ -243,7 +255,7 @@ public class TemplateOptions { @Deprecated public TemplateOptions installPrivateKey(String privateKey) { checkArgument(checkNotNull(privateKey, "privateKey").startsWith("-----BEGIN RSA PRIVATE KEY-----"), - "key should start with -----BEGIN RSA PRIVATE KEY-----"); + "key should start with -----BEGIN RSA PRIVATE KEY-----"); Payload payload = newStringPayload(privateKey); payload.setContentType("text/plain"); return installPrivateKey(payload); @@ -265,8 +277,8 @@ public class TemplateOptions { } /** - * if true, return when node(s) are NODE_RUNNING, if false, return as soon as - * the server is provisioned. + * if true, return when node(s) are NODE_RUNNING, if false, return as soon as the server is + * provisioned. *

* default is true */ @@ -344,8 +356,7 @@ public class TemplateOptions { } /** - * please use alternative that uses the {@link org.jclouds.io.Payload} - * object + * please use alternative that uses the {@link org.jclouds.io.Payload} object * * @see org.jclouds.io.Payloads * @see #runScript(Payload) @@ -366,8 +377,16 @@ public class TemplateOptions { } /** - * please use alternative that uses the {@link org.jclouds.io.Payload} - * object + * @see TemplateOptions#runScript + * @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 #installPrivateKey(Payload) @@ -388,8 +407,7 @@ public class TemplateOptions { } /** - * please use alternative that uses the {@link org.jclouds.io.Payload} - * object + * please use alternative that uses the {@link org.jclouds.io.Payload} object * * @see org.jclouds.io.Payloads * @see #authorizePublicKey(Payload) @@ -419,9 +437,9 @@ public class TemplateOptions { @Override public String toString() { return "TemplateOptions [inboundPorts=" + Arrays.toString(inboundPorts) + ", privateKey=" + (privateKey != null) - + ", publicKey=" + (publicKey != null) + ", runScript=" + (script != null) + ", blockUntilRunning=" - + blockUntilRunning + ", port:seconds=" + port + ":" + seconds + ", metadata/details: " + includeMetadata - + "]"; + + ", publicKey=" + (publicKey != null) + ", runScript=" + (script != null) + ", blockUntilRunning=" + + blockUntilRunning + ", port:seconds=" + port + ":" + seconds + ", metadata/details: " + + includeMetadata + "]"; } @Override diff --git a/compute/src/main/java/org/jclouds/compute/predicates/OperatingSystemPredicates.java b/compute/src/main/java/org/jclouds/compute/predicates/OperatingSystemPredicates.java index f7fd99fcbb..581c92b727 100644 --- a/compute/src/main/java/org/jclouds/compute/predicates/OperatingSystemPredicates.java +++ b/compute/src/main/java/org/jclouds/compute/predicates/OperatingSystemPredicates.java @@ -34,6 +34,33 @@ import com.google.common.collect.ImmutableSet; * @author Adrian Cole */ public class OperatingSystemPredicates { + /** + * evaluates true if the OperatingSystem is unix like + * + */ + public static Predicate isUnix() { + return new Predicate() { + @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 * diff --git a/compute/src/main/java/org/jclouds/compute/util/ComputeServiceUtils.java b/compute/src/main/java/org/jclouds/compute/util/ComputeServiceUtils.java index c175579da3..6a79d2c7bd 100644 --- a/compute/src/main/java/org/jclouds/compute/util/ComputeServiceUtils.java +++ b/compute/src/main/java/org/jclouds/compute/util/ComputeServiceUtils.java @@ -19,6 +19,12 @@ 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.Map; import java.util.NoSuchElementException; @@ -38,15 +44,13 @@ import org.jclouds.compute.domain.internal.NodeMetadataImpl; import org.jclouds.domain.Credentials; import org.jclouds.http.HttpRequest; 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.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.Throwables; 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 */ - public static String buildCurlsh(HttpRequest request) { - String headers = Joiner.on(' ').join( - Iterables.transform(request.getHeaders().entries(), new Function, String>() { + public static Statement execHttpResponse(HttpRequest request) { + return pipeHttpResponseToBash(request.getMethod(), request.getEndpoint(), request.getHeaders()); + } - @Override - public String apply(Entry from) { - return String.format("-H \"%s: %s\"", from.getKey(), from.getValue()); - } + public static Statement execHttpResponse(URI location) { + return execHttpResponse(new HttpRequest("GET", location)); + } - })); - return String.format("curl -s --retry 20 %s %s |bash\n", headers, request.getEndpoint().toASCIIString()); + /** + * 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) { @@ -113,11 +125,11 @@ public class ComputeServiceUtils { if (NAME_VERSION_MAP.containsKey(family)) { CONTAINS_SUBSTRING contains = new CONTAINS_SUBSTRING(in.replace('-', '.')); 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); } catch (NoSuchElementException e) { try { - return Iterables.find(NAME_VERSION_MAP.get(family).values(), contains); + return find(NAME_VERSION_MAP.get(family).values(), contains); } catch (NoSuchElementException e1) { } } @@ -140,7 +152,7 @@ public class ComputeServiceUtils { int index = 1; for (Entry errorMessage : executionExceptions.entrySet()) { 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(); } @@ -150,14 +162,14 @@ public class ComputeServiceUtils { int index = 1; for (Entry errorMessage : failedNodes.entrySet()) { 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(); } public static Iterable filterByName(Iterable nodes, final String name) { - return Iterables.filter(nodes, new Predicate() { + return filter(nodes, new Predicate() { @Override public boolean apply(ComputeMetadata input) { return input.getName().equalsIgnoreCase(name); diff --git a/compute/src/test/java/org/jclouds/compute/util/ComputeServiceUtilsTest.java b/compute/src/test/java/org/jclouds/compute/util/ComputeServiceUtilsTest.java index 7322ac597f..e7e9d5489c 100644 --- a/compute/src/test/java/org/jclouds/compute/util/ComputeServiceUtilsTest.java +++ b/compute/src/test/java/org/jclouds/compute/util/ComputeServiceUtilsTest.java @@ -24,6 +24,7 @@ import static org.testng.Assert.assertEquals; import java.net.URI; import org.jclouds.http.HttpRequest; +import org.jclouds.scriptbuilder.domain.OsFamily; import org.testng.annotations.Test; import com.google.common.collect.ImmutableMultimap; @@ -38,14 +39,26 @@ import com.google.common.collect.ImmutableMultimap; public class ComputeServiceUtilsTest { @Test - public void testCurlSh() { + public void testExecHttpResponse() { 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.buildCurlsh(request), - "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"); + ComputeServiceUtils.execHttpResponse(request).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"); + + } + + @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"); } } diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/PipeHttpResponseTo.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/PipeHttpResponseTo.java new file mode 100644 index 0000000000..2c63d23abc --- /dev/null +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/PipeHttpResponseTo.java @@ -0,0 +1,65 @@ +/** + * + * Copyright (C) 2010 Cloud Conscious, LLC. + * + * ==================================================================== + * 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 headers) { + super(String.format("curl -X %s -s --retry 20 %s %s |(%s)\n", method, Joiner.on(' ').join( + Iterables.transform(headers.entries(), new Function, String>() { + + @Override + public String apply(Entry 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); + } +} \ No newline at end of file diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/PipeHttpResponseToBash.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/PipeHttpResponseToBash.java new file mode 100644 index 0000000000..2e7d4ce462 --- /dev/null +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/PipeHttpResponseToBash.java @@ -0,0 +1,46 @@ +/** + * + * Copyright (C) 2010 Cloud Conscious, LLC. + * + * ==================================================================== + * 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 headers) { + super(Statements.interpret("bash"), method, endpoint, headers); + } + +} \ No newline at end of file diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/PipeHttpResponseToTarxpzfIntoDirectory.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/PipeHttpResponseToTarxpzfIntoDirectory.java new file mode 100644 index 0000000000..2fb6c65e51 --- /dev/null +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/PipeHttpResponseToTarxpzfIntoDirectory.java @@ -0,0 +1,47 @@ +/** + * + * Copyright (C) 2010 Cloud Conscious, LLC. + * + * ==================================================================== + * 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 headers, + String directory) { + super(Statements.interpret(String.format("{md} %s &&{cd} %s &&tar -xpzf -", directory, directory)), method, + endpoint, headers); + } +} \ No newline at end of file diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/Statements.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/Statements.java index fdb69039c5..ebf7207d73 100644 --- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/Statements.java +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/Statements.java @@ -19,8 +19,11 @@ package org.jclouds.scriptbuilder.domain; +import java.net.URI; import java.util.Map; +import com.google.common.collect.Multimap; + /** * Statements used in shell scripts. * @@ -97,7 +100,7 @@ public class Statements { * * @see ShellToken */ - public static Statement interpret(String ... portableStatements) { + public static Statement interpret(String... portableStatements) { return new InterpretableStatement(portableStatements); } @@ -107,4 +110,33 @@ public class Statements { public static Statement exec(String portableStatement) { 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 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 headers) { + return new PipeHttpResponseToBash(method, endpoint, headers); + } } \ No newline at end of file diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/PipeHttpResponseToTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/PipeHttpResponseToTest.java new file mode 100644 index 0000000000..5c8fa31e49 --- /dev/null +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/PipeHttpResponseToTest.java @@ -0,0 +1,76 @@ +/** + * + * Copyright (C) 2010 Cloud Conscious, LLC. + * + * ==================================================================== + * 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)); + + } + +}