From bd9fcca7248c059cb09d56b7038a778afdd9451e Mon Sep 17 00:00:00 2001 From: Vincent Behar Date: Tue, 5 Jul 2011 15:25:44 +0200 Subject: [PATCH] add support for running ad-hoc commands --- .../java/org/rundeck/api/RundeckClient.java | 152 +++++++++- .../rundeck/api/parser/ExecutionParser.java | 10 +- .../rundeck/api/util/NodeFiltersBuilder.java | 274 ++++++++++++++++++ .../{ArgsUtil.java => ParametersUtil.java} | 55 +++- .../api/parser/ExecutionParserTest.java | 40 +++ .../org/rundeck/api/util/ArgsUtilTest.java | 30 -- .../rundeck/api/util/ParametersUtilTest.java | 43 +++ .../rundeck/api/parser/execution-adhoc.xml | 1 + .../api/parser/execution-minimalist.xml | 1 + 9 files changed, 556 insertions(+), 50 deletions(-) create mode 100644 src/main/java/org/rundeck/api/util/NodeFiltersBuilder.java rename src/main/java/org/rundeck/api/util/{ArgsUtil.java => ParametersUtil.java} (50%) delete mode 100644 src/test/java/org/rundeck/api/util/ArgsUtilTest.java create mode 100644 src/test/java/org/rundeck/api/util/ParametersUtilTest.java create mode 100644 src/test/resources/org/rundeck/api/parser/execution-adhoc.xml create mode 100644 src/test/resources/org/rundeck/api/parser/execution-minimalist.xml diff --git a/src/main/java/org/rundeck/api/RundeckClient.java b/src/main/java/org/rundeck/api/RundeckClient.java index 5024244..f4b6dde 100644 --- a/src/main/java/org/rundeck/api/RundeckClient.java +++ b/src/main/java/org/rundeck/api/RundeckClient.java @@ -17,9 +17,10 @@ import org.rundeck.api.parser.JobParser; import org.rundeck.api.parser.JobsParser; import org.rundeck.api.parser.ProjectParser; import org.rundeck.api.parser.ProjectsParser; -import org.rundeck.api.util.ArgsUtil; import org.rundeck.api.util.AssertUtil; +import org.rundeck.api.util.NodeFiltersBuilder; import org.rundeck.api.util.OptionsBuilder; +import org.rundeck.api.util.ParametersUtil; /** * Main entry point to talk to a RunDeck instance @@ -239,8 +240,9 @@ public class RundeckClient implements Serializable { RundeckApiLoginException, IllegalArgumentException { AssertUtil.notBlank(jobId, "jobId is mandatory to trigger a job !"); StringBuilder apiPath = new StringBuilder("/job/").append(jobId).append("/run"); - if (options != null) { - apiPath.append("?argString=").append(ArgsUtil.generateUrlEncodedArgString(options)); + String argString = ParametersUtil.generateArgString(options); + if (StringUtils.isNotBlank(argString)) { + apiPath.append("?argString=").append(ParametersUtil.urlEncode(argString)); } return new ApiCall(this).get(apiPath.toString(), new ExecutionParser("result/executions/execution")); } @@ -320,6 +322,150 @@ public class RundeckClient implements Serializable { return execution; } + /* + * Ad-hoc executions + */ + + /** + * Trigger the execution of an ad-hoc command, and return immediately (without waiting the end of the execution). + * The command will not be dispatched to nodes, but be executed on the RunDeck server. + * + * @param project name of the project - mandatory + * @param command to be executed - mandatory + * @return a {@link RundeckExecution} instance for the newly created (and running) execution - won't be null + * @throws RundeckApiException in case of error when calling the API (non-existent project with this name) + * @throws RundeckApiLoginException if the login failed + * @throws IllegalArgumentException if the project or command is blank (null, empty or whitespace) + * @see #triggerAdhocCommand(String, String, Properties) + */ + public RundeckExecution triggerAdhocCommand(String project, String command) throws RundeckApiException, + RundeckApiLoginException, IllegalArgumentException { + return triggerAdhocCommand(project, command, null); + } + + /** + * Trigger the execution of an ad-hoc command, and return immediately (without waiting the end of the execution). + * The command will be dispatched to nodes, accordingly to the nodeFilters parameter. + * + * @param project name of the project - mandatory + * @param command to be executed - mandatory + * @param nodeFilters for selecting nodes on which the command will be executed. See {@link NodeFiltersBuilder} + * @return a {@link RundeckExecution} instance for the newly created (and running) execution - won't be null + * @throws RundeckApiException in case of error when calling the API (non-existent project with this name) + * @throws RundeckApiLoginException if the login failed + * @throws IllegalArgumentException if the project or command is blank (null, empty or whitespace) + * @see #triggerAdhocCommand(String, String) + */ + public RundeckExecution triggerAdhocCommand(String project, String command, Properties nodeFilters) + throws RundeckApiException, RundeckApiLoginException, IllegalArgumentException { + AssertUtil.notBlank(project, "project is mandatory to trigger an ad-hoc command !"); + AssertUtil.notBlank(command, "command is mandatory to trigger an ad-hoc command !"); + StringBuilder apiPath = new StringBuilder("/run/command"); + apiPath.append("?project=").append(project); + apiPath.append("&exec=").append(ParametersUtil.urlEncode(command)); + String filters = ParametersUtil.generateNodeFiltersString(nodeFilters); + if (StringUtils.isNotBlank(filters)) { + apiPath.append("&").append(filters); + } + RundeckExecution execution = new ApiCall(this).get(apiPath.toString(), new ExecutionParser("result/execution")); + // the first call just returns the ID of the execution, so we need another call to get a "real" execution + return getExecution(execution.getId()); + } + + /** + * Run an ad-hoc command, and wait until its execution is finished (or aborted) to return. We will poll the RunDeck + * server at regular interval (every 5 seconds) to know if the execution is finished (or aborted) or is still + * running. The command will not be dispatched to nodes, but be executed on the RunDeck server. + * + * @param project name of the project - mandatory + * @param command to be executed - mandatory + * @return a {@link RundeckExecution} instance for the (finished/aborted) execution - won't be null + * @throws RundeckApiException in case of error when calling the API (non-existent project with this name) + * @throws RundeckApiLoginException if the login failed + * @throws IllegalArgumentException if the project or command is blank (null, empty or whitespace) + */ + public RundeckExecution runAdhocCommand(String project, String command) throws RundeckApiException, + RundeckApiLoginException, IllegalArgumentException { + return runAdhocCommand(project, command, null); + } + + /** + * Run an ad-hoc command, and wait until its execution is finished (or aborted) to return. We will poll the RunDeck + * server at regular interval (configured by the poolingInterval/poolingUnit couple) to know if the execution is + * finished (or aborted) or is still running. The command will not be dispatched to nodes, but be executed on the + * RunDeck server. + * + * @param project name of the project - mandatory + * @param command to be executed - mandatory + * @param poolingInterval for checking the status of the execution. Must be > 0. + * @param poolingUnit unit (seconds, milli-seconds, ...) of the interval. Default to seconds. + * @return a {@link RundeckExecution} instance for the (finished/aborted) execution - won't be null + * @throws RundeckApiException in case of error when calling the API (non-existent project with this name) + * @throws RundeckApiLoginException if the login failed + * @throws IllegalArgumentException if the project or command is blank (null, empty or whitespace) + */ + public RundeckExecution runAdhocCommand(String project, String command, long poolingInterval, TimeUnit poolingUnit) + throws RundeckApiException, RundeckApiLoginException, IllegalArgumentException { + return runAdhocCommand(project, command, null, poolingInterval, poolingUnit); + } + + /** + * Run an ad-hoc command, and wait until its execution is finished (or aborted) to return. We will poll the RunDeck + * server at regular interval (every 5 seconds) to know if the execution is finished (or aborted) or is still + * running. The command will be dispatched to nodes, accordingly to the nodeFilters parameter. + * + * @param project name of the project - mandatory + * @param command to be executed - mandatory + * @param nodeFilters for selecting nodes on which the command will be executed. See {@link NodeFiltersBuilder} + * @return a {@link RundeckExecution} instance for the (finished/aborted) execution - won't be null + * @throws RundeckApiException in case of error when calling the API (non-existent project with this name) + * @throws RundeckApiLoginException if the login failed + * @throws IllegalArgumentException if the project or command is blank (null, empty or whitespace) + */ + public RundeckExecution runAdhocCommand(String project, String command, Properties nodeFilters) + throws RundeckApiException, RundeckApiLoginException, IllegalArgumentException { + return runAdhocCommand(project, command, nodeFilters, 5, TimeUnit.SECONDS); + } + + /** + * Run an ad-hoc command, and wait until its execution is finished (or aborted) to return. We will poll the RunDeck + * server at regular interval (configured by the poolingInterval/poolingUnit couple) to know if the execution is + * finished (or aborted) or is still running. The command will be dispatched to nodes, accordingly to the + * nodeFilters parameter. + * + * @param project name of the project - mandatory + * @param command to be executed - mandatory + * @param nodeFilters for selecting nodes on which the command will be executed. See {@link NodeFiltersBuilder} + * @param poolingInterval for checking the status of the execution. Must be > 0. + * @param poolingUnit unit (seconds, milli-seconds, ...) of the interval. Default to seconds. + * @return a {@link RundeckExecution} instance for the (finished/aborted) execution - won't be null + * @throws RundeckApiException in case of error when calling the API (non-existent project with this name) + * @throws RundeckApiLoginException if the login failed + * @throws IllegalArgumentException if the project or command is blank (null, empty or whitespace) + */ + public RundeckExecution runAdhocCommand(String project, String command, Properties nodeFilters, + long poolingInterval, TimeUnit poolingUnit) throws RundeckApiException, RundeckApiLoginException, + IllegalArgumentException { + if (poolingInterval <= 0) { + poolingInterval = 5; + poolingUnit = TimeUnit.SECONDS; + } + if (poolingUnit == null) { + poolingUnit = TimeUnit.SECONDS; + } + + RundeckExecution execution = triggerAdhocCommand(project, command, nodeFilters); + while (ExecutionStatus.RUNNING.equals(execution.getStatus())) { + try { + Thread.sleep(poolingUnit.toMillis(poolingInterval)); + } catch (InterruptedException e) { + break; + } + execution = getExecution(execution.getId()); + } + return execution; + } + /* * Executions */ diff --git a/src/main/java/org/rundeck/api/parser/ExecutionParser.java b/src/main/java/org/rundeck/api/parser/ExecutionParser.java index 43ee79f..85affb4 100644 --- a/src/main/java/org/rundeck/api/parser/ExecutionParser.java +++ b/src/main/java/org/rundeck/api/parser/ExecutionParser.java @@ -36,11 +36,17 @@ public class ExecutionParser implements NodeParser { execution.setId(Long.valueOf(execNode.valueOf("@id"))); execution.setUrl(StringUtils.trimToNull(execNode.valueOf("@href"))); - execution.setStatus(ExecutionStatus.valueOf(StringUtils.upperCase(execNode.valueOf("@status")))); + try { + execution.setStatus(ExecutionStatus.valueOf(StringUtils.upperCase(execNode.valueOf("@status")))); + } catch (IllegalArgumentException e) { + } execution.setDescription(StringUtils.trimToNull(execNode.valueOf("description"))); execution.setStartedBy(StringUtils.trimToNull(execNode.valueOf("user"))); - execution.setStartedAt(new Date(Long.valueOf(execNode.valueOf("date-started/@unixtime")))); execution.setAbortedBy(StringUtils.trimToNull(execNode.valueOf("abortedby"))); + String startedAt = StringUtils.trimToNull(execNode.valueOf("date-started/@unixtime")); + if (startedAt != null) { + execution.setStartedAt(new Date(Long.valueOf(startedAt))); + } String endedAt = StringUtils.trimToNull(execNode.valueOf("date-ended/@unixtime")); if (endedAt != null) { execution.setEndedAt(new Date(Long.valueOf(endedAt))); diff --git a/src/main/java/org/rundeck/api/util/NodeFiltersBuilder.java b/src/main/java/org/rundeck/api/util/NodeFiltersBuilder.java new file mode 100644 index 0000000..d63c9ec --- /dev/null +++ b/src/main/java/org/rundeck/api/util/NodeFiltersBuilder.java @@ -0,0 +1,274 @@ +package org.rundeck.api.util; + +import java.util.Properties; +import org.apache.commons.lang.StringUtils; + +/** + * Builder for node filters + * + * @author Vincent Behar + */ +public class NodeFiltersBuilder { + + private final Properties filters; + + /** + * Build a new instance. At the end, use {@link #toProperties()}. + */ + public NodeFiltersBuilder() { + filters = new Properties(); + } + + /** + * Include nodes matching the given hostname + * + * @param hostname + * @return this, for method chaining + * @see #excludeHostname(String) + */ + public NodeFiltersBuilder hostname(String hostname) { + if (StringUtils.isNotBlank(hostname)) { + filters.put("hostname", hostname); + } + return this; + } + + /** + * Include nodes matching the given type + * + * @param type + * @return this, for method chaining + * @see #excludeType(String) + */ + public NodeFiltersBuilder type(String type) { + if (StringUtils.isNotBlank(type)) { + filters.put("type", type); + } + return this; + } + + /** + * Include nodes matching the given tags + * + * @param tags + * @return this, for method chaining + * @see #excludeTags(String) + */ + public NodeFiltersBuilder tags(String tags) { + if (StringUtils.isNotBlank(tags)) { + filters.put("tags", tags); + } + return this; + } + + /** + * Include nodes matching the given name + * + * @param name + * @return this, for method chaining + * @see #excludeName(String) + */ + public NodeFiltersBuilder name(String name) { + if (StringUtils.isNotBlank(name)) { + filters.put("name", name); + } + return this; + } + + /** + * Include nodes matching the given OS-name + * + * @param osName + * @return this, for method chaining + * @see #excludeOsName(String) + */ + public NodeFiltersBuilder osName(String osName) { + if (StringUtils.isNotBlank(osName)) { + filters.put("os-name", osName); + } + return this; + } + + /** + * Include nodes matching the given OS-family + * + * @param osFamily + * @return this, for method chaining + * @see #excludeOsFamily(String) + */ + public NodeFiltersBuilder osFamily(String osFamily) { + if (StringUtils.isNotBlank(osFamily)) { + filters.put("os-family", osFamily); + } + return this; + } + + /** + * Include nodes matching the given OS-arch + * + * @param osArch + * @return this, for method chaining + * @see #excludeOsArch(String) + */ + public NodeFiltersBuilder osArch(String osArch) { + if (StringUtils.isNotBlank(osArch)) { + filters.put("os-arch", osArch); + } + return this; + } + + /** + * Include nodes matching the given OS-version + * + * @param osVersion + * @return this, for method chaining + * @see #excludeOsVersion(String) + */ + public NodeFiltersBuilder osVersion(String osVersion) { + if (StringUtils.isNotBlank(osVersion)) { + filters.put("os-version", osVersion); + } + return this; + } + + /** + * Exclude nodes matching the given hostname + * + * @param hostname + * @return this, for method chaining + * @see #hostname(String) + * @see #excludePrecedence(boolean) + */ + public NodeFiltersBuilder excludeHostname(String hostname) { + if (StringUtils.isNotBlank(hostname)) { + filters.put("exclude-hostname", hostname); + } + return this; + } + + /** + * Exclude nodes matching the given type + * + * @param type + * @return this, for method chaining + * @see #type(String) + * @see #excludePrecedence(boolean) + */ + public NodeFiltersBuilder excludeType(String type) { + if (StringUtils.isNotBlank(type)) { + filters.put("exclude-type", type); + } + return this; + } + + /** + * Exclude nodes matching the given tags + * + * @param tags + * @return this, for method chaining + * @see #tags(String) + * @see #excludePrecedence(boolean) + */ + public NodeFiltersBuilder excludeTags(String tags) { + if (StringUtils.isNotBlank(tags)) { + filters.put("exclude-tags", tags); + } + return this; + } + + /** + * Exclude nodes matching the given name + * + * @param name + * @return this, for method chaining + * @see #name(String) + * @see #excludePrecedence(boolean) + */ + public NodeFiltersBuilder excludeName(String name) { + if (StringUtils.isNotBlank(name)) { + filters.put("exclude-name", name); + } + return this; + } + + /** + * Exclude nodes matching the given OS-name + * + * @param osName + * @return this, for method chaining + * @see #osName(String) + * @see #excludePrecedence(boolean) + */ + public NodeFiltersBuilder excludeOsName(String osName) { + if (StringUtils.isNotBlank(osName)) { + filters.put("exclude-os-name", osName); + } + return this; + } + + /** + * Exclude nodes matching the given OS-family + * + * @param osFamily + * @return this, for method chaining + * @see #osFamily(String) + * @see #excludePrecedence(boolean) + */ + public NodeFiltersBuilder excludeOsFamily(String osFamily) { + if (StringUtils.isNotBlank(osFamily)) { + filters.put("exclude-os-family", osFamily); + } + return this; + } + + /** + * Exclude nodes matching the given OS-arch + * + * @param osArch + * @return this, for method chaining + * @see #osArch(String) + * @see #excludePrecedence(boolean) + */ + public NodeFiltersBuilder excludeOsArch(String osArch) { + if (StringUtils.isNotBlank(osArch)) { + filters.put("exclude-os-arch", osArch); + } + return this; + } + + /** + * Exclude nodes matching the given OS-version + * + * @param osVersion + * @return this, for method chaining + * @see #osVersion(String) + * @see #excludePrecedence(boolean) + */ + public NodeFiltersBuilder excludeOsVersion(String osVersion) { + if (StringUtils.isNotBlank(osVersion)) { + filters.put("exclude-os-version", osVersion); + } + return this; + } + + /** + * Whether exclusion filters take precedence (default to yes). + * + * @param excludePrecedence + * @return this, for method chaining + */ + public NodeFiltersBuilder excludePrecedence(boolean excludePrecedence) { + filters.put("exclude-precedence", Boolean.toString(excludePrecedence)); + return this; + } + + /** + * @return a new {@link Properties} instance + */ + public Properties toProperties() { + Properties filters = new Properties(); + filters.putAll(this.filters); + return filters; + } + +} diff --git a/src/main/java/org/rundeck/api/util/ArgsUtil.java b/src/main/java/org/rundeck/api/util/ParametersUtil.java similarity index 50% rename from src/main/java/org/rundeck/api/util/ArgsUtil.java rename to src/main/java/org/rundeck/api/util/ParametersUtil.java index b2bb6e4..86f4484 100644 --- a/src/main/java/org/rundeck/api/util/ArgsUtil.java +++ b/src/main/java/org/rundeck/api/util/ParametersUtil.java @@ -2,33 +2,31 @@ package org.rundeck.api.util; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.List; import java.util.Properties; import java.util.Map.Entry; import org.apache.commons.lang.StringUtils; /** - * Utility class for RunDeck arguments + * Utility class for API parameters that should be passed in URLs. * * @author Vincent Behar */ -public class ArgsUtil { +public class ParametersUtil { /** - * Generates and url-encode a RunDeck "argString" representing the given options. Format of the argString is - * "-key1 value1 -key2 'value 2 with spaces'" + * URL-encode the given string * - * @param options to be converted - * @return an url-encoded string. null if options is null, empty if there are no valid options. - * @see #generateArgString(Properties) + * @param input string to be encoded + * @return an url-encoded string */ - public static String generateUrlEncodedArgString(Properties options) { - String argString = generateArgString(options); - if (StringUtils.isBlank(argString)) { - return argString; + public static String urlEncode(String input) { + if (StringUtils.isBlank(input)) { + return input; } - try { - return URLEncoder.encode(argString, "UTF-8"); + return URLEncoder.encode(input, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } @@ -36,11 +34,10 @@ public class ArgsUtil { /** * Generates a RunDeck "argString" representing the given options. Format of the argString is - * "-key1 value1 -key2 'value 2 with spaces'" + * "-key1 value1 -key2 'value 2 with spaces'". You might want to url-encode this string... * * @param options to be converted * @return a string. null if options is null, empty if there are no valid options. - * @see #generateUrlEncodedArgString(Properties) */ public static String generateArgString(Properties options) { if (options == null) { @@ -69,4 +66,32 @@ public class ArgsUtil { return argString.toString(); } + /** + * Generates an url-encoded string representing the given nodeFilters. Format of the string is + * "filter1=value1&filter2=value2". + * + * @param nodeFilters to be converted + * @return an url-encoded string. null if nodeFilters is null, empty if there are no valid filters. + */ + public static String generateNodeFiltersString(Properties nodeFilters) { + if (nodeFilters == null) { + return null; + } + + List filters = new ArrayList(); + for (Entry filter : nodeFilters.entrySet()) { + String key = String.valueOf(filter.getKey()); + String value = String.valueOf(filter.getValue()); + + if (StringUtils.isNotBlank(key) && StringUtils.isNotBlank(value)) { + try { + filters.add(URLEncoder.encode(key, "UTF-8") + "=" + URLEncoder.encode(value, "UTF-8")); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + } + return StringUtils.join(filters, "&"); + } + } diff --git a/src/test/java/org/rundeck/api/parser/ExecutionParserTest.java b/src/test/java/org/rundeck/api/parser/ExecutionParserTest.java index d81fe66..066e72b 100644 --- a/src/test/java/org/rundeck/api/parser/ExecutionParserTest.java +++ b/src/test/java/org/rundeck/api/parser/ExecutionParserTest.java @@ -64,4 +64,44 @@ public class ExecutionParserTest { Assert.assertEquals("list files", job.getDescription()); } + @Test + public void parseAdhocNode() throws Exception { + InputStream input = getClass().getResourceAsStream("execution-adhoc.xml"); + Document document = ParserHelper.loadDocument(input); + + RundeckExecution execution = new ExecutionParser("result/executions/execution").parseNode(document); + RundeckJob job = execution.getJob(); + + Assert.assertEquals(new Long(1), execution.getId()); + Assert.assertEquals("http://localhost:4440/execution/follow/1", execution.getUrl()); + Assert.assertEquals(ExecutionStatus.SUCCEEDED, execution.getStatus()); + Assert.assertEquals("admin", execution.getStartedBy()); + Assert.assertEquals(new Date(1309857539137L), execution.getStartedAt()); + Assert.assertEquals(new Date(1309857539606L), execution.getEndedAt()); + Assert.assertEquals(null, execution.getAbortedBy()); + Assert.assertEquals("w", execution.getDescription()); + + Assert.assertNull(job); + } + + @Test + public void parseMinimalistNode() throws Exception { + InputStream input = getClass().getResourceAsStream("execution-minimalist.xml"); + Document document = ParserHelper.loadDocument(input); + + RundeckExecution execution = new ExecutionParser("result/execution").parseNode(document); + RundeckJob job = execution.getJob(); + + Assert.assertEquals(new Long(1), execution.getId()); + Assert.assertNull(execution.getUrl()); + Assert.assertNull(execution.getStatus()); + Assert.assertNull(execution.getStartedBy()); + Assert.assertNull(execution.getStartedAt()); + Assert.assertNull(execution.getEndedAt()); + Assert.assertNull(execution.getAbortedBy()); + Assert.assertNull(execution.getDescription()); + + Assert.assertNull(job); + } + } diff --git a/src/test/java/org/rundeck/api/util/ArgsUtilTest.java b/src/test/java/org/rundeck/api/util/ArgsUtilTest.java deleted file mode 100644 index f89d788..0000000 --- a/src/test/java/org/rundeck/api/util/ArgsUtilTest.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.rundeck.api.util; - -import java.util.Properties; -import org.junit.Assert; -import org.junit.Test; - -/** - * Test the {@link ArgsUtil} - * - * @author Vincent Behar - */ -public class ArgsUtilTest { - - @Test - public void generateArgString() throws Exception { - Assert.assertNull(ArgsUtil.generateArgString(null)); - Assert.assertEquals("", ArgsUtil.generateArgString(new Properties())); - - Properties options = new Properties(); - options.put("key1", "value1"); - options.put("key2", "value 2 with spaces"); - String argString = ArgsUtil.generateArgString(options); - if (argString.startsWith("-key1")) { - Assert.assertEquals("-key1 value1 -key2 'value 2 with spaces'", argString); - } else { - Assert.assertEquals("-key2 'value 2 with spaces' -key1 value1", argString); - } - } - -} diff --git a/src/test/java/org/rundeck/api/util/ParametersUtilTest.java b/src/test/java/org/rundeck/api/util/ParametersUtilTest.java new file mode 100644 index 0000000..c1022c2 --- /dev/null +++ b/src/test/java/org/rundeck/api/util/ParametersUtilTest.java @@ -0,0 +1,43 @@ +package org.rundeck.api.util; + +import java.util.Properties; +import org.junit.Assert; +import org.junit.Test; + +/** + * Test the {@link ParametersUtil} + * + * @author Vincent Behar + */ +public class ParametersUtilTest { + + @Test + public void generateArgString() throws Exception { + Assert.assertNull(ParametersUtil.generateArgString(null)); + Assert.assertEquals("", ParametersUtil.generateArgString(new Properties())); + + Properties options = new Properties(); + options.put("key1", "value1"); + options.put("key2", "value 2 with spaces"); + String argString = ParametersUtil.generateArgString(options); + if (argString.startsWith("-key1")) { + Assert.assertEquals("-key1 value1 -key2 'value 2 with spaces'", argString); + } else { + Assert.assertEquals("-key2 'value 2 with spaces' -key1 value1", argString); + } + } + + @Test + public void generateNodeFiltersString() throws Exception { + Assert.assertNull(ParametersUtil.generateNodeFiltersString(null)); + Assert.assertEquals("", ParametersUtil.generateNodeFiltersString(new Properties())); + + Properties filters = new Properties(); + filters.put("tags", "appserv+front"); + filters.put("exclude-tags", "qa,dev"); + filters.put("os-family", "unix"); + String result = ParametersUtil.generateNodeFiltersString(filters); + Assert.assertEquals("os-family=unix&exclude-tags=qa%2Cdev&tags=appserv%2Bfront", result); + } + +} diff --git a/src/test/resources/org/rundeck/api/parser/execution-adhoc.xml b/src/test/resources/org/rundeck/api/parser/execution-adhoc.xml new file mode 100644 index 0000000..930f46b --- /dev/null +++ b/src/test/resources/org/rundeck/api/parser/execution-adhoc.xml @@ -0,0 +1 @@ +admin2011-07-05T09:18:59Z2011-07-05T09:18:59Zw \ No newline at end of file diff --git a/src/test/resources/org/rundeck/api/parser/execution-minimalist.xml b/src/test/resources/org/rundeck/api/parser/execution-minimalist.xml new file mode 100644 index 0000000..404da33 --- /dev/null +++ b/src/test/resources/org/rundeck/api/parser/execution-minimalist.xml @@ -0,0 +1 @@ +Immediate execution scheduled (1) \ No newline at end of file