提交 7cb015e1 编写于 作者: K Kohsuke Kawaguchi

Added two new CLI commands, inspired by pull-91.

上级 745039bb
......@@ -58,6 +58,8 @@ Upcoming changes</a>
<li class=bug>
Some french strings are incorrect after renaming to Jenkins
(<a href="http://issues.jenkins-ci.org/browse/JENKINS-9334">issue 9334</a>)
<li class=rfe>
Added two new CLI commands "wait-node-online" and "wait-node-offline" to block until a slave becomes online/offline.
</ul>
</div><!--=TRUNK-END=-->
......
......@@ -49,7 +49,7 @@ import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
/**
* CLI entry point to Hudson.
* CLI entry point to Jenkins.
*
* @author Kohsuke Kawaguchi
*/
......@@ -59,12 +59,12 @@ public class CLI {
private final CliEntryPoint entryPoint;
private final boolean ownsPool;
public CLI(URL hudson) throws IOException, InterruptedException {
this(hudson,null);
public CLI(URL jenkins) throws IOException, InterruptedException {
this(jenkins,null);
}
public CLI(URL hudson, ExecutorService exec) throws IOException, InterruptedException {
String url = hudson.toExternalForm();
public CLI(URL jenkins, ExecutorService exec) throws IOException, InterruptedException {
String url = jenkins.toExternalForm();
if(!url.endsWith("/")) url+='/';
ownsPool = exec==null;
......@@ -79,17 +79,17 @@ public class CLI {
DataOutputStream dos = new DataOutputStream(s.getOutputStream());
dos.writeUTF("Protocol:CLI-connect");
channel = new Channel("CLI connection to "+hudson, pool,
channel = new Channel("CLI connection to "+jenkins, pool,
new BufferedInputStream(new SocketInputStream(s)),
new BufferedOutputStream(new SocketOutputStream(s)));
} else {
// connect via HTTP
LOGGER.fine("Trying to connect to "+url+" via HTTP");
url+="cli";
hudson = new URL(url);
jenkins = new URL(url);
FullDuplexHttpStream con = new FullDuplexHttpStream(hudson);
channel = new Channel("Chunked connection to "+hudson,
FullDuplexHttpStream con = new FullDuplexHttpStream(jenkins);
channel = new Channel("Chunked connection to "+jenkins,
pool,con.getInputStream(),con.getOutputStream());
new PingThread(channel,30*1000) {
protected void onDead() {
......
/*
* The MIT License
*
*
* Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi,
* Red Hat, Inc., Seiji Sogabe, Stephen Connolly, Thomas J. Black, Tom Huybrechts, CloudBees, 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
......@@ -27,6 +27,7 @@ package hudson.model;
import hudson.EnvVars;
import hudson.Util;
import hudson.cli.declarative.CLIMethod;
import hudson.cli.declarative.CLIResolver;
import hudson.console.AnnotatedLargeText;
import hudson.model.Descriptor.FormException;
import hudson.model.queue.WorkUnit;
......@@ -46,11 +47,14 @@ import hudson.slaves.OfflineCause.ByCLI;
import hudson.tasks.BuildWrapper;
import hudson.tasks.Publisher;
import hudson.util.DaemonThreadFactory;
import hudson.util.EditDistance;
import hudson.util.ExceptionCatchingThreadFactory;
import hudson.util.RemotingDiagnostics;
import hudson.util.RemotingDiagnostics.HeapDump;
import hudson.util.RunList;
import hudson.util.Futures;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.QueryParameter;
......@@ -121,7 +125,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
private long connectTime = 0;
/**
* True if Hudson shouldn't start new builds on this node.
* True if Jenkins shouldn't start new builds on this node.
*/
private boolean temporarilyOffline;
......@@ -141,6 +145,8 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
protected transient List<Action> transientActions;
protected final Object statusChangeLock = new Object();
public Computer(Node node) {
assert node.getNumExecutors()!=0 : "Computer created with 0 executors";
setNode(node);
......@@ -521,6 +527,9 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
this.temporarilyOffline = temporarilyOffline;
getNode().setTemporaryOfflineCause(offlineCause);
Hudson.getInstance().getQueue().scheduleMaintenance();
synchronized (statusChangeLock) {
statusChangeLock.notifyAll();
}
}
@Exported
......@@ -1061,6 +1070,25 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
return new HttpRedirect("..");
}
/**
* Blocks until the node becomes online/offline.
*/
@CLIMethod(name="wait-node-online")
public void waitUntilOnline() throws InterruptedException {
synchronized (statusChangeLock) {
while (!isOnline())
wait(1000);
}
}
@CLIMethod(name="wait-node-offline")
public void waitUntilOffline() throws InterruptedException {
synchronized (statusChangeLock) {
while (!isOffline())
wait(1000);
}
}
/**
* Handles incremental log.
*/
......@@ -1089,6 +1117,24 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
return true;
}
/**
* Used for CLI binding.
*/
@CLIResolver
public static Computer resolveForCLI(
@Argument(required=true,metaVar="NAME",usage="Slave name, or empty string for master") String name) throws CmdLineException {
Hudson h = Hudson.getInstance();
Computer item = h.getComputer(name);
if (item==null) {
List<String> names = new ArrayList<String>();
for (Computer c : h.getComputers())
if (c.getName().length()>0)
names.add(c.getName());
throw new CmdLineException(null,Messages.Computer_NoSuchSlaveExists(name,EditDistance.findNearest(name,names)));
}
return item;
}
public static final PermissionGroup PERMISSIONS = new PermissionGroup(Computer.class,Messages._Computer_Permissions_Title());
/**
* Permission to configure slaves.
......
......@@ -365,6 +365,10 @@ public class SlaveComputer extends Computer {
numRetryAttempt = 0;
this.channel = channel;
defaultCharset = Charset.forName(defaultCharsetName);
synchronized (statusChangeLock) {
statusChangeLock.notifyAll();
}
}
for (ComputerListener cl : ComputerListener.all())
cl.onOnline(this,taskListener);
......
......@@ -79,8 +79,11 @@ 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
Computer.Caption=Slave {0}
Computer.NoSuchSlaveExists=No such slave "{0}" exists. Did you mean "{1}"?
Computer.Permissions.Title=Slave
Computer.ConfigurePermission.Description=This permission allows users to configure slaves.
Computer.DeletePermission.Description=This permission allows users to delete existing slaves.
......
......@@ -23,6 +23,8 @@
*/
package hudson.model;
import hudson.cli.CLI;
import hudson.slaves.DumbSlave;
import org.jvnet.hudson.test.HudsonTestCase;
import org.jvnet.hudson.test.Bug;
import com.gargoylesoftware.htmlunit.html.HtmlForm;
......@@ -46,4 +48,20 @@ public class ComputerSetTest extends HudsonTestCase {
HtmlForm form = client.goTo("computer/configure").getFormByName("config");
submit(form);
}
public void testNodeOfflineCli() throws Exception {
DumbSlave s = createSlave();
CLI cli = new CLI(getURL());
try {
assertTrue(cli.execute("wait-node-offline","xxx")!=0);
assertTrue(cli.execute("wait-node-online",s.getNodeName())==0);
s.toComputer().disconnect().get();
assertTrue(cli.execute("wait-node-offline",s.getNodeName())==0);
} finally {
cli.close();
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册