提交 0e846fca 编写于 作者: O Oliver Gondža

Merge pull request #1704 from pjanouse/JENKINS-28041

[FIXED JENKINS-28041] - Allow delete-* CLI commands to operate on multiple arguments
/*
* 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;
}
}
/*
* 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;
}
}
/*
* 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 class DeleteViewCommand extends CLICommand {
@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.OptionHandler;
import org.kohsuke.args4j.spi.Parameters;
import org.kohsuke.args4j.spi.Setter;
import javax.annotation.CheckForNull;
/**
* Refers to {@link View} by its name.
*
......@@ -73,10 +75,27 @@ public class ViewOptionHandler extends OptionHandler<View> {
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()) {
......@@ -84,18 +103,23 @@ public class ViewOptionHandler extends OptionHandler<View> {
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 abstract class AbstractItem extends Actionable implements Item, HttpDelet
* 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 /*transient*/ abstract class Computer extends Actionable implements Acces
/**
* Really deletes the slave.
*/
@CLIMethod(name="delete-node")
@RequirePOST
public HttpResponse doDoDelete() throws IOException {
checkPermission(DELETE);
......
......@@ -21,7 +21,9 @@ CreateViewCommand.ShortDescription=\
DeleteBuildsCommand.ShortDescription=\
Deletes build record(s).
DeleteViewCommand.ShortDescription=\
Deletes view.
Deletes view(s).
DeleteJobCommand.ShortDescription=\
Deletes job(s).
GroovyCommand.ShortDescription=\
Executes the specified Groovy script.
GroovyshCommand.ShortDescription=\
......@@ -78,3 +80,7 @@ BuildCommand.CLICause.CannotBuildConfigNotSaved=\
Cannot build {0} because its configuration has not been saved.
BuildCommand.CLICause.CannotBuildUnknownReasons=\
Cannot build {0} for unknown reasons.
DeleteNodeCommand.ShortDescription=\
Deletes node(s)
DeleteNodeCommand.ShortDescription=Sletter en node
DeleteJobCommand.ShortDescription=Sletter et job
DeleteNodeCommand.ShortDescription=Knoten l\u00f6schen.
DeleteJobCommand.ShortDescription=Job l\u00f6schen.
......@@ -47,4 +47,6 @@ WhoAmICommand.ShortDescription=Muestra tus credenciales y permisos
UpdateJobCommand.ShortDescription=Actualiza el fichero XML de la definicin de una tarea desde la entrada estndard. Es lo contrario al comando get-job.
GroovyshCommand.ShortDescription=Ejecuta una shell interactiva de groovy.
SetBuildDescriptionCommand.ShortDescription=Establece la descripcin de una ejecucin.
DeleteJobCommand.ShortDescription=Borrar una tarea
DeleteNodeCommand.ShortDescription=Borrar un nodo
DeleteNodeCommand.ShortDescription=Cancella un nodo
DeleteJobCommand.ShortDescription=Cancella un job
......@@ -68,4 +68,7 @@ BuildCommand.CLICause.CannotBuildDisabled=\
BuildCommand.CLICause.CannotBuildConfigNotSaved=\
\u8a2d\u5b9a\u304c\u4fdd\u5b58\u3055\u308c\u3066\u3044\u306a\u3044\u306e\u3067{0}\u3092\u30d3\u30eb\u30c9\u3067\u304d\u307e\u305b\u3093\u3002
BuildCommand.CLICause.CannotBuildUnknownReasons=\
\u30d3\u30eb\u30c9\u3067\u304d\u307e\u305b\u3093(\u539f\u56e0\u4e0d\u660e)\u3002
\ No newline at end of file
\u30d3\u30eb\u30c9\u3067\u304d\u307e\u305b\u3093(\u539f\u56e0\u4e0d\u660e)\u3002
DeleteNodeCommand.ShortDescription=\u30ce\u30fc\u30c9\u3092\u524a\u9664\u3057\u307e\u3059\u3002
DeleteJobCommand.ShortDescription=\u30b8\u30e7\u30d6\u3092\u524a\u9664\u3057\u307e\u3059\u3002
......@@ -110,3 +110,8 @@ SessionIdCommand.ShortDescription=Exibe o ID de sess\u00e3o, que muda toda vez q
InstallPluginCommand.InstallingPluginFromUrl=Instalando um plugin de {0}
# Installs a plugin either from a file, an URL, or from update center.
InstallPluginCommand.ShortDescription=Instala um plugin a partir de um arquivo, uma URL, ou da central de atualiza\u00e7\u00f5es.
# Deletes a node
CLI.delete-node.shortDescription=Remover o n\u00f3
# Deletes a job
DeleteJobCommand.ShortDescription=Remover uma job
DeleteNodeCommand.ShortDescription=Deletes a node
DeleteJobCommand.ShortDescription=Deletes a job
......@@ -72,3 +72,6 @@ WhoAmICommand.ShortDescription=\
UpdateJobCommand.ShortDescription=\
\u7531 stdin \u66f4\u65b0\u4f5c\u696d\u5b9a\u7fa9 XML\u3002get-job \u6307\u4ee4\u7684\u76f8\u53cd
BuildCommand.CLICause.ShortDescription=\u7531 {0} \u7684\u547d\u4ee4\u5217\u4ecb\u9762\u555f\u52d5
DeleteNodeCommand.ShortDescription=\u522a\u9664\u6307\u5b9a\u7bc0\u9ede\u3002
DeleteJobCommand.ShortDescription=\u522a\u9664\u4f5c\u696d\u3002
......@@ -94,11 +94,9 @@ BallColor.Unstable=Unstable
Build.post_build_steps_failed=Post-build steps failed
CLI.clear-queue.shortDescription=Clears the build queue.
CLI.delete-job.shortDescription=Deletes a job.
CLI.reload-job.shortDescription=Reloads this job from disk.
CLI.disable-job.shortDescription=Disables a job.
CLI.enable-job.shortDescription=Enables a job.
CLI.delete-node.shortDescription=Deletes a node.
CLI.disconnect-node.shortDescription=Disconnects from a node.
CLI.connect-node.shortDescription=Reconnect to a node.
CLI.online-node.shortDescription=Resume using a node for performing builds, to cancel out the earlier "offline-node" command.
......
......@@ -82,7 +82,6 @@ UpdateCenter.PluginCategory.cli=Kommandolinieinterface
UpdateCenter.PluginCategory.builder=Byggev\u00e6rkt\u00f8jer
Slave.UnixSlave=Dette er en Unix slave
FileParameterDefinition.DisplayName=Filparametre
CLI.delete-job.shortDescription=Sletter et job
Run.Summary.Unstable=Ustabil
CLI.reload-configuration.shortDescription=Genindl\u00e6s alle data fra filsystemet. \
Nyttigt hvis du har modificeret konfigurationsfiler direkte, udenom Jenkins.
......@@ -144,7 +143,6 @@ UpdateCenter.PluginCategory.scm=Kildekodestyring (SCM)
View.ConfigurePermission.Description=Denne rettighed tillader brugere at \u00e6ndre konfigurationen af visninger.
AbstractProject.NewBuildForWorkspace=Skedulerer et nyt byg for at f\u00e5 et arbejdsomr\u00e5de
Node.LabelMissing={0} har ikke etiket {1}
CLI.delete-node.shortDescription=Sletter en node
Queue.BlockedBy=Blokeret af {0}
Node.BecauseNodeIsReserved={0} er reserveret til jobs bundet(tied) til den
Job.minutes=min
......
......@@ -83,12 +83,10 @@ CLI.quiet-down.shortDescription=Jenkins' Aktivit\u00e4t reduzieren, z.B. zur Vor
CLI.cancel-quiet-down.shortDescription=Wirkung des Befehls "quiet-down" wieder aufheben.
CLI.reload-configuration.shortDescription=Alle Daten im Speicher verwerfen und Konfiguration neu von Festplatte laden. Dies ist n\u00fctzlich, wenn Sie \u00c4nderungen direkt im Dateisystem vorgenommen haben.
CLI.clear-queue.shortDescription=Build-Warteschlange l\u00f6schen.
CLI.delete-job.shortDescription=Job l\u00f6schen.
CLI.disable-job.shortDescription=Job deaktivieren.
CLI.enable-job.shortDescription=Job aktivieren.
CLI.connect-node.shortDescription=Erneut mit Knoten verbinden.
CLI.disconnect-node.shortDescription=Knoten trennen.
CLI.delete-node.shortDescription=Knoten l\u00f6schen.
CLI.offline-node.shortDescription=Knoten wird bis zum n\u00e4chsten "online-node"-Kommando f\u00fcr keine neuen Builds verwendet.
CLI.online-node.shortDescription=Knoten wird wieder f\u00fcr neue Builds verwendet. Hebt ein vorausgegangenes "offline-node"-Kommando auf.
CLI.safe-restart.shortDescription=Startet Jenkins neu.
......
......@@ -59,10 +59,8 @@ BallColor.Success=Correcto
BallColor.Unstable=Inestable
CLI.clear-queue.shortDescription=Limpiar la cola de trabajos
CLI.delete-job.shortDescription=Borrar una tarea
CLI.disable-job.shortDescription=Desactivar una tarea
CLI.enable-job.shortDescription=Activar una tarea
CLI.delete-node.shortDescription=Borrar un nodo
CLI.disconnect-node.shortDescription=Desconectarse de un nodo
CLI.connect-node.shortDescription=Reconectarse con un nodo
CLI.online-node.shortDescription=Continuar usando un nodo y candelar el comando "offline-node" mas reciente.
......
......@@ -77,10 +77,8 @@ BallColor.Success=Successo
BallColor.Unstable=Instabile
CLI.clear-queue.shortDescription=Pulisce la coda di lavoro
CLI.delete-job.shortDescription=Cancella un job
CLI.disable-job.shortDescription=Disabilita un job
CLI.enable-job.shortDescription=Abilita un job
CLI.delete-node.shortDescription=Cancella un nodo
CLI.disconnect-node.shortDescription=Disconnects from a node
CLI.connect-node.shortDescription=Riconnettersi ad un nodo
CLI.online-node.shortDescription=Resume using a node for performing builds, to cancel out the earlier "offline-node" command.
......
......@@ -320,10 +320,8 @@ CLI.quiet-down.shortDescription=Jenkins\u306f\u518d\u8d77\u52d5\u306b\u5411\u305
CLI.cancel-quiet-down.shortDescription="quite-down"\u30b3\u30de\u30f3\u30c9\u306e\u51e6\u7406\u3092\u30ad\u30e3\u30f3\u30bb\u30eb\u3057\u307e\u3059\u3002
CLI.reload-configuration.shortDescription=\u30e1\u30e2\u30ea\u306b\u3042\u308b\u3059\u3079\u3066\u306e\u30c7\u30fc\u30bf\u3092\u7834\u68c4\u3057\u3066\u3001\u30d5\u30a1\u30a4\u30eb\u304b\u3089\u518d\u30ed\u30fc\u30c9\u3057\u307e\u3059\u3002\u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u3092\u76f4\u63a5\u4fee\u6b63\u3057\u305f\u5834\u5408\u306b\u5f79\u306b\u7acb\u3061\u307e\u3059\u3002
CLI.clear-queue.shortDescription=\u30d3\u30eb\u30c9\u30ad\u30e5\u30fc\u3092\u30af\u30ea\u30a2\u3057\u307e\u3059\u3002
CLI.delete-job.shortDescription=\u30b8\u30e7\u30d6\u3092\u524a\u9664\u3057\u307e\u3059\u3002
CLI.disable-job.shortDescription=\u30b8\u30e7\u30d6\u3092\u7121\u52b9\u5316\u3057\u307e\u3059\u3002
CLI.enable-job.shortDescription=\u30b8\u30e7\u30d6\u3092\u6709\u52b9\u5316\u3057\u307e\u3059\u3002
CLI.delete-node.shortDescription=\u30ce\u30fc\u30c9\u3092\u524a\u9664\u3057\u307e\u3059\u3002
CLI.disconnect-node.shortDescription=\u30ce\u30fc\u30c9\u3068\u306e\u63a5\u7d9a\u3092\u5207\u65ad\u3057\u307e\u3059\u3002
CLI.connect-node.shortDescription=\u30ce\u30fc\u30c9\u3068\u518d\u63a5\u7d9a\u3057\u307e\u3059\u3002
CLI.online-node.shortDescription=\u76f4\u524d\u306b\u5b9f\u884c\u3057\u305f"online-node"\u30b3\u30de\u30f3\u30c9\u3092\u53d6\u308a\u6d88\u3057\u3001\u30d3\u30eb\u30c9\u3092\u5b9f\u884c\u3059\u308b\u30ce\u30fc\u30c9\u306e\u4f7f\u7528\u3092\u518d\u958b\u3057\u307e\u3059\u3002
......
......@@ -187,8 +187,6 @@ UpdateCenter.PluginCategory.builder=Ferramentas de build
# File Parameter
FileParameterDefinition.DisplayName=Par\u00e2metros de arquivo
# {0} {0,choice,0#tests are|1#test is|1<tests are} still failing
# Deletes a job
CLI.delete-job.shortDescription=Remover uma job
# unstable
Run.Summary.Unstable=Inst\u00e1vel
# Discard all the loaded data in memory and reload everything from file system. Useful when you modified config files directly on disk.
......@@ -278,8 +276,6 @@ View.ConfigurePermission.Description=Permite aos usu\u00e1rios configurar as vie
AbstractProject.NewBuildForWorkspace=Agendando uma novo build para obter um novo workspace
# {0} doesn''t have label {1}
Node.LabelMissing=R\u00f3tulo n\u00e3o encontrado
# Deletes a node
CLI.delete-node.shortDescription=Remover o n\u00f3
# {0} is reserved for jobs tied to it
Node.BecauseNodeIsReserved={0} foi reservado para trabalhos vinculados
# Started by remote host {0} with note: {1}
......
......@@ -63,10 +63,8 @@ BallColor.Success=Success
BallColor.Unstable=Unstable
CLI.clear-queue.shortDescription=Clears the build queue
CLI.delete-job.shortDescription=Deletes a job
CLI.disable-job.shortDescription=Disables a job
CLI.enable-job.shortDescription=Enables a job
CLI.delete-node.shortDescription=Deletes a node
CLI.disconnect-node.shortDescription=Disconnects from a node
CLI.connect-node.shortDescription=Reconnect to a node
CLI.online-node.shortDescription=Resume using a node for performing builds, to cancel out the earlier "offline-node" command.
......
......@@ -82,10 +82,8 @@ BallColor.Success=\u6210\u529f
BallColor.Unstable=\u4e0d\u7a69\u5b9a
CLI.clear-queue.shortDescription=\u6e05\u9664\u5efa\u7f6e\u4f47\u5217\u3002
CLI.delete-job.shortDescription=\u522a\u9664\u4f5c\u696d\u3002
CLI.disable-job.shortDescription=\u505c\u7528\u4f5c\u696d\u3002
CLI.enable-job.shortDescription=\u555f\u7528\u4f5c\u696d\u3002
CLI.delete-node.shortDescription=\u522a\u9664\u6307\u5b9a\u7bc0\u9ede\u3002
CLI.disconnect-node.shortDescription=\u4e2d\u65b7\u8207\u6307\u5b9a\u7bc0\u9ede\u7684\u9023\u7dda\u3002
CLI.connect-node.shortDescription=\u9023\u7dda\u5230\u6307\u5b9a\u7bc0\u9ede\u3002
CLI.online-node.shortDescription=\u7e7c\u7e8c\u4f7f\u7528\u6307\u5b9a\u7bc0\u9ede\u4f86\u5efa\u7f6e\uff0c\u53d6\u6d88\u5148\u524d\u7684 "offline-node" \u6307\u4ee4\u3002
......
/*
* The MIT License
*
* Copyright 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.model.Job;
import jenkins.model.Jenkins;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;
import java.io.IOException;
import static hudson.cli.CLICommandInvoker.Matcher.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.nullValue;
/**
* @author pjanouse
*/
public class DeleteJobCommandTest {
private CLICommandInvoker command;
@Rule public final JenkinsRule j = new JenkinsRule();
@Before public void setUp() {
command = new CLICommandInvoker(j, new DeleteJobCommand());
}
@Test public void deleteJobShouldFailWithoutJobDeletePermission() throws IOException {
j.createFreeStyleProject("aProject");
final CLICommandInvoker.Result result = command
.authorizedTo(Job.READ, Jenkins.READ)
.invokeWithArgs("aProject");
assertThat(result, failedWith(-1));
assertThat(result, hasNoStandardOutput());
assertThat(result.stderr(), containsString("user is missing the Job/Delete permission"));
}
@Test public void deleteJobShouldFailWithoutJobReadPermission() throws IOException {
j.createFreeStyleProject("aProject");
final CLICommandInvoker.Result result = command
.authorizedTo(Job.DELETE, Jenkins.READ)
.invokeWithArgs("aProject");
assertThat(result, failedWith(-1));
assertThat(result, hasNoStandardOutput());
assertThat(result.stderr(), containsString("No such job 'aProject'"));
}
@Test public void deleteJobShouldSucceed() throws Exception {
j.createFreeStyleProject("aProject");
final CLICommandInvoker.Result result = command
.authorizedTo(Job.READ, Job.DELETE, Jenkins.READ)
.invokeWithArgs("aProject");
assertThat(result, succeededSilently());
assertThat(j.jenkins.getItem("aProject"), nullValue());
}
@Test public void deleteJobShouldFailIfJobDoesNotExist() {
final CLICommandInvoker.Result result = command
.authorizedTo(Job.READ, Job.DELETE, Jenkins.READ)
.invokeWithArgs("never_created");
assertThat(result, failedWith(-1));
assertThat(result, hasNoStandardOutput());
assertThat(result.stderr(), containsString("No such job 'never_created'"));
}
@Test public void deleteJobManyShouldSucceed() throws Exception {
j.createFreeStyleProject("aProject1");
j.createFreeStyleProject("aProject2");
j.createFreeStyleProject("aProject3");
final CLICommandInvoker.Result result = command
.authorizedTo(Job.READ, Job.DELETE, Jenkins.READ)
.invokeWithArgs("aProject1", "aProject2", "aProject3");
assertThat(result, succeededSilently());
assertThat(j.jenkins.getItem("aProject1"), nullValue());
assertThat(j.jenkins.getItem("aProject2"), nullValue());
assertThat(j.jenkins.getItem("aProject3"), nullValue());
}
@Test public void deleteJobManyShouldFailIfAJobDoesNotExist() throws Exception {
j.createFreeStyleProject("aProject1");
j.createFreeStyleProject("aProject2");
final CLICommandInvoker.Result result = command
.authorizedTo(Job.READ, Job.DELETE, Jenkins.READ)
.invokeWithArgs("aProject1", "aProject2", "never_created");
assertThat(result, failedWith(-1));
assertThat(result, hasNoStandardOutput());
assertThat(result.stderr(), containsString("No such job 'never_created'"));
assertThat(j.jenkins.getItem("aProject1"), nullValue());
assertThat(j.jenkins.getItem("aProject2"), nullValue());
assertThat(j.jenkins.getItem("never_created"), nullValue());
}
@Test public void deleteJobManyShouldSucceedEvenAJobIsSpecifiedTwice() throws Exception {
j.createFreeStyleProject("aProject1");
j.createFreeStyleProject("aProject2");
final CLICommandInvoker.Result result = command
.authorizedTo(Job.READ, Job.DELETE, Jenkins.READ)
.invokeWithArgs("aProject1", "aProject2", "aProject1");
assertThat(result, succeededSilently());
assertThat(j.jenkins.getItem("aProject1"), nullValue());
assertThat(j.jenkins.getItem("aProject2"), nullValue());
}
}
/*
* The MIT License
*
* Copyright 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.
*/
/**
* @author pjanouse
*/
package hudson.cli;
import hudson.model.Computer;
import jenkins.model.Jenkins;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;
import static hudson.cli.CLICommandInvoker.Matcher.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.nullValue;
public class DeleteNodeCommandTest {
private CLICommandInvoker command;
@Rule public final JenkinsRule j = new JenkinsRule();
@Before public void setUp() {
command = new CLICommandInvoker(j, new DeleteNodeCommand());
}
@Test public void deleteNodeShouldFailWithoutNodeDeletePermission() throws Exception {
j.createSlave("aNode", "", null);
final CLICommandInvoker.Result result = command
.authorizedTo(Jenkins.READ)
.invokeWithArgs("aNode")
;
assertThat(result, failedWith(-1));
assertThat(result, hasNoStandardOutput());
assertThat(result.stderr(), containsString("user is missing the Slave/Delete permission"));
}
@Test public void deleteNodeShouldSucceed() throws Exception {
j.createSlave("aNode", "", null);
final CLICommandInvoker.Result result = command
.authorizedTo(Computer.DELETE, Jenkins.READ)
.invokeWithArgs("aNode")
;
assertThat(result, succeededSilently());
assertThat(j.jenkins.getNode("aNode"), nullValue());
}
@Test public void deleteNodeShouldFailIfNodeDoesNotExist() {
final CLICommandInvoker.Result result = command
.authorizedTo(Computer.DELETE, Jenkins.READ)
.invokeWithArgs("never_created")
;
assertThat(result, failedWith(-1));
assertThat(result, hasNoStandardOutput());
assertThat(result.stderr(), containsString("No such node 'never_created'"));
}
@Test public void deleteNodeManyShouldSucceed() throws Exception {
j.createSlave("aNode1", "", null);
j.createSlave("aNode2", "", null);
j.createSlave("aNode3", "", null);
final CLICommandInvoker.Result result = command
.authorizedTo(Computer.DELETE, Jenkins.READ)
.invokeWithArgs("aNode1", "aNode2", "aNode3");
assertThat(result, succeededSilently());
assertThat(j.jenkins.getView("aNode1"), nullValue());
assertThat(j.jenkins.getView("aNode2"), nullValue());
assertThat(j.jenkins.getView("aNode3"), nullValue());
}
@Test public void deleteNodeManyShouldFailIfANodeDoesNotExist() throws Exception {
j.createSlave("aNode1", "", null);
j.createSlave("aNode2", "", null);
final CLICommandInvoker.Result result = command
.authorizedTo(Computer.DELETE, Jenkins.READ)
.invokeWithArgs("aNode1", "aNode2", "never_created");
assertThat(result, failedWith(-1));
assertThat(result, hasNoStandardOutput());
assertThat(result.stderr(), containsString("No such node 'never_created'"));
assertThat(j.jenkins.getView("aNode1"), nullValue());
assertThat(j.jenkins.getView("aNode2"), nullValue());
assertThat(j.jenkins.getView("never_created"), nullValue());
}
@Test public void deleteNodeManyShouldSucceedEvenANodeIsSpecifiedTwice() throws Exception {
j.createSlave("aNode1", "", null);
j.createSlave("aNode2", "", null);
final CLICommandInvoker.Result result = command
.authorizedTo(Computer.DELETE, Jenkins.READ)
.invokeWithArgs("aNode1", "aNode2", "aNode1");
assertThat(result, succeededSilently());
assertThat(j.jenkins.getView("aNode1"), nullValue());
assertThat(j.jenkins.getView("aNode2"), nullValue());
}
}
/*
* The MIT License
*
* Copyright 2013 Red Hat, Inc.
* Copyright 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
......@@ -27,10 +27,9 @@ package hudson.cli;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.notNullValue;
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.succeededSilently;
import static hudson.cli.CLICommandInvoker.Matcher.failedWith;
......@@ -45,6 +44,9 @@ import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;
/**
* @author ogondza, pjanouse
*/
public class DeleteViewCommandTest {
private CLICommandInvoker command;
......@@ -70,6 +72,20 @@ public class DeleteViewCommandTest {
assertThat(result.stderr(), containsString("user is missing the View/Delete permission"));
}
@Test public void deleteViewShouldFailWithoutViewReadPermission() throws IOException {
j.jenkins.addView(new ListView("aView"));
final CLICommandInvoker.Result result = command
.authorizedTo(View.DELETE, 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 deleteViewShouldSucceed() throws Exception {
j.jenkins.addView(new ListView("aView"));
......@@ -105,6 +121,73 @@ public class DeleteViewCommandTest {
assertThat(result, failedWith(-1));
assertThat(result, hasNoStandardOutput());
assertThat(j.jenkins.getView("All"), notNullValue());
assertThat(result.stderr(), containsString("Jenkins does not allow to delete 'All' view"));
}
@Test public void deleteViewManyShouldSucceed() throws Exception {
j.jenkins.addView(new ListView("aView1"));
j.jenkins.addView(new ListView("aView2"));
j.jenkins.addView(new ListView("aView3"));
final CLICommandInvoker.Result result = command
.authorizedTo(View.READ, View.DELETE, Jenkins.READ)
.invokeWithArgs("aView1", "aView2", "aView3");
assertThat(result, succeededSilently());
assertThat(j.jenkins.getView("aView1"), nullValue());
assertThat(j.jenkins.getView("aView2"), nullValue());
assertThat(j.jenkins.getView("aView3"), nullValue());
}
@Test public void deleteViewManyShouldFailIfAViewDoesNotExist() throws Exception {
j.jenkins.addView(new ListView("aView1"));
j.jenkins.addView(new ListView("aView2"));
final CLICommandInvoker.Result result = command
.authorizedTo(View.READ, View.DELETE, Jenkins.READ)
.invokeWithArgs("aView1", "aView2", "never_created");
assertThat(result, failedWith(-1));
assertThat(result, hasNoStandardOutput());
assertThat(result.stderr(), containsString("No view named never_created inside view Jenkins"));
assertThat(j.jenkins.getView("aView1"), nullValue());
assertThat(j.jenkins.getView("aView2"), nullValue());
assertThat(j.jenkins.getView("never_created"), nullValue());
}
@Test public void deleteViewManyShouldSucceedEvenAViewSpecifiedTwice() throws Exception {
j.jenkins.addView(new ListView("aView1"));
j.jenkins.addView(new ListView("aView2"));
final CLICommandInvoker.Result result = command
.authorizedTo(View.READ, View.DELETE, Jenkins.READ)
.invokeWithArgs("aView1", "aView2", "aView1");
assertThat(result, succeededSilently());
assertThat(j.jenkins.getView("aView1"), nullValue());
assertThat(j.jenkins.getView("aView2"), nullValue());
}
@Test public void deleteViewManyShouldFailWithoutViewDeletePermissionButOthersShouldBeDeleted() throws Exception {
j.jenkins.addView(new ListView("aView1"));
j.jenkins.addView(new ListView("aView2"));
final CLICommandInvoker.Result result = command
.authorizedTo(View.READ, View.DELETE, Jenkins.READ)
.invokeWithArgs("aView1", "aView2", "All");
assertThat(result, failedWith(-1));
assertThat(result, hasNoStandardOutput());
assertThat(result.stderr(), containsString("Jenkins does not allow to delete 'All' view"));
assertThat(j.jenkins.getView("aView1"), nullValue());
assertThat(j.jenkins.getView("aView2"), nullValue());
assertThat(j.jenkins.getView("All"), notNullValue());
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册