Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

salt.modules.cmdmod additions #253

Merged
merged 4 commits into from
Mar 1, 2019
Merged
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
38 changes: 25 additions & 13 deletions src/main/java/com/suse/salt/netapi/calls/modules/Cmd.java
Original file line number Diff line number Diff line change
@@ -1,44 +1,56 @@
package com.suse.salt.netapi.calls.modules;

import com.suse.salt.netapi.calls.LocalCall;
import com.suse.salt.netapi.results.CmdExecCodeAll;
import com.suse.salt.netapi.results.CmdResult;

import com.google.gson.reflect.TypeToken;

import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Optional;

import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;

/**
* salt.modules.cmdmod
*/
public class Cmd {

private Cmd() { }

public static LocalCall<CmdExecCodeAll> execCodeAll(String lang, String code) {
LinkedHashMap<String, Object> args = new LinkedHashMap<>();
public static LocalCall<CmdResult> execCodeAll(String lang, String code) {
return new LocalCall<>("cmd.exec_code_all", Optional.of(Arrays.asList(lang, code)),
Optional.of(args), new TypeToken<CmdExecCodeAll>(){});
Optional.of(emptyMap()), new TypeToken<CmdResult>(){});
}

public static LocalCall<String> run(String cmd) {
LinkedHashMap<String, Object> args = new LinkedHashMap<>();
args.put("cmd", cmd);
return new LocalCall<>("cmd.run", Optional.empty(), Optional.of(args),
return new LocalCall<>("cmd.run", Optional.empty(), Optional.of(singletonMap("cmd", cmd)),
new TypeToken<String>(){});
}

public static LocalCall<CmdResult> runAll(String cmd) {
return new LocalCall<>("cmd.run_all", Optional.empty(), Optional.of(singletonMap("cmd", cmd)),
new TypeToken<CmdResult>(){});
}

public static LocalCall<String> execCode(String lang, String code) {
LinkedHashMap<String, Object> args = new LinkedHashMap<>();
return new LocalCall<>("cmd.exec_code", Optional.of(Arrays.asList(lang, code)),
Optional.of(args), new TypeToken<String>(){});
Optional.of(emptyMap()), new TypeToken<String>(){});
}

public static LocalCall<Boolean> hasExec(String cmd) {
LinkedHashMap<String, Object> args = new LinkedHashMap<>();
args.put("cmd", cmd);
return new LocalCall<>("cmd.has_exec", Optional.empty(), Optional.of(args),
return new LocalCall<>("cmd.has_exec", Optional.empty(), Optional.of(singletonMap("cmd", cmd)),
new TypeToken<Boolean>(){});
}

public static LocalCall<CmdResult> script(String source) {
return new LocalCall<>("cmd.script", Optional.of(singletonList(source)), Optional.of(emptyMap()),
new TypeToken<CmdResult>(){});
}

public static LocalCall<Integer> scriptRetcode(String source) {
return new LocalCall<>("cmd.script_retcode", Optional.of(singletonList(source)), Optional.of(emptyMap()),
new TypeToken<Integer>(){});
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package com.suse.salt.netapi.results;

/**
* Result structure as returned by cmd.exec_code_all to be parsed from event data.
* Result structure as returned by cmd.exec_code_all, cmd.run_all, cmd.script to be parsed from event data.
*/
public class CmdExecCodeAll {
public class CmdResult {

private long pid;
private int retcode;
Expand All @@ -25,4 +25,12 @@ public String getStderr() {
public String getStdout() {
return stdout;
}

@Override
public String toString() {
return "CmdResult(pid=" + pid +
", retcode=" + retcode +
", stderr=\"" + stderr +
"\", stdout=\"" + stdout + "\")";
}
}
176 changes: 150 additions & 26 deletions src/test/java/com/suse/salt/netapi/calls/modules/CmdTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import com.suse.salt.netapi.datatypes.AuthMethod;
import com.suse.salt.netapi.datatypes.Token;
import com.suse.salt.netapi.datatypes.target.MinionList;
import com.suse.salt.netapi.results.CmdExecCodeAll;
import com.suse.salt.netapi.results.CmdResult;
import com.suse.salt.netapi.results.Result;
import com.suse.salt.netapi.utils.ClientUtils;
import com.suse.salt.netapi.utils.TestUtils;
Expand All @@ -33,7 +33,13 @@ public class CmdTest {
private static final int MOCK_HTTP_PORT = 8888;

static final String JSON_RUN_RESPONSE = ClientUtils.streamToString(
SaltUtilTest.class.getResourceAsStream("/modules/cmd/uptime.json"));
SaltUtilTest.class.getResourceAsStream("/modules/cmd/run.json"));

static final String JSON_RUN_ALL_RESPONSE_SUCCESS = ClientUtils.streamToString(
SaltUtilTest.class.getResourceAsStream("/modules/cmd/run_all.json"));

static final String JSON_RUN_ALL_RESPONSE_ERROR = ClientUtils.streamToString(
SaltUtilTest.class.getResourceAsStream("/modules/cmd/run_all_error.json"));

static final String JSON_HAS_EXEC_RESPONSE = ClientUtils.streamToString(
SaltUtilTest.class.getResourceAsStream("/modules/cmd/has_exec.json"));
Expand All @@ -44,6 +50,18 @@ public class CmdTest {
static final String JSON_EXEC_CODE_ALL_RESPONSE = ClientUtils.streamToString(
SaltUtilTest.class.getResourceAsStream("/modules/cmd/exec_code_all.json"));

static final String JSON_SCRIPT_RESPONSE_SUCCESS = ClientUtils.streamToString(
SaltUtilTest.class.getResourceAsStream("/modules/cmd/script.json"));

static final String JSON_SCRIPT_RESPONSE_ERROR = ClientUtils.streamToString(
SaltUtilTest.class.getResourceAsStream("/modules/cmd/script_error.json"));

static final String JSON_SCRIPT_RETCODE_RESPONSE_SUCCESS = ClientUtils.streamToString(
SaltUtilTest.class.getResourceAsStream("/modules/cmd/script_retcode.json"));

static final String JSON_SCRIPT_RETCODE_RESPONSE_ERROR = ClientUtils.streamToString(
SaltUtilTest.class.getResourceAsStream("/modules/cmd/script_retcode_error.json"));

static final AuthMethod AUTH = new AuthMethod(new Token());

private SaltClient client;
Expand All @@ -58,17 +76,13 @@ public void init() {
}

@Test
public void testCmdUptime() {
public void testCmdRun() {
// First we get the call to use in the tests
LocalCall<String> call = Cmd.run("uptime");
assertEquals("cmd.run", call.getPayload().get("fun"));

// Test with an successful response
stubFor(any(urlMatching("/"))
.willReturn(aResponse()
.withStatus(HttpURLConnection.HTTP_OK)
.withHeader("Content-Type", "application/json")
.withBody(JSON_RUN_RESPONSE)));
mockOkResponseWith(JSON_RUN_RESPONSE);

Map<String, Result<String>> response =
call.callSync(client, new MinionList("minion"), AUTH)
Expand All @@ -81,18 +95,54 @@ public void testCmdUptime() {
output);
}

@Test
public void testCmdRunAllSuccess() {
LocalCall<CmdResult> call = Cmd.runAll("uptime");
assertEquals("cmd.run_all", call.getPayload().get("fun"));

mockOkResponseWith(JSON_RUN_ALL_RESPONSE_SUCCESS);

Map<String, Result<CmdResult>> response =
call.callSync(client, new MinionList("minion"), AUTH)
.toCompletableFuture().join();

assertNotNull(response.get("minion"));

CmdResult result = response.get("minion").result().get();
assertEquals(28870, result.getPid());
assertEquals(0, result.getRetcode());
assertEquals("", result.getStderr());
assertEquals(" 15:13:42 up 2:55, 1 user, load average: 0.01, 0.00, 0.00", result.getStdout());
}

@Test
public void testCmdRunAllError() {
LocalCall<CmdResult> call = Cmd.runAll("misspelled_command");
assertEquals("cmd.run_all", call.getPayload().get("fun"));

mockOkResponseWith(JSON_RUN_ALL_RESPONSE_ERROR);

Map<String, Result<CmdResult>> response =
call.callSync(client, new MinionList("minion"), AUTH)
.toCompletableFuture().join();

assertNotNull(response.get("minion"));

CmdResult result = response.get("minion").result().get();
assertEquals(29333, result.getPid());
assertEquals(127, result.getRetcode());
assertEquals("/bin/sh: 1: misspelled_command: not found", result.getStderr());
assertEquals("", result.getStdout());
}

@Test
public void testCmdHasExec() {
// First we get the call to use in the tests
LocalCall<Boolean> call = Cmd.hasExec("uptime");
assertEquals("cmd.has_exec", call.getPayload().get("fun"));

// Test with an successful response
stubFor(any(urlMatching("/"))
.willReturn(aResponse()
.withStatus(HttpURLConnection.HTTP_OK)
.withHeader("Content-Type", "application/json")
.withBody(JSON_HAS_EXEC_RESPONSE)));
mockOkResponseWith(JSON_HAS_EXEC_RESPONSE);

Map<String, Result<Boolean>> response =
call.callSync(client, new MinionList("minion"), AUTH)
Expand All @@ -112,11 +162,7 @@ public void testCmdExecCode() {
assertEquals("cmd.exec_code", call.getPayload().get("fun"));

// Test with an successful response
stubFor(any(urlMatching("/"))
.willReturn(aResponse()
.withStatus(HttpURLConnection.HTTP_OK)
.withHeader("Content-Type", "application/json")
.withBody(JSON_EXEC_CODE_RESPONSE)));
mockOkResponseWith(JSON_EXEC_CODE_RESPONSE);

Map<String, Result<String>> response =
call.callSync(client, new MinionList("minion"), AUTH)
Expand All @@ -132,27 +178,105 @@ public void testCmdExecCode() {
@Test
public void testCmdExecCodeAll() {
// First we get the call to use in the tests
LocalCall<CmdExecCodeAll> call =
LocalCall<CmdResult> call =
Cmd.execCodeAll("python", "import sys; print sys.version");
assertEquals("cmd.exec_code_all", call.getPayload().get("fun"));

// Test with an successful response
stubFor(any(urlMatching("/"))
.willReturn(aResponse()
.withStatus(HttpURLConnection.HTTP_OK)
.withHeader("Content-Type", "application/json")
.withBody(JSON_EXEC_CODE_ALL_RESPONSE)));
mockOkResponseWith(JSON_EXEC_CODE_ALL_RESPONSE);

Map<String, Result<CmdExecCodeAll>> response =
Map<String, Result<CmdResult>> response =
call.callSync(client, new MinionList("minion"), AUTH)
.toCompletableFuture().join();

assertNotNull(response.get("minion"));
CmdExecCodeAll result = response.get("minion").result().get();
CmdResult result = response.get("minion").result().get();
assertEquals(27299, result.getPid());
assertEquals(0, result.getRetcode());
assertEquals("", result.getStderr());
assertEquals("2.6.6 (r266:84292, Jul 23 2015, 15:22:56) "
+ "[GCC 4.4.7 20120313 (Red Hat 4.4.7-11)]", result.getStdout());
}

@Test
public void testScriptSuccess() {
LocalCall<CmdResult> call = Cmd.script("salt://foo.sh");
assertEquals("cmd.script", call.getPayload().get("fun"));

mockOkResponseWith(JSON_SCRIPT_RESPONSE_SUCCESS);

Map<String, Result<CmdResult>> response =
call.callSync(client, new MinionList("minion"), AUTH)
.toCompletableFuture().join();

assertNotNull(response.get("minion"));

CmdResult result = response.get("minion").result().get();
assertEquals(29059, result.getPid());
assertEquals(0, result.getRetcode());
assertEquals("", result.getStderr());
assertEquals("Test echo script", result.getStdout());
}

@Test
public void testScriptError() {
LocalCall<CmdResult> call = Cmd.script("salt://err.sh");
assertEquals("cmd.script", call.getPayload().get("fun"));

mockOkResponseWith(JSON_SCRIPT_RESPONSE_ERROR);

Map<String, Result<CmdResult>> response =
call.callSync(client, new MinionList("minion"), AUTH)
.toCompletableFuture().join();

assertNotNull(response.get("minion"));

CmdResult result = response.get("minion").result().get();
assertEquals(29200, result.getPid());
assertEquals(127, result.getRetcode());
assertEquals("/tmp/__salt.tmp.9B1I7L.sh: line 3: misspelled_command: command not found", result.getStderr());
assertEquals("", result.getStdout());
}

@Test
public void testCmdScriptRetcodeSuccess() {
LocalCall<Integer> call = Cmd.scriptRetcode("salt://foo.sh");
assertEquals("cmd.script_retcode", call.getPayload().get("fun"));

mockOkResponseWith(JSON_SCRIPT_RETCODE_RESPONSE_SUCCESS);

Map<String, Result<Integer>> response =
call.callSync(client, new MinionList("minion"), AUTH)
.toCompletableFuture().join();

assertNotNull(response.get("minion"));

Integer output = response.get("minion").result().get();
assertEquals(Integer.valueOf(0), output);
}

@Test
public void testCmdScriptRetcodeError() {
LocalCall<Integer> call = Cmd.scriptRetcode("salt://err.sh");
assertEquals("cmd.script_retcode", call.getPayload().get("fun"));

mockOkResponseWith(JSON_SCRIPT_RETCODE_RESPONSE_ERROR);

Map<String, Result<Integer>> response =
call.callSync(client, new MinionList("minion"), AUTH)
.toCompletableFuture().join();

assertNotNull(response.get("minion"));

Integer output = response.get("minion").result().get();
assertEquals(Integer.valueOf(127), output);
}

private static void mockOkResponseWith(String json) {
stubFor(any(urlMatching("/"))
.willReturn(aResponse()
.withStatus(HttpURLConnection.HTTP_OK)
.withHeader("Content-Type", "application/json")
.withBody(json)));
}
}
12 changes: 12 additions & 0 deletions src/test/resources/modules/cmd/run_all.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"return": [
{
"minion": {
"pid": 28870,
"retcode": 0,
"stderr": "",
"stdout": " 15:13:42 up 2:55, 1 user, load average: 0.01, 0.00, 0.00"
}
}
]
}
12 changes: 12 additions & 0 deletions src/test/resources/modules/cmd/run_all_error.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"return": [
{
"minion": {
"pid": 29333,
"retcode": 127,
"stderr": "/bin/sh: 1: misspelled_command: not found",
"stdout": ""
}
}
]
}
12 changes: 12 additions & 0 deletions src/test/resources/modules/cmd/script.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"return": [
{
"minion": {
"pid": 29059,
"retcode": 0,
"stderr": "",
"stdout": "Test echo script"
}
}
]
}
Loading