Issue 126: ability to run scripts

git-svn-id: http://jclouds.googlecode.com/svn/trunk@2360 3d8758e0-26b5-11de-8745-db77d3ebf521
This commit is contained in:
adrian.f.cole 2009-12-02 06:53:58 +00:00
parent df529b2592
commit c98245a66c
10 changed files with 355 additions and 17 deletions

View File

@ -0,0 +1,184 @@
/**
*
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* 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 static org.jclouds.scriptbuilder.domain.Statements.interpret;
import java.util.Collections;
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.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
*
* @author Adrian Cole
*/
public class CreateRunScript implements Statement {
final String instanceName;
final List<String> exports;
final String pwd;
final String execLine;
public CreateRunScript(String instanceName, List<String> exports, String pwd, String execLine) {
this.instanceName = checkNotNull(instanceName, "instanceName");
this.exports = checkNotNull(exports, "exports");
this.pwd = checkNotNull(pwd, "pwd").replaceAll("[/\\\\]", "{fs}");
this.execLine = checkNotNull(execLine, "execLine");
}
public static class AddTitleToFile implements Statement {
final String title;
final String file;
public AddTitleToFile(String title, String file) {
this.title = checkNotNull(title, "title");
this.file = checkNotNull(file, "file");
}
public static final Map<OsFamily, String> 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");
@Override
public Iterable<String> functionDependecies(OsFamily family) {
return Collections.emptyList();
}
@Override
public String render(OsFamily family) {
return addSpaceToEnsureWeDontAccidentallyRedirectFd(Utils.replaceTokens(
OS_TO_TITLE_PATTERN.get(family), ImmutableMap.of("title", title, "file", file)));
}
}
public static class AddExportToFile implements Statement {
final String export;
final String value;
final String file;
public AddExportToFile(String export, String value, String file) {
this.export = checkNotNull(CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, export),
"export");
this.value = checkNotNull(value, "value");
this.file = checkNotNull(file, "file");
}
public static final Map<OsFamily, String> OS_TO_EXPORT_PATTERN = ImmutableMap.of(
OsFamily.UNIX, "echo \"export {export}='{value}'\">>{file}\n", OsFamily.WINDOWS,
"echo set {export}={value}>>{file}\r\n");
@Override
public Iterable<String> functionDependecies(OsFamily family) {
return Collections.emptyList();
}
@Override
public String render(OsFamily family) {
return addSpaceToEnsureWeDontAccidentallyRedirectFd(Utils.replaceTokens(
OS_TO_EXPORT_PATTERN.get(family), ImmutableMap.of("export", export, "value",
value, "file", file)));
}
}
public static String escapeVarTokens(String toEscape, OsFamily family) {
Map<String, String> inputToEscape = Maps.newHashMap();
for (ShellToken token : ImmutableList.of(ShellToken.VARL, ShellToken.VARR)) {
if (!token.to(family).equals("")) {
String tokenS = "{" + token.toString().toLowerCase() + "}";
inputToEscape.put(tokenS, "{escvar}" + tokenS);
}
}
for (Entry<String, String> entry : inputToEscape.entrySet()) {
toEscape = toEscape.replace(entry.getKey(), entry.getValue());
}
return toEscape;
}
@Override
public Iterable<String> functionDependecies(OsFamily family) {
return Collections.emptyList();
}
public static final Map<OsFamily, String> OS_TO_CHMOD_PATTERN = ImmutableMap.of(OsFamily.UNIX,
"chmod u+x {file}\n", OsFamily.WINDOWS, "");
@Override
public String render(OsFamily family) {
List<Statement> statements = Lists.newArrayList();
Map<String, String> tokenMap = ShellToken.tokenValueMap(family);
String runScript = Utils.replaceTokens(pwd + "{fs}" + instanceName + ".{sh}", tokenMap);
statements.add(interpret(String.format("{md} %s{lf}", pwd)));
statements.add(interpret(String.format("{rm} %s 2{closeFd}{lf}", runScript)));
for (String line : Splitter.on(ShellToken.LF.to(family)).split(
ShellToken.BEGIN_SCRIPT.to(family))) {
if (!line.equals(""))
statements.add(appendToFile(line, runScript, family));
}
statements.add(new AddTitleToFile(instanceName, runScript));
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(appendToFile("{cd} " + pwd, runScript, family));
statements.add(appendToFile(execLine, runScript, family));
for (String line : Splitter.on(ShellToken.LF.to(family)).split(
ShellToken.END_SCRIPT.to(family))) {
if (!line.equals(""))
statements.add(appendToFile(line, runScript, family));
}
statements.add(interpret(Utils.replaceTokens(OS_TO_CHMOD_PATTERN.get(family), ImmutableMap
.of("file", runScript))));
return new StatementList(statements).render(family);
}
private Statement appendToFile(String line, String runScript, OsFamily family) {
String quote = "";
if (!ShellToken.VQ.to(family).equals("")) {
quote = "'";
} else {
line = escapeVarTokens(line, family);
}
return interpret(addSpaceToEnsureWeDontAccidentallyRedirectFd(String.format(
"echo %s%s%s>>%s{lf}", quote, line, quote, runScript)));
}
static String addSpaceToEnsureWeDontAccidentallyRedirectFd(String line) {
return line.matches(".*[0-2]>>.*") ? line.replace(">>", " >>") : line;
}
}

View File

@ -39,7 +39,7 @@ import com.google.common.collect.Maps;
*/
public enum ShellToken {
FS, PS,
FS, RM, CD, TMP, UID, ROOT, CLOSE_FD, PS, MD, ESCVAR,
/**
* If variable values need to be quoted when they include spaces, this will contain quotation
@ -96,7 +96,6 @@ public enum ShellToken {
case UNIX:
return "function ";
}
case FNCR:
switch (family) {
case WINDOWS:
@ -111,6 +110,13 @@ public enum ShellToken {
case UNIX:
return " return 0\n}\n";
}
case ESCVAR:
switch (family) {
case WINDOWS:
return "%";
case UNIX:
return "\\";
}
case PS:
switch (family) {
case WINDOWS:
@ -118,6 +124,27 @@ public enum ShellToken {
case UNIX:
return ":";
}
case CLOSE_FD:
switch (family) {
case WINDOWS:
return ">NUL";
case UNIX:
return ">&-";
}
case RM:
switch (family) {
case WINDOWS:
return "del";
case UNIX:
return "rm";
}
case MD:
switch (family) {
case WINDOWS:
return "md";
case UNIX:
return "mkdir -p";
}
case VQ:
switch (family) {
case WINDOWS:
@ -174,6 +201,27 @@ public enum ShellToken {
case UNIX:
return "exit";
}
case ROOT:
switch (family) {
case WINDOWS:
return "c:\\";
case UNIX:
return "/";
}
case TMP:
switch (family) {
case WINDOWS:
return "%TEMP%";
case UNIX:
return "/tmp";
}
case UID:
switch (family) {
case WINDOWS:
return "%USERNAME%";
case UNIX:
return "$USER";
}
case LF:
switch (family) {
case WINDOWS:
@ -202,6 +250,13 @@ public enum ShellToken {
case UNIX:
return ".";
}
case CD:
switch (family) {
case WINDOWS:
return "cd /d";
case UNIX:
return "cd";
}
case REM:
switch (family) {
case WINDOWS:

View File

@ -23,6 +23,7 @@
*/
package org.jclouds.scriptbuilder.domain;
import java.util.List;
import java.util.Map;
/**
@ -45,6 +46,11 @@ public class Statements {
return new Call(function, args);
}
public static Statement createRunScript(String instanceName, List<String> exports, String pwd,
String execLine) {
return new CreateRunScript(instanceName, exports, pwd, execLine);
}
/**
* Stores the pid into the variable {@code FOUND_PID} if successful.
*

View File

@ -51,8 +51,8 @@ public class SwitchArg implements Statement {
OsFamily.UNIX, "esac\n", OsFamily.WINDOWS, ":END_SWITCH\r\n");
public static final Map<OsFamily, String> OS_TO_CASE_PATTERN = ImmutableMap.of(OsFamily.UNIX,
"{value})\n{action} ;;\n", OsFamily.WINDOWS,
":CASE_{value}\r\n{action} GOTO END_SWITCH\r\n");
"{value})\n{action};;\n", OsFamily.WINDOWS,
":CASE_{value}\r\n{action}GOTO END_SWITCH\r\n");
private final int arg;
@ -91,7 +91,8 @@ public class SwitchArg implements Statement {
for (Entry<String, Statement> entry : valueToActions.entrySet()) {
switchClause.append(Utils.replaceTokens(OS_TO_CASE_PATTERN.get(family), ImmutableMap.of(
"value", entry.getKey(), "action", entry.getValue().render(family).replaceAll(
"^", " "))));
"^", " ").replace(ShellToken.LF.to(family),
ShellToken.LF.to(family) + " "))));
}
switchClause.append(OS_TO_END_SWITCH_PATTERN.get(family));

View File

@ -0,0 +1,66 @@
/**
*
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* 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.jclouds.scriptbuilder.domain.Statements.createRunScript;
import static org.testng.Assert.assertEquals;
import java.io.IOException;
import org.testng.annotations.Test;
import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableList;
import com.google.common.io.CharStreams;
import com.google.common.io.Resources;
/**
* @author Adrian Cole
*/
@Test(groups = "unit", testName = "scriptbuilder.CreateRunScriptTest")
public class CreateRunScriptTest {
Statement statement = createRunScript("yahooprod", ImmutableList.<String> of("javaHome"),
"{tmp}{fs}{uid}{fs}scripttest",
"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.getResource("test_runrun."
+ ShellToken.SH.to(OsFamily.UNIX)), Charsets.UTF_8)));
}
public void testWINDOWS() throws IOException {
assertEquals(statement.render(OsFamily.WINDOWS),CharStreams.toString(Resources
.newReaderSupplier(Resources.getResource("test_runrun."
+ ShellToken.SH.to(OsFamily.WINDOWS)), Charsets.UTF_8)));
}
public void testRedirectGuard(){
assertEquals(CreateRunScript.addSpaceToEnsureWeDontAccidentallyRedirectFd("foo>>"),"foo>>");
assertEquals(CreateRunScript.addSpaceToEnsureWeDontAccidentallyRedirectFd("foo0>>"),"foo0 >>");
assertEquals(CreateRunScript.addSpaceToEnsureWeDontAccidentallyRedirectFd("foo1>>"),"foo1 >>");
assertEquals(CreateRunScript.addSpaceToEnsureWeDontAccidentallyRedirectFd("foo2>>"),"foo2 >>");
}
}

View File

@ -47,20 +47,24 @@ public class ShellTokenTest {
"#!/bin/bash\nset +u\nshopt -s xpg_echo\nshopt -s expand_aliases\n").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();
" return 0\n}\n").put("export", "export").put("rm", "rm").put("cd", "cd").put(
"tmp", "/tmp").put("uid", "$USER").put("root", "/").put("closeFd", ">&-").put("md",
"mkdir -p").put("escvar", "\\").build();
assertEquals(ShellToken.tokenValueMap(OsFamily.UNIX), expected);
}
public void testTokenValueMapWindows() {
Map<String, String> expected = new ImmutableMap.Builder<String, String>().put("fs", "\\")
.put("ps", ";").put("lf", "\r\n").put("sh", "cmd").put("source", "@call").put("rem",
"@rem").put("args", "%*").put("varl", "%").put("exit", "exit /b").put("varr",
"%").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 /b").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 /b 0\r\n").put("export", "set").build();
" exit /b 0\r\n").put("export", "set").put("rm", "del")
.put("cd", "cd /d").put("tmp", "%TEMP%").put("uid", "%USERNAME%")
.put("root", "c:\\").put("closeFd", ">NUL").put("md", "md").put("escvar", "%")
.build();
assertEquals(ShellToken.tokenValueMap(OsFamily.WINDOWS), expected);
}

View File

@ -0,0 +1,9 @@
md %TEMP%\%USERNAME%\scripttest
del %TEMP%\%USERNAME%\scripttest\yahooprod.cmd 2>NUL
echo @echo off>>%TEMP%\%USERNAME%\scripttest\yahooprod.cmd
echo title yahooprod>>%TEMP%\%USERNAME%\scripttest\yahooprod.cmd
echo set INSTANCE_NAME=yahooprod>>%TEMP%\%USERNAME%\scripttest\yahooprod.cmd
echo set JAVA_HOME=%JAVA_HOME%>>%TEMP%\%USERNAME%\scripttest\yahooprod.cmd
echo cd /d %TEMP%\%USERNAME%\scripttest>>%TEMP%\%USERNAME%\scripttest\yahooprod.cmd
echo echo %%JAVA_HOME%%\bin\java -DinstanceName=%%INSTANCE_NAME%% myServer.Main>>%TEMP%\%USERNAME%\scripttest\yahooprod.cmd
echo exit /b 0 >>%TEMP%\%USERNAME%\scripttest\yahooprod.cmd

View File

@ -0,0 +1,13 @@
mkdir -p /tmp/$USER/scripttest
rm /tmp/$USER/scripttest/yahooprod.sh 2>&-
echo '#!/bin/bash'>>/tmp/$USER/scripttest/yahooprod.sh
echo 'set +u'>>/tmp/$USER/scripttest/yahooprod.sh
echo 'shopt -s xpg_echo'>>/tmp/$USER/scripttest/yahooprod.sh
echo 'shopt -s expand_aliases'>>/tmp/$USER/scripttest/yahooprod.sh
echo "PROMPT_COMMAND='echo -ne \"\033]0;yahooprod\007\"'">>/tmp/$USER/scripttest/yahooprod.sh
echo "export INSTANCE_NAME='yahooprod'">>/tmp/$USER/scripttest/yahooprod.sh
echo "export JAVA_HOME='$JAVA_HOME'">>/tmp/$USER/scripttest/yahooprod.sh
echo 'cd /tmp/$USER/scripttest'>>/tmp/$USER/scripttest/yahooprod.sh
echo 'echo $JAVA_HOME/bin/java -DinstanceName=$INSTANCE_NAME myServer.Main'>>/tmp/$USER/scripttest/yahooprod.sh
echo 'exit 0'>>/tmp/$USER/scripttest/yahooprod.sh
chmod u+x /tmp/$USER/scripttest/yahooprod.sh

View File

@ -19,13 +19,13 @@ if not "%1" == "start" if not "%1" == "stop" if not "%1" == "status" (
goto CASE_%1
:CASE_start
call :default
if errorlevel 1 goto abort
echo start %RUNTIME%
if errorlevel 1 goto abort
echo start %RUNTIME%
GOTO END_SWITCH
:CASE_stop
call :default
if errorlevel 1 goto abort
echo stop %RUNTIME%
if errorlevel 1 goto abort
echo stop %RUNTIME%
GOTO END_SWITCH
:CASE_status
echo the following should be []: [%RUNTIME%]

View File

@ -15,11 +15,11 @@ export PATH=/usr/ucb/bin:/bin:/usr/bin:/usr/sbin
case $1 in
start)
default || exit 1
echo start $RUNTIME
echo start $RUNTIME
;;
stop)
default || exit 1
echo stop $RUNTIME
echo stop $RUNTIME
;;
status)
echo "the following should be []: [$RUNTIME]"