diff --git a/core/src/main/java/hudson/cli/CreateNodeCommand.java b/core/src/main/java/hudson/cli/CreateNodeCommand.java new file mode 100644 index 0000000000000000000000000000000000000000..bd94932955fe219b625971f0cc52ad52d39580d5 --- /dev/null +++ b/core/src/main/java/hudson/cli/CreateNodeCommand.java @@ -0,0 +1,83 @@ +/* + * The MIT License + * + * Copyright 2013 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package hudson.cli; + +import hudson.Extension; +import hudson.model.Node; +import hudson.model.Slave; +import hudson.model.User; +import jenkins.model.Jenkins; + +import org.kohsuke.args4j.Argument; +import org.kohsuke.args4j.CmdLineException; + +/** + * @author ogondza + * @since XXX + */ +@Extension +public class CreateNodeCommand extends CLICommand { + + @Argument(metaVar="NODE", usage="Name of the node") + public String nodeName; + + @Override + public String getShortDescription() { + + return Messages.CreateNodeCommand_ShortDescription(); + } + + @Override + protected int run() throws Exception { + + final Jenkins jenkins = Jenkins.getInstance(); + jenkins.checkPermission(Jenkins.ADMINISTER); + + final Node newNode = (Node) Jenkins.XSTREAM2.fromXML(stdin); + + if (nodeName != null) { + + // Using deprecated method but it's contract is preserved + newNode.setNodeName(nodeName); + } + + if(newNode instanceof Slave) { //change userId too + User user = User.current(); + ((Slave) newNode).setUserId(user==null ? "anonymous" : user.getId()); + } + + if (jenkins.getNode(newNode.getNodeName()) != null) { + + throw new CmdLineException( + null, "Node '" + newNode.getNodeName() + "' already exists" + ); + } + + jenkins.addNode(newNode); + + return 0; + } + +} diff --git a/core/src/main/java/hudson/cli/GetNodeCommand.java b/core/src/main/java/hudson/cli/GetNodeCommand.java new file mode 100644 index 0000000000000000000000000000000000000000..e2bf3410dabea911e69cc6a69a2122dba78d1521 --- /dev/null +++ b/core/src/main/java/hudson/cli/GetNodeCommand.java @@ -0,0 +1,60 @@ +/* + * The MIT License + * + * Copyright (c) 2013, Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package hudson.cli; + +import hudson.Extension; +import hudson.model.Node; + +import java.io.IOException; + +import jenkins.model.Jenkins; + +import org.kohsuke.args4j.Argument; + +/** + * @author ogondza + * @since XXX + */ +@Extension +public class GetNodeCommand extends CLICommand { + + @Argument(metaVar="NODE", usage="Name of the node", required=true) + public Node node; + + @Override + public String getShortDescription() { + + return Messages.GetNodeCommand_ShortDescription(); + } + + @Override + protected int run() throws IOException { + + node.checkPermission(Jenkins.ADMINISTER); + + Jenkins.XSTREAM2.toXMLUTF8(node, stdout); + + return 0; + } +} diff --git a/core/src/main/java/hudson/cli/UpdateNodeCommand.java b/core/src/main/java/hudson/cli/UpdateNodeCommand.java new file mode 100644 index 0000000000000000000000000000000000000000..5af2b54eb8c9aa9a5b9a70a1cd982c6a74272f77 --- /dev/null +++ b/core/src/main/java/hudson/cli/UpdateNodeCommand.java @@ -0,0 +1,59 @@ +/* + * The MIT License + * + * Copyright 2013 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package hudson.cli; + +import java.io.IOException; + +import javax.servlet.ServletException; + +import hudson.Extension; +import hudson.model.Node; + +import org.kohsuke.args4j.Argument; + +/** + * @author ogondza + * @since XXX + */ +@Extension +public class UpdateNodeCommand extends CLICommand { + + @Argument(metaVar="NODE", usage="Name of the node", required=true) + public Node node; + + @Override + public String getShortDescription() { + + return Messages.UpdateNodeCommand_ShortDescription(); + } + + @Override + protected int run() throws IOException, ServletException { + + node.toComputer().updateByXml(stdin); + + return 0; + } +} diff --git a/core/src/main/java/hudson/cli/handlers/NodeOptionHandler.java b/core/src/main/java/hudson/cli/handlers/NodeOptionHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..24e7a5aed62a41526ca8c5b58cd9b13c14eeda36 --- /dev/null +++ b/core/src/main/java/hudson/cli/handlers/NodeOptionHandler.java @@ -0,0 +1,68 @@ +/* + * The MIT License + * + * Copyright (c) 2013, Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package hudson.cli.handlers; + +import hudson.model.Node; +import jenkins.model.Jenkins; + +import org.kohsuke.MetaInfServices; +import org.kohsuke.args4j.CmdLineException; +import org.kohsuke.args4j.CmdLineParser; +import org.kohsuke.args4j.OptionDef; +import org.kohsuke.args4j.spi.OptionHandler; +import org.kohsuke.args4j.spi.Parameters; +import org.kohsuke.args4j.spi.Setter; + +/** + * Refers to {@link Node} by its name. + * + * @author ogondza + * @since XXX + */ +@MetaInfServices +public class NodeOptionHandler extends OptionHandler { + + public NodeOptionHandler(CmdLineParser parser, OptionDef option, Setter setter) { + + super(parser, option, setter); + } + + @Override + public int parseArguments(Parameters params) throws CmdLineException { + + String nodeName = params.getParameter(0); + + final Node node = Jenkins.getInstance().getNode(nodeName); + if (node == null) throw new CmdLineException(owner, "No such node '" + nodeName + "'"); + + setter.addValue(node); + return 1; + } + + @Override + public String getDefaultMetaVariable() { + + return "NODE"; + } +} diff --git a/core/src/main/java/hudson/model/Computer.java b/core/src/main/java/hudson/model/Computer.java index 469b863c22202dae27069c2b6fca37ee885f5fa6..c1756bb635fa3cf33e1935e4f7ea39a7b3f83205 100644 --- a/core/src/main/java/hudson/model/Computer.java +++ b/core/src/main/java/hudson/model/Computer.java @@ -77,6 +77,7 @@ import javax.servlet.ServletException; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; +import java.io.InputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.util.*; @@ -1194,9 +1195,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces } if (req.getMethod().equals("POST")) { // submission - Node result = (Node)Jenkins.XSTREAM2.fromXML(req.getReader()); - - replaceBy(result); + updateByXml(req.getInputStream()); return; } @@ -1223,6 +1222,17 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces } } + /** + * Updates Job by its XML definition. + * + * @since XXX + */ + public void updateByXml(final InputStream source) throws IOException, ServletException { + checkPermission(Jenkins.ADMINISTER); + Node result = (Node)Jenkins.XSTREAM2.fromXML(source); + replaceBy(result); + } + /** * Really deletes the slave. */ diff --git a/core/src/main/resources/hudson/cli/Messages.properties b/core/src/main/resources/hudson/cli/Messages.properties index b57837eca499eb1a468ffb933acafca17f57cbc9..d013eb968d74b57b50a4ad62aaa4205647c2de25 100644 --- a/core/src/main/resources/hudson/cli/Messages.properties +++ b/core/src/main/resources/hudson/cli/Messages.properties @@ -11,6 +11,8 @@ BuildCommand.ShortDescription=\ CopyJobCommand.ShortDescription=Copies a job. CreateJobCommand.ShortDescription=\ Creates a new job by reading stdin as a configuration XML file. +CreateNodeCommand.ShortDescription=\ + Creates a new node by reading stdin as a XML configuration. DeleteBuildsCommand.ShortDescription=\ Deletes build record(s). GroovyCommand.ShortDescription=\ @@ -44,10 +46,14 @@ VersionCommand.ShortDescription=\ Outputs the current version. GetJobCommand.ShortDescription=\ Dumps the job definition XML to stdout +GetNodeCommand.ShortDescription=\ + Dumps the node definition XML to stdout SetBuildDisplayNameCommand.ShortDescription=\ Sets the displayName of a build WhoAmICommand.ShortDescription=\ Reports your credential and permissions UpdateJobCommand.ShortDescription=\ Updates the job definition XML from stdin. The opposite of the get-job command +UpdateNodeCommand.ShortDescription=\ + Updates the node definition XML from stdin. The opposite of the get-node command BuildCommand.CLICause.ShortDescription=Started by command line by {0} diff --git a/test/src/main/java/hudson/cli/CLICommandInvoker.java b/test/src/main/java/hudson/cli/CLICommandInvoker.java new file mode 100644 index 0000000000000000000000000000000000000000..85cda1d04b27b3bbf0e646b35e5659e9c769e5cb --- /dev/null +++ b/test/src/main/java/hudson/cli/CLICommandInvoker.java @@ -0,0 +1,154 @@ +/* + * The MIT License + * + * Copyright 2013 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package hudson.cli; + +import hudson.model.User; +import hudson.security.Permission; +import hudson.security.GlobalMatrixAuthorizationStrategy; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.PrintStream; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Locale; + +import org.jvnet.hudson.test.JenkinsRule; + +/** + * Helper class to invoke {@link CLICommand} and check the response. + * + * @author ogondza + */ +public class CLICommandInvoker { + + private static final String username = "user"; + private final JenkinsRule rule; + private final CLICommand command; + + private InputStream stdin; + private List args = Collections.emptyList(); + private List permissions = Collections.emptyList(); + private Locale locale = Locale.ENGLISH; + + public CLICommandInvoker(final JenkinsRule rule, final CLICommand command) { + + this.rule = rule; + this.command = command; + } + + public CLICommandInvoker authorizedTo(final Permission... permissions) { + + this.permissions = Arrays.asList(permissions); + return this; + } + + public CLICommandInvoker withStdin(final InputStream stdin) { + + this.stdin = stdin; + return this; + } + + public CLICommandInvoker withArgs(final String... args) { + + this.args = Arrays.asList(args); + return this; + } + + public Result invokeWithArgs(final String... args) { + + return withArgs(args).invoke(); + } + + public Result invoke() { + + setAuth(); + + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + final ByteArrayOutputStream err = new ByteArrayOutputStream(); + + final int returnCode = command.main( + args, locale, stdin, new PrintStream(out), new PrintStream(err) + ); + + return new Result(returnCode, out, err); + } + + private void setAuth() { + + if (permissions.isEmpty()) return; + + JenkinsRule.DummySecurityRealm realm = rule.createDummySecurityRealm(); + realm.addGroups(username, "group"); + rule.jenkins.setSecurityRealm(realm); + + GlobalMatrixAuthorizationStrategy auth = new GlobalMatrixAuthorizationStrategy(); + for(Permission p: permissions) { + auth.add(p, username); + } + rule.jenkins.setAuthorizationStrategy(auth); + + command.setTransportAuth(user().impersonate()); + } + + public User user() { + + return User.get(username); + } + + public static class Result { + + private final int result; + private final ByteArrayOutputStream out; + private final ByteArrayOutputStream err; + + private Result( + final int result, + final ByteArrayOutputStream out, + final ByteArrayOutputStream err + ) { + + this.result = result; + this.out = out; + this.err = err; + } + + public int returnCode() { + + return result; + } + + public String stdout() { + + return out.toString(); + } + + public String stderr() { + + return err.toString(); + } + } +} diff --git a/test/src/test/java/hudson/cli/CreateNodeCommandTest.java b/test/src/test/java/hudson/cli/CreateNodeCommandTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d116776a1e7f3acbc8383ca00c92e07b345f334c --- /dev/null +++ b/test/src/test/java/hudson/cli/CreateNodeCommandTest.java @@ -0,0 +1,152 @@ +/* + * The MIT License + * + * Copyright 2013 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package hudson.cli; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.nullValue; +import static org.hamcrest.text.IsEmptyString.isEmptyString; +import hudson.model.Node; +import hudson.model.Slave; +import hudson.security.Permission; +import jenkins.model.Jenkins; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.JenkinsRule; + +public class CreateNodeCommandTest { + + private CLICommandInvoker command; + + @Rule public final JenkinsRule j = new JenkinsRule(); + + @Before public void setUp() { + + command = new CLICommandInvoker(j, new CreateNodeCommand()); + } + + @Test public void createNodeShouldFailWithoutAdministerPermision() throws Exception { + + final CLICommandInvoker.Result result = command + .authorizedTo(Permission.READ) + .withStdin(getClass().getResourceAsStream("node.xml")) + .invoke() + ; + + assertThat(result.stderr(), containsString("user is missing the Overall/Administer permission")); + assertThat("No output expected", result.stdout(), isEmptyString()); + assertThat("Command is expected to fail", result.returnCode(), equalTo(-1)); + } + + @Test public void createNode() throws Exception { + + final CLICommandInvoker.Result result = command + .authorizedTo(Jenkins.ADMINISTER) + .withStdin(getClass().getResourceAsStream("node.xml")) + .invoke() + ; + + assertThat("No error output expected", result.stderr(), isEmptyString()); + assertThat("Command is expected to succeed", result.returnCode(), equalTo(0)); + + final Slave updatedSlave = (Slave) j.jenkins.getNode("SlaveFromXML"); + assertThat(updatedSlave.getNodeName(), equalTo("SlaveFromXML")); + assertThat(updatedSlave.getNumExecutors(), equalTo(42)); + assertThat(updatedSlave.getUserId(), equalTo(command.user().getId())); + } + + @Test public void createNodeSpecifyingNameExplicitly() throws Exception { + + final CLICommandInvoker.Result result = command + .authorizedTo(Jenkins.ADMINISTER) + .withStdin(getClass().getResourceAsStream("node.xml")) + .invokeWithArgs("CustomSlaveName") + ; + + assertThat("No error output expected", result.stderr(), isEmptyString()); + assertThat("Command is expected to succeed", result.returnCode(), equalTo(0)); + + assertThat("A slave with original name should not exist", j.jenkins.getNode("SlaveFromXml"), nullValue()); + + final Slave updatedSlave = (Slave) j.jenkins.getNode("CustomSlaveName"); + assertThat(updatedSlave.getNodeName(), equalTo("CustomSlaveName")); + assertThat(updatedSlave.getNumExecutors(), equalTo(42)); + assertThat(updatedSlave.getUserId(), equalTo(command.user().getId())); + } + + @Test public void createNodeSpecifyingDifferentNameExplicitly() throws Exception { + + final Node originalSlave = j.createSlave("SlaveFromXml", null, null); + + final CLICommandInvoker.Result result = command + .authorizedTo(Jenkins.ADMINISTER) + .withStdin(getClass().getResourceAsStream("node.xml")) + .invokeWithArgs("CustomSlaveName") + ; + + assertThat("No error output expected", result.stderr(), isEmptyString()); + assertThat("Command is expected to succeed", result.returnCode(), equalTo(0)); + + assertThat("A slave with original name should be left untouched", j.jenkins.getNode("SlaveFromXml"), equalTo(originalSlave)); + + final Slave updatedSlave = (Slave) j.jenkins.getNode("CustomSlaveName"); + assertThat(updatedSlave.getNodeName(), equalTo("CustomSlaveName")); + assertThat(updatedSlave.getNumExecutors(), equalTo(42)); + assertThat(updatedSlave.getUserId(), equalTo(command.user().getId())); + } + + @Test public void createNodeShouldFailIfNodeAlreadyExist() throws Exception { + + j.createSlave("SlaveFromXML", null, null); + + final CLICommandInvoker.Result result = command + .authorizedTo(Jenkins.ADMINISTER) + .withStdin(getClass().getResourceAsStream("node.xml")) + .invoke() + ; + + assertThat(result.stderr(), containsString("Node 'SlaveFromXML' already exists")); + assertThat("No output expected", result.stdout(), isEmptyString()); + assertThat("Command is expected to fail", result.returnCode(), equalTo(-1)); + } + + @Test public void createNodeShouldFailIfNodeAlreadyExistWhenNameSpecifiedExplicitly() throws Exception { + + j.createSlave("ExistingSlave", null, null); + + final CLICommandInvoker.Result result = command + .authorizedTo(Jenkins.ADMINISTER) + .withStdin(getClass().getResourceAsStream("node.xml")) + .invokeWithArgs("ExistingSlave") + ; + + assertThat(result.stderr(), containsString("Node 'ExistingSlave' already exists")); + assertThat("No output expected", result.stdout(), isEmptyString()); + assertThat("Command is expected to fail", result.returnCode(), equalTo(-1)); + } +} diff --git a/test/src/test/java/hudson/cli/GetNodeCommandTest.java b/test/src/test/java/hudson/cli/GetNodeCommandTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e6d0952013163ceeea7485309bc411db1d9e9d18 --- /dev/null +++ b/test/src/test/java/hudson/cli/GetNodeCommandTest.java @@ -0,0 +1,91 @@ +/* + * The MIT License + * + * Copyright 2013 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package hudson.cli; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.startsWith; +import static org.hamcrest.text.IsEmptyString.isEmptyString; +import hudson.security.Permission; +import jenkins.model.Jenkins; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.JenkinsRule; + +public class GetNodeCommandTest { + + private CLICommandInvoker command; + + @Rule public final JenkinsRule j = new JenkinsRule(); + + @Before public void setUp() { + + command = new CLICommandInvoker(j, new GetNodeCommand()); + } + + @Test public void getNodeShouldFailWithoutAdministerPermision() throws Exception { + + j.createSlave("MySlave", null, null); + + final CLICommandInvoker.Result result = command + .authorizedTo(Permission.READ) + .invokeWithArgs("MySlave") + ; + + assertThat(result.stderr(), containsString("user is missing the Overall/Administer permission")); + assertThat("No output expected", result.stdout(), isEmptyString()); + assertThat("Command is expected to fail", result.returnCode(), equalTo(-1)); + } + + @Test public void getNodeShouldYieldConfigXml() throws Exception { + + j.createSlave("MySlave", null, null); + + final CLICommandInvoker.Result result = command + .authorizedTo(Jenkins.ADMINISTER) + .invokeWithArgs("MySlave") + ; + + assertThat(result.stdout(), startsWith("")); + assertThat(result.stdout(), containsString("MySlave")); + assertThat("No error output expected", result.stderr(), isEmptyString()); + assertThat("Command is expected to succeed", result.returnCode(), equalTo(0)); + } + + @Test public void getNodeShouldFailIfNodeDoesNotExist() throws Exception { + + final CLICommandInvoker.Result result = command + .authorizedTo(Jenkins.ADMINISTER) + .invokeWithArgs("MySlave") + ; + + assertThat(result.stderr(), containsString("No such node 'MySlave'")); + assertThat("No output expected", result.stdout(), isEmptyString()); + assertThat("Command is expected to fail", result.returnCode(), equalTo(-1)); + } +} diff --git a/test/src/test/java/hudson/cli/UpdateNodeCommandTest.java b/test/src/test/java/hudson/cli/UpdateNodeCommandTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9dff1fc335d9702c5f7288bfcca7075687e0bc84 --- /dev/null +++ b/test/src/test/java/hudson/cli/UpdateNodeCommandTest.java @@ -0,0 +1,98 @@ +/* + * The MIT License + * + * Copyright 2013 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package hudson.cli; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.nullValue; +import static org.hamcrest.text.IsEmptyString.isEmptyString; +import hudson.model.Node; +import hudson.security.Permission; +import jenkins.model.Jenkins; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.JenkinsRule; + +public class UpdateNodeCommandTest { + + private CLICommandInvoker command; + + @Rule public final JenkinsRule j = new JenkinsRule(); + + @Before public void setUp() { + + command = new CLICommandInvoker(j, new UpdateNodeCommand()); + } + + @Test public void updateNodeShouldFailWithoutAdministerPermision() throws Exception { + + j.createSlave("MySlave", null, null); + + final CLICommandInvoker.Result result = command + .authorizedTo(Permission.READ) + .invokeWithArgs("MySlave") + ; + + assertThat(result.stderr(), containsString("user is missing the Overall/Administer permission")); + assertThat("No output expected", result.stdout(), isEmptyString()); + assertThat("Command is expected to fail", result.returnCode(), equalTo(-1)); + } + + @Test public void updateNodeShouldModifyNodeConfiguration() throws Exception { + + j.createSlave("MySlave", null, null); + + final CLICommandInvoker.Result result = command + .authorizedTo(Jenkins.ADMINISTER) + .withStdin(getClass().getResourceAsStream("node.xml")) + .invokeWithArgs("MySlave") + ; + + assertThat("No error output expected", result.stderr(), isEmptyString()); + assertThat("Command is expected to succeed", result.returnCode(), equalTo(0)); + + assertThat("A slave with old name should not exist", j.jenkins.getNode("MySlave"), nullValue()); + + final Node updatedSlave = j.jenkins.getNode("SlaveFromXML"); + assertThat(updatedSlave.getNodeName(), equalTo("SlaveFromXML")); + assertThat(updatedSlave.getNumExecutors(), equalTo(42)); + } + + @Test public void updateNodeShouldFailIfNodeDoesNotExist() throws Exception { + + final CLICommandInvoker.Result result = command + .authorizedTo(Jenkins.ADMINISTER) + .withStdin(getClass().getResourceAsStream("node.xml")) + .invokeWithArgs("MySlave") + ; + + assertThat(result.stderr(), containsString("No such node 'MySlave'")); + assertThat("No output expected", result.stdout(), isEmptyString()); + assertThat("Command is expected to fail", result.returnCode(), equalTo(-1)); + } +} diff --git a/test/src/test/resources/hudson/cli/node.xml b/test/src/test/resources/hudson/cli/node.xml new file mode 100644 index 0000000000000000000000000000000000000000..65685361ebff25ea503b04fdd3e54ba8a59e7192 --- /dev/null +++ b/test/src/test/resources/hudson/cli/node.xml @@ -0,0 +1,19 @@ + + + SlaveFromXML + XML slave description + /user/hudson/wokspace + 42 + NORMAL + + + + + + + "/opt/java6/jre/bin/java" -jar "slave.jar" + + + + SYSTEM +