提交 dd2ac731 编写于 作者: K Kohsuke Kawaguchi

Merge branch 'master' of github.com:jenkinsci/jenkins

......@@ -74,6 +74,19 @@ Upcoming changes</a>
<li class="major bug">
Extended Choice parameter definitions could not be saved since 1.637.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-31458">issue 31458</a>)
<li class="rfe">
Display expected CRON run times even if a warning occurs.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-29059">issue 29059</a>)
<li class="rfe">
Rework the <code>online-node</code> command implementation, no functional changes.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-31776">issue 31776</a>)
<li class="bug">
Fix the footer behavior in particular cases.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-30304">issue 30304</a>,
<a href="https://issues.jenkins-ci.org/browse/JENKINS-31395">issue 31395</a>)
<li class="rfe">
API changes: Deprecate subclassing of <code>hudson.Plugin</code>.
(<a href="https://github.com/jenkinsci/jenkins/pull/1940">PR 1940</a>)
</ul>
<h3><a name=v1.639>What's new in 1.639</a> (2015/11/29)</h3>
<ul class=image>
......
......@@ -40,15 +40,18 @@ import java.io.File;
import net.sf.json.JSONObject;
import com.thoughtworks.xstream.XStream;
import hudson.init.Initializer;
import hudson.init.Terminator;
import java.net.URI;
import java.net.URISyntaxException;
import jenkins.model.GlobalConfiguration;
import org.kohsuke.stapler.HttpResponses;
/**
* Base class of Hudson plugin.
*
* <p>
* A plugin may derive from this class, or it may directly define extension
* A plugin may {@linkplain #Plugin derive from this class}, or it may directly define extension
* points annotated with {@link hudson.Extension}. For a list of extension
* points, see <a href="https://wiki.jenkins-ci.org/display/JENKINS/Extension+points">
* https://wiki.jenkins-ci.org/display/JENKINS/Extension+points</a>.
......@@ -78,6 +81,22 @@ import org.kohsuke.stapler.HttpResponses;
*/
public abstract class Plugin implements Saveable {
/**
* You do not need to create custom subtypes:
* <ul>
* <li>{@code config.jelly}, {@link #configure(StaplerRequest, JSONObject)}, {@link #load}, and {@link #save}
* can be replaced by {@link GlobalConfiguration}
* <li>{@link #start} and {@link #postInitialize} can be replaced by {@link Initializer} (or {@link ItemListener#onLoaded})
* <li>{@link #stop} can be replaced by {@link Terminator}
* <li>{@link #setServletContext} can be replaced by {@link Jenkins#servletContext}
* </ul>
* Note that every plugin gets a {@link DummyImpl} by default,
* which will still route the URL space, serve {@link #getWrapper}, and so on.
* @deprecated Use more modern APIs rather than subclassing.
*/
@Deprecated
protected Plugin() {}
/**
* Set by the {@link PluginManager}, before the {@link #start()} method is called.
* This points to the {@link PluginWrapper} that wraps
......
/*
* 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.Extension;
import hudson.model.Computer;
import jenkins.model.Jenkins;
import org.acegisecurity.AccessDeniedException;
import org.kohsuke.args4j.Argument;
import java.util.logging.Logger;
/**
* @author pjanouse
* @since TODO
*/
@Extension
public class OnlineNodeCommand extends CLICommand {
@Argument(metaVar="NAME", usage="Slave name, or empty string for master")
public String computerName;
private static final Logger LOGGER = Logger.getLogger(OnlineNodeCommand.class.getName());
@Override
public String getShortDescription() {
return Messages.OnlineNodeCommand_ShortDescription();
}
@Override
protected int run() throws Exception {
boolean errorOccurred = false;
final Jenkins jenkins = Jenkins.getInstance();
Computer computer = jenkins.getComputer(computerName);
if (computer == null) {
stderr.println(hudson.model.Messages.Computer_NoSuchSlaveExists(computerName, null));
errorOccurred = true;
} else {
try {
computer.cliOnline();
} catch (AccessDeniedException e) {
stderr.println(e.getMessage());
errorOccurred = true;
} catch (Exception e) {
final String errorMsg = String.format("Unexpected exception occurred during performing online operation on node '%s': %s",
computer == null ? "(null)" : computer.getName(),
e.getMessage());
stderr.println(errorMsg);
LOGGER.warning(errorMsg);
errorOccurred = true;
}
}
return errorOccurred ? 1 : 0;
}
}
......@@ -522,7 +522,6 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
setTemporarilyOffline(true, new ByCLI(cause));
}
@CLIMethod(name="online-node")
public void cliOnline() throws ExecutionException, InterruptedException {
checkPermission(CONNECT);
setTemporarilyOffline(false, null);
......
......@@ -25,7 +25,6 @@ package hudson.model;
import hudson.ExtensionPoint;
import hudson.Launcher;
import hudson.Plugin;
import hudson.model.Descriptor.FormException;
import hudson.model.queue.SubTask;
import hudson.tasks.BuildStep;
......@@ -49,7 +48,7 @@ import javax.annotation.Nonnull;
* Extensible property of {@link Job}.
*
* <p>
* {@link Plugin}s can extend this to define custom properties
* Plugins can extend this to define custom properties
* for {@link Job}s. {@link JobProperty}s show up in the user
* configuration screen, and they are persisted with the job object.
*
......
......@@ -24,7 +24,6 @@
package hudson.model;
import hudson.ExtensionPoint;
import hudson.Plugin;
import hudson.Extension;
import hudson.ExtensionList;
import hudson.util.DescriptorList;
......@@ -45,7 +44,7 @@ import java.util.List;
*
* <h2>Life-cycle</h2>
* <p>
* {@link Plugin}s that contribute this extension point
* Plugins that contribute this extension point
* should implement a new decorator and put {@link Extension} on the class.
*
* <h2>Associated Views</h2>
......
......@@ -24,7 +24,6 @@
package hudson.model;
import hudson.ExtensionPoint;
import hudson.Plugin;
import hudson.DescriptorExtensionList;
import hudson.model.Descriptor.FormException;
import jenkins.model.Jenkins;
......@@ -37,7 +36,7 @@ import org.kohsuke.stapler.export.ExportedBean;
* Extensible property of {@link User}.
*
* <p>
* {@link Plugin}s can extend this to define custom properties
* Plugins can extend this to define custom properties
* for {@link User}s. {@link UserProperty}s show up in the user
* configuration screen, and they are persisted with the user object.
*
......
......@@ -34,7 +34,10 @@ import hudson.scheduler.CronTabList;
import hudson.scheduler.Hash;
import hudson.util.FormValidation;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import org.kohsuke.stapler.AncestorInPath;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;
......@@ -83,22 +86,32 @@ public class TimerTrigger extends Trigger<BuildableItem> {
public FormValidation doCheckSpec(@QueryParameter String value, @AncestorInPath Item item) {
try {
CronTabList ctl = CronTabList.create(fixNull(value), item != null ? Hash.from(item.getFullName()) : null);
String msg = ctl.checkSanity();
if(msg!=null) return FormValidation.warning(msg);
Calendar prev = ctl.previous();
Calendar next = ctl.next();
if (prev != null && next != null) {
DateFormat fmt = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL);
return FormValidation.ok(Messages.TimerTrigger_would_last_have_run_at_would_next_run_at(fmt.format(prev.getTime()), fmt.format(next.getTime())));
} else {
return FormValidation.warning(Messages.TimerTrigger_no_schedules_so_will_never_run());
}
Collection<FormValidation> validations = new ArrayList<>();
updateValidationsForSanity(validations, ctl);
updateValidationsForNextRun(validations, ctl);
return FormValidation.aggregate(validations);
} catch (ANTLRException e) {
if (value.trim().indexOf('\n')==-1 && value.contains("**"))
return FormValidation.error(Messages.TimerTrigger_MissingWhitespace());
return FormValidation.error(e.getMessage());
}
}
private void updateValidationsForSanity(Collection<FormValidation> validations, CronTabList ctl) {
String msg = ctl.checkSanity();
if(msg!=null) validations.add(FormValidation.warning(msg));
}
private void updateValidationsForNextRun(Collection<FormValidation> validations, CronTabList ctl) {
Calendar prev = ctl.previous();
Calendar next = ctl.next();
if (prev != null && next != null) {
DateFormat fmt = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL);
validations.add(FormValidation.ok(Messages.TimerTrigger_would_last_have_run_at_would_next_run_at(fmt.format(prev.getTime()), fmt.format(next.getTime()))));
} else {
validations.add(FormValidation.warning(Messages.TimerTrigger_no_schedules_so_will_never_run()));
}
}
}
public static class TimerTriggerCause extends Cause {
......
......@@ -85,3 +85,4 @@ DeleteNodeCommand.ShortDescription=Deletes node(s)
ReloadJobCommand.ShortDescription=Reload job(s)
OnlineNodeCommand.ShortDescription=Resume using a node for performing builds, to cancel out the earlier "offline-node" command.
DeleteNodeCommand.ShortDescription=Sletter en node
DeleteJobCommand.ShortDescription=Sletter et job
OnlineNodeCommand.ShortDescription=Genoptag brugen af en byggenode, for at annullere en tidligere udstedt "offline-node" kommando.
DeleteNodeCommand.ShortDescription=Knoten l\u00f6schen.
DeleteJobCommand.ShortDescription=Job l\u00f6schen.
OnlineNodeCommand.ShortDescription=Knoten wird wieder f\u00fcr neue Builds verwendet. Hebt ein vorausgegangenes "offline-node"-Kommando auf.
......@@ -50,3 +50,5 @@ SetBuildDescriptionCommand.ShortDescription=Establece la descripci
DeleteJobCommand.ShortDescription=Borrar una tarea
DeleteNodeCommand.ShortDescription=Borrar un nodo
OnlineNodeCommand.ShortDescription=Continuar usando un nodo y candelar el comando "offline-node" mas reciente.
DeleteNodeCommand.ShortDescription=Cancella un nodo
DeleteJobCommand.ShortDescription=Cancella un job
OnlineNodeCommand.ShortDescription=Resume using a node for performing builds, to cancel out the earlier "offline-node" command.
......@@ -72,3 +72,5 @@ BuildCommand.CLICause.CannotBuildUnknownReasons=\
DeleteNodeCommand.ShortDescription=\u30ce\u30fc\u30c9\u3092\u524a\u9664\u3057\u307e\u3059\u3002
DeleteJobCommand.ShortDescription=\u30b8\u30e7\u30d6\u3092\u524a\u9664\u3057\u307e\u3059\u3002
OnlineNodeCommand.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
......@@ -118,3 +118,5 @@ DeleteJobCommand.ShortDescription=Remover uma job(s)
ReloadJobCommand.ShortDescription=\
Recarrega job(s) do disco.
OnlineNodeCommand.ShortDescription=Continuar usando um n\u00f3 para realizar os builds
......@@ -95,7 +95,6 @@ CLI.disable-job.shortDescription=Disables a job.
CLI.enable-job.shortDescription=Enables a job.
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.
CLI.offline-node.shortDescription=Stop using a node for performing builds temporarily, until the next "online-node" command.
CLI.wait-node-online.shortDescription=Wait for a node to become online.
CLI.wait-node-offline.shortDescription=Wait for a node to become offline.
......
......@@ -216,7 +216,6 @@ Queue.InProgress=Et byg er allerede i gang
Computer.DeletePermission.Description=Denne rettighed tillader brugere at slette eksisterende slaver.
UpdateCenter.Status.ConnectionFailed=
UpdateCenter.PluginCategory.maven=Maven
CLI.online-node.shortDescription=Genoptag brugen af en byggenode, for at annullere en tidligere udstedt "offline-node" kommando.
Computer.Permissions.Title=Slave
BallColor.Success=Succes
UpdateCenter.PluginCategory.upload=Artifaktsendere
......
......@@ -84,7 +84,6 @@ CLI.enable-job.shortDescription=Job aktivieren.
CLI.connect-node.shortDescription=Erneut mit Knoten verbinden.
CLI.disconnect-node.shortDescription=Knoten trennen.
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.
Queue.init=Build-Warteschlange neu initialisieren
......
......@@ -61,7 +61,6 @@ CLI.disable-job.shortDescription=Desactivar una tarea
CLI.enable-job.shortDescription=Activar una tarea
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.
CLI.offline-node.shortDescription=Dejar de utilizar un nodo temporalmente hasta que se ejecute el comando "online-node".
Computer.Caption=Remoto {0}
......
......@@ -78,7 +78,6 @@ CLI.disable-job.shortDescription=Disabilita un job
CLI.enable-job.shortDescription=Abilita un job
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.
CLI.offline-node.shortDescription=Stop using a node for performing builds temporarily, until the next "online-node" command.
CLI.wait-node-online.shortDescription=Attende che il nodo sia attivo
CLI.wait-node-offline.shortDescription=Attende che un nodo sia disattivo
......
......@@ -320,7 +320,6 @@ CLI.disable-job.shortDescription=\u30b8\u30e7\u30d6\u3092\u7121\u52b9\u5316\u305
CLI.enable-job.shortDescription=\u30b8\u30e7\u30d6\u3092\u6709\u52b9\u5316\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
CLI.offline-node.shortDescription="online-node"\u30b3\u30de\u30f3\u30c9\u304c\u5b9f\u884c\u3055\u308c\u308b\u307e\u3067\u3001\u30d3\u30eb\u30c9\u3092\u5b9f\u884c\u3059\u308b\u30ce\u30fc\u30c9\u306e\u4f7f\u7528\u3092\u4e00\u6642\u7684\u306b\u505c\u6b62\u3057\u307e\u3059\u3002
CLI.wait-node-online.shortDescription=\u30ce\u30fc\u30c9\u304c\u30aa\u30f3\u30e9\u30a4\u30f3\u306b\u306a\u308b\u306e\u3092\u5f85\u3061\u307e\u3059\u3002
CLI.wait-node-offline.shortDescription=\u30ce\u30fc\u30c9\u304c\u30aa\u30d5\u30e9\u30a4\u30f3\u306b\u306a\u308b\u306e\u3092\u5f85\u3061\u307e\u3059\u3002
......
......@@ -357,8 +357,6 @@ UpdateCenter.Status.ConnectionFailed=Erro ao conectar {0} /
UpdateCenter.PluginCategory.maven=Maven
# Slave
Computer.Permissions.Title=Slave
# Resume using a node for performing builds, to cancel out the earlier "offline-node" command.
CLI.online-node.shortDescription=Continuar usando um n\u00f3 para realizar os builds
# Artifact Uploaders
UpdateCenter.PluginCategory.upload=Carregadores de artefatos
# Last unstable build
......
......@@ -227,7 +227,7 @@ ${h.initPageVariables(context)}
</div>
</div>
<div id="page-body">
<div id="page-body" class="clear">
<div id="side-panel">
<j:set var="mode" value="side-panel" />
<d:invokeBody />
......
/*
* 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 hudson.slaves.DumbSlave;
import jenkins.model.Jenkins;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;
import static hudson.cli.CLICommandInvoker.Matcher.failedWith;
import static hudson.cli.CLICommandInvoker.Matcher.hasNoStandardOutput;
import static hudson.cli.CLICommandInvoker.Matcher.succeededSilently;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
public class OnlineNodeCommandTest {
private CLICommandInvoker command;
@Rule public final JenkinsRule j = new JenkinsRule();
@Before public void setUp() {
command = new CLICommandInvoker(j, "online-node");
}
@Test public void onlineNodeShouldFailWithoutComputerConnectPermission() 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/Connect permission"));
}
@Test public void onlineNodeShouldFailIfNodeDoesNotExist() throws Exception {
final CLICommandInvoker.Result result = command
.authorizedTo(Computer.CONNECT, Jenkins.READ)
.invokeWithArgs("never_created");
assertThat(result, failedWith(1));
assertThat(result, hasNoStandardOutput());
assertThat(result.stderr(), containsString("No such slave \"never_created\" exists."));
}
@Test public void onlineNodeShouldSucceed() throws Exception {
DumbSlave slave = j.createSlave("aNode", "", null);
CLICommandInvoker.Result result = command
.authorizedTo(Computer.CONNECT, Jenkins.READ)
.invokeWithArgs("aNode");
assertThat(result, succeededSilently());
if (slave.toComputer().isConnecting()) {
System.out.println("Waiting until online in progress...");
slave.toComputer().waitUntilOnline();
}
assertThat(slave.toComputer().isOnline(), equalTo(true));
}
@Test public void onlineNodeShouldSucceedOnOnlineNode() throws Exception {
DumbSlave slave = j.createSlave("aNode", "", null);
if (slave.toComputer().isConnecting()) {
System.out.println("Waiting until online in progress...");
slave.toComputer().waitUntilOnline();
}
assertThat(slave.toComputer().isOnline(), equalTo(true));
CLICommandInvoker.Result result = command
.authorizedTo(Computer.CONNECT, Jenkins.READ)
.invokeWithArgs("aNode");
assertThat(result, succeededSilently());
assertThat(slave.toComputer().isOnline(), equalTo(true));
}
@Test public void onlineNodeShouldSucceedOnOfflieNode() throws Exception {
DumbSlave slave = j.createSlave("aNode", "", null);
if (slave.toComputer().isConnecting()) {
System.out.println("Waiting until online in progress...");
slave.toComputer().waitUntilOnline();
}
assertThat(slave.toComputer().isOnline(), equalTo(true));
slave.toComputer().setTemporarilyOffline(true);
slave.toComputer().waitUntilOffline();
assertThat(slave.toComputer().isOffline(), equalTo(true));
CLICommandInvoker.Result result = command
.authorizedTo(Computer.CONNECT, Jenkins.READ)
.invokeWithArgs("aNode");
assertThat(result, succeededSilently());
if (slave.toComputer().isConnecting()) {
System.out.println("Waiting until online in progress...");
slave.toComputer().waitUntilOnline();
}
assertThat(slave.toComputer().isOnline(), equalTo(true));
}
@Test public void onlineNodeShouldSucceedOnDisconnectedNode() throws Exception {
DumbSlave slave = j.createSlave("aNode", "", null);
if (slave.toComputer().isConnecting()) {
System.out.println("Waiting until online in progress...");
slave.toComputer().waitUntilOnline();
}
assertThat(slave.toComputer().isOnline(), equalTo(true));
slave.toComputer().disconnect();
slave.toComputer().waitUntilOffline();
assertThat(slave.toComputer().isOffline(), equalTo(true));
CLICommandInvoker.Result result = command
.authorizedTo(Computer.CONNECT, Jenkins.READ)
.invokeWithArgs("aNode");
assertThat(result, succeededSilently());
if (slave.toComputer().isConnecting()) {
System.out.println("Waiting until online in progress...");
slave.toComputer().waitUntilOnline();
}
assertThat(slave.toComputer().isOnline(), equalTo(false));
}
@Test public void onlineNodeShouldSucceedOnDisconnectingNode() throws Exception {
DumbSlave slave = j.createSlave("aNode", "", null);
if (slave.toComputer().isConnecting()) {
System.out.println("Waiting until online in progress...");
slave.toComputer().waitUntilOnline();
}
assertThat(slave.toComputer().isOnline(), equalTo(true));
slave.toComputer().disconnect();
CLICommandInvoker.Result result = command
.authorizedTo(Computer.CONNECT, Jenkins.READ)
.invokeWithArgs("aNode");
assertThat(result, succeededSilently());
if (slave.toComputer().isConnecting()) {
System.out.println("Waiting until online in progress...");
slave.toComputer().waitUntilOnline();
}
assertThat(slave.toComputer().isOnline(), equalTo(false));
}
}
......@@ -31,10 +31,7 @@ html {
body {
margin: 0;
padding: 0;
background: white;
margin-right: auto;
margin-left: auto;
padding: 0 0 40px 0;
}
#header {
......@@ -74,14 +71,20 @@ body {
background-color: #f6faf2;
}
#page-body.clear:after {
clear: both;
content: "";
display: table;
}
#side-panel {
padding: 15px 15px 80px 15px;
padding: 15px 15px 40px 15px;
float: left;
width: 320px;
}
#main-panel {
padding: 15px 15px 80px 15px;
padding: 15px 15px 40px 15px;
margin-left: 320px;
}
......@@ -129,13 +132,15 @@ body {
/* footer */
footer {
padding: 10px 0;
padding: 11px 0;
background-color: #f6faf2;
border-top: 1px solid #d3d7cf;
border-bottom: 1px solid #f6faf2;
width: 100%;
position: absolute;
bottom: 0;
left: 0;
clear: both;
font-size: 12px;
text-align: right;
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册