Skip to content
Permalink
Browse files

Merge pull request #1704 from pjanouse/JENKINS-28041

[FIXED JENKINS-28041] - Allow delete-* CLI commands to operate on multiple arguments
  • Loading branch information
olivergondza committed Jun 9, 2015
2 parents 998b2f2 + 65d78d5 commit 0e846fca9fb850ffc242bb016c6e9e16aa3a1626
Showing with 698 additions and 51 deletions.
  1. +101 −0 core/src/main/java/hudson/cli/DeleteJobCommand.java
  2. +102 −0 core/src/main/java/hudson/cli/DeleteNodeCommand.java
  3. +56 −15 core/src/main/java/hudson/cli/DeleteViewCommand.java
  4. +33 −9 core/src/main/java/hudson/cli/handlers/ViewOptionHandler.java
  5. +0 −1 core/src/main/java/hudson/model/AbstractItem.java
  6. +0 −1 core/src/main/java/hudson/model/Computer.java
  7. +7 −1 core/src/main/resources/hudson/cli/Messages.properties
  8. +2 −0 core/src/main/resources/hudson/cli/Messages_da.properties
  9. +2 −0 core/src/main/resources/hudson/cli/Messages_de.properties
  10. +2 −0 core/src/main/resources/hudson/cli/Messages_es.properties
  11. +2 −0 core/src/main/resources/hudson/cli/Messages_it.properties
  12. +4 −1 core/src/main/resources/hudson/cli/Messages_ja.properties
  13. +5 −0 core/src/main/resources/hudson/cli/Messages_pt_BR.properties
  14. +2 −0 core/src/main/resources/hudson/cli/Messages_zh_CN.properties
  15. +3 −0 core/src/main/resources/hudson/cli/Messages_zh_TW.properties
  16. +0 −2 core/src/main/resources/hudson/model/Messages.properties
  17. +0 −2 core/src/main/resources/hudson/model/Messages_da.properties
  18. +0 −2 core/src/main/resources/hudson/model/Messages_de.properties
  19. +0 −2 core/src/main/resources/hudson/model/Messages_es.properties
  20. +0 −2 core/src/main/resources/hudson/model/Messages_it.properties
  21. +0 −2 core/src/main/resources/hudson/model/Messages_ja.properties
  22. +0 −4 core/src/main/resources/hudson/model/Messages_pt_BR.properties
  23. +0 −2 core/src/main/resources/hudson/model/Messages_zh_CN.properties
  24. +0 −2 core/src/main/resources/hudson/model/Messages_zh_TW.properties
  25. +151 −0 test/src/test/java/hudson/cli/DeleteJobCommandTest.java
  26. +140 −0 test/src/test/java/hudson/cli/DeleteNodeCommandTest.java
  27. +86 −3 test/src/test/java/hudson/cli/DeleteViewCommandTest.java
@@ -0,0 +1,101 @@
/*
* The MIT License
*
* Copyright (c) 2015 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.AbstractItem;
import jenkins.model.Jenkins;
import org.kohsuke.args4j.Argument;

import java.util.List;
import java.util.HashSet;
import java.util.logging.Logger;

/**
* @author pjanouse
* @since TODO
*/
@Extension
public class DeleteJobCommand extends CLICommand {

@Argument(usage="Name of the job(s) to delete", required=true, multiValued=true)
private List<String> jobs;

private static final Logger LOGGER = Logger.getLogger(DeleteJobCommand.class.getName());

@Override
public String getShortDescription() {

return Messages.DeleteJobCommand_ShortDescription();
}

@Override
protected int run() throws Exception {

boolean errorOccurred = false;
final Jenkins jenkins = Jenkins.getInstance();

if (jenkins == null) {
stderr.println("The Jenkins instance has not been started, or was already shut down!");
return -1;
}

final HashSet<String> hs = new HashSet<String>();
hs.addAll(jobs);

for (String job_s: hs) {
AbstractItem job = null;

try {
job = (AbstractItem) jenkins.getItemByFullName(job_s);

if(job == null) {
stderr.format("No such job '%s'\n", job_s);
errorOccurred = true;
continue;
}

try {
job.checkPermission(AbstractItem.DELETE);
} catch (Exception e) {
stderr.println(e.getMessage());
errorOccurred = true;
continue;
}

job.delete();
} catch (Exception e) {
final String errorMsg = String.format("Unexpected exception occurred during deletion of job '%s': %s",
job == null ? "(null)" : job.getFullName(),
e.getMessage());
stderr.println(errorMsg);
LOGGER.warning(errorMsg);
errorOccurred = true;
//noinspection UnnecessaryContinue
continue;
}
}
return errorOccurred ? -1 : 0;
}
}
@@ -0,0 +1,102 @@
/*
* The MIT License
*
* Copyright (c) 2015 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.Computer;
import hudson.model.Node;
import jenkins.model.Jenkins;
import org.kohsuke.args4j.Argument;

import java.util.HashSet;
import java.util.List;
import java.util.logging.Logger;

/**
* @author pjanouse
* @since TODO
*/
@Extension
public class DeleteNodeCommand extends CLICommand {

@Argument(usage="Nodes name to delete", required=true, multiValued=true)
private List<String> nodes;

private static final Logger LOGGER = Logger.getLogger(DeleteNodeCommand.class.getName());

@Override
public String getShortDescription() {

return Messages.DeleteNodeCommand_ShortDescription();
}

@Override
protected int run() throws Exception {

boolean errorOccurred = false;
final Jenkins jenkins = Jenkins.getInstance();

if (jenkins == null) {
stderr.println("The Jenkins instance has not been started, or was already shut down!");
return -1;
}

final HashSet<String> hs = new HashSet<String>();
hs.addAll(nodes);

for (String node_s : hs) {
Node node = null;

try {
node = jenkins.getNode(node_s);

if(node == null) {
stderr.format("No such node '%s'\n", node_s);
errorOccurred = true;
continue;
}

try {
node.checkPermission(Computer.DELETE);
} catch (Exception e) {
stderr.println(e.getMessage());
errorOccurred = true;
continue;
}

jenkins.removeNode(node);
} catch (Exception e) {
final String errorMsg = String.format("Unexpected exception occurred during deletion of node '%s': %s",
node == null ? "(null)" : node.toComputer().getName(),
e.getMessage());
stderr.println(errorMsg);
LOGGER.warning(errorMsg);
errorOccurred = true;
//noinspection UnnecessaryContinue
continue;
}
}
return errorOccurred ? -1 : 0;
}
}
@@ -1,7 +1,7 @@
/*
* The MIT License
*
* Copyright (c) 2013 Red Hat, Inc.
* Copyright (c) 2013-5 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
@@ -24,20 +24,27 @@
package hudson.cli;

import hudson.Extension;
import hudson.cli.handlers.ViewOptionHandler;
import hudson.model.ViewGroup;
import hudson.model.View;

import org.kohsuke.args4j.Argument;

import java.util.HashSet;
import java.util.List;
import java.util.logging.Logger;

/**
* @author ogondza
* @author ogondza, pjanouse
* @since 1.538
*/
@Extension
public class DeleteViewCommand extends CLICommand {

@Argument(usage="Name of the view to delete", required=true)
private View view;
@Argument(usage="View names to delete", required=true, multiValued=true)
private List<String> views;

private static final Logger LOGGER = Logger.getLogger(DeleteViewCommand.class.getName());

@Override
public String getShortDescription() {
@@ -48,20 +55,54 @@ public String getShortDescription() {
@Override
protected int run() throws Exception {

view.checkPermission(View.DELETE);
boolean errorOccurred = false;

final ViewGroup group = view.getOwner();
if (!group.canDelete(view)) {
// Remove duplicates
final HashSet<String> hs = new HashSet<String>();
hs.addAll(views);

stderr.format("%s does not allow to delete '%s' view\n",
group.getDisplayName(),
view.getViewName()
);
return -1;
}
ViewOptionHandler voh = new ViewOptionHandler(null, null, null);

group.deleteView(view);;
for(String view_s : hs) {
View view = null;

return 0;
try {
try {
view = voh.getView(view_s);
if (view == null) {
stderr.println("user is missing the View/Read permission");
errorOccurred = true;
continue;
}
view.checkPermission(View.DELETE);
} catch (Exception e) {
stderr.println(e.getMessage());
errorOccurred = true;
continue;
}

ViewGroup group = view.getOwner();
if (!group.canDelete(view)) {
stderr.format("%s does not allow to delete '%s' view\n",
group.getDisplayName(),
view.getViewName()
);
errorOccurred = true;
continue;
}

group.deleteView(view);
} catch (Exception e) {
final String errorMsg = String.format("Unexpected exception occurred during deletion of view '%s': %s",
view == null ? "(null)" : view.getViewName(),
e.getMessage());
stderr.println(errorMsg);
LOGGER.warning(errorMsg);
errorOccurred = true;
//noinspection UnnecessaryContinue
continue;
}
}
return errorOccurred ? -1 : 0;
}
}
@@ -38,6 +38,8 @@
import org.kohsuke.args4j.spi.Parameters;
import org.kohsuke.args4j.spi.Setter;

import javax.annotation.CheckForNull;

/**
* Refers to {@link View} by its name.
*
@@ -73,29 +75,51 @@ public int parseArguments(Parameters params) throws CmdLineException {
return 1;
}

private View getView(String name) throws CmdLineException {
/**
*
* Gets a view by its name
* Note: Personal user's views aren't supported now.
*
* @param name A view name
* @return The {@link View} instance. Null if {@link Jenkins#getInstance()} returns null
* sor user doesn't have a READ permission.
* @throws CmdLineException
* If view isn't found or an un-expected error occurred
* @since TODO
*/
@CheckForNull
public View getView(final String name) throws CmdLineException {

View view = null;
ViewGroup group = Jenkins.getInstance();
View view = null;

if (group == null)
throw new CmdLineException(owner,
"The Jenkins instance has not been started, or was already shut down!");

final StringTokenizer tok = new StringTokenizer(name, "/");
while(tok.hasMoreTokens()) {

String viewName = tok.nextToken();

view = group.getView(viewName);
if (view == null) throw new CmdLineException(owner, String.format(
"No view named %s inside view %s",
viewName, group.getDisplayName()
));

view.checkPermission(View.READ);
if (view == null)
throw new CmdLineException(owner, String.format(
"No view named %s inside view %s",
viewName, group.getDisplayName()
));

try {
view.checkPermission(View.READ);
} catch (Exception e) {
throw new CmdLineException(owner, e.getMessage());
}

if (view instanceof ViewGroup) {
group = (ViewGroup) view;
} else if (tok.hasMoreTokens()) {
throw new CmdLineException(
owner, view.getViewName() + " view can not contain views"
owner, view.getViewName() + " view can not contain views"
);
}
}
@@ -538,7 +538,6 @@ public synchronized void doSubmitDescription( StaplerRequest req, StaplerRespons
* since it predates {@code <l:confirmationLink>}. {@code /delete} goes to a Jelly page
* which should now be unused by core but is left in case plugins are still using it.
*/
@CLIMethod(name="delete-job")
@RequirePOST
public void doDoDelete( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException, InterruptedException {
delete();
@@ -1439,7 +1439,6 @@ public void updateByXml(final InputStream source) throws IOException, ServletExc
/**
* Really deletes the slave.
*/
@CLIMethod(name="delete-node")
@RequirePOST
public HttpResponse doDoDelete() throws IOException {
checkPermission(DELETE);

0 comments on commit 0e846fc

Please sign in to comment.
You can’t perform that action at this time.