View Javadoc

1   /**
2    *
3    * Copyright (C) 2011 Cloud Conscious, LLC. <info@cloudconscious.com>
4    *
5    * ====================================================================
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   * ====================================================================
18   */
19  package org.jclouds.scriptbuilder.domain;
20  
21  import static com.google.common.base.Preconditions.checkNotNull;
22  import static org.jclouds.scriptbuilder.domain.Statements.interpret;
23  
24  import java.util.Collections;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Map.Entry;
28  import java.util.regex.Pattern;
29  
30  import org.jclouds.scriptbuilder.util.Utils;
31  
32  import com.google.common.base.CaseFormat;
33  import com.google.common.base.Splitter;
34  import com.google.common.collect.ImmutableList;
35  import com.google.common.collect.ImmutableMap;
36  import com.google.common.collect.Lists;
37  import com.google.common.collect.Maps;
38  
39  /**
40   * Creates a run script
41   * 
42   * @author Adrian Cole
43   */
44  public class CreateRunScript implements Statement {
45     public final static String MARKER = "END_OF_SCRIPT";
46     final String instanceName;
47     final Iterable<String> exports;
48     final String pwd;
49     final Iterable<Statement> statements;
50  
51     public CreateRunScript(String instanceName, Iterable<String> exports, String pwd, Iterable<Statement> statements) {
52        this.instanceName = checkNotNull(instanceName, "instanceName");
53        this.exports = checkNotNull(exports, "exports");
54        this.pwd = checkNotNull(pwd, "pwd").replaceAll("[/\\\\]", "{fs}");
55        this.statements = checkNotNull(statements, "statements");
56     }
57  
58     public static class AddTitleToFile implements Statement {
59        final String title;
60        final String file;
61  
62        public AddTitleToFile(String title, String file) {
63           this.title = checkNotNull(title, "title");
64           this.file = checkNotNull(file, "file");
65        }
66  
67        public static final Map<OsFamily, String> OS_TO_TITLE_PATTERN = ImmutableMap.of(OsFamily.UNIX,
68                 "echo \"PROMPT_COMMAND='echo -ne \\\"\\033]0;{title}\\007\\\"'\">>{file}\n", OsFamily.WINDOWS,
69                 "echo title {title}>>{file}\r\n");
70  
71        @Override
72        public Iterable<String> functionDependencies(OsFamily family) {
73           return Collections.emptyList();
74        }
75  
76        @Override
77        public String render(OsFamily family) {
78           return addSpaceToEnsureWeDontAccidentallyRedirectFd(Utils.replaceTokens(OS_TO_TITLE_PATTERN.get(family),
79                    ImmutableMap.of("title", title, "file", file)));
80        }
81     }
82  
83     public static class AddExportToFile implements Statement {
84        final String export;
85        final String value;
86        final String file;
87  
88        public AddExportToFile(String export, String value, String file) {
89           this.export = checkNotNull(CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, export), "export");
90           this.value = checkNotNull(value, "value");
91           this.file = checkNotNull(file, "file");
92        }
93  
94        public static final Map<OsFamily, String> OS_TO_EXPORT_PATTERN = ImmutableMap.of(OsFamily.UNIX,
95                 "echo \"export {export}='{value}'\">>{file}\n", OsFamily.WINDOWS,
96                 "echo set {export}={value}>>{file}\r\n");
97  
98        @Override
99        public Iterable<String> functionDependencies(OsFamily family) {
100          return Collections.emptyList();
101       }
102 
103       @Override
104       public String render(OsFamily family) {
105          return addSpaceToEnsureWeDontAccidentallyRedirectFd(Utils.replaceTokens(OS_TO_EXPORT_PATTERN.get(family),
106                   ImmutableMap.of("export", export, "value", value, "file", file)));
107       }
108    }
109 
110    public static String escapeVarTokens(String toEscape, OsFamily family) {
111       Map<String, String> inputToEscape = Maps.newHashMap();
112       for (ShellToken token : ImmutableList.of(ShellToken.VARL, ShellToken.VARR)) {
113          if (!token.to(family).equals("")) {
114             String tokenS = "{" + token.toString().toLowerCase() + "}";
115             inputToEscape.put(tokenS, "{escvar}" + tokenS);
116          }
117       }
118       for (Entry<String, String> entry : inputToEscape.entrySet()) {
119          toEscape = toEscape.replace(entry.getKey(), entry.getValue());
120       }
121       return toEscape;
122    }
123 
124    @Override
125    public Iterable<String> functionDependencies(OsFamily family) {
126       return Collections.emptyList();
127    }
128 
129    public static final Map<OsFamily, String> OS_TO_CHMOD_PATTERN = ImmutableMap.of(OsFamily.UNIX, "chmod u+x {file}\n",
130             OsFamily.WINDOWS, "");
131 
132    @Override
133    public String render(OsFamily family) {
134       List<Statement> statements = Lists.newArrayList();
135       Map<String, String> tokenMap = ShellToken.tokenValueMap(family);
136       String runScript = Utils.replaceTokens(pwd + "{fs}" + instanceName + ".{sh}", tokenMap);
137       statements.add(interpret(String.format("{md} %s{lf}", pwd)));
138       if (family == OsFamily.UNIX) {
139          StringBuilder builder = new StringBuilder();
140          builder.append("\n");
141          addUnixRunScriptHeader(family, runScript, builder);
142          builder.append("\n");
143          addUnixRunScript(runScript, builder);
144          builder.append("\n");
145          addUnixRunScriptFooter(family, runScript, builder);
146          builder.append("\n");
147          statements.add(interpret(builder.toString()));
148       } else {
149          statements.add(interpret(String.format("{rm} %s 2{closeFd}{lf}", runScript)));
150          for (String line : Splitter.on(ShellToken.LF.to(family)).split(ShellToken.BEGIN_SCRIPT.to(family))) {
151             if (!line.equals(""))
152                statements.add(appendToFile(line, runScript, family));
153          }
154          statements.add(new AddTitleToFile(instanceName, runScript));
155          statements.add(appendToFile(Utils.writeZeroPath(family).replace(ShellToken.LF.to(family), ""), runScript,
156                   family));
157          statements.add(new AddExportToFile("instanceName", instanceName, runScript));
158          for (String export : exports) {
159             statements
160                      .add(new AddExportToFile(export, Utils.replaceTokens("{varl}"
161                               + CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, export) + "{varr}", tokenMap),
162                               runScript));
163          }
164          statements.add(appendToFile("{cd} " + pwd, runScript, family));
165          statements.addAll(statements);
166          for (String line : Splitter.on(ShellToken.LF.to(family)).split(ShellToken.END_SCRIPT.to(family))) {
167             if (!line.equals(""))
168                statements.add(appendToFile(line, runScript, family));
169          }
170       }
171       statements
172                .add(interpret(Utils.replaceTokens(OS_TO_CHMOD_PATTERN.get(family), ImmutableMap.of("file", runScript))));
173       return new StatementList(statements).render(family);
174    }
175 
176    private void addUnixRunScriptFooter(OsFamily family, String runScript, StringBuilder builder) {
177       builder.append("# add runscript footer\n");
178       builder.append("cat >> ").append(runScript).append(" <<'").append(MARKER).append("'\n");
179       builder.append(ShellToken.END_SCRIPT.to(family));
180       builder.append(MARKER).append("\n");
181    }
182 
183    private void addUnixRunScript(String runScript, StringBuilder builder) {
184       builder.append("# add desired commands from the user\n");
185       builder.append("cat >> ").append(runScript).append(" <<'").append(MARKER).append("'\n");
186       builder.append("cd ").append(pwd).append("\n");
187       for (Statement statement : statements) {
188          builder.append(statement.render(OsFamily.UNIX)).append("\n");
189       }
190       builder.append(MARKER).append("\n");
191    }
192 
193    private void addUnixRunScriptHeader(OsFamily family, String runScript, StringBuilder builder) {
194       builder.append("# create runscript header\n");
195       builder.append("cat > ").append(runScript).append(" <<").append(MARKER).append("\n");
196       builder.append(ShellToken.BEGIN_SCRIPT.to(family));
197       builder.append("PROMPT_COMMAND='echo -ne \"\\033]0;").append(instanceName).append("\\007\"'\n");
198       builder.append(Utils.writeZeroPath(family));
199       builder.append("export INSTANCE_NAME='").append(instanceName).append("'\n");
200       for (String export : exports) {
201          String variableNameInUpper = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, export);
202          builder.append("export ").append(variableNameInUpper).append("='$").append(variableNameInUpper).append("'\n");
203       }
204       builder.append(MARKER).append("\n");
205    }
206 
207    private Statement appendToFile(String line, String runScript, OsFamily family) {
208       String quote = "";
209       if (!ShellToken.VQ.to(family).equals("")) {
210          quote = "'";
211       } else {
212          line = escapeVarTokens(line, family);
213       }
214       return interpret(addSpaceToEnsureWeDontAccidentallyRedirectFd(String.format("echo %s%s%s>>%s{lf}", quote, line,
215                quote, runScript)));
216    }
217 
218    public static final Pattern REDIRECT_FD_PATTERN = Pattern.compile(".*[0-2]>>.*");
219 
220    static String addSpaceToEnsureWeDontAccidentallyRedirectFd(String line) {
221       return REDIRECT_FD_PATTERN.matcher(line).matches() ? line.replace(">>", " >>") : line;
222    }
223 
224 }