提交 29d44133 编写于 作者: O Oliver Gondža

[FIXED JENKINS-19996] Support view manipulation via Jenkins CLI

上级 8445725c
/*
* 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 org.kohsuke.args4j.Argument;
import jenkins.model.Jenkins;
import hudson.Extension;
import hudson.model.Failure;
import hudson.model.View;
/**
* @author ogondza
* @since TODO
*/
@Extension
public class CreateViewCommand extends CLICommand {
@Argument(usage="Name of the view to use instead of the one in XML")
public String viewName = null;
@Override
public String getShortDescription() {
return Messages.CreateViewCommand_ShortDescription();
}
@Override
protected int run() throws Exception {
final Jenkins jenkins = Jenkins.getInstance();
jenkins.checkPermission(View.CREATE);
View newView;
try {
newView = View.createViewFromXML(viewName, stdin);
} catch (Failure ex) {
stderr.format("Invalid view name: %s\n", ex.getMessage());
return -1;
}
final String newName = newView.getViewName();
if (jenkins.getView(newName) != null) {
stderr.format("View '%s' already exists\n", newName);
return -1;
}
jenkins.addView(newView);
return 0;
}
}
/*
* 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.ViewGroup;
import hudson.model.View;
import org.kohsuke.args4j.Argument;
/**
* @author ogondza
* @since TODO
*/
@Extension
public class DeleteViewCommand extends CLICommand {
@Argument(usage="Name of the view to delete", required=true)
private View view;
@Override
public String getShortDescription() {
return Messages.DeleteViewCommand_ShortDescription();
}
@Override
protected int run() throws Exception {
view.checkPermission(View.DELETE);
final ViewGroup group = view.getOwner();
if (!group.canDelete(view)) {
stderr.format("%s does not allow to delete '%s' view\n",
group.getDisplayName(),
view.getViewName()
);
return -1;
}
group.deleteView(view);;
return 0;
}
}
/*
* 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.View;
import org.kohsuke.args4j.Argument;
/**
* @author ogondza
* @since TODO
*/
@Extension
public class GetViewCommand extends CLICommand {
@Argument(usage="Name of the view to obtain", required=true)
private View view;
@Override
public String getShortDescription() {
return Messages.GetViewCommand_ShortDescription();
}
@Override
protected int run() throws Exception {
view.checkPermission(View.READ);
view.writeXml(stdout);
return 0;
}
}
/*
* 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 javax.xml.transform.stream.StreamSource;
import hudson.Extension;
import hudson.model.View;
import org.kohsuke.args4j.Argument;
/**
* @author ogondza
* @since TODO
*/
@Extension
public class UpdateViewCommand extends CLICommand {
@Argument(usage="Name of the view to update", required=true)
private View view;
@Override
public String getShortDescription() {
return Messages.UpdateViewCommand_ShortDescription();
}
@Override
protected int run() throws Exception {
view.updateByXml(new StreamSource(stdin));
return 0;
}
}
/*
* 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.View;
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 View} by its name.
*
* @author ogondza
* @since TODO
*/
@MetaInfServices
public class ViewOptionHandler extends OptionHandler<View> {
public ViewOptionHandler(CmdLineParser parser, OptionDef option, Setter<View> setter) {
super(parser, option, setter);
}
@Override
public int parseArguments(Parameters params) throws CmdLineException {
String viewName = params.getParameter(0);
final View view = Jenkins.getInstance().getView(viewName);
if (view == null) throw new CmdLineException(owner, "No such view '" + viewName + "'");
setter.addValue(view);
return 1;
}
@Override
public String getDefaultMetaVariable() {
return "VIEW";
}
}
......@@ -83,6 +83,7 @@ import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
......@@ -1044,16 +1045,13 @@ public abstract class View extends AbstractModelObject implements AccessControll
return new HttpResponse() {
public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node) throws IOException, ServletException {
rsp.setContentType("application/xml");
// pity we don't have a handy way to clone Jenkins.XSTREAM to temp add the omit Field
XStream2 xStream2 = new XStream2();
xStream2.omitField(View.class, "owner");
xStream2.toXMLUTF8(View.this, rsp.getOutputStream());
View.this.writeXml(rsp.getOutputStream());
}
};
}
if (req.getMethod().equals("POST")) {
// submission
updateByXml((Source)new StreamSource(req.getReader()));
updateByXml(new StreamSource(req.getReader()));
return HttpResponses.ok();
}
......@@ -1061,6 +1059,16 @@ public abstract class View extends AbstractModelObject implements AccessControll
return HttpResponses.error(SC_BAD_REQUEST, "Unexpected request method " + req.getMethod());
}
/**
* @since TODO
*/
public void writeXml(OutputStream out) throws IOException {
// pity we don't have a handy way to clone Jenkins.XSTREAM to temp add the omit Field
XStream2 xStream2 = new XStream2();
xStream2.omitField(View.class, "owner");
xStream2.toXMLUTF8(View.this, out);
}
/**
* Updates Job by its XML definition.
*/
......@@ -1083,7 +1091,11 @@ public abstract class View extends AbstractModelObject implements AccessControll
// try to reflect the changes by reloading
InputStream in = new BufferedInputStream(new ByteArrayInputStream(out.toString().getBytes("UTF-8")));
try {
// Do not allow overwriting view name as it might collide with another
// view in same ViewGroup and might not satisfy Jenkins.checkGoodName.
String oldname = name;
Jenkins.XSTREAM.unmarshal(new XppDriver().createReader(in), this);
name = oldname;
} catch (StreamException e) {
throw new IOException2("Unable to read",e);
} catch(ConversionException e) {
......@@ -1180,11 +1192,17 @@ public abstract class View extends AbstractModelObject implements AccessControll
return v;
}
/**
* Instantiate View subtype from XML stream.
*
* @param name Alternative name to use or <tt>null</tt> to keep the one in xml.
*/
public static View createViewFromXML(String name, InputStream xml) throws IOException {
InputStream in = new BufferedInputStream(xml);
try {
View v = (View) Jenkins.XSTREAM.fromXML(in);
v.name = name;
if (name != null) v.name = name;
checkGoodName(v.name);
return v;
} catch(StreamException e) {
throw new IOException2("Unable to read",e);
......
......@@ -14,8 +14,12 @@ 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.
CreateViewCommand.ShortDescription=\
Creates a new view by reading stdin as a XML configuration.
DeleteBuildsCommand.ShortDescription=\
Deletes build record(s).
DeleteViewCommand.ShortDescription=\
Deletes view.
GroovyCommand.ShortDescription=\
Executes the specified Groovy script.
GroovyshCommand.ShortDescription=\
......@@ -49,6 +53,8 @@ GetJobCommand.ShortDescription=\
Dumps the job definition XML to stdout.
GetNodeCommand.ShortDescription=\
Dumps the node definition XML to stdout.
GetViewCommand.ShortDescription=\
Dumps the view definition XML to stdout.
SetBuildDisplayNameCommand.ShortDescription=\
Sets the displayName of a build.
WhoAmICommand.ShortDescription=\
......@@ -58,6 +64,8 @@ UpdateJobCommand.ShortDescription=\
UpdateNodeCommand.ShortDescription=\
Updates the node definition XML from stdin. The opposite of the get-node command.
SessionIdCommand.ShortDescription=Outputs the session ID, which changes every time Jenkins restarts.
UpdateViewCommand.ShortDescription=\
Updates the view definition XML from stdin. The opposite of the get-view command.
BuildCommand.CLICause.ShortDescription=Started by command line by {0}
BuildCommand.CLICause.CannotBuildDisabled=\
......
......@@ -24,6 +24,7 @@
package hudson.cli;
import hudson.Extension;
import hudson.model.User;
import hudson.security.Permission;
import hudson.security.GlobalMatrixAuthorizationStrategy;
......@@ -31,11 +32,16 @@ import hudson.security.GlobalMatrixAuthorizationStrategy;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import jenkins.model.Jenkins;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
import org.jvnet.hudson.test.JenkinsRule;
/**
......@@ -56,6 +62,14 @@ public class CLICommandInvoker {
public CLICommandInvoker(final JenkinsRule rule, final CLICommand command) {
if (command.getClass().getAnnotation(Extension.class) == null) {
throw new AssertionError(String.format(
"Command %s is missing @Extension annotation.",
command.getClass()
));
}
this.rule = rule;
this.command = command;
}
......@@ -68,6 +82,8 @@ public class CLICommandInvoker {
public CLICommandInvoker withStdin(final InputStream stdin) {
if (stdin == null) throw new NullPointerException("No stdin provided");
this.stdin = stdin;
return this;
}
......@@ -151,5 +167,71 @@ public class CLICommandInvoker {
return err.toString();
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder("CLI command exited with ").append(result);
String stdout = stdout();
if (!"".equals(stdout)) {
builder.append("\nSTDOUT:\n").append(stdout);
}
String stderr = stderr();
if (!"".equals(stderr)) {
builder.append("\nSTDERR:\n").append(stderr);
}
return builder.toString();
}
}
public abstract static class Matcher extends TypeSafeMatcher<Result> {
private final String description;
private Matcher(String description) {
this.description = description;
}
@Override
protected void describeMismatchSafely(Result result, Description description) {
description.appendText(result.toString());
}
public void describeTo(Description description) {
description.appendText(this.description);
}
public static Matcher hasNoStandardOutput() {
return new Matcher("No standard output") {
@Override protected boolean matchesSafely(Result result) {
return "".equals(result.stdout());
}
};
}
public static Matcher hasNoErrorOutput() {
return new Matcher("No error output") {
@Override protected boolean matchesSafely(Result result) {
return "".equals(result.stderr());
}
};
}
public static Matcher succeeded() {
return new Matcher("Exited with 0 return code") {
@Override protected boolean matchesSafely(Result result) {
return result.result == 0;
}
};
}
public static Matcher failedWith(final long expectedCode) {
return new Matcher("Exited with " + expectedCode + " return code") {
@Override protected boolean matchesSafely(Result result) {
return result.result == expectedCode;
}
};
}
}
}
/*
* 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 hudson.cli.CLICommandInvoker.Matcher.failedWith;
import static hudson.cli.CLICommandInvoker.Matcher.hasNoStandardOutput;
import static hudson.cli.CLICommandInvoker.Matcher.hasNoErrorOutput;
import static hudson.cli.CLICommandInvoker.Matcher.succeeded;
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 java.io.IOException;
import hudson.model.ListView;
import hudson.model.View;
import jenkins.model.Jenkins;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;
public class CreateViewCommandTest {
private CLICommandInvoker command;
@Rule public final JenkinsRule j = new JenkinsRule();
@Before public void setUp() {
command = new CLICommandInvoker(j, new CreateViewCommand());
}
@Test public void createViewShouldFailWithoutViewCreatePermission() {
final CLICommandInvoker.Result result = command
.authorizedTo(Jenkins.READ)
.withStdin(this.getClass().getResourceAsStream("/hudson/cli/view.xml"))
.invoke()
;
assertThat(result, failedWith(-1));
assertThat(result, hasNoStandardOutput());
assertThat(result.stderr(), containsString("user is missing the View/Create permission"));
}
@Test public void createViewShouldSucceed() {
final CLICommandInvoker.Result result = command
.authorizedTo(View.CREATE, Jenkins.READ)
.withStdin(this.getClass().getResourceAsStream("/hudson/cli/view.xml"))
.invoke()
;
assertThat(result, succeeded());
assertThat(result, hasNoErrorOutput());
assertThat(result, hasNoStandardOutput());
final View updatedView = j.jenkins.getView("ViewFromXML");
assertThat(updatedView.getViewName(), equalTo("ViewFromXML"));
assertThat(updatedView.isFilterExecutors(), equalTo(true));
assertThat(updatedView.isFilterQueue(), equalTo(false));
}
@Test public void createViewSpecifyingNameExplicitlyShouldSucceed() {
final CLICommandInvoker.Result result = command
.authorizedTo(View.CREATE, Jenkins.READ)
.withStdin(this.getClass().getResourceAsStream("/hudson/cli/view.xml"))
.invokeWithArgs("CustomViewName")
;
assertThat(result, succeeded());
assertThat(result, hasNoErrorOutput());
assertThat(result, hasNoStandardOutput());
assertThat("A view with original name should not exist", j.jenkins.getView("ViewFromXML"), nullValue());
final View updatedView = j.jenkins.getView("CustomViewName");
assertThat(updatedView.getViewName(), equalTo("CustomViewName"));
assertThat(updatedView.isFilterExecutors(), equalTo(true));
assertThat(updatedView.isFilterQueue(), equalTo(false));
}
@Test public void createViewShouldFailIfViewAlreadyExists() throws IOException {
j.jenkins.addView(new ListView("ViewFromXML"));
final CLICommandInvoker.Result result = command
.authorizedTo(View.CREATE, Jenkins.READ)
.withStdin(this.getClass().getResourceAsStream("/hudson/cli/view.xml"))
.invoke()
;
assertThat(result, failedWith(-1));
assertThat(result, hasNoStandardOutput());
assertThat(result.stderr(), containsString("View 'ViewFromXML' already exists"));
}
@Test public void createViewShouldFailUsingInvalidName() {
final CLICommandInvoker.Result result = command
.authorizedTo(View.CREATE, Jenkins.READ)
.withStdin(this.getClass().getResourceAsStream("/hudson/cli/view.xml"))
.invokeWithArgs("..")
;
assertThat(result, failedWith(-1));
assertThat(result, hasNoStandardOutput());
assertThat(result.stderr(), containsString("Invalid view name"));
}
}
/*
* 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.nullValue;
import static hudson.cli.CLICommandInvoker.Matcher.hasNoStandardOutput;
import static hudson.cli.CLICommandInvoker.Matcher.hasNoErrorOutput;
import static hudson.cli.CLICommandInvoker.Matcher.succeeded;
import static hudson.cli.CLICommandInvoker.Matcher.failedWith;
import java.io.IOException;
import hudson.model.ListView;
import hudson.model.View;
import jenkins.model.Jenkins;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;
public class DeleteViewCommandTest {
private CLICommandInvoker command;
@Rule public final JenkinsRule j = new JenkinsRule();
@Before public void setUp() {
command = new CLICommandInvoker(j, new DeleteViewCommand());
}
@Test public void deleteViewShouldFailWithoutViewDeletePermission() throws IOException {
j.jenkins.addView(new ListView("aView"));
final CLICommandInvoker.Result result = command
.authorizedTo(Jenkins.READ)
.invokeWithArgs("aView")
;
assertThat(result, failedWith(-1));
assertThat(result, hasNoStandardOutput());
assertThat(result.stderr(), containsString("user is missing the View/Delete permission"));
}
@Test public void deleteViewShouldSucceed() throws Exception {
j.jenkins.addView(new ListView("aView"));
final CLICommandInvoker.Result result = command
.authorizedTo(View.DELETE, Jenkins.READ)
.invokeWithArgs("aView")
;
assertThat(result, succeeded());
assertThat(result, hasNoStandardOutput());
assertThat(result, hasNoErrorOutput());
assertThat(j.jenkins.getView("aView"), nullValue());
}
@Test public void deleteViewShouldFailIfViewDoesNotExist() {
final CLICommandInvoker.Result result = command
.authorizedTo(View.DELETE, Jenkins.READ)
.invokeWithArgs("never_created")
;
assertThat(result, failedWith(-1));
assertThat(result, hasNoStandardOutput());
assertThat(result.stderr(), containsString("No such view 'never_created'"));
}
// ViewGroup.canDelete()
@Test public void deleteViewShouldFailIfViewGroupDoesNotAllowDeletion() {
final CLICommandInvoker.Result result = command
.authorizedTo(View.DELETE, Jenkins.READ)
.invokeWithArgs("All")
;
assertThat(result, failedWith(-1));
assertThat(result, hasNoStandardOutput());
assertThat(result.stderr(), containsString("Jenkins does not allow to delete 'All' view"));
}
}
/*
* 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 static hudson.cli.CLICommandInvoker.Matcher.failedWith;
import static hudson.cli.CLICommandInvoker.Matcher.hasNoStandardOutput;
import static hudson.cli.CLICommandInvoker.Matcher.hasNoErrorOutput;
import static hudson.cli.CLICommandInvoker.Matcher.succeeded;
import java.io.IOException;
import hudson.model.ListView;
import hudson.model.View;
import jenkins.model.Jenkins;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;
public class GetViewCommandTest {
private CLICommandInvoker command;
@Rule public final JenkinsRule j = new JenkinsRule();
@Before public void setUp() {
command = new CLICommandInvoker(j, new GetViewCommand());
}
@Test public void getViewShouldFailWithoutViewReadPermission() throws IOException {
j.jenkins.addView(new ListView("aView"));
final CLICommandInvoker.Result result = command
.authorizedTo(Jenkins.READ)
.invokeWithArgs("aView")
;
assertThat(result, failedWith(-1));
assertThat(result, hasNoStandardOutput());
assertThat(result.stderr(), containsString("user is missing the View/Read permission"));
}
@Test public void getViewShouldYieldConfigXml() throws Exception {
j.jenkins.addView(new ListView("aView"));
final CLICommandInvoker.Result result = command
.authorizedTo(View.READ, Jenkins.READ)
.invokeWithArgs("aView")
;
assertThat(result, succeeded());
assertThat(result, hasNoErrorOutput());
assertThat(result.stdout(), startsWith("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"));
assertThat(result.stdout(), containsString("<name>aView</name>"));
}
@Test public void getViewShouldFailIfViewDoesNotExist() {
final CLICommandInvoker.Result result = command
.authorizedTo(View.READ, Jenkins.READ)
.invokeWithArgs("never_created")
;
assertThat(result, failedWith(-1));
assertThat(result, hasNoStandardOutput());
assertThat(result.stderr(), containsString("No such view 'never_created'"));
}
}
/*
* 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 hudson.cli.CLICommandInvoker.Matcher.failedWith;
import static hudson.cli.CLICommandInvoker.Matcher.hasNoStandardOutput;
import static hudson.cli.CLICommandInvoker.Matcher.hasNoErrorOutput;
import static hudson.cli.CLICommandInvoker.Matcher.succeeded;
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 hudson.model.ListView;
import hudson.model.View;
import jenkins.model.Jenkins;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;
public class UpdateViewCommandTest {
private CLICommandInvoker command;
@Rule public final JenkinsRule j = new JenkinsRule();
@Before public void setUp() {
command = new CLICommandInvoker(j, new UpdateViewCommand());
}
@Test public void updateViewShouldFailWithoutViewConfigurePermission() throws Exception {
j.jenkins.addView(new ListView("aView"));
final CLICommandInvoker.Result result = command
.authorizedTo(Jenkins.READ)
.withStdin(this.getClass().getResourceAsStream("/hudson/cli/view.xml"))
.invokeWithArgs("aView")
;
assertThat(result, failedWith(-1));
assertThat(result, hasNoStandardOutput());
assertThat(result.stderr(), containsString("user is missing the View/Configure permission"));
}
@Test public void updateViewShouldModifyViewConfiguration() throws Exception {
j.jenkins.addView(new ListView("aView"));
final CLICommandInvoker.Result result = command
.authorizedTo(View.CONFIGURE, Jenkins.READ)
.withStdin(this.getClass().getResourceAsStream("/hudson/cli/view.xml"))
.invokeWithArgs("aView")
;
assertThat(result, succeeded());
assertThat(result, hasNoErrorOutput());
assertThat("Update should not modify view name", j.jenkins.getView("ViewFromXML"), nullValue());
final View updatedView = j.jenkins.getView("aView");
assertThat(updatedView.getViewName(), equalTo("aView"));
assertThat(updatedView.isFilterExecutors(), equalTo(true));
assertThat(updatedView.isFilterQueue(), equalTo(false));
}
@Test public void updateViewShouldFailIfViewDoesNotExist() {
final CLICommandInvoker.Result result = command
.authorizedTo(View.CONFIGURE, Jenkins.READ)
.withStdin(this.getClass().getResourceAsStream("/hudson/cli/view.xml"))
.invokeWithArgs("not_created")
;
assertThat(result, failedWith(-1));
assertThat(result, hasNoStandardOutput());
assertThat(result.stderr(), containsString("No such view 'not_created'"));
}
}
<?xml version="1.0" encoding="UTF-8"?>
<hudson.model.ListView>
<name>ViewFromXML</name>
<filterExecutors>true</filterExecutors>
<filterQueue>false</filterQueue>
<properties class="hudson.model.View$PropertyList"/>
<jobNames>
<comparator class="hudson.util.CaseInsensitiveComparator"/>
<string>core_selenium-test</string>
<string>jenkins_lts_branch</string>
<string>jenkins_main_maven-3.1.0</string>
<string>jenkins_main_trunk</string>
<string>jenkins_pom</string>
<string>jenkins_rc_branch</string>
<string>jenkins_ui-changes_branch</string>
<string>remoting</string>
<string>selenium-tests</string>
</jobNames>
<jobFilters/>
<columns>
<hudson.views.StatusColumn/>
<hudson.views.WeatherColumn/>
<hudson.views.JobColumn/>
<hudson.views.LastSuccessColumn/>
<hudson.views.LastFailureColumn/>
<hudson.views.LastDurationColumn/>
<org.jenkins.ci.plugins.column.console.LastBuildColumn plugin="console-column-plugin@1.5"/>
<hudson.views.BuildButtonColumn/>
</columns>
<recurse>false</recurse>
</hudson.model.ListView>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册