19 |
19 |
import java.util.Scanner;
|
20 |
20 |
import java.util.regex.Matcher;
|
21 |
21 |
import java.util.regex.Pattern;
|
|
22 |
import java.util.stream.Collectors;
|
22 |
23 |
|
23 |
24 |
import org.apache.commons.io.IOUtils;
|
24 |
25 |
import org.apache.commons.lang3.SystemUtils;
|
... | ... | |
53 |
54 |
|
54 |
55 |
private String sshHost = null;
|
55 |
56 |
|
56 |
|
public DrushExecuter() throws IOException, InterruptedException {
|
|
57 |
public DrushExecuter() throws IOException, InterruptedException, DrushExecutionFailure {
|
57 |
58 |
findDrushCommand();
|
58 |
59 |
}
|
59 |
60 |
|
60 |
61 |
/**
|
61 |
62 |
* The execution of this command via
|
62 |
|
* <code>DrushExecuter.execute({@linkplain DrushCommand#version})</code> results in
|
|
63 |
* <code>DrushExecuter.execute({@linkplain #version})</code> results in
|
63 |
64 |
* a {@code List<String>} return variable with the following elements:
|
64 |
65 |
*
|
65 |
66 |
* <ol>
|
... | ... | |
79 |
80 |
* Executes {@code drush vget --exact <variable-key>}
|
80 |
81 |
* <p>
|
81 |
82 |
* The execution of this command via
|
82 |
|
* <code>DrushExecuter.execute({@linkplain DrushCommand#variableSet})</code> results in
|
|
83 |
* <code>DrushExecuter.execute({@linkplain DrushExecuter#variableSet})</code> results in
|
83 |
84 |
* a {@code List<String>} return variable with the following elements:
|
84 |
85 |
*
|
85 |
86 |
* <ol>
|
... | ... | |
88 |
89 |
* <li>value</li>
|
89 |
90 |
* <ol>
|
90 |
91 |
*/
|
91 |
|
public static DrushCommand variableGet = new DrushCommand(Arrays.asList("vget", "--exact", "--format=json", "%s"));
|
|
92 |
public static DrushCommand variableGet = new DrushCommand(Arrays.asList("vget", "--exact", "--format=json", "%s"), true);
|
92 |
93 |
/**
|
93 |
94 |
* Executes {@code drush vset --exact <variable-key> <variable-value>}
|
94 |
95 |
* <p>
|
95 |
96 |
* The execution of this command via
|
96 |
|
* <code>DrushExecuter.execute({@linkplain DrushCommand#variableSet})</code> results in
|
97 |
|
* a {@code List<String>} return variable with the following elements:
|
98 |
|
*
|
99 |
|
* <ol>
|
100 |
|
* <li>value</li>
|
101 |
|
* <li>status</li>
|
102 |
|
* <ol>
|
|
97 |
* <code>DrushExecuter.execute({@linkplain DrushExecuter#variableSet})</code> will not return any values.
|
|
98 |
* The command will fail with an {@link DrushExecutionFailure} if setting the variable was not successful.
|
103 |
99 |
*/
|
104 |
|
public static DrushCommand variableSet = new DrushCommand(Arrays.asList("--yes", "vset", "%s", "%s"), null,
|
105 |
|
".*set\\sto*(.*)\\..*\\[(\\w+)\\]"
|
106 |
|
);
|
|
100 |
public static DrushCommand variableSet = new DrushCommand(Arrays.asList("--yes", "vset", "%s", "%s"), false);
|
107 |
101 |
|
108 |
102 |
/**
|
109 |
103 |
* @throws IOException
|
110 |
104 |
* if an I/O error occurs in the ProcessBuilder
|
111 |
105 |
* @throws InterruptedException
|
112 |
106 |
* if the Process was interrupted
|
|
107 |
* @throws DrushExecutionFailure
|
|
108 |
* if the drush command execution fails with an error code
|
113 |
109 |
*/
|
114 |
|
private void findDrushCommand() throws IOException, InterruptedException {
|
|
110 |
private void findDrushCommand() throws IOException, InterruptedException, DrushExecutionFailure {
|
115 |
111 |
|
116 |
112 |
if (SystemUtils.IS_OS_WINDOWS) {
|
117 |
113 |
throw new RuntimeException("not yet implmented for Windows");
|
118 |
114 |
}
|
119 |
|
|
120 |
|
List<Object> matches = execute(version);
|
121 |
|
assert !((String) matches.get(0)).isEmpty() : "No suitable drush command found in the system";
|
122 |
|
String majorVersion = (String) matches.get(0);
|
123 |
|
if (Integer.valueOf(majorVersion) < 8) {
|
|
115 |
if(DrushCommand.majorVersion == null) {
|
|
116 |
List<Object> matches = execute(version);
|
|
117 |
DrushCommand.majorVersion = (String) matches.get(0);
|
|
118 |
DrushCommand.minorVersion = (String) matches.get(1);
|
|
119 |
DrushCommand.patchLevel = (String) matches.get(2);
|
|
120 |
}
|
|
121 |
if(DrushCommand.majorVersion.isEmpty()) {
|
|
122 |
throw new RuntimeException("No suitable drush command found in the system");
|
|
123 |
}
|
|
124 |
if (Integer.valueOf(DrushCommand.majorVersion) < 8) {
|
124 |
125 |
throw new RuntimeException("drush version >= 8 required");
|
125 |
126 |
}
|
|
127 |
}
|
126 |
128 |
|
|
129 |
public String drushVersion() {
|
|
130 |
return DrushCommand.majorVersion + "." + DrushCommand.minorVersion + "." + DrushCommand.patchLevel;
|
127 |
131 |
}
|
128 |
132 |
|
129 |
133 |
/**
|
... | ... | |
131 |
135 |
* if an I/O error occurs in the ProcessBuilder
|
132 |
136 |
* @throws InterruptedException
|
133 |
137 |
* if the Process was interrupted
|
|
138 |
* @throws DrushExecutionFailure
|
|
139 |
* if the drush command execution fails with an error code
|
134 |
140 |
*/
|
135 |
|
public List<Object> execute(DrushCommand cmd, String... value) throws IOException, InterruptedException {
|
|
141 |
public List<Object> execute(DrushCommand cmd, String... value) throws IOException, InterruptedException, DrushExecutionFailure {
|
136 |
142 |
|
137 |
143 |
List<String> executableWithArgs = new ArrayList<>();
|
138 |
144 |
|
... | ... | |
186 |
192 |
logger.error(error);
|
187 |
193 |
}
|
188 |
194 |
} else {
|
189 |
|
throw new RuntimeException(IOUtils.toString(process.getErrorStream()));
|
|
195 |
throw new DrushExecutionFailure(
|
|
196 |
executableWithArgs,
|
|
197 |
IOUtils.toString(process.getInputStream()),
|
|
198 |
IOUtils.toString(process.getErrorStream())
|
|
199 |
);
|
190 |
200 |
}
|
191 |
201 |
return matches;
|
192 |
202 |
}
|
... | ... | |
200 |
210 |
if (out == null) {
|
201 |
211 |
break;
|
202 |
212 |
}
|
203 |
|
if (out != null) {
|
204 |
|
Matcher m = regex.matcher(out);
|
205 |
|
int patternMatchCount = 0;
|
206 |
|
while (m.find()) {
|
207 |
|
patternMatchCount++;
|
208 |
|
if (m.groupCount() > 0) {
|
209 |
|
for (int g = 1; g <= m.groupCount(); g++) {
|
210 |
|
matches.add(m.group(g));
|
211 |
|
logger.debug("match[" + patternMatchCount + "." + g + "]: " + m.group(g));
|
212 |
|
}
|
213 |
|
} else {
|
214 |
|
matches.add(m.group(0));
|
215 |
|
logger.debug("entire pattern match[" + patternMatchCount + ".0]: " + m.group(0));
|
|
213 |
Matcher m = regex.matcher(out);
|
|
214 |
int patternMatchCount = 0;
|
|
215 |
while (m.find()) {
|
|
216 |
patternMatchCount++;
|
|
217 |
if (m.groupCount() > 0) {
|
|
218 |
for (int g = 1; g <= m.groupCount(); g++) {
|
|
219 |
matches.add(m.group(g));
|
|
220 |
logger.debug("match[" + patternMatchCount + "." + g + "]: " + m.group(g));
|
216 |
221 |
}
|
|
222 |
} else {
|
|
223 |
matches.add(m.group(0));
|
|
224 |
logger.debug("entire pattern match[" + patternMatchCount + ".0]: " + m.group(0));
|
217 |
225 |
}
|
218 |
226 |
}
|
219 |
227 |
}
|
... | ... | |
227 |
235 |
}
|
228 |
236 |
|
229 |
237 |
/**
|
230 |
|
* @param matches
|
231 |
|
* @param stream
|
232 |
238 |
* @return depending on the drupal variable type different return types are possible:
|
233 |
239 |
* <ul>
|
234 |
240 |
* <li>Object</li>
|
... | ... | |
238 |
244 |
* <li>Double</li>
|
239 |
245 |
* <li>Integer</li>
|
240 |
246 |
* </ul>
|
241 |
|
*
|
242 |
|
* @throws IOException
|
243 |
247 |
*/
|
244 |
248 |
protected String readExecutionResponse(List<Object> matches, InputStream stream) throws IOException {
|
245 |
249 |
String out = IOUtils.toString(stream);
|
... | ... | |
265 |
269 |
|
266 |
270 |
public static class DrushCommand {
|
267 |
271 |
|
|
272 |
private static String majorVersion;
|
|
273 |
private static String minorVersion;
|
|
274 |
private static String patchLevel;
|
268 |
275 |
Pattern outRegex;
|
269 |
276 |
Pattern errRegex;
|
270 |
277 |
boolean jsonResult = false;
|
|
278 |
boolean failOnError = false;
|
271 |
279 |
List<String> args = new ArrayList<>();
|
272 |
280 |
|
|
281 |
/**
|
|
282 |
* For drush commands not supporting output formatting.
|
|
283 |
*
|
|
284 |
* @param args
|
|
285 |
* the command arguments
|
|
286 |
* @param outRegex
|
|
287 |
* Regular expression to parse the error stream, capture
|
|
288 |
* groups will be put into the <code>List</code> of strings
|
|
289 |
* returned by
|
|
290 |
* {@link DrushExecuter#execute(DrushCommand, String...)}
|
|
291 |
* @param errRegex
|
|
292 |
* Regular expression to parse the error stream, capture
|
|
293 |
* groups will be put into the <code>List</code> of strings
|
|
294 |
* returned by
|
|
295 |
* {@link DrushExecuter#execute(DrushCommand, String...)}
|
|
296 |
*/
|
273 |
297 |
public DrushCommand(List<String> args, String outRegex, String errRegex) {
|
274 |
298 |
this.args = args;
|
275 |
299 |
if (outRegex != null) {
|
... | ... | |
281 |
305 |
}
|
282 |
306 |
|
283 |
307 |
/**
|
284 |
|
* For drush commands suopporting the {@code --format=json} option.
|
|
308 |
* For drush commands which don't require return value parsing by regex or
|
|
309 |
* which support the {@code --format=json} option to return structured data.
|
|
310 |
*
|
285 |
311 |
* @param args
|
|
312 |
* the command arguments
|
286 |
313 |
*/
|
287 |
|
public DrushCommand(List<String> args) {
|
|
314 |
public DrushCommand(List<String> args, boolean jsonResult) {
|
288 |
315 |
this.args = args;
|
289 |
|
this.jsonResult = true;
|
|
316 |
this.jsonResult = jsonResult;
|
|
317 |
}
|
|
318 |
|
|
319 |
public String commandLineString() {
|
|
320 |
return args.stream().collect(Collectors.joining(" "));
|
290 |
321 |
}
|
|
322 |
|
291 |
323 |
}
|
292 |
324 |
|
293 |
325 |
/**
|
... | ... | |
295 |
327 |
* it too much dependent from the local environment. Once the
|
296 |
328 |
* <code>DrushExecuter</code> is being used in the selenium test suite will
|
297 |
329 |
* be tested implicitly anyway.
|
|
330 |
*
|
|
331 |
* @throws DrushExecutionFailure
|
|
332 |
* if the drush command execution fails with an error code
|
298 |
333 |
*/
|
299 |
|
public static void main(String[] args) throws URISyntaxException {
|
|
334 |
public static void main(String[] args) throws URISyntaxException, DrushExecutionFailure {
|
300 |
335 |
DrushExecuter.logger.setLevel(Level.DEBUG);
|
301 |
336 |
try {
|
302 |
337 |
DrushExecuter dex = new DrushExecuter();
|
... | ... | |
307 |
342 |
// dex.execute(help);
|
308 |
343 |
results = dex.execute(variableSet, "cdm_webservice_url",
|
309 |
344 |
"http://api.cybertaxonomy.org/cyprus/");
|
|
345 |
results = dex.execute(variableGet, "cdm_webservice_url");
|
310 |
346 |
if (!results.get(0).equals("http://api.cybertaxonomy.org/cyprus/")) {
|
311 |
347 |
throw new RuntimeException("unexpected result item 0: " + results.get(0));
|
312 |
348 |
}
|
313 |
|
if (!results.get(1).equals("success")) {
|
314 |
|
throw new RuntimeException("unexpected result item 1: " + results.get(0));
|
|
349 |
// test for command failure:
|
|
350 |
DrushExecutionFailure expectedFailure = null;
|
|
351 |
try {
|
|
352 |
dex.setDrupalRoot(new File("/home/andreas/workspaces/www/invalid-folder"));
|
|
353 |
results = dex.execute(variableGet, "cdm_webservice_url");
|
|
354 |
} catch(DrushExecutionFailure e) {
|
|
355 |
expectedFailure = e;
|
315 |
356 |
}
|
316 |
|
results = dex.execute(variableGet, "cdm_webservice_url");
|
317 |
|
if (!results.get(0).equals("http://api.cybertaxonomy.org/cyprus/")) {
|
318 |
|
throw new RuntimeException("unexpected result item 0: " + results.get(0));
|
|
357 |
if(expectedFailure == null) {
|
|
358 |
throw new AssertionError("DrushExecutionFailure expected due to command failure");
|
|
359 |
} else {
|
|
360 |
logger.debug("invalid command has failed as expected");
|
319 |
361 |
}
|
320 |
362 |
// testing remote execution via ssh
|
321 |
363 |
dex.sshHost = "edit-int";
|
ref #9188 more robust and efficient DrushExecuter