Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@
<plugin>
<groupId>org.jvnet.jax-ws-commons</groupId>
<artifactId>jaxws-maven-plugin</artifactId>
<version>2.3</version>
<version>2.2</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
Expand Down
53 changes: 38 additions & 15 deletions winrm4j/src/main/java/io/cloudsoft/winrm4j/winrm/WinRmTool.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,30 @@ private WinRmTool(String address, String username, String password) {
this.password = password;
}

/**
* Execute a list of Windows Native commands as one command.
* The method translates the list of commands to a single String command with a <code>" & "</code> delimiter and a terminating one.
*/
public WinRmToolResponse executeScript(List<String> commands) {
return executeScript(joinScript(commands));
return executeCommand(joinCommands(commands));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea using & as the delimiter. Since it's the recommended approach to chaining commands we can now provide a well behaved executeCommand(List), wdyt?

}

/**
* @deprecated please use {@link #executeCommand(String)}
*/
public WinRmToolResponse executeScript(String commands) {
return executeCommand(commands);
}

/**
* Executes a Native Windows command.
* It is creating a new Shell on the destination host each time it is being called.
* @param command The command is limited to 8096 bytes.
* Maximum length of the command can be even smaller depending on the platform.
* https://support.microsoft.com/en-us/kb/830473
* @since 0.2
*/
public WinRmToolResponse executeCommand(String command) {
Builder builder = WinRmClient.builder(getEndpointUrl());
if (username != null && password != null) {
builder.credentials(username, password);
Expand All @@ -40,7 +59,7 @@ public WinRmToolResponse executeScript(String commands) {
StringWriter err = new StringWriter();

try {
int code = client.command(commands, out, err);
int code = client.command(command, out, err);
return new WinRmToolResponse(out.toString(), err.toString(), code);
} finally {
client.disconnect();
Expand All @@ -58,27 +77,31 @@ private String getEndpointUrl() {
}
}

/**
* Executes a Power Shell command.
* It is creating a new Shell on the destination host each time it is being called.
* @since 0.2
*/
public WinRmToolResponse executePs(String psCommand) {
return executeCommand(compilePs(psCommand));
}

/**
* Execute a list of Power Shell commands as one command.
* The method translates the list of commands to a single String command with a <code>"\r\n"</code> delimiter and a terminating one.
*/
public WinRmToolResponse executePs(List<String> commands) {
return executeScript(compilePs(joinPs(commands)));
return executeCommand(compilePs(joinPs(commands)));
}

private String compilePs(String psScript) {
public static String compilePs(String psScript) {
byte[] cmd = psScript.getBytes(Charset.forName("UTF-16LE"));
String arg = javax.xml.bind.DatatypeConverter.printBase64Binary(cmd);
return "powershell -encodedcommand " + arg;
}

/**
* Batch Script commands appear verbatim in the XML and JAXB will
* serialize "\r\n" as "&#xD;\n" in XML which is not recognized
* by the Windows service (doesn't unescape &#xD;). Since new lines
* in XML are represented as a single "\n" anyway it's fine to
* use it as a separator here.
*
* TODO cover the case where \r appears in the command?
*/
private String joinScript(List<String> commands) {
return join(commands, "\n");
private String joinCommands(List<String> commands) {
return join(commands, " & ");
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@ protected void assertExecFails(List<String> cmds) {

protected void assertExecPsFails(String cmd) {
Stopwatch stopwatch = Stopwatch.createStarted();
assertFailed(cmd, executePs(ImmutableList.of(cmd)), stopwatch);
assertFailed(cmd, executePs(cmd), stopwatch);
}

protected void assertExecPsFails(List<String> cmds) {
Stopwatch stopwatch = Stopwatch.createStarted();
assertFailed(cmds, executePs(cmds), stopwatch);
Expand All @@ -82,14 +82,19 @@ protected void assertExecSucceeds(String cmd, String stdout, String stderr) {
assertSucceeded(cmd, executeScript(ImmutableList.of(cmd)), stdout, stderr, stopwatch);
}

protected void assertExecCommand(String cmd, String stdout, String stderr, int exitCode) {
Stopwatch stopwatch = Stopwatch.createStarted();
assertWinRmToolResponse(cmd, executeCommand(cmd), stdout, stderr, stopwatch, exitCode);
}

protected void assertExecSucceeds(List<String> cmds, String stdout, String stderr) {
Stopwatch stopwatch = Stopwatch.createStarted();
assertSucceeded(cmds, executeScript(cmds), stdout, stderr, stopwatch);
}

protected void assertExecPsSucceeds(String cmd, String stdout, String stderr) {
Stopwatch stopwatch = Stopwatch.createStarted();
assertSucceeded(cmd, executePs(ImmutableList.of(cmd)), stdout, stderr, stopwatch);
assertSucceeded(cmd, executePs(cmd), stdout, stderr, stopwatch);
}

protected void assertExecPsSucceeds(List<String> cmds, String stdout, String stderr) {
Expand All @@ -111,6 +116,15 @@ protected WinRmToolResponse assertSucceeded(Object cmd, WinRmToolResponse respon
if (stderr != null) assertEquals(response.getStdErr().trim(), stderr, msg);
return response;
}

protected WinRmToolResponse assertWinRmToolResponse(Object cmd, WinRmToolResponse response, String stdout, String stderr, Stopwatch stopwatch, int exitCode) {
String msg = "statusCode="+response.getStatusCode()+"; out="+response.getStdOut()+"; err="+response.getStdErr();
LOG.info("Executed in "+makeTimeStringRounded(stopwatch)+" (asserting success): "+msg+"; cmd="+cmd);
assertEquals(response.getStatusCode(), exitCode, msg);
if (stdout != null) assertEquals(response.getStdOut().trim(), stdout, msg);
if (stderr != null) assertEquals(response.getStdErr().trim(), stderr, msg);
return response;
}

protected WinRmToolResponse executeScript(String script) {
return executeScript(ImmutableList.of(script));
Expand All @@ -124,25 +138,37 @@ protected WinRmToolResponse executeScript(final List<String> script) {
}});
}

protected WinRmToolResponse executePs(String script) {
return executePs(ImmutableList.of(script));
protected WinRmToolResponse executeCommand(final String command) {
return callWithRetries(new Callable<WinRmToolResponse>() {
@Override public WinRmToolResponse call() throws Exception {
WinRmTool winRmTool = WinRmTool.connect(VM_HOST + ":" + VM_PORT, VM_USER, VM_PASSWORD);
return winRmTool.executeCommand(command);
}});
}
protected WinRmToolResponse executePs(final List<String> script) {

protected WinRmToolResponse executePs(final String command) {
return callWithRetries(new Callable<WinRmToolResponse>() {
@Override public WinRmToolResponse call() throws Exception {
WinRmTool winRmTool = WinRmTool.connect(VM_HOST + ":" + VM_PORT, VM_USER, VM_PASSWORD);
return winRmTool.executePs(script);
return winRmTool.executePs(command);
}});
}

protected WinRmToolResponse executePs(final WinRmTool winRmTool, final List<String> script) {
protected WinRmToolResponse executePs(final List<String> script) {
return callWithRetries(new Callable<WinRmToolResponse>() {
@Override public WinRmToolResponse call() throws Exception {
WinRmTool winRmTool = WinRmTool.connect(VM_HOST + ":" + VM_PORT, VM_USER, VM_PASSWORD);
return winRmTool.executePs(script);
}});
}

protected WinRmToolResponse executePs(final WinRmTool winRmTool, final String command) {
return callWithRetries(new Callable<WinRmToolResponse>() {
@Override public WinRmToolResponse call() throws Exception {
return winRmTool.executePs(script);
return winRmTool.executePs(command);
}});
}

protected WinRmTool connect() throws Exception {
return callWithRetries(new Callable<WinRmTool>() {
@Override public WinRmTool call() throws Exception {
Expand Down Expand Up @@ -184,9 +210,9 @@ protected void copyTo(InputStream source, String destination) throws Exception {
} else {
chunk = Arrays.copyOf(inputData, bytesRead);
}
executePs(ImmutableList.of("If ((!(Test-Path " + destination + ")) -or ((Get-Item '" + destination + "').length -eq " +
executePs("If ((!(Test-Path " + destination + ")) -or ((Get-Item '" + destination + "').length -eq " +
expectedFileSize + ")) {Add-Content -Encoding Byte -path " + destination +
" -value ([System.Convert]::FromBase64String(\"" + new String(BaseEncoding.base64().encode(chunk)) + "\"))}"));
" -value ([System.Convert]::FromBase64String(\"" + new String(BaseEncoding.base64().encode(chunk)) + "\"))}");
expectedFileSize += bytesRead;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,39 @@ public void testExecFailingScript() throws Exception {
@Test(groups="Live")
public void testExecScriptExit0() throws Exception {
assertExecSucceeds("exit /B 0", "", "");
assertExecSucceeds(ImmutableList.of("exit /B 0"), "", "");
}

@Test(groups = "Live")
public void testChainCommands() {
assertExecCommand("echo Hi & echo World", "Hi \r\nWorld", "", 0);
}

/**
* Please bear in mind this behavior when executing commands.
*/
@Test(groups="Live")
public void testExecRNSplitExit() throws Exception {
assertExecCommand("echo Hi\r\necho World\r\n", "Hi", "", 0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you saying that the echo World has not been executed? I presume that is the case. Would we be better off having a failing test, and marking that as enabled=false (with a comment about why it fails)? Clearly this test is not demonstrating desired behaviour.

Other similar styles of test are marked as groups="WIP", with a comment to say why it fails and what the behaviour actually is.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aledsage I wouldn't say it should be a failing test.
This is the way it behaves and I think this is the way it should be.
Everybody advice to use & for chaining commands. \r\n is our fiction appeared initially in the Jython's version.

assertExecCommand("echo Hi\r\necho World", "Hi", "", 0);
assertExecCommand("echo Hi\necho World\n", "Hi", "", 0);
assertExecCommand("echo Hi\necho World", "Hi", "", 0);
}

/**
* Please bear in mind this behavior when executing commands.
*/
@Test(groups="Live")
public void testExecCommandExit() throws Exception {
assertExecCommand("exit /B 0", "", "", 0);
assertExecCommand("exit /B 1", "", "", 0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above: this test is not demonstrating desired behaviour.

assertExecCommand("exit 1", "", "", 0);
assertExecCommand("dslfkdsfjskl", "", null, 1);
}

@Test(groups = "Live")
public void testExecPowershellExit() throws Exception {
assertExecCommand(WinRmTool.compilePs("exit 123"), "", "", 123);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wht not create analogous assertExecPs, instead of making compilePs public?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 will change that in my branch of this.

assertExecCommand(WinRmTool.compilePs("Write-Host Hi World\r\nexit 123"), "Hi World", "", 123);
}

/*
Expand Down Expand Up @@ -239,7 +271,7 @@ public void testExecPsBatchFileExit3() throws Exception {
String scriptPath = "C:\\myscript-"+makeRandomString(8)+".bat";
copyTo(new ByteArrayInputStream(script.getBytes()), scriptPath);

WinRmToolResponse response = executePs(ImmutableList.of("& '"+scriptPath+"'"));
WinRmToolResponse response = executePs("& '"+scriptPath+"'");
String msg = "statusCode="+response.getStatusCode()+"; out="+response.getStdOut()+"; err="+response.getStdErr();
assertEquals(response.getStatusCode(), 3, msg);
}
Expand Down Expand Up @@ -347,7 +379,7 @@ public void testConfirmUseOfErrorActionPreferenceDoesNotCauseErr() throws Except
public void testExecPsExit1() throws Exception {
// Single commands
assertExecPsFails("exit 1");
assertExecPsFails(ImmutableList.of("exit 1"));
assertExecPsFails("exit 1");

// Multi-part
assertExecPsFails(ImmutableList.of(PS_ERR_ACTION_PREF_EQ_STOP, "Write-Host myline", "exit 1"));
Expand All @@ -372,11 +404,11 @@ public void testToolReuse() throws Exception {
WinRmTool winRmTool = connect();

Stopwatch stopwatch = Stopwatch.createStarted();
WinRmToolResponse response = executePs(winRmTool, ImmutableList.of("echo myline"));
WinRmToolResponse response = executePs(winRmTool, "echo myline");
assertSucceeded("echo myline", response, "myline", "", stopwatch);

stopwatch = Stopwatch.createStarted();
WinRmToolResponse response2 = executePs(winRmTool, ImmutableList.of("echo myline"));
WinRmToolResponse response2 = executePs(winRmTool, "echo myline");
assertSucceeded("echo myline", response2, "myline", "", stopwatch);
}

Expand All @@ -397,7 +429,7 @@ public Void call() throws Exception {
String line = "myline" + makeRandomString(8);
Stopwatch stopwatch = Stopwatch.createStarted();
try {
WinRmToolResponse response = executePs(winRmTool, ImmutableList.of("echo " + line));
WinRmToolResponse response = executePs(winRmTool, "echo " + line);
assertSucceeded("echo " + line, response, line, "", stopwatch);
LOG.info("Executed `echo "+line+"` in "+makeTimeStringRounded(stopwatch)+", in thread "+Thread.currentThread()+"; total "+counter.incrementAndGet()+" methods done");
return null;
Expand Down