View Javadoc

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