diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/ScriptBuilder.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/ScriptBuilder.java index 400bd48cf3..d34ca651ed 100644 --- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/ScriptBuilder.java +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/ScriptBuilder.java @@ -27,10 +27,10 @@ import static com.google.common.base.Preconditions.checkNotNull; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import org.jclouds.scriptbuilder.domain.OsFamily; import org.jclouds.scriptbuilder.domain.ShellToken; +import org.jclouds.scriptbuilder.domain.Statement; import org.jclouds.scriptbuilder.util.Utils; import com.google.common.annotations.VisibleForTesting; @@ -47,7 +47,7 @@ import com.google.common.collect.Maps; public class ScriptBuilder { @VisibleForTesting - Map> switchExec = Maps.newHashMap(); + List statements = Lists.newArrayList(); @VisibleForTesting List variableScopes = Lists.newArrayList(); @@ -58,28 +58,8 @@ public class ScriptBuilder { @VisibleForTesting List variablesToUnset = Lists.newArrayList("path", "javaHome", "libraryPath"); - /** - * Adds a switch statement to the script. If its value is found, it will invoke the corresponding - * action. - * - *

- * Ex. variable is {@code 1} - the first argument to the script
- * and valueToActions is {"start" -> "echo hello", "stop" -> "echo goodbye"}
- * the script created will respond accordingly:
- * {@code ./script start }
- * << returns hello
- * {@code ./script stop }
- * << returns goodbye
- * - * @param variable - * - shell variable to switch on - * @param valueToActions - * - case statements, if the value of the variable matches a key, the corresponding - * value will be invoked. - */ - public ScriptBuilder switchOn(String variable, Map valueToActions) { - switchExec.put(checkNotNull(variable, "variable"), checkNotNull(valueToActions, - "valueToActions")); + public ScriptBuilder addStatement(Statement statement) { + statements.add(checkNotNull(statement, "statement")); return this; } @@ -127,6 +107,7 @@ public class ScriptBuilder { } })), osFamily)); + resolveFunctionDependencies(osFamily); if (functions.size() > 0) { builder.append(ShellToken.BEGIN_FUNCTIONS.to(osFamily)); builder.append(Utils.writeFunctionFromResource("abort", osFamily)); @@ -136,10 +117,29 @@ public class ScriptBuilder { builder.append(ShellToken.END_FUNCTIONS.to(osFamily)); } builder.append(Utils.writeZeroPath(osFamily)); - for (Entry> entry : switchExec.entrySet()) { - builder.append(Utils.writeSwitch(entry.getKey(), entry.getValue(), 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(ShellToken.END_SCRIPT.to(osFamily)); return builder.toString(); } + + @VisibleForTesting + void resolveFunctionDependencies(final OsFamily osFamily) { + Iterable dependentFunctions = Iterables.concat(Iterables.transform(statements, + new Function>() { + @Override + public Iterable apply(Statement from) { + return from.functionDependecies(); + } + })); + List unresolvedFunctions = Lists.newArrayList(dependentFunctions); + Iterables.removeAll(unresolvedFunctions, this.functions.keySet()); + for (String functionName : dependentFunctions) { + functions.put(functionName, Utils.writeFunctionFromResource(functionName, osFamily)); + } + } } \ No newline at end of file diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/Call.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/Call.java new file mode 100644 index 0000000000..79ae40ddb5 --- /dev/null +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/Call.java @@ -0,0 +1,99 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + */ +package org.jclouds.scriptbuilder.domain; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Arrays; +import java.util.Map; + +import org.jclouds.scriptbuilder.util.Utils; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +/** + * Statement used in a shell script + * + * @author Adrian Cole + */ +public class Call implements Statement { + + public static final Map OS_TO_CALL = ImmutableMap.of(OsFamily.UNIX, + "{function}{args} || return 1\n", OsFamily.WINDOWS, + "call :{function}{args}\r\nif errorlevel 1 goto abort\r\n"); + + private String function; + private String[] args; + + public Call(String function, String... args) { + this.function = checkNotNull(function, "function"); + this.args = checkNotNull(args, "args"); + } + + public String render(OsFamily family) { + StringBuilder args = new StringBuilder(); + for (String arg : this.args) { + args.append(String.format(" \"%s\"", Utils.replaceTokens(arg, ShellToken + .tokenValueMap(family)))); + } + StringBuilder call = new StringBuilder(); + call.append(Utils.replaceTokens(OS_TO_CALL.get(family), ImmutableMap.of("function", function, + "args", args.toString()))); + return call.toString(); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + Arrays.hashCode(args); + result = prime * result + ((function == null) ? 0 : function.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; + Call other = (Call) obj; + if (!Arrays.equals(args, other.args)) + return false; + if (function == null) { + if (other.function != null) + return false; + } else if (!function.equals(other.function)) + return false; + return true; + } + + @Override + public Iterable functionDependecies() { + return ImmutableList.of(function); + } +} \ No newline at end of file diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/InterpretableStatement.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/InterpretableStatement.java new file mode 100644 index 0000000000..8f718d3c5b --- /dev/null +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/InterpretableStatement.java @@ -0,0 +1,78 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + */ +package org.jclouds.scriptbuilder.domain; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.jclouds.scriptbuilder.util.Utils; + +import com.google.common.collect.ImmutableList; + +/** + * Statement used in a shell script + * + * @author Adrian Cole + */ +public class InterpretableStatement implements Statement { + + private String statement; + + public InterpretableStatement(String statement) { + this.statement = checkNotNull(statement, "statement"); + } + + public String render(OsFamily family) { + return Utils.replaceTokens(statement, ShellToken.tokenValueMap(family)); + } + + @Override + public Iterable functionDependecies() { + return ImmutableList.of(); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((statement == null) ? 0 : statement.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; + InterpretableStatement other = (InterpretableStatement) obj; + if (statement == null) { + if (other.statement != null) + return false; + } else if (!statement.equals(other.statement)) + return false; + return true; + } +} \ No newline at end of file diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/ShellToken.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/ShellToken.java index bc813313f1..d40fe2fa58 100644 --- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/ShellToken.java +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/ShellToken.java @@ -58,7 +58,7 @@ public enum ShellToken { /** * End the function. exits successfully and closes the code block. */ - FNCE, BEGIN_SCRIPT, END_SCRIPT, BEGIN_FUNCTIONS, END_FUNCTIONS, EXPORT, LF, SH, SOURCE, REM, RETURN, ARGS, VARSTART, VAREND, LIBRARY_PATH_VARIABLE; + FNCE, BEGIN_SCRIPT, END_SCRIPT, BEGIN_FUNCTIONS, EXIT, END_FUNCTIONS, EXPORT, LF, SH, SOURCE, REM, RETURN, ARGS, VARL, VARR, LIBRARY_PATH_VARIABLE; private static final Map> familyToTokenValueMap = new MapMaker() .makeComputingMap(new Function>() { @@ -149,9 +149,9 @@ public enum ShellToken { case END_SCRIPT: switch (family) { case WINDOWS: - return "exit /b 0\r\n"; + return "exit 0\r\n"; case UNIX: - return "set -u\nreturn 0\n"; + return "exit 0\n"; } case EXPORT: switch (family) { @@ -167,6 +167,13 @@ public enum ShellToken { case UNIX: return "return"; } + case EXIT: + switch (family) { + case WINDOWS: + return "exit"; + case UNIX: + return "exit"; + } case LF: switch (family) { case WINDOWS: @@ -209,14 +216,14 @@ public enum ShellToken { case UNIX: return "$@"; } - case VARSTART: + case VARL: switch (family) { case WINDOWS: return "%"; case UNIX: return "$"; } - case VAREND: + case VARR: switch (family) { case WINDOWS: return "%"; diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/Statement.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/Statement.java new file mode 100644 index 0000000000..80503e855d --- /dev/null +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/Statement.java @@ -0,0 +1,35 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + */ +package org.jclouds.scriptbuilder.domain; + +/** + * Statement used in a shell script + * + * @author Adrian Cole + */ +public interface Statement { + Iterable functionDependecies(); + + String render(OsFamily family); +} \ 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 new file mode 100644 index 0000000000..8904a8aa49 --- /dev/null +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/Statements.java @@ -0,0 +1,57 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + */ +package org.jclouds.scriptbuilder.domain; + +import java.util.Map; + +/** + * Statements used in shell scripts. + * + * @author Adrian Cole + */ +public class Statements { + + public static Statement switchOn(String variable, Map valueToActions) { + return new Switch(variable, valueToActions); + } + + public static Statement call(String function, String... args) { + return new Call(function, args); + } + + /** + * Stores the pid into the variable foundPid if successful. + * + * @param args + * - what to search for in the process tree. + */ + public static Statement findPid(String args) { + return new Call("findPid", args); + } + + public static Statement interpret(String portableStatement) { + return new InterpretableStatement(portableStatement); + } + +} \ No newline at end of file diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/Switch.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/Switch.java new file mode 100644 index 0000000000..f1acada635 --- /dev/null +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/domain/Switch.java @@ -0,0 +1,137 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + */ +package org.jclouds.scriptbuilder.domain; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.jclouds.scriptbuilder.util.Utils; + +import com.google.common.base.CaseFormat; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +/** + * Statement used in a shell script + * + * @author Adrian Cole + */ +public class Switch implements Statement { + + public static final Map OS_TO_SWITCH_PATTERN = ImmutableMap.of(OsFamily.UNIX, + "case ${variable} in\n", OsFamily.WINDOWS, "goto CASE%{variable}\r\n"); + + public static final Map OS_TO_END_SWITCH_PATTERN = ImmutableMap.of( + OsFamily.UNIX, "esac\n", OsFamily.WINDOWS, ":END_SWITCH\r\n"); + + public static final Map OS_TO_CASE_PATTERN = ImmutableMap.of(OsFamily.UNIX, + "{value})\n {action} ;;\n", OsFamily.WINDOWS, + ":CASE_{value}\r\n {action} GOTO END_SWITCH\r\n"); + + private final String variable; + + private final Map valueToActions; + + /** + * Generates a switch statement based on {@code variable}. If its value is found to be a key in + * {@code valueToActions}, the corresponding action is invoked. + * + *

+ * Ex. variable is {@code 1} - the first argument to the script
+ * and valueToActions is {"start" -> "echo hello", "stop" -> "echo goodbye"}
+ * the script created will respond accordingly:
+ * {@code ./script start }
+ * << returns hello
+ * {@code ./script stop }
+ * << returns goodbye
+ * + * @param variable + * - shell variable to switch on + * @param valueToActions + * - case statements, if the value of the variable matches a key, the corresponding + * value will be invoked. + */ + public Switch(String variable, Map valueToActions) { + this.variable = checkNotNull(variable, "variable"); + this.valueToActions = checkNotNull(valueToActions, "valueToActions"); + } + + public String render(OsFamily family) { + StringBuilder switchClause = new StringBuilder(); + switchClause.append(Utils.replaceTokens(OS_TO_SWITCH_PATTERN.get(family), ImmutableMap.of( + "variable", CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, variable)))); + + for (Entry entry : valueToActions.entrySet()) { + switchClause.append(Utils.replaceTokens(OS_TO_CASE_PATTERN.get(family), ImmutableMap.of( + "value", entry.getKey(), "action", entry.getValue().render(family)))); + } + + switchClause.append(OS_TO_END_SWITCH_PATTERN.get(family)); + return switchClause.toString(); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((valueToActions == null) ? 0 : valueToActions.hashCode()); + result = prime * result + ((variable == null) ? 0 : variable.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; + Switch other = (Switch) obj; + if (valueToActions == null) { + if (other.valueToActions != null) + return false; + } else if (!valueToActions.equals(other.valueToActions)) + return false; + if (variable == null) { + if (other.variable != null) + return false; + } else if (!variable.equals(other.variable)) + return false; + return true; + } + + @Override + public Iterable functionDependecies() { + List functions = Lists.newArrayList(); + for (Statement statement : valueToActions.values()) { + Iterables.addAll(functions, statement.functionDependecies()); + } + return functions; + } +} \ No newline at end of file diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/util/Utils.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/util/Utils.java index 46927b8a84..65e75d6151 100644 --- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/util/Utils.java +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/util/Utils.java @@ -141,8 +141,20 @@ public class Utils { return CharStreams.toString(Resources.newReaderSupplier(Resources.getResource(String .format("functions/%s.%s", function, ShellToken.SH.to(family))), Charsets.UTF_8)); } catch (IOException e) { - // TODO - throw new RuntimeException(e); + throw new FunctionNotFoundException(function, family, e); + } + } + + public static class FunctionNotFoundException extends RuntimeException { + /** The serialVersionUID */ + private static final long serialVersionUID = 1L; + + public FunctionNotFoundException(String functionName, OsFamily family) { + super("function: " + functionName + " not found for famiy: " + family); + } + + public FunctionNotFoundException(String functionName, OsFamily family, Throwable cause) { + super("function: " + functionName + " not found for famiy: " + family, cause); } } @@ -214,50 +226,6 @@ public class Utils { return OS_TO_ZERO_PATH.get(family); } - public static final Map OS_TO_SWITCH_PATTERN = ImmutableMap.of(OsFamily.UNIX, - "case ${variable} in\n", OsFamily.WINDOWS, "goto CASE%{variable}\r\n"); - - public static final Map OS_TO_END_SWITCH_PATTERN = ImmutableMap.of( - OsFamily.UNIX, "esac\n", OsFamily.WINDOWS, ":END_SWITCH\r\n"); - - public static final Map OS_TO_CASE_PATTERN = ImmutableMap.of(OsFamily.UNIX, - "{value})\n {action}\n ;;\n", OsFamily.WINDOWS, - ":CASE_{value}\r\n {action}\r\n GOTO END_SWITCH\r\n"); - - /** - * Generates a switch statement based on {@code variable}. If its value is found to be a key in - * {@code valueToActions}, the corresponding action is invoked. - * - *

- * Ex. variable is {@code 1} - the first argument to the script
- * and valueToActions is {"start" -> "echo hello", "stop" -> "echo goodbye"}
- * the script created will respond accordingly:
- * {@code ./script start }
- * << returns hello
- * {@code ./script stop }
- * << returns goodbye
- * - * @param variable - * - shell variable to switch on - * @param valueToActions - * - case statements, if the value of the variable matches a key, the corresponding - * value will be invoked. - */ - public static String writeSwitch(String variable, Map valueToActions, - OsFamily family) { - StringBuilder switchClause = new StringBuilder(); - switchClause.append(replaceTokens(OS_TO_SWITCH_PATTERN.get(family), ImmutableMap.of( - "variable", CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, variable)))); - - for (Entry entry : valueToActions.entrySet()) { - switchClause.append(replaceTokens(OS_TO_CASE_PATTERN.get(family), ImmutableMap.of("value", - entry.getKey(), "action", entry.getValue()))); - } - - switchClause.append(OS_TO_END_SWITCH_PATTERN.get(family)); - return switchClause.toString(); - } - public static String writeComment(String comment, OsFamily family) { return String.format("%s%s%s", ShellToken.REM.to(family), comment, ShellToken.LF.to(family)); } diff --git a/scriptbuilder/src/main/resources/functions/abort.bash b/scriptbuilder/src/main/resources/functions/abort.bash index 148d83eda7..ef34809a00 100644 --- a/scriptbuilder/src/main/resources/functions/abort.bash +++ b/scriptbuilder/src/main/resources/functions/abort.bash @@ -1,4 +1,4 @@ function abort { echo "aborting: $@" 1>&2 - set -u + exit 1 } diff --git a/scriptbuilder/src/main/resources/functions/abort.cmd b/scriptbuilder/src/main/resources/functions/abort.cmd index 9ca47282a3..3d79b48c4f 100644 --- a/scriptbuilder/src/main/resources/functions/abort.cmd +++ b/scriptbuilder/src/main/resources/functions/abort.cmd @@ -1,3 +1,3 @@ :abort echo aborting: %EXCEPTION% - exit /b 1 + exit 1 diff --git a/scriptbuilder/src/main/resources/functions/findPid.bash b/scriptbuilder/src/main/resources/functions/findPid.bash new file mode 100644 index 0000000000..2285cd06b9 --- /dev/null +++ b/scriptbuilder/src/main/resources/functions/findPid.bash @@ -0,0 +1,15 @@ +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|awk '{print $2}'` + [ -n "$_FOUND" ] && { + export FOUND_PID=$_FOUND + return 0 + } || { + return 1 + } +} diff --git a/scriptbuilder/src/main/resources/functions/findPid.cmd b/scriptbuilder/src/main/resources/functions/findPid.cmd new file mode 100644 index 0000000000..7653c210e5 --- /dev/null +++ b/scriptbuilder/src/main/resources/functions/findPid.cmd @@ -0,0 +1,16 @@ +:findPid + set FOUND_PID= + SETLOCAL + set _pid= + set _expression=%1 + shift + set FIND_PROCESS=wmic process where (CommandLine like "%_expression%%%") get ProcessId + for /f "usebackq skip=1" %%a in (`cmd /c "%FIND_PROCESS% 2>NUL"`) do ( + if not defined _proc ( + set _pid=%%a + goto :done + ) + ) + :done + ENDLOCAL&SET FOUND_PID=%_pid% + exit /b 0 diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/ScriptBuilderTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/ScriptBuilderTest.java index 4ac371c966..2de7189633 100644 --- a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/ScriptBuilderTest.java +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/ScriptBuilderTest.java @@ -1,16 +1,18 @@ package org.jclouds.scriptbuilder; +import static org.jclouds.scriptbuilder.domain.Statements.*; import static org.testng.Assert.assertEquals; import java.io.IOException; import java.net.MalformedURLException; -import org.jclouds.scriptbuilder.ScriptBuilder; import org.jclouds.scriptbuilder.domain.OsFamily; import org.jclouds.scriptbuilder.domain.ShellToken; +import org.jclouds.scriptbuilder.domain.Switch; import org.testng.annotations.Test; import com.google.common.base.Charsets; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.io.CharStreams; import com.google.common.io.Resources; @@ -22,9 +24,10 @@ import com.google.common.io.Resources; */ public class ScriptBuilderTest { - ScriptBuilder testScriptBuilder = new ScriptBuilder().switchOn("1", - ImmutableMap.of("start", "echo started", "stop", "echo stopped")) - .addEnvironmentVariableScope("default", ImmutableMap.of("javaHome", "/apps/jdk1.6")); + ScriptBuilder testScriptBuilder = new ScriptBuilder().addStatement( + switchOn("1", ImmutableMap.of("start", interpret("echo started{lf}"), "stop", + interpret("echo stopped{lf}")))).addEnvironmentVariableScope("default", + ImmutableMap.of("javaHome", "/apps/jdk1.6")); @Test public void testBuildSimpleWindows() throws MalformedURLException, IOException { @@ -40,25 +43,44 @@ public class ScriptBuilderTest { + ShellToken.SH.to(OsFamily.UNIX)), Charsets.UTF_8))); } + ScriptBuilder findPidBuilder = new ScriptBuilder().addStatement(findPid("{args}")).addStatement( + interpret("echo {varl}FOUND_PID{varr}{lf}")); + + @Test + public void testFindPidWindows() throws MalformedURLException, IOException { + assertEquals(findPidBuilder.build(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.build(OsFamily.UNIX), CharStreams.toString(Resources + .newReaderSupplier(Resources.getResource("test_find_pid." + + ShellToken.SH.to(OsFamily.UNIX)), Charsets.UTF_8))); + } + @Test public void testSwitchOn() { ScriptBuilder builder = new ScriptBuilder(); - builder.switchOn("1", ImmutableMap.of("start", "echo started", "stop", "echo stopped")); - assertEquals(builder.switchExec, ImmutableMap.of("1", ImmutableMap.of("start", - "echo started", "stop", "echo stopped"))); + builder.addStatement(switchOn("1", ImmutableMap.of("start", interpret("echo started{lf}"), + "stop", interpret("echo stopped{lf}")))); + assertEquals(builder.statements, ImmutableList.of(new Switch("1", ImmutableMap.of("start", + interpret("echo started{lf}"), "stop", interpret("echo stopped{lf}"))))); } @Test public void testNoSwitchOn() { ScriptBuilder builder = new ScriptBuilder(); - assertEquals(builder.switchExec.size(), 0); + assertEquals(builder.statements.size(), 0); } @Test public void testExport() { ScriptBuilder builder = new ScriptBuilder(); builder.addEnvironmentVariableScope("default", ImmutableMap.of("javaHome", "/apps/jdk1.6")); - assertEquals(builder.functions, ImmutableMap.of("default", "{fncl}default{fncr} {export} JAVA_HOME={vq}/apps/jdk1.6{vq}{lf}{fnce}")); + assertEquals(builder.functions, ImmutableMap.of("default", + "{fncl}default{fncr} {export} JAVA_HOME={vq}/apps/jdk1.6{vq}{lf}{fnce}")); } @Test diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/CallTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/CallTest.java new file mode 100644 index 0000000000..298c904c42 --- /dev/null +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/CallTest.java @@ -0,0 +1,56 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + */ +package org.jclouds.scriptbuilder.domain; + +import static org.testng.Assert.assertEquals; + +import org.testng.annotations.Test; + +/** + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "scriptbuilder.CallTest") +public class CallTest { + + public void testCallNoArgsUNIX() { + Call call = new Call("help"); + assertEquals(call.render(OsFamily.UNIX), "help || return 1\n"); + } + + public void testCallNoArgsWINDOWS() { + Call call = new Call("help"); + assertEquals(call.render(OsFamily.WINDOWS), "call :help\r\nif errorlevel 1 goto abort\r\n"); + } + + public void testCallArgsUNIX() { + Call call = new Call("help", "me", "rhonda"); + assertEquals(call.render(OsFamily.UNIX), "help \"me\" \"rhonda\" || return 1\n"); + } + + public void testCallArgsWINDOWS() { + Call call = new Call("help", "me", "rhonda"); + assertEquals(call.render(OsFamily.WINDOWS), + "call :help \"me\" \"rhonda\"\r\nif errorlevel 1 goto abort\r\n"); + } +} diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/ShellTokenTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/ShellTokenTest.java index f75ca7080e..ab69032114 100644 --- a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/ShellTokenTest.java +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/ShellTokenTest.java @@ -42,10 +42,10 @@ public class ShellTokenTest { public void testTokenValueMapUNIX() { Map expected = new ImmutableMap.Builder().put("fs", "/").put( "ps", ":").put("lf", "\n").put("sh", "bash").put("source", ".").put("rem", "#").put( - "args", "$@").put("varstart", "$").put("return", "return").put("varend", "").put( - "libraryPathVariable", "LD_LIBRARY_PATH").put("beginScript", + "args", "$@").put("varl", "$").put("return", "return").put("exit", "exit").put( + "varr", "").put("libraryPathVariable", "LD_LIBRARY_PATH").put("beginScript", "#!/bin/bash\nset +u\nshopt -s xpg_echo\nshopt -s expand_aliases\n").put( - "endScript", "set -u\nreturn 0\n").put("vq", "\"").put("beginFunctions", "").put( + "endScript", "exit 0\n").put("vq", "\"").put("beginFunctions", "").put( "endFunctions", "").put("fncl", "function ").put("fncr", " {\n").put("fnce", " return 0\n}\n").put("export", "export").build(); @@ -55,11 +55,11 @@ public class ShellTokenTest { public void testTokenValueMapWindows() { Map expected = new ImmutableMap.Builder().put("fs", "\\") .put("ps", ";").put("lf", "\r\n").put("sh", "cmd").put("source", "@call").put("rem", - "@rem").put("args", "%*").put("varstart", "%").put("varend", "%").put( - "libraryPathVariable", "PATH").put("return", "exit /b").put("vq", "").put( - "beginFunctions", "GOTO FUNCTION_END\r\n").put("endFunctions", + "@rem").put("args", "%*").put("varl", "%").put("exit", "exit").put("varr", + "%").put("libraryPathVariable", "PATH").put("return", "exit /b").put("vq", + "").put("beginFunctions", "GOTO FUNCTION_END\r\n").put("endFunctions", ":FUNCTION_END\r\n").put("beginScript", "@echo off\r\n").put("endScript", - "exit /b 0\r\n").put("fncl", ":").put("fncr", "\r\n").put("fnce", + "exit 0\r\n").put("fncl", ":").put("fncr", "\r\n").put("fnce", " exit /b 0\r\n").put("export", "set").build(); assertEquals(ShellToken.tokenValueMap(OsFamily.WINDOWS), expected); diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/SwitchTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/SwitchTest.java new file mode 100644 index 0000000000..788e437cff --- /dev/null +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/domain/SwitchTest.java @@ -0,0 +1,50 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + */ +package org.jclouds.scriptbuilder.domain; + +import static org.testng.Assert.assertEquals; + +import org.testng.annotations.Test; +import static org.jclouds.scriptbuilder.domain.Statements.*; +import com.google.common.collect.ImmutableMap; + +/** + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "scriptbuilder.SwitchTest") +public class SwitchTest { + + public void testSwitchUNIX() { + assertEquals(new Switch("i", ImmutableMap.of("0", interpret("echo hello zero{lf}"), "1", + interpret("echo hello one{lf}"))).render(OsFamily.UNIX), + "case $I in\n0)\n echo hello zero\n ;;\n1)\n echo hello one\n ;;\nesac\n"); + } + + public void testSwitchWindows() { + assertEquals( + new Switch("i", ImmutableMap.of("0", interpret("echo hello zero{lf}"), "1", + interpret("echo hello one{lf}"))).render(OsFamily.WINDOWS), + "goto CASE%I\r\n:CASE_0\r\n echo hello zero\r\n GOTO END_SWITCH\r\n:CASE_1\r\n echo hello one\r\n GOTO END_SWITCH\r\n:END_SWITCH\r\n"); + } +} diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/util/UtilsTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/util/UtilsTest.java index a76cb9bfd8..046ec694cd 100644 --- a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/util/UtilsTest.java +++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/util/UtilsTest.java @@ -28,7 +28,6 @@ import static org.testng.Assert.assertEquals; import java.io.UnsupportedEncodingException; import org.jclouds.scriptbuilder.domain.OsFamily; -import org.jclouds.scriptbuilder.util.Utils; import org.testng.annotations.Test; import com.google.common.collect.ImmutableList; @@ -77,16 +76,4 @@ public class UtilsTest { "set HOST=\r\nset PORT=\r\n"); } - public void testWriteSwitchUNIX() { - assertEquals(Utils.writeSwitch("i", ImmutableMap.of("0", "echo hello zero", "1", - "echo hello one"), OsFamily.UNIX), - "case $I in\n0)\n echo hello zero\n ;;\n1)\n echo hello one\n ;;\nesac\n"); - } - - public void testWriteSwitchWindows() { - assertEquals( - Utils.writeSwitch("i", ImmutableMap - .of("0", "echo hello zero", "1", "echo hello one"), OsFamily.WINDOWS), - "goto CASE%I\r\n:CASE_0\r\n echo hello zero\r\n GOTO END_SWITCH\r\n:CASE_1\r\n echo hello one\r\n GOTO END_SWITCH\r\n:END_SWITCH\r\n"); - } } diff --git a/scriptbuilder/src/test/resources/test_find_pid.bash b/scriptbuilder/src/test/resources/test_find_pid.bash new file mode 100644 index 0000000000..802a7f75a2 --- /dev/null +++ b/scriptbuilder/src/test/resources/test_find_pid.bash @@ -0,0 +1,28 @@ +#!/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 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|awk '{print $2}'` + [ -n "$_FOUND" ] && { + export FOUND_PID=$_FOUND + return 0 + } || { + return 1 + } +} +export PATH=/usr/ucb/bin:/bin:/usr/bin:/usr/sbin +findPid "$@" || exit 1 +echo $FOUND_PID +exit 0 diff --git a/scriptbuilder/src/test/resources/test_find_pid.cmd b/scriptbuilder/src/test/resources/test_find_pid.cmd new file mode 100644 index 0000000000..5b3d075815 --- /dev/null +++ b/scriptbuilder/src/test/resources/test_find_pid.cmd @@ -0,0 +1,30 @@ +@echo off +set PATH= +set JAVA_HOME= +set PATH= +GOTO FUNCTION_END +:abort + echo aborting: %EXCEPTION% + exit 1 +:findPid + set FOUND_PID= + SETLOCAL + set _pid= + set _expression=%1 + shift + set FIND_PROCESS=wmic process where (CommandLine like "%_expression%%%") get ProcessId + for /f "usebackq skip=1" %%a in (`cmd /c "%FIND_PROCESS% 2>NUL"`) do ( + if not defined _proc ( + set _pid=%%a + goto :done + ) + ) + :done + ENDLOCAL&SET FOUND_PID=%_pid% + exit /b 0 +:FUNCTION_END +set PATH=c:\windows\;C:\windows\system32 +call :findPid "%*" +if errorlevel 1 goto abort +echo %FOUND_PID% +exit 0 diff --git a/scriptbuilder/src/test/resources/test_script.bash b/scriptbuilder/src/test/resources/test_script.bash index 1455799a7a..04474d967a 100644 --- a/scriptbuilder/src/test/resources/test_script.bash +++ b/scriptbuilder/src/test/resources/test_script.bash @@ -5,7 +5,7 @@ shopt -s expand_aliases unset PATH JAVA_HOME LD_LIBRARY_PATH function abort { echo "aborting: $@" 1>&2 - set -u + exit 1 } function default { export JAVA_HOME="/apps/jdk1.6" @@ -20,5 +20,4 @@ stop) echo stopped ;; esac -set -u -return 0 +exit 0 diff --git a/scriptbuilder/src/test/resources/test_script.cmd b/scriptbuilder/src/test/resources/test_script.cmd index 53e02dfef6..843a6aebd9 100644 --- a/scriptbuilder/src/test/resources/test_script.cmd +++ b/scriptbuilder/src/test/resources/test_script.cmd @@ -5,7 +5,7 @@ set PATH= GOTO FUNCTION_END :abort echo aborting: %EXCEPTION% - exit /b 1 + exit 1 :default set JAVA_HOME=/apps/jdk1.6 exit /b 0 @@ -19,4 +19,4 @@ goto CASE%1 echo stopped GOTO END_SWITCH :END_SWITCH -exit /b 0 +exit 0