diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/ExitInsteadOfReturn.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/ExitInsteadOfReturn.java new file mode 100644 index 0000000000..d079cd9f2b --- /dev/null +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/ExitInsteadOfReturn.java @@ -0,0 +1,55 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.jclouds.scriptbuilder; + +import org.jclouds.scriptbuilder.domain.OsFamily; +import org.jclouds.scriptbuilder.domain.ShellToken; +import org.jclouds.scriptbuilder.domain.Statement; + +import com.google.common.collect.ForwardingObject; + +/** + * you cannot return from a top-level script, so if you are using snippets that + * issue {@code return} then you'll want to wrap them in this. + * + * @author Adrian Cole + * + */ +public class ExitInsteadOfReturn extends ForwardingObject implements Statement { + private final Statement delegate; + + public ExitInsteadOfReturn(Statement delegate) { + this.delegate = delegate; + } + + @Override + public Iterable functionDependencies(OsFamily family) { + return delegate().functionDependencies(family); + } + + @Override + public String render(OsFamily family) { + return delegate().render(family).toString().replaceAll(ShellToken.RETURN.to(family), ShellToken.EXIT.to(family)); + } + + @Override + protected Statement delegate() { + return delegate; + } +} \ No newline at end of file diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/InitBuilder.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/InitBuilder.java index f19db77aa3..4d8b01d0ad 100644 --- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/InitBuilder.java +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/InitBuilder.java @@ -19,10 +19,7 @@ package org.jclouds.scriptbuilder; import static com.google.common.base.Preconditions.checkNotNull; -import static org.jclouds.scriptbuilder.domain.Statements.call; import static org.jclouds.scriptbuilder.domain.Statements.createRunScript; -import static org.jclouds.scriptbuilder.domain.Statements.findPid; -import static org.jclouds.scriptbuilder.domain.Statements.forget; import static org.jclouds.scriptbuilder.domain.Statements.interpret; import static org.jclouds.scriptbuilder.domain.Statements.kill; import static org.jclouds.scriptbuilder.domain.Statements.newStatementList; @@ -51,68 +48,67 @@ public class InitBuilder extends ScriptBuilder { private final StatementList initStatement; private final CreateRunScript createRunScript; + public InitBuilder(String instanceName, Statement initStatement, Statement runStatement) { + this(instanceName, ImmutableSet.of(initStatement), ImmutableSet.of(runStatement)); + } + + public InitBuilder(String instanceName, Iterable initStatements, Iterable statements) { + this(instanceName, String.format("{varl}HOME{varr}{fs}instances{fs}%s", instanceName), String.format( + "{varl}HOME{varr}{fs}instances{fs}%s", instanceName), ImmutableMap. of(), initStatements, + statements); + } + public InitBuilder(String instanceName, String instanceHome, String logDir, Map variables, - Iterable statements) { + Iterable statements) { this(instanceName, instanceHome, logDir, variables, ImmutableSet. of(), statements); } public InitBuilder(String instanceName, String instanceHome, String logDir, Map variables, - Iterable initStatements, Iterable statements) { + Iterable initStatements, Iterable statements) { Map defaultVariables = ImmutableMap.of("instanceName", instanceName, "instanceHome", - instanceHome, "logDir", logDir); + instanceHome, "logDir", logDir); this.initStatement = new StatementList(initStatements); this.createRunScript = createRunScript(instanceName,// TODO: convert - // so - // that - // createRunScript - // can take from a - // variable - Iterables.concat(variables.keySet(), defaultVariables.keySet()), "{varl}INSTANCE_HOME{varr}", statements); + // so + // that + // createRunScript + // can take from a + // variable + Iterables.concat(variables.keySet(), defaultVariables.keySet()), "{varl}INSTANCE_HOME{varr}", statements); this.instanceName = checkNotNull(instanceName, "instanceName"); this.instanceHome = checkNotNull(instanceHome, "instanceHome"); this.logDir = checkNotNull(logDir, "logDir"); addEnvironmentVariableScope("default", defaultVariables) - .addEnvironmentVariableScope(instanceName, variables) - .addStatement( - switchArg( - 1, - new ImmutableMap.Builder() - .put( - "init", - newStatementList(call("default"), call(instanceName), initStatement, - createRunScript)) - .put( - "status", - newStatementList(call("default"), - findPid("{varl}INSTANCE_NAME{varr}"), - interpret("echo [{varl}FOUND_PID{varr}]{lf}"))) - .put( - "stop", - newStatementList(call("default"), - findPid("{varl}INSTANCE_NAME{varr}"), kill())) - .put( - "start", - newStatementList( - call("default"), - forget( - "{varl}INSTANCE_NAME{varr}", - "{varl}INSTANCE_HOME{varr}{fs}{varl}INSTANCE_NAME{varr}.{sh}", - "{varl}LOG_DIR{varr}"))) - .put( - "tail", - newStatementList(call("default"), - interpret("tail {varl}LOG_DIR{varr}{fs}stdout.log{lf}"))) - .put( - "tailerr", - newStatementList(call("default"), - interpret("tail {varl}LOG_DIR{varr}{fs}stderr.log{lf}"))) - .put( - "run", - newStatementList( - call("default"), - interpret("{varl}INSTANCE_HOME{varr}{fs}{varl}INSTANCE_NAME{varr}.{sh}{lf}"))) - .build())); + .addEnvironmentVariableScope(instanceName, variables) + .addStatement( + switchArg( + 1, + new ImmutableMap.Builder() + .put("init", + newStatementList(call("default"), call(instanceName), initStatement, + createRunScript)) + .put("status", + newStatementList(call("default"), findPid("{varl}INSTANCE_NAME{varr}"), + interpret("echo [{varl}FOUND_PID{varr}]{lf}"))) + .put("stop", + newStatementList(call("default"), findPid("{varl}INSTANCE_NAME{varr}"), kill())) + .put("start", + newStatementList( + call("default"), + forget("{varl}INSTANCE_NAME{varr}", + "{varl}INSTANCE_HOME{varr}{fs}{varl}INSTANCE_NAME{varr}.{sh}", + "{varl}LOG_DIR{varr}"))) + .put("tail", + newStatementList(call("default"), + interpret("tail {varl}LOG_DIR{varr}{fs}stdout.log{lf}"))) + .put("tailerr", + newStatementList(call("default"), + interpret("tail {varl}LOG_DIR{varr}{fs}stderr.log{lf}"))) + .put("run", + newStatementList(call("default"), + interpret("{varl}INSTANCE_HOME{varr}{fs}{varl}INSTANCE_NAME{varr}.{sh}{lf}"))) + .build())); } @Override diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/ScriptBuilder.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/ScriptBuilder.java index 22362f2fc5..6b29ac5966 100644 --- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/ScriptBuilder.java +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/ScriptBuilder.java @@ -23,20 +23,25 @@ import static com.google.common.base.Preconditions.checkNotNull; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import org.jclouds.scriptbuilder.domain.AcceptsStatementVisitor; import org.jclouds.scriptbuilder.domain.OsFamily; import org.jclouds.scriptbuilder.domain.ShellToken; import org.jclouds.scriptbuilder.domain.Statement; import org.jclouds.scriptbuilder.domain.StatementVisitor; +import org.jclouds.scriptbuilder.domain.Statements; import org.jclouds.scriptbuilder.util.Utils; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMap.Builder; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; +import com.google.common.collect.Sets; /** * Creates a shell script. @@ -75,6 +80,20 @@ public class ScriptBuilder implements Statement, AcceptsStatementVisitor { return this; } + // TODO: make scriptbuilder smart enough to know when a statement is a direct + // child of the script, and automatically convert + public static Statement forget(String instanceName, String script, String logDir) { + return new ExitInsteadOfReturn(Statements.forget(instanceName, script, logDir)); + } + + public static Statement findPid(String pid) { + return new ExitInsteadOfReturn(Statements.findPid(pid)); + } + + public static Statement call(String fn, String... args) { + return new ExitInsteadOfReturn(Statements.call(fn, args)); + } + /** * builds the shell script, by adding the following *
    @@ -93,63 +112,66 @@ public class ScriptBuilder implements Statement, AcceptsStatementVisitor { functions.put("abort", Utils.writeFunctionFromResource("abort", osFamily)); for (Entry> entry : variableScopes.entrySet()) { - functions.put(entry.getKey(), Utils.writeFunction(entry.getKey(), Utils.writeVariableExporters(entry - .getValue()))); + functions.put(entry.getKey(), + Utils.writeFunction(entry.getKey(), Utils.writeVariableExporters(entry.getValue()))); } final Map tokenValueMap = ShellToken.tokenValueMap(osFamily); StringBuilder builder = new StringBuilder(); builder.append(ShellToken.BEGIN_SCRIPT.to(osFamily)); - builder.append(Utils.writeUnsetVariables(Lists.newArrayList(Iterables.transform(variablesToUnset, - new Function() { - @Override - public String apply(String from) { - if (tokenValueMap.containsKey(from + "Variable")) - return Utils.FUNCTION_UPPER_UNDERSCORE_TO_LOWER_CAMEL.apply(tokenValueMap - .get(from + "Variable")); - return from; - } + builder.append(Utils.writeUnsetVariables( + Lists.newArrayList(Iterables.transform(variablesToUnset, new Function() { + @Override + public String apply(String from) { + if (tokenValueMap.containsKey(from + "Variable")) + return Utils.FUNCTION_UPPER_UNDERSCORE_TO_LOWER_CAMEL.apply(tokenValueMap.get(from + "Variable")); + return from; + } - })), osFamily)); - resolveFunctionDependencies(functions, osFamily); - if (functions.size() > 0) { - builder.append(ShellToken.BEGIN_FUNCTIONS.to(osFamily)); - for (String function : functions.values()) { - builder.append(Utils.replaceTokens(function, tokenValueMap)); - } - builder.append(ShellToken.END_FUNCTIONS.to(osFamily)); - } + })), osFamily)); + Map functionsToWrite = resolveFunctionDependenciesForStatements(functions, statements, osFamily); + writeFunctions(functionsToWrite, osFamily, builder); builder.append(Utils.writeZeroPath(osFamily)); StringBuilder statementBuilder = new StringBuilder(); for (Statement statement : statements) { statementBuilder.append(statement.render(osFamily)); } - builder.append(statementBuilder.toString().replaceAll(ShellToken.RETURN.to(osFamily), - ShellToken.EXIT.to(osFamily))); + builder.append(statementBuilder.toString()); builder.append(ShellToken.END_SCRIPT.to(osFamily)); return builder.toString(); } - @VisibleForTesting - void resolveFunctionDependencies(Map functions, final OsFamily osFamily) { - Iterable dependentFunctions = Iterables.concat(Iterables.transform(statements, - new Function>() { - @Override - public Iterable apply(Statement from) { - return from.functionDependencies(osFamily); - } - })); - List unresolvedFunctions = Lists.newArrayList(dependentFunctions); - Iterables.removeAll(unresolvedFunctions, functions.keySet()); - for (String functionName : unresolvedFunctions) { - functions.put(functionName, Utils.writeFunctionFromResource(functionName, osFamily)); + public static void writeFunctions(Map functionsToWrite, OsFamily osFamily, StringBuilder builder) { + if (functionsToWrite.size() > 0) { + builder.append(ShellToken.BEGIN_FUNCTIONS.to(osFamily)); + for (String function : functionsToWrite.values()) { + builder.append(Utils.replaceTokens(function, ShellToken.tokenValueMap(osFamily))); + } + builder.append(ShellToken.END_FUNCTIONS.to(osFamily)); } } + @VisibleForTesting + public static Map resolveFunctionDependenciesForStatements(Map knownFunctions, + Iterable statements, final OsFamily osFamily) { + Builder builder = ImmutableMap. builder(); + builder.putAll(knownFunctions); + Set dependentFunctions = ImmutableSet.copyOf(Iterables.concat(Iterables.transform(statements, + new Function>() { + @Override + public Iterable apply(Statement from) { + return from.functionDependencies(osFamily); + } + }))); + for (String unresolved : Sets.difference(dependentFunctions, knownFunctions.keySet())) + builder.put(unresolved, Utils.writeFunctionFromResource(unresolved, osFamily)); + return builder.build(); + } + @Override public Iterable functionDependencies(OsFamily family) { return ImmutableSet. of(); } - + @Override public void accept(StatementVisitor visitor) { for (Statement statement : statements) { diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/CreateRunScript.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/CreateRunScript.java index d935860444..0a3674a118 100644 --- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/CreateRunScript.java +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/CreateRunScript.java @@ -19,6 +19,10 @@ package org.jclouds.scriptbuilder.domain; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Predicates.instanceOf; +import static com.google.common.collect.Iterables.any; +import static com.google.common.collect.Lists.newArrayList; +import static com.google.common.collect.Maps.newHashMap; import static org.jclouds.scriptbuilder.domain.Statements.interpret; import java.util.Collections; @@ -27,14 +31,14 @@ import java.util.Map; import java.util.Map.Entry; import java.util.regex.Pattern; +import org.jclouds.scriptbuilder.ExitInsteadOfReturn; +import org.jclouds.scriptbuilder.ScriptBuilder; import org.jclouds.scriptbuilder.util.Utils; import com.google.common.base.CaseFormat; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; /** * Creates a run script @@ -64,8 +68,8 @@ public class CreateRunScript extends StatementList { } public static final Map OS_TO_TITLE_PATTERN = ImmutableMap.of(OsFamily.UNIX, - "echo \"PROMPT_COMMAND='echo -ne \\\"\\033]0;{title}\\007\\\"'\">>{file}\n", OsFamily.WINDOWS, - "echo title {title}>>{file}\r\n"); + "echo \"PROMPT_COMMAND='echo -ne \\\"\\033]0;{title}\\007\\\"'\">>{file}\n", OsFamily.WINDOWS, + "echo title {title}>>{file}\r\n"); @Override public Iterable functionDependencies(OsFamily family) { @@ -75,7 +79,7 @@ public class CreateRunScript extends StatementList { @Override public String render(OsFamily family) { return addSpaceToEnsureWeDontAccidentallyRedirectFd(Utils.replaceTokens(OS_TO_TITLE_PATTERN.get(family), - ImmutableMap.of("title", title, "file", file))); + ImmutableMap.of("title", title, "file", file))); } } @@ -91,8 +95,7 @@ public class CreateRunScript extends StatementList { } public static final Map OS_TO_EXPORT_PATTERN = ImmutableMap.of(OsFamily.UNIX, - "echo \"export {export}='{value}'\">>{file}\n", OsFamily.WINDOWS, - "echo set {export}={value}>>{file}\r\n"); + "echo \"export {export}='{value}'\">>{file}\n", OsFamily.WINDOWS, "echo set {export}={value}>>{file}\r\n"); @Override public Iterable functionDependencies(OsFamily family) { @@ -102,12 +105,12 @@ public class CreateRunScript extends StatementList { @Override public String render(OsFamily family) { return addSpaceToEnsureWeDontAccidentallyRedirectFd(Utils.replaceTokens(OS_TO_EXPORT_PATTERN.get(family), - ImmutableMap.of("export", export, "value", value, "file", file))); + ImmutableMap.of("export", export, "value", value, "file", file))); } } public static String escapeVarTokens(String toEscape, OsFamily family) { - Map inputToEscape = Maps.newHashMap(); + Map inputToEscape = newHashMap(); for (ShellToken token : ImmutableList.of(ShellToken.VARL, ShellToken.VARR)) { if (!token.to(family).equals("")) { String tokenS = "{" + token.toString().toLowerCase() + "}"; @@ -126,11 +129,11 @@ public class CreateRunScript extends StatementList { } public static final Map OS_TO_CHMOD_PATTERN = ImmutableMap.of(OsFamily.UNIX, "chmod u+x {file}\n", - OsFamily.WINDOWS, ""); + OsFamily.WINDOWS, ""); @Override public String render(OsFamily family) { - List statements = Lists.newArrayList(); + List statements = newArrayList(); Map tokenMap = ShellToken.tokenValueMap(family); String runScript = Utils.replaceTokens(pwd + "{fs}" + instanceName + ".{sh}", tokenMap); statements.add(interpret(String.format("{md} %s{lf}", pwd))); @@ -152,13 +155,12 @@ public class CreateRunScript extends StatementList { } statements.add(new AddTitleToFile(instanceName, runScript)); statements.add(appendToFile(Utils.writeZeroPath(family).replace(ShellToken.LF.to(family), ""), runScript, - family)); + family)); statements.add(new AddExportToFile("instanceName", instanceName, runScript)); for (String export : exports) { - statements - .add(new AddExportToFile(export, Utils.replaceTokens("{varl}" - + CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, export) + "{varr}", tokenMap), - runScript)); + statements.add(new AddExportToFile(export, Utils.replaceTokens( + "{varl}" + CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, export) + "{varr}", tokenMap), + runScript)); } statements.add(appendToFile("{cd} " + pwd, runScript, family)); statements.addAll(statements); @@ -168,7 +170,7 @@ public class CreateRunScript extends StatementList { } } statements - .add(interpret(Utils.replaceTokens(OS_TO_CHMOD_PATTERN.get(family), ImmutableMap.of("file", runScript)))); + .add(interpret(Utils.replaceTokens(OS_TO_CHMOD_PATTERN.get(family), ImmutableMap.of("file", runScript)))); return new StatementList(statements).render(family); } @@ -184,6 +186,11 @@ public class CreateRunScript extends StatementList { builder.append("cat >> ").append(runScript).append(" <<'").append(MARKER).append("'\n"); builder.append("cd ").append(pwd).append("\n"); for (Statement statement : statements) { + if (statement instanceof Call + || (statement instanceof StatementList && any(StatementList.class.cast(statement).delegate(), + instanceOf(Call.class)))) { + statement = new ExitInsteadOfReturn(statement); + } builder.append(statement.render(OsFamily.UNIX)).append("\n"); } builder.append(MARKER).append("\n"); @@ -200,6 +207,16 @@ public class CreateRunScript extends StatementList { String variableNameInUpper = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, export); builder.append("export ").append(variableNameInUpper).append("='$").append(variableNameInUpper).append("'\n"); } + + Map functionsToWrite = ScriptBuilder.resolveFunctionDependenciesForStatements( + ImmutableMap. of("abort", Utils.writeFunctionFromResource("abort", family)), statements, + family); + // if there are more functions than simply abort + if (functionsToWrite.size() > 1) { + StringBuilder inNeedOfEscaping = new StringBuilder(); + ScriptBuilder.writeFunctions(functionsToWrite, family, inNeedOfEscaping); + builder.append(inNeedOfEscaping.toString().replace("$", "\\$")); + } builder.append(MARKER).append("\n"); } @@ -211,7 +228,7 @@ public class CreateRunScript extends StatementList { line = escapeVarTokens(line, family); } return interpret(addSpaceToEnsureWeDontAccidentallyRedirectFd(String.format("echo %s%s%s>>%s{lf}", quote, line, - quote, runScript))); + quote, runScript))); } public static final Pattern REDIRECT_FD_PATTERN = Pattern.compile(".*[0-2]>>.*"); diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/SaveHttpResponseTo.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/SaveHttpResponseTo.java index 615e5ab06d..f2c011fb6b 100644 --- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/SaveHttpResponseTo.java +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/SaveHttpResponseTo.java @@ -46,17 +46,22 @@ public class SaveHttpResponseTo extends InterpretableStatement { * request headers to send */ public SaveHttpResponseTo(String dir, String file, String method, URI endpoint, Multimap headers) { - super(String.format("({md} %s && {cd} %s && [ ! -f %s ] && %s -C - -X %s %s %s >%s)\n", dir, dir, file, CURL, - method, Joiner.on(' ').join( - Iterables.transform(headers.entries(), new Function, String>() { + super(String.format( + "({md} %s && {cd} %s && [ ! -f %s ] && %s -C - -X %s %s %s >%s)\n", + dir, + dir, + file, + CURL, + method, + Joiner.on(' ').join( + Iterables.transform(headers.entries(), new Function, String>() { - @Override - public String apply(Map.Entry from) { - return String.format("-H \"%s: %s\"", from.getKey(), from.getValue()); - } + @Override + public String apply(Map.Entry from) { + return String.format("-H \"%s: %s\"", from.getKey(), from.getValue()); + } - })), endpoint.toASCIIString(), file)); + })), endpoint.toASCIIString(), file)); } - } \ No newline at end of file diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/StatementList.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/StatementList.java index f0da7f8dbc..db5f4a3aaa 100644 --- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/StatementList.java +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/StatementList.java @@ -22,6 +22,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import java.util.List; +import com.google.common.collect.ForwardingList; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList.Builder; @@ -30,7 +31,7 @@ import com.google.common.collect.ImmutableList.Builder; * * @author Adrian Cole */ -public class StatementList implements Statement, AcceptsStatementVisitor { +public class StatementList extends ForwardingList implements Statement, AcceptsStatementVisitor { public final List statements; @@ -44,7 +45,7 @@ public class StatementList implements Statement, AcceptsStatementVisitor { public String render(OsFamily family) { StringBuilder statementsBuilder = new StringBuilder(); - for (Statement statement : statements) { + for (Statement statement : delegate()) { statementsBuilder.append(statement.render(family)); } return statementsBuilder.toString(); @@ -53,41 +54,21 @@ public class StatementList implements Statement, AcceptsStatementVisitor { @Override public Iterable functionDependencies(OsFamily family) { Builder functions = ImmutableList. builder(); - for (Statement statement : statements) { + for (Statement statement : delegate()) { functions.addAll(statement.functionDependencies(family)); } return functions.build(); } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((statements == null) ? 0 : statements.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - StatementList other = (StatementList) obj; - if (statements == null) { - if (other.statements != null) - return false; - } else if (!statements.equals(other.statements)) - return false; - return true; - } - @Override public void accept(StatementVisitor visitor) { - for (Statement statement : statements) { + for (Statement statement : delegate()) { visitor.visit(statement); } } + + @Override + public List delegate() { + return statements; + } } \ 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 5fe30ccb85..a226e63491 100644 --- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/Statements.java +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/Statements.java @@ -22,6 +22,7 @@ import java.net.URI; import java.util.Map; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.Multimap; /** @@ -157,7 +158,11 @@ public class Statements { String directory) { return new PipeHttpResponseToTarxpzfIntoDirectory(method, endpoint, headers, directory); } - + + public static Statement extractTargzIntoDirectory(URI targz, String directory) { + return extractTargzIntoDirectory("GET", targz, ImmutableMultimap.of(), directory); + } + /** * unzip the data received from the request parameters. * @@ -174,6 +179,10 @@ public class Statements { return new UnzipHttpResponseIntoDirectory(method, endpoint, headers, directory); } + public static Statement saveHttpResponseTo(URI source, String dir, String file) { + return new SaveHttpResponseTo(dir, file, "GET", source, ImmutableMultimap. of()); + } + /** * exec the data received from the request parameters. * diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/java/InstallJDK.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/java/InstallJDK.java new file mode 100644 index 0000000000..2ddb6dae26 --- /dev/null +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/java/InstallJDK.java @@ -0,0 +1,76 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.jclouds.scriptbuilder.statements.java; + +import static org.jclouds.scriptbuilder.domain.Statements.appendFile; +import static org.jclouds.scriptbuilder.domain.Statements.call; +import static org.jclouds.scriptbuilder.domain.Statements.exec; +import static org.jclouds.scriptbuilder.domain.Statements.extractTargzIntoDirectory; + +import java.net.URI; + +import org.jclouds.scriptbuilder.domain.Statement; +import org.jclouds.scriptbuilder.domain.StatementList; + +import com.google.common.collect.ImmutableSet; + +/** + * Installs a default JDK to a host + * + * @author Adrian Cole + */ +public class InstallJDK { + public static Statement fromURL() { + return new FromURL(); + } + + public static Statement fromURL(URI url) { + return new FromURL(url); + } + + static class FromURL extends StatementList { + + public static final URI JDK7_URL = URI.create(System.getProperty("jdk7-url", + "http://download.oracle.com/otn-pub/java/jdk/7/jdk-7-linux-x64.tar.gz")); + + public FromURL() { + this(JDK7_URL); + } + + public static final ImmutableSet exportJavaHomeAndAddToPath = ImmutableSet.of( + "export JAVA_HOME=/usr/local/jdk", "export PATH=$JAVA_HOME/bin:$PATH"); + + public FromURL(URI jdk7Url) { + super(call("setupPublicCurl"), // + extractTargzIntoDirectory(jdk7Url, "/usr/local"),// + exec("mv /usr/local/jdk* /usr/local/jdk/"),// + exec("test -n \"$SUDO_USER\" && "), // + appendFile("/home/$SUDO_USER/.bashrc", exportJavaHomeAndAddToPath),// + appendFile("/etc/bashrc", exportJavaHomeAndAddToPath),// + appendFile("$HOME/.bashrc", exportJavaHomeAndAddToPath),// + appendFile("/etc/skel/.bashrc", exportJavaHomeAndAddToPath),// + // TODO: + // eventhough we are setting the above, sometimes images (ex. + // cloudservers ubuntu) kick out of .bashrc (ex. [ -z "$PS1" ] && + // return), for this reason, we should also explicitly link. + // A better way would be to update using alternatives or the like + exec("ln -fs /usr/local/jdk/bin/java /usr/bin/java")); + } + } +} \ No newline at end of file diff --git a/scriptbuilder/src/main/resources/functions/setupPublicCurl.sh b/scriptbuilder/src/main/resources/functions/setupPublicCurl.sh new file mode 100644 index 0000000000..005f891c83 --- /dev/null +++ b/scriptbuilder/src/main/resources/functions/setupPublicCurl.sh @@ -0,0 +1,50 @@ +alias apt-get-install="apt-get install -f -y -qq --force-yes" +alias apt-get-upgrade="(apt-get update -qq&&apt-get upgrade -y -qq)" + +function ensure_cmd_or_install_package_apt(){ + local cmd=$1 + local pkg=$2 + + hash $cmd 2>/dev/null || apt-get-install $pkg || ( apt-get-upgrade && apt-get-install $pkg ) +} + +function ensure_cmd_or_install_package_yum(){ + local cmd=$1 + local pkg=$2 + hash $cmd 2>/dev/null || yum --nogpgcheck -y ensure $pkg +} + +function ensure_netutils_apt() { + ensure_cmd_or_install_package_apt nslookup dnsutils + ensure_cmd_or_install_package_apt curl curl +} + +function ensure_netutils_yum() { + ensure_cmd_or_install_package_yum nslookup bind-utils + ensure_cmd_or_install_package_yum curl curl +} + +# most network services require that the hostname is in +# the /etc/hosts file, or they won't operate +function ensure_hostname_in_hosts() { + egrep -q `hostname` /etc/hosts || awk -v hostname=`hostname` 'END { print $1" "hostname }' /proc/net/arp >> /etc/hosts +} + +# download locations for many services are at public dns +function ensure_can_resolve_public_dns() { + nslookup yahoo.com > /dev/null || echo nameserver 208.67.222.222 >> /etc/resolv.conf +} + +function setupPublicCurl() { + ensure_hostname_in_hosts + if hash apt-get 2>/dev/null; then + ensure_netutils_apt + elif hash yum 2>/dev/null; then + ensure_netutils_yum + else + abort "we only support apt-get and yum right now... please contribute!" + return 1 + fi + ensure_can_resolve_public_dns + return 0 +} diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/InitBuilderTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/InitBuilderTest.java index 86e0c3e372..0c022c9a28 100644 --- a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/InitBuilderTest.java +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/InitBuilderTest.java @@ -18,8 +18,8 @@ */ package org.jclouds.scriptbuilder; -import static org.jclouds.scriptbuilder.domain.Statements.call; import static org.jclouds.scriptbuilder.domain.Statements.appendFile; +import static org.jclouds.scriptbuilder.domain.Statements.exec; import static org.testng.Assert.assertEquals; import java.io.IOException; @@ -45,48 +45,57 @@ import com.google.common.io.Resources; public class InitBuilderTest { InitBuilder testInitBuilder = new InitBuilder("mkebsboot", "/mnt/tmp", "/mnt/tmp", ImmutableMap.of("tmpDir", - "/mnt/tmp"), ImmutableList. of( - appendFile("{tmp}{fs}{uid}{fs}scripttest{fs}temp.txt", ImmutableList. of("hello world")), call("find /"))); + "/mnt/tmp"), ImmutableList. of( + appendFile("{tmp}{fs}{uid}{fs}scripttest{fs}temp.txt", ImmutableList. of("hello world")), + exec("find /"))); @Test public void testBuildSimpleWindows() throws MalformedURLException, IOException { - assertEquals(testInitBuilder.render(OsFamily.WINDOWS), CharStreams.toString(Resources.newReaderSupplier(Resources - .getResource("test_init." + ShellToken.SH.to(OsFamily.WINDOWS)), Charsets.UTF_8))); + assertEquals( + testInitBuilder.render(OsFamily.WINDOWS), + CharStreams.toString(Resources.newReaderSupplier( + Resources.getResource("test_init." + ShellToken.SH.to(OsFamily.WINDOWS)), Charsets.UTF_8))); } @Test public void testBuildSimpleUNIX() throws MalformedURLException, IOException { - assertEquals(testInitBuilder.render(OsFamily.UNIX), CharStreams.toString(Resources.newReaderSupplier(Resources - .getResource("test_init." + ShellToken.SH.to(OsFamily.UNIX)), Charsets.UTF_8))); + assertEquals( + testInitBuilder.render(OsFamily.UNIX), + CharStreams.toString(Resources.newReaderSupplier( + Resources.getResource("test_init." + ShellToken.SH.to(OsFamily.UNIX)), Charsets.UTF_8))); } @Test public void testBuildEBS() throws MalformedURLException, IOException { assertEquals( - new InitBuilder( - "mkebsboot",// name of the script - "/tmp",// working directory - "/tmp/logs",// location of stdout.log and stderr.log - ImmutableMap.of("imageDir", "/mnt/tmp", "ebsDevice", "/dev/sdh", "ebsMountPoint", "/mnt/ebs"),// variables - // used - // inside - // of - // the - // script - ImmutableList. of(Statements.interpret("echo creating a filesystem and mounting the ebs volume",// what to execute - "{md} {varl}IMAGE_DIR{varr} {varl}EBS_MOUNT_POINT{varr}", - "rm -rf {varl}IMAGE_DIR{varr}/*", - "yes| mkfs -t ext3 {varl}EBS_DEVICE{varr} 2>&-", - "mount {varl}EBS_DEVICE{varr} {varl}EBS_MOUNT_POINT{varr}", - "echo making a local working copy of the boot disk", - "rsync -ax --exclude /ubuntu/.bash_history --exclude /home/*/.bash_history --exclude /etc/ssh/ssh_host_* --exclude /etc/ssh/moduli --exclude /etc/udev/rules.d/*persistent-net.rules --exclude /var/lib/ec2/* --exclude=/mnt/* --exclude=/proc/* --exclude=/tmp/* --exclude=/dev/log / {varl}IMAGE_DIR{varr}", - "echo preparing the local working copy", - "touch {varl}IMAGE_DIR{varr}/etc/init.d/ec2-init-user-data", - "echo copying the local working copy to the ebs mount", "{cd} {varl}IMAGE_DIR{varr}", - "tar -cSf - * | tar xf - -C {varl}EBS_MOUNT_POINT{varr}", "echo size of ebs", - "du -sk {varl}EBS_MOUNT_POINT{varr}", "echo size of source", "du -sk {varl}IMAGE_DIR{varr}", - "rm -rf {varl}IMAGE_DIR{varr}/*", "umount {varl}EBS_MOUNT_POINT{varr}", "echo ----COMPLETE----") - )).render(OsFamily.UNIX), CharStreams.toString(Resources.newReaderSupplier(Resources - .getResource("test_ebs." + ShellToken.SH.to(OsFamily.UNIX)), Charsets.UTF_8))); + new InitBuilder("mkebsboot",// name of the script + "/tmp",// working directory + "/tmp/logs",// location of stdout.log and stderr.log + ImmutableMap.of("imageDir", "/mnt/tmp", "ebsDevice", "/dev/sdh", "ebsMountPoint", "/mnt/ebs"),// variables + // used + // inside + // of + // the + // script + ImmutableList. of(Statements + .interpret( + "echo creating a filesystem and mounting the ebs volume",// what + // to + // execute + "{md} {varl}IMAGE_DIR{varr} {varl}EBS_MOUNT_POINT{varr}", + "rm -rf {varl}IMAGE_DIR{varr}/*", + "yes| mkfs -t ext3 {varl}EBS_DEVICE{varr} 2>&-", + "mount {varl}EBS_DEVICE{varr} {varl}EBS_MOUNT_POINT{varr}", + "echo making a local working copy of the boot disk", + "rsync -ax --exclude /ubuntu/.bash_history --exclude /home/*/.bash_history --exclude /etc/ssh/ssh_host_* --exclude /etc/ssh/moduli --exclude /etc/udev/rules.d/*persistent-net.rules --exclude /var/lib/ec2/* --exclude=/mnt/* --exclude=/proc/* --exclude=/tmp/* --exclude=/dev/log / {varl}IMAGE_DIR{varr}", + "echo preparing the local working copy", + "touch {varl}IMAGE_DIR{varr}/etc/init.d/ec2-init-user-data", + "echo copying the local working copy to the ebs mount", "{cd} {varl}IMAGE_DIR{varr}", + "tar -cSf - * | tar xf - -C {varl}EBS_MOUNT_POINT{varr}", "echo size of ebs", + "du -sk {varl}EBS_MOUNT_POINT{varr}", "echo size of source", + "du -sk {varl}IMAGE_DIR{varr}", "rm -rf {varl}IMAGE_DIR{varr}/*", + "umount {varl}EBS_MOUNT_POINT{varr}", "echo ----COMPLETE----"))).render(OsFamily.UNIX), + CharStreams.toString(Resources.newReaderSupplier( + Resources.getResource("test_ebs." + ShellToken.SH.to(OsFamily.UNIX)), Charsets.UTF_8))); } } diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/ScriptBuilderTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/ScriptBuilderTest.java index 5c8e727db9..2710927bfc 100644 --- a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/ScriptBuilderTest.java +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/ScriptBuilderTest.java @@ -18,9 +18,9 @@ */ package org.jclouds.scriptbuilder; -import static org.jclouds.scriptbuilder.domain.Statements.call; +import static org.jclouds.scriptbuilder.ScriptBuilder.call; +import static org.jclouds.scriptbuilder.ScriptBuilder.findPid; import static org.jclouds.scriptbuilder.domain.Statements.appendFile; -import static org.jclouds.scriptbuilder.domain.Statements.findPid; import static org.jclouds.scriptbuilder.domain.Statements.interpret; import static org.jclouds.scriptbuilder.domain.Statements.kill; import static org.jclouds.scriptbuilder.domain.Statements.newStatementList; @@ -49,50 +49,53 @@ import com.google.common.io.Resources; public class ScriptBuilderTest { ScriptBuilder testScriptBuilder = new ScriptBuilder() - .unsetEnvironmentVariable("runtime") - .addEnvironmentVariableScope("default", ImmutableMap.of("runtime", "Moo")) - .addStatement( - switchArg( - 1, - ImmutableMap - .of( - "start", - newStatementList(call("default"), - interpret("echo start {varl}RUNTIME{varr}{lf}")), - "stop", - newStatementList(call("default"), - interpret("echo stop {varl}RUNTIME{varr}{lf}")), - "status", - newStatementList( - appendFile("{tmp}{fs}{uid}{fs}scripttest{fs}temp.txt", - ImmutableList. of("hello world")), - interpret("echo {vq}the following should be []: [{varl}RUNTIME{varr}]{vq}{lf}"))))); + .unsetEnvironmentVariable("runtime") + .addEnvironmentVariableScope("default", ImmutableMap.of("runtime", "Moo")) + .addStatement( + switchArg(1, ImmutableMap.of( + "start", + newStatementList(call("default"), interpret("echo start {varl}RUNTIME{varr}{lf}")), + "stop", + newStatementList(call("default"), interpret("echo stop {varl}RUNTIME{varr}{lf}")), + "status", + newStatementList( + appendFile("{tmp}{fs}{uid}{fs}scripttest{fs}temp.txt", + ImmutableList. of("hello world")), + interpret("echo {vq}the following should be []: [{varl}RUNTIME{varr}]{vq}{lf}"))))); @Test public void testBuildSimpleWindows() throws MalformedURLException, IOException { - assertEquals(testScriptBuilder.render(OsFamily.WINDOWS), CharStreams.toString(Resources.newReaderSupplier( - Resources.getResource("test_script." + ShellToken.SH.to(OsFamily.WINDOWS)), Charsets.UTF_8))); + assertEquals( + testScriptBuilder.render(OsFamily.WINDOWS), + CharStreams.toString(Resources.newReaderSupplier( + Resources.getResource("test_script." + ShellToken.SH.to(OsFamily.WINDOWS)), Charsets.UTF_8))); } @Test public void testBuildSimpleUNIX() throws MalformedURLException, IOException { - assertEquals(testScriptBuilder.render(OsFamily.UNIX), CharStreams.toString(Resources.newReaderSupplier(Resources - .getResource("test_script." + ShellToken.SH.to(OsFamily.UNIX)), Charsets.UTF_8))); + assertEquals( + testScriptBuilder.render(OsFamily.UNIX), + CharStreams.toString(Resources.newReaderSupplier( + Resources.getResource("test_script." + ShellToken.SH.to(OsFamily.UNIX)), Charsets.UTF_8))); } ScriptBuilder findPidBuilder = new ScriptBuilder().addStatement(findPid("{args}")).addStatement( - interpret("echo {varl}FOUND_PID{varr}{lf}")); + interpret("echo {varl}FOUND_PID{varr}{lf}")); @Test public void testFindPidWindows() throws MalformedURLException, IOException { - assertEquals(findPidBuilder.render(OsFamily.WINDOWS), CharStreams.toString(Resources.newReaderSupplier(Resources - .getResource("test_find_pid." + ShellToken.SH.to(OsFamily.WINDOWS)), Charsets.UTF_8))); + assertEquals( + findPidBuilder.render(OsFamily.WINDOWS), + CharStreams.toString(Resources.newReaderSupplier( + Resources.getResource("test_find_pid." + ShellToken.SH.to(OsFamily.WINDOWS)), Charsets.UTF_8))); } @Test public void testFindPidUNIX() throws MalformedURLException, IOException { - assertEquals(findPidBuilder.render(OsFamily.UNIX), CharStreams.toString(Resources.newReaderSupplier(Resources - .getResource("test_find_pid." + ShellToken.SH.to(OsFamily.UNIX)), Charsets.UTF_8))); + assertEquals( + findPidBuilder.render(OsFamily.UNIX), + CharStreams.toString(Resources.newReaderSupplier( + Resources.getResource("test_find_pid." + ShellToken.SH.to(OsFamily.UNIX)), Charsets.UTF_8))); } ScriptBuilder seekAndDestroyBuilder = new ScriptBuilder().addStatement(findPid("{args}")).addStatement(kill()); @@ -100,22 +103,24 @@ public class ScriptBuilderTest { @Test public void testSeekAndDestroyWindows() throws MalformedURLException, IOException { assertEquals(seekAndDestroyBuilder.render(OsFamily.WINDOWS), CharStreams.toString(Resources.newReaderSupplier( - Resources.getResource("test_seek_and_destroy." + ShellToken.SH.to(OsFamily.WINDOWS)), Charsets.UTF_8))); + Resources.getResource("test_seek_and_destroy." + ShellToken.SH.to(OsFamily.WINDOWS)), Charsets.UTF_8))); } @Test public void testSeekAndDestroyUNIX() throws MalformedURLException, IOException { - assertEquals(seekAndDestroyBuilder.render(OsFamily.UNIX), CharStreams.toString(Resources.newReaderSupplier( - Resources.getResource("test_seek_and_destroy." + ShellToken.SH.to(OsFamily.UNIX)), Charsets.UTF_8))); + assertEquals( + seekAndDestroyBuilder.render(OsFamily.UNIX), + CharStreams.toString(Resources.newReaderSupplier( + Resources.getResource("test_seek_and_destroy." + ShellToken.SH.to(OsFamily.UNIX)), Charsets.UTF_8))); } @Test public void testSwitchOn() { ScriptBuilder builder = new ScriptBuilder(); - builder.addStatement(switchArg(1, ImmutableMap.of("start", interpret("echo started{lf}"), "stop", - interpret("echo stopped{lf}")))); + builder.addStatement(switchArg(1, + ImmutableMap.of("start", interpret("echo started{lf}"), "stop", interpret("echo stopped{lf}")))); assertEquals(builder.statements, ImmutableList.of(new SwitchArg(1, ImmutableMap.of("start", - interpret("echo started{lf}"), "stop", interpret("echo stopped{lf}"))))); + interpret("echo started{lf}"), "stop", interpret("echo stopped{lf}"))))); } @Test diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/CreateRunScriptTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/CreateRunScriptTest.java index 3a404c2372..021ec55ed7 100644 --- a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/CreateRunScriptTest.java +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/CreateRunScriptTest.java @@ -18,7 +18,7 @@ */ package org.jclouds.scriptbuilder.domain; -import static org.jclouds.scriptbuilder.domain.Statements.call; +import static org.jclouds.scriptbuilder.domain.Statements.exec; import static org.jclouds.scriptbuilder.domain.Statements.appendFile; import static org.jclouds.scriptbuilder.domain.Statements.createRunScript; import static org.testng.Assert.assertEquals; @@ -43,10 +43,10 @@ public class CreateRunScriptTest { "{tmp}{fs}{uid}{fs}scripttest", ImmutableList . of( - call("echo hello"), + exec("echo hello"), appendFile("{tmp}{fs}{uid}{fs}scripttest{fs}temp.txt", ImmutableList . of("hello world")), - call("echo {varl}JAVA_HOME{varr}{fs}bin{fs}java -DinstanceName={varl}INSTANCE_NAME{varr} myServer.Main"))); + exec("echo {varl}JAVA_HOME{varr}{fs}bin{fs}java -DinstanceName={varl}INSTANCE_NAME{varr} myServer.Main"))); public void testUNIX() throws IOException { assertEquals(statement.render(OsFamily.UNIX), CharStreams.toString(Resources.newReaderSupplier(Resources diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/SaveHttpResponseToTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/SaveHttpResponseToTest.java new file mode 100644 index 0000000000..ed5e950b15 --- /dev/null +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/SaveHttpResponseToTest.java @@ -0,0 +1,45 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.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 = "SaveHttpResponseToTest") +public class SaveHttpResponseToTest { + + public void testSaveHttpResponseToDirAndFileUNIX() { + SaveHttpResponseTo testWithDir = new SaveHttpResponseTo("/tmp", "install", "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( + testWithDir.render(OsFamily.UNIX), + "(mkdir -p /tmp && cd /tmp && [ ! -f install ] && curl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 -C - -X GET -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 >install)\n"); + } + +} diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/StatementsTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/StatementsTest.java new file mode 100644 index 0000000000..af30767aba --- /dev/null +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/StatementsTest.java @@ -0,0 +1,52 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.jclouds.scriptbuilder.domain; + +import static org.testng.Assert.assertEquals; + +import java.net.URI; + +import org.testng.annotations.Test; + +/** + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "StatementsTest") +public class StatementsTest { + + public void testSaveHttpResponseToUNIX() { + Statement save = Statements.saveHttpResponseTo( + URI.create("https://s3.amazonaws.com/MinecraftDownload/launcher/minecraft_server.jar"), "/opt/minecraft", + "minecraft_server.jar"); + assertEquals( + save.render(OsFamily.UNIX), + "(mkdir -p /opt/minecraft && cd /opt/minecraft && [ ! -f minecraft_server.jar ] && curl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 -C - -X GET https://s3.amazonaws.com/MinecraftDownload/launcher/minecraft_server.jar >minecraft_server.jar)\n"); + } + + public void testExtractTargzIntoDirectoryUNIX() { + Statement save = Statements + .extractTargzIntoDirectory( + URI.create("https://s3.amazonaws.com/MinecraftDownload/launcher/minecraft_server.tar.gz"), + "/opt/minecraft"); + assertEquals( + save.render(OsFamily.UNIX), + "curl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 -X GET https://s3.amazonaws.com/MinecraftDownload/launcher/minecraft_server.tar.gz |(mkdir -p /opt/minecraft &&cd /opt/minecraft &&tar -xpzf -)\n"); + } + +} diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/java/InstallJDKTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/java/InstallJDKTest.java new file mode 100644 index 0000000000..8351d74a72 --- /dev/null +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/java/InstallJDKTest.java @@ -0,0 +1,67 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.jclouds.scriptbuilder.statements.java; + +import static org.testng.Assert.assertEquals; + +import java.io.IOException; +import java.net.URI; + +import org.jclouds.scriptbuilder.InitBuilder; +import org.jclouds.scriptbuilder.domain.OsFamily; +import org.jclouds.scriptbuilder.domain.ShellToken; +import org.jclouds.scriptbuilder.domain.Statement; +import org.testng.annotations.Test; + +import com.google.common.base.Charsets; +import com.google.common.collect.ImmutableSet; +import com.google.common.io.CharStreams; +import com.google.common.io.Resources; + +/** + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "InstallJDKTest") +public class InstallJDKTest { + + Statement installJDK = InstallJDK.fromURL(); + + public void testInstallJDKUNIX() throws IOException { + assertEquals(InstallJDK.fromURL().render(OsFamily.UNIX), CharStreams.toString(Resources.newReaderSupplier( + Resources.getResource("test_install_jdk_from_url." + ShellToken.SH.to(OsFamily.UNIX)), Charsets.UTF_8))); + } + + public void testInstallJDKUNIXInScriptBuilderSourcesSetupPublicCurl() throws IOException { + assertEquals( + new InitBuilder("install_jdk", ImmutableSet. of(), ImmutableSet. of(InstallJDK + .fromURL())).render(OsFamily.UNIX), + CharStreams.toString(Resources.newReaderSupplier( + Resources.getResource("test_install_jdk_scriptbuilder." + ShellToken.SH.to(OsFamily.UNIX)), Charsets.UTF_8))); + } + + public void testInstallJDKUNIXWithURL() throws IOException { + assertEquals( + InstallJDK.fromURL(URI.create("http://foo")).render(OsFamily.UNIX), + CharStreams.toString( + Resources.newReaderSupplier( + Resources.getResource("test_install_jdk_from_url." + ShellToken.SH.to(OsFamily.UNIX)), + Charsets.UTF_8)).replace(InstallJDK.FromURL.JDK7_URL.toASCIIString(), "http://foo")); + } + +} diff --git a/scriptbuilder/src/test/resources/functions/test_install_jdk_from_url.sh b/scriptbuilder/src/test/resources/functions/test_install_jdk_from_url.sh new file mode 100644 index 0000000000..a96b138d20 --- /dev/null +++ b/scriptbuilder/src/test/resources/functions/test_install_jdk_from_url.sh @@ -0,0 +1,21 @@ +setupPublicCurl || return 1 +curl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 -X GET http://download.oracle.com/otn-pub/java/jdk/7/jdk-7-linux-x64.tar.gz |(mkdir -p /usr/local &&cd /usr/local &&tar -xpzf -) +mv /usr/local/jdk* /usr/local/jdk/ +test -n "$SUDO_USER" && +cat >> /home/$SUDO_USER/.bashrc <<'END_OF_FILE' +export JAVA_HOME=/usr/local/jdk +export PATH=$JAVA_HOME/bin:$PATH +END_OF_FILE +cat >> /etc/bashrc <<'END_OF_FILE' +export JAVA_HOME=/usr/local/jdk +export PATH=$JAVA_HOME/bin:$PATH +END_OF_FILE +cat >> $HOME/.bashrc <<'END_OF_FILE' +export JAVA_HOME=/usr/local/jdk +export PATH=$JAVA_HOME/bin:$PATH +END_OF_FILE +cat >> /etc/skel/.bashrc <<'END_OF_FILE' +export JAVA_HOME=/usr/local/jdk +export PATH=$JAVA_HOME/bin:$PATH +END_OF_FILE +ln -fs /usr/local/jdk/bin/java /usr/bin/java diff --git a/scriptbuilder/src/test/resources/test_init.sh b/scriptbuilder/src/test/resources/test_init.sh index e2231a2458..f4ad0e84a7 100644 --- a/scriptbuilder/src/test/resources/test_init.sh +++ b/scriptbuilder/src/test/resources/test_init.sh @@ -84,7 +84,7 @@ cat >> /tmp/$USER/scripttest/temp.txt <<'END_OF_FILE' hello world END_OF_FILE -find / || exit 1 +find / END_OF_SCRIPT diff --git a/scriptbuilder/src/test/resources/test_install_jdk_from_url.sh b/scriptbuilder/src/test/resources/test_install_jdk_from_url.sh new file mode 100644 index 0000000000..a96b138d20 --- /dev/null +++ b/scriptbuilder/src/test/resources/test_install_jdk_from_url.sh @@ -0,0 +1,21 @@ +setupPublicCurl || return 1 +curl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 -X GET http://download.oracle.com/otn-pub/java/jdk/7/jdk-7-linux-x64.tar.gz |(mkdir -p /usr/local &&cd /usr/local &&tar -xpzf -) +mv /usr/local/jdk* /usr/local/jdk/ +test -n "$SUDO_USER" && +cat >> /home/$SUDO_USER/.bashrc <<'END_OF_FILE' +export JAVA_HOME=/usr/local/jdk +export PATH=$JAVA_HOME/bin:$PATH +END_OF_FILE +cat >> /etc/bashrc <<'END_OF_FILE' +export JAVA_HOME=/usr/local/jdk +export PATH=$JAVA_HOME/bin:$PATH +END_OF_FILE +cat >> $HOME/.bashrc <<'END_OF_FILE' +export JAVA_HOME=/usr/local/jdk +export PATH=$JAVA_HOME/bin:$PATH +END_OF_FILE +cat >> /etc/skel/.bashrc <<'END_OF_FILE' +export JAVA_HOME=/usr/local/jdk +export PATH=$JAVA_HOME/bin:$PATH +END_OF_FILE +ln -fs /usr/local/jdk/bin/java /usr/bin/java diff --git a/scriptbuilder/src/test/resources/test_install_jdk_scriptbuilder.sh b/scriptbuilder/src/test/resources/test_install_jdk_scriptbuilder.sh new file mode 100644 index 0000000000..b6e0722e1d --- /dev/null +++ b/scriptbuilder/src/test/resources/test_install_jdk_scriptbuilder.sh @@ -0,0 +1,196 @@ +#!/bin/bash +set +u +shopt -s xpg_echo +shopt -s expand_aliases +unset PATH JAVA_HOME LD_LIBRARY_PATH +function abort { + echo "aborting: $@" 1>&2 + exit 1 +} +function default { + export INSTANCE_NAME="install_jdk" +export INSTANCE_HOME="$HOME/instances/install_jdk" +export LOG_DIR="$HOME/instances/install_jdk" + return 0 +} +function install_jdk { + return 0 +} +function findPid { + unset FOUND_PID; + [ $# -eq 1 ] || { + abort "findPid requires a parameter of pattern to match" + return 1 + } + local PATTERN="$1"; shift + local _FOUND=`ps auxwww|grep "$PATTERN"|grep -v " $0"|grep -v grep|grep -v $$|awk '{print $2}'` + [ -n "$_FOUND" ] && { + export FOUND_PID=$_FOUND + return 0 + } || { + return 1 + } +} +function forget { + unset FOUND_PID; + [ $# -eq 3 ] || { + abort "forget requires parameters INSTANCE_NAME SCRIPT LOG_DIR" + return 1 + } + local INSTANCE_NAME="$1"; shift + local SCRIPT="$1"; shift + local LOG_DIR="$1"; shift + mkdir -p $LOG_DIR + findPid $INSTANCE_NAME + [ -n "$FOUND_PID" -a -f $LOG_DIR/stdout.log ] && { + echo $INSTANCE_NAME already running pid [$FOUND_PID] + return 1; + } || { + nohup $SCRIPT >$LOG_DIR/stdout.log 2>$LOG_DIR/stderr.log & + RETURN=$? + # this is generally followed by findPid, so we shouldn't exit + # immediately as the proc may not have registered in ps, yet + test $RETURN && sleep 1 + return $RETURN; + } +} +export PATH=/usr/ucb/bin:/bin:/sbin:/usr/bin:/usr/sbin +case $1 in +init) + default || exit 1 + install_jdk || exit 1 + mkdir -p $INSTANCE_HOME + + # create runscript header + cat > $INSTANCE_HOME/install_jdk.sh <&2 + exit 1 +} +alias apt-get-install="apt-get install -f -y -qq --force-yes" +alias apt-get-upgrade="(apt-get update -qq&&apt-get upgrade -y -qq)" + +function ensure_cmd_or_install_package_apt(){ + local cmd=\$1 + local pkg=\$2 + + hash \$cmd 2>/dev/null || apt-get-install \$pkg || ( apt-get-upgrade && apt-get-install \$pkg ) +} + +function ensure_cmd_or_install_package_yum(){ + local cmd=\$1 + local pkg=\$2 + hash \$cmd 2>/dev/null || yum --nogpgcheck -y ensure \$pkg +} + +function ensure_netutils_apt() { + ensure_cmd_or_install_package_apt nslookup dnsutils + ensure_cmd_or_install_package_apt curl curl +} + +function ensure_netutils_yum() { + ensure_cmd_or_install_package_yum nslookup bind-utils + ensure_cmd_or_install_package_yum curl curl +} + +# most network services require that the hostname is in +# the /etc/hosts file, or they won't operate +function ensure_hostname_in_hosts() { + egrep -q `hostname` /etc/hosts || awk -v hostname=`hostname` 'END { print \$1" "hostname }' /proc/net/arp >> /etc/hosts +} + +# download locations for many services are at public dns +function ensure_can_resolve_public_dns() { + nslookup yahoo.com > /dev/null || echo nameserver 208.67.222.222 >> /etc/resolv.conf +} + +function setupPublicCurl() { + ensure_hostname_in_hosts + if hash apt-get 2>/dev/null; then + ensure_netutils_apt + elif hash yum 2>/dev/null; then + ensure_netutils_yum + else + abort "we only support apt-get and yum right now... please contribute!" + return 1 + fi + ensure_can_resolve_public_dns + return 0 +} +END_OF_SCRIPT + + # add desired commands from the user + cat >> $INSTANCE_HOME/install_jdk.sh <<'END_OF_SCRIPT' +cd $INSTANCE_HOME +setupPublicCurl || exit 1 +curl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 -X GET http://download.oracle.com/otn-pub/java/jdk/7/jdk-7-linux-x64.tar.gz |(mkdir -p /usr/local &&cd /usr/local &&tar -xpzf -) +mv /usr/local/jdk* /usr/local/jdk/ +test -n "$SUDO_USER" && +cat >> /home/$SUDO_USER/.bashrc <<'END_OF_FILE' +export JAVA_HOME=/usr/local/jdk +export PATH=$JAVA_HOME/bin:$PATH +END_OF_FILE +cat >> /etc/bashrc <<'END_OF_FILE' +export JAVA_HOME=/usr/local/jdk +export PATH=$JAVA_HOME/bin:$PATH +END_OF_FILE +cat >> $HOME/.bashrc <<'END_OF_FILE' +export JAVA_HOME=/usr/local/jdk +export PATH=$JAVA_HOME/bin:$PATH +END_OF_FILE +cat >> /etc/skel/.bashrc <<'END_OF_FILE' +export JAVA_HOME=/usr/local/jdk +export PATH=$JAVA_HOME/bin:$PATH +END_OF_FILE +ln -fs /usr/local/jdk/bin/java /usr/bin/java + +END_OF_SCRIPT + + # add runscript footer + cat >> $INSTANCE_HOME/install_jdk.sh <<'END_OF_SCRIPT' +exit 0 +END_OF_SCRIPT + + chmod u+x $INSTANCE_HOME/install_jdk.sh + ;; +status) + default || exit 1 + findPid $INSTANCE_NAME || exit 1 + echo [$FOUND_PID] + ;; +stop) + default || exit 1 + findPid $INSTANCE_NAME || exit 1 + [ -n "$FOUND_PID" ] && { + echo stopping $FOUND_PID + kill -9 $FOUND_PID + } + ;; +start) + default || exit 1 + forget $INSTANCE_NAME $INSTANCE_HOME/$INSTANCE_NAME.sh $LOG_DIR || exit 1 + ;; +tail) + default || exit 1 + tail $LOG_DIR/stdout.log + ;; +tailerr) + default || exit 1 + tail $LOG_DIR/stderr.log + ;; +run) + default || exit 1 + $INSTANCE_HOME/$INSTANCE_NAME.sh + ;; +esac +exit 0 diff --git a/scriptbuilder/src/test/resources/test_runrun.sh b/scriptbuilder/src/test/resources/test_runrun.sh index 54ce111083..ee687974f6 100644 --- a/scriptbuilder/src/test/resources/test_runrun.sh +++ b/scriptbuilder/src/test/resources/test_runrun.sh @@ -15,13 +15,13 @@ END_OF_SCRIPT # add desired commands from the user cat >> /tmp/$USER/scripttest/yahooprod.sh <<'END_OF_SCRIPT' cd /tmp/$USER/scripttest -echo hello || return 1 +echo hello cat >> /tmp/$USER/scripttest/temp.txt <<'END_OF_FILE' hello world END_OF_FILE -echo $JAVA_HOME/bin/java -DinstanceName=$INSTANCE_NAME myServer.Main || return 1 +echo $JAVA_HOME/bin/java -DinstanceName=$INSTANCE_NAME myServer.Main END_OF_SCRIPT