Merge branch '2.0' of github.com:jenkinsci/jenkins into JENKINS-14538

Conflicts:
	core/src/main/resources/hudson/model/Computer/sidepanel.jelly
This commit is contained in:
Daniel Beck committed 2016-03-08 19:08:09 +01:00
commit 03291cf891
572 files changed
+4385 -3218

No files matched your search

Vendored
+191
View File
@@ -0,0 +1,191 @@
#!groovy
/*
* This Jenkinsfile is intended to run on https://ci.jenkins-ci.org and may fail anywhere else.
* It makes assumptions about plugins being installed, labels mapping to nodes that can build what is needed, etc.
*
* The required labels are "java" and "docker" - "java" would be any node that can run Java builds. It doesn't need
* to have Java installed, but some setups may have nodes that shouldn't have heavier builds running on them, so we
* make this explicit. "docker" would be any node with docker installed.
*/
// TEST FLAG - to make it easier to turn on/off unit tests for speeding up access to later stuff.
def runTests = true
// Only keep the 10 most recent builds.
properties([[$class: 'jenkins.model.BuildDiscarderProperty', strategy: [$class: 'LogRotator',
numToKeepStr: '50',
artifactNumToKeepStr: '20']]])
String packagingBranch = (binding.hasVariable('packagingBranch')) ? packagingBranch : 'master'
timestampedNode('java') {
// First stage is actually checking out the source. Since we're using Multibranch
// currently, we can use "checkout scm".
stage "Checkout source"
checkout scm
// Now run the actual build.
stage "Build and test"
// We're wrapping this in a timeout - if it takes more than 180 minutes, kill it.
timeout(time: 180, unit: 'MINUTES') {
// See below for what this method does - we're passing an arbitrary environment
// variable to it so that JAVA_OPTS and MAVEN_OPTS are set correctly.
withMavenEnv(["JAVA_OPTS=-Xmx1536m -Xms512m -XX:MaxPermSize=1024m",
"MAVEN_OPTS=-Xmx1536m -Xms512m -XX:MaxPermSize=1024m"]) {
// Actually run Maven!
// The -Dmaven.repo.local=${pwd()}/.repository means that Maven will create a
// .repository directory at the root of the build (which it gets from the
// pwd() Workflow call) and use that for the local Maven repository.
sh "mvn -Pdebug -U clean install ${runTests ? '-Dmaven.test.failure.ignore=true -Dconcurrency=1' : '-DskipTests'} -V -B -Dmaven.repo.local=${pwd()}/.repository"
}
}
// Once we've built, archive the artifacts and the test results.
stage "Archive artifacts and test results"
step([$class: 'ArtifactArchiver', artifacts: '**/target/*.jar, **/target/*.war, **/target/*.hpi', fingerprint: true])
if (runTests) {
step([$class: 'JUnitResultArchiver', healthScaleFactor: 20.0, testResults: '**/target/surefire-reports/*.xml'])
}
}
def debFileName
def rpmFileName
def suseFileName
// Run the packaging build on a node with the "docker" label.
timestampedNode('docker') {
// First stage here is getting prepped for packaging.
stage "packaging - docker prep"
// Docker environment to build packagings
dir('packaging-docker') {
git branch: packagingBranch, url: 'https://github.com/jenkinsci/packaging.git'
sh 'docker build -t jenkins-packaging-builder:0.1 docker'
}
stage "packaging - actually packaging"
// Working packaging code, separate branch with fixes
dir('packaging') {
deleteDir()
docker.image("jenkins-packaging-builder:0.1").inside("-u root") {
git branch: packagingBranch, url: 'https://github.com/jenkinsci/packaging.git'
try {
// Saw issues with unstashing inside a container, and not sure copy artifact plugin would work here.
// So, simple wget.
sh "wget -q ${currentBuild.absoluteUrl}/artifact/war/target/jenkins.war"
sh "make clean deb rpm suse BRAND=./branding/jenkins.mk BUILDENV=./env/test.mk CREDENTIAL=./credentials/test.mk WAR=jenkins.war"
} catch (Exception e) {
error "Packaging failed: ${e}"
} finally {
// Needed to make sure the output of the build can be deleted by later runs.
// Hackish, yes, but rpm builds as a numeric UID only user fail, so...
sh "chmod -R a+w target || true"
sh "chmod a+w jenkins.war || true"
}
dir("target/debian") {
def debFilesFound = findFiles(glob: "*.deb")
if (debFilesFound.size() > 0) {
debFileName = debFilesFound[0]?.name
}
}
dir("target/rpm") {
def rpmFilesFound = findFiles(glob: "*.rpm")
if (rpmFilesFound.size() > 0) {
rpmFileName = rpmFilesFound[0]?.name
}
}
dir("target/suse") {
def suseFilesFound = findFiles(glob: "*.rpm")
if (suseFilesFound.size() > 0) {
suseFileName = suseFilesFound[0]?.name
}
}
step([$class: 'ArtifactArchiver', artifacts: 'target/**/*', fingerprint: true])
// Fail the build if we didn't find at least one of the packages, meaning they weren't built but
// somehow make didn't error out.
if (debFileName == null || rpmFileName == null || suseFileName == null) {
error "At least one of Debian, RPM or SuSE packages are missing, so failing the build."
}
}
}
}
stage "Package testing"
if (runTests) {
if (!env.BRANCH_NAME.startsWith("PR")) {
// NOTE: As of now, a lot of package tests will fail. See https://issues.jenkins-ci.org/issues/?filter=15257 for
// possible open JIRAs.
// Basic parameters
String artifactName = (binding.hasVariable('artifactName')) ? artifactName : 'jenkins'
String jenkinsPort = (binding.hasVariable('jenkinsPort')) ? jenkinsPort : '8080'
// Set up
String debfile = "artifact://${env.JOB_NAME}/${env.BUILD_NUMBER}#target/debian/${debFileName}"
String rpmfile = "artifact://${env.JOB_NAME}/${env.BUILD_NUMBER}#target/rpm/${rpmFileName}"
String susefile = "artifact://${env.JOB_NAME}/${env.BUILD_NUMBER}#target/suse/${suseFileName}"
timestampedNode("docker") {
stage "Load Lib"
dir('workflowlib') {
deleteDir()
git branch: packagingBranch, url: 'https://github.com/jenkinsci/packaging.git'
flow = load 'workflow/installertest.groovy'
}
}
// Run the real tests within docker node label
flow.fetchAndRunJenkinsInstallerTest("docker", rpmfile, susefile, debfile,
packagingBranch, artifactName, jenkinsPort)
} else {
echo "Not running package testing against pull requests"
}
} else {
echo "Skipping package tests"
}
// This method sets up the Maven and JDK tools, puts them in the environment along
// with whatever other arbitrary environment variables we passed in, and runs the
// body we passed in within that environment.
void withMavenEnv(List envVars = [], def body) {
// The names here are currently hardcoded for my test environment. This needs
// to be made more flexible.
// Using the "tool" Workflow call automatically installs those tools on the
// node.
String mvntool = tool name: "mvn3.3.3", type: 'hudson.tasks.Maven$MavenInstallation'
String jdktool = tool name: "jdk7_80", type: 'hudson.model.JDK'
// Set JAVA_HOME, MAVEN_HOME and special PATH variables for the tools we're
// using.
List mvnEnv = ["PATH+MVN=${mvntool}/bin", "PATH+JDK=${jdktool}/bin", "JAVA_HOME=${jdktool}", "MAVEN_HOME=${mvntool}"]
// Add any additional environment variables.
mvnEnv.addAll(envVars)
// Invoke the body closure we're passed within the environment we've created.
withEnv(mvnEnv) {
body.call()
}
}
// Runs the given body within a Timestamper wrapper on the given label.
def timestampedNode(String label, Closure body) {
node(label) {
wrap([$class: 'TimestamperBuildWrapper']) {
body.call()
}
}
}
+16 -3
View File
@@ -1,19 +1,32 @@
[![][ButlerImage]][website]
# About
In a nutshell, Jenkins CI is the leading open-source continuous integration server. Built with Java, it provides over 1000 plugins to support building and testing virtually any project.
In a nutshell, Jenkins is the leading open-source automation server.
Built with Java, it provides over 1000 plugins to support automating virtually anything,
so that humans can actually spend their time doing things machines cannot.
# What to Use Jenkins for and When to Use It
Use Jenkins to automate your development workflow so you can focus on work that matters most. Jenkins is commonly used for:
- Building projects
- Running tests to detect bugs and other issues as soon as they are introduced
- Static code analysis
- Deployment
Execute repetitive tasks, save time, and optimize your development process with Jenkins.
# Downloads
Non-source downloads such as WAR files and several Linux packages can be found on our [Mirrors].
# Source
Our latest and greatest source of Jenkins CI can be found on [GitHub]. Fork us!
Our latest and greatest source of Jenkins can be found on [GitHub]. Fork us!
# Contributing to Jenkins
Follow [contributing](CONTRIBUTING.md) file.
# News and Website
All information about Jenkins CI can be found on our [website]. Follow us on Twitter [@jenkinsci].
All information about Jenkins can be found on our [website]. Follow us on Twitter [@jenkinsci].
# License
Jenkins is **licensed** under the **[MIT License]**. The terms of the license are as follows:
+44 -3
View File
@@ -55,9 +55,50 @@ Upcoming changes</a>
<!-- Record your changes in the trunk here. -->
<div id="trunk" style="display:none"><!--=TRUNK-BEGIN=-->
<ul class=image>
<li class=>
<li class="rfe">
Move periodic task log files from <code>JENKINS_HOME/*.log</code> to <code>JENKINS_HOME/logs/tasks/*.log</code> and rotate them periodically rather than overwrite every execution
(<a href-"https://issues.jenkins-ci.org/browse/JENKINS-33068">issue 33068</a>)
</ul>
</div><!--=TRUNK-END=-->
<h3><a name=v1.650>What's new in 1.650</a> (2016/02/24)</h3>
<ul class=image>
<li class="major bug">
<strong>Important security fixes</strong>
(<a href="https://wiki.jenkins-ci.org/display/SECURITY/Jenkins+Security+Advisory+2016-02-24">security advisory</a>)
</ul>
<h3><a name=v1.649>What's new in 1.649</a> (2016/02/21)</h3>
<ul class=image>
<li class="rfe">
Allow changing the directory used for the extraction of plugin archives via the <code>--pluginroot</code> CLI option (also controllable via the <code>hudson.PluginManager.workDir</code> system property / context parameter. Also document the <code>--webroot</code> CLI parameter in <code>java -jar jenkins.war --help</code>
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-32765">issue 32765</a>)
<li class="rfe">
Unify CLI exit code semantics.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-32273">issue 32273</a>)
<li class="bug">
ArrayIndexOutOfBoundsException when parsing range set.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-32852">issue 32852</a>)
<li class="rfe">
Improved Polish translation.
(<a href="https://github.com/jenkinsci/jenkins/pull/2041">pull 2041</a>,
<a href="https://github.com/jenkinsci/jenkins/pull/2044">pull 2044</a>)
</ul>
<h3><a name=v1.648>What's new in 1.648</a> (2016/02/17)</h3>
<ul class=image>
<li class="rfe">
Improved Czech and Polish translation.
(<a href="https://github.com/jenkinsci/jenkins/pull/2008">pull 2008</a>,
<a href="https://github.com/jenkinsci/jenkins/pull/2018">pull 2017</a>,
<a href="https://github.com/jenkinsci/jenkins/pull/2017">pull 2018</a>)
<li class="bug">
Generate new instance identity file when the existing one is found to be corrupt.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-29240">issue 29240</a>)
</ul>
<h3><a name=v1.647>What's new in 1.647</a> (2016/02/04)</h3>
<ul class=image>
<li class="bug">
Retrieve tool installer metadata from all update sites.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-32328">issue 32328</a>)
</ul>
<h3><a name=v1.646>What's new in 1.646</a> (2016/01/25)</h3>
<ul class=image>
<li class="major rfe">
@@ -1479,7 +1520,7 @@ Upcoming changes</a>
<h3><a name=v1.563>What's new in 1.563</a> (2014/05/11)</h3>
<ul class=image>
<li class="major bug">
Memory exhausion in remoting channel since 1.560.
Memory exhaustion in remoting channel since 1.560.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-22734">issue 22734</a>)
<li class="rfe">
Configurable size for log buffer.
@@ -1623,7 +1664,7 @@ Upcoming changes</a>
Fixed NoSuchMethodException when destroying processes using JDK1.8.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-21341">issue 21341</a>)
<li class=rfe>
Avoid irrelevant job queing while node is offline.
Avoid irrelevant job queuing while node is offline.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-21394">issue 21394</a>)
<li class=rfe>
Debian package now creates 'jenkins' group
+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<groupId>org.jenkins-ci.main</groupId>
<artifactId>pom</artifactId>
<version>2.0-alpha-1-SNAPSHOT</version>
<version>2.0-alpha-3-SNAPSHOT</version>
</parent>
<artifactId>cli</artifactId>
+4 -4
View File
@@ -29,7 +29,7 @@ THE SOFTWARE.
<parent>
<groupId>org.jenkins-ci.main</groupId>
<artifactId>pom</artifactId>
<version>2.0-alpha-1-SNAPSHOT</version>
<version>2.0-alpha-3-SNAPSHOT</version>
</parent>
<artifactId>jenkins-core</artifactId>
@@ -39,7 +39,7 @@ THE SOFTWARE.
<properties>
<staplerFork>true</staplerFork>
<stapler.version>1.237</stapler.version>
<stapler.version>1.239</stapler.version>
<spring.version>2.5.6.SEC03</spring.version>
<groovy.version>1.8.9</groovy.version>
</properties>
@@ -243,8 +243,8 @@ THE SOFTWARE.
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.4</version>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
@@ -1,5 +1,6 @@
# This file is under the MIT License by authors
NewVersionAvailable=Nowa wersja Jenkinsa ({0}) jest dost\u0119pna do <a href="{1}">pobrania</a> (<a href="${changelog.url}">changelog</a>).
NewVersionAvailable=Nowa wersja Jenkinsa ({0}) jest dost\u0119pna do <a href="{1}">pobrania</a> (<a href="${changelog.url}">historia zmian</a>).
Or\ Upgrade\ Automatically=Albo uaktualnij automatycznie
UpgradeCompleteRestartNotSupported=Aktualizacja Jenkinsa do wersji {0} zako\u0144czy\u0142a si\u0119 pomy\u015Blnie, oczekiwanie na ponowne uruchomienie.
UpgradeProgress=Aktualizacja Jenkinsa do wersji {0} <a href="{1}">trwa lub nie powiod\u0142a si\u0119</a>.
@@ -167,7 +167,8 @@ public class ClassicPluginStrategy implements PluginStrategy {
if (archive.isDirectory()) {// already expanded
expandDir = archive;
} else {
expandDir = new File(archive.getParentFile(), getBaseName(archive.getName()));
File f = pluginManager.getWorkDir();
expandDir = new File(f == null ? archive.getParentFile() : f, getBaseName(archive.getName()));
explode(archive, expandDir);
}
+4 -4
View File
@@ -63,7 +63,7 @@ import java.util.logging.Logger;
*
* <p>
* In Jenkins, often we need to build up "environment variable overrides"
* on master, then to execute the process on slaves. This causes a problem
* on master, then to execute the process on agents. This causes a problem
* when working with variables like <tt>PATH</tt>. So to make this work,
* we introduce a special convention <tt>PATH+FOO</tt> &mdash; all entries
* that starts with <tt>PATH+</tt> are merged and prepended to the inherited
@@ -134,7 +134,7 @@ public class EnvVars extends TreeMap<String,String> {
String v = get(realKey);
if(v==null) v=value;
else {
// we might be handling environment variables for a slave that can have different path separator
// we might be handling environment variables for a agent that can have different path separator
// than the master, so the following is an attempt to get it right.
// it's still more error prone that I'd like.
char ch = platform==null ? File.pathSeparatorChar : platform.pathSeparator;
@@ -421,8 +421,8 @@ public class EnvVars extends TreeMap<String,String> {
* variables only when you access this from the master.
*
* <p>
* If you access this field from slaves, then this is the environment
* variable of the slave agent.
* If you access this field from agents, then this is the environment
* variable of the agent agent.
*/
public static final Map<String,String> masterEnvVars = initMaster();
@@ -175,7 +175,7 @@ public abstract class ExtensionFinder implements ExtensionPoint {
* from here.
*
* <p>
* See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6459208 for how to force a class initialization.
* See https://bugs.openjdk.java.net/browse/JDK-4993813 for how to force a class initialization.
* Also see http://kohsuke.org/2010/09/01/deadlock-that-you-cant-avoid/ for how class initialization
* can results in a dead lock.
*/
@@ -668,8 +668,7 @@ public abstract class ExtensionFinder implements ExtensionPoint {
extType = ((Method)e).getReturnType();
} else
throw new AssertionError();
// according to http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6459208
// this appears to be the only way to force a class initialization
// according to JDK-4993813 this is the only way to force class initialization
Class.forName(extType.getName(),true,extType.getClassLoader());
} catch (Exception | LinkageError e) {
LOGGER.log(logLevel(item), "Failed to scout "+item.className(), e);
+8 -8
View File
@@ -127,7 +127,7 @@ import org.jenkinsci.remoting.RoleSensitive;
*
* <p>
* Unlike {@link File}, which always implies a file path on the current computer,
* {@link FilePath} represents a file path on a specific slave or the master.
* {@link FilePath} represents a file path on a specific agent or the master.
*
* Despite that, {@link FilePath} can be used much like {@link File}. It exposes
* a bunch of operations (and we should add more operations as long as they are
@@ -191,19 +191,19 @@ public final class FilePath implements Serializable {
/**
* When this {@link FilePath} represents the remote path,
* this field is always non-null on master (the field represents
* the channel to the remote slave.) When transferred to a slave via remoting,
* the channel to the remote agent.) When transferred to a agent via remoting,
* this field reverts back to null, since it's transient.
*
* When this {@link FilePath} represents a path on the master,
* this field is null on master. When transferred to a slave via remoting,
* this field is null on master. When transferred to a agent via remoting,
* this field becomes non-null, representing the {@link Channel}
* back to the master.
*
* This is used to determine whether we are running on the master or the slave.
* This is used to determine whether we are running on the master or the agent.
*/
private transient VirtualChannel channel;
// since the platform of the slave might be different, can't use java.io.File
// since the platform of the agent might be different, can't use java.io.File
private final String remote;
/**
@@ -259,7 +259,7 @@ public final class FilePath implements Serializable {
return base.remote+'/'+rel.replace('\\','/');
} else {
// need this replace, see Slave.getWorkspaceFor and AbstractItem.getFullName, nested jobs on Windows
// slaves will always have a rel containing at least one '/' character. JENKINS-13649
// agents will always have a rel containing at least one '/' character. JENKINS-13649
return base.remote+'\\'+rel.replace('/','\\');
}
}
@@ -802,14 +802,14 @@ public final class FilePath implements Serializable {
listener.getLogger().println(message);
if (isRemote()) {
// First try to download from the slave machine.
// First try to download from the agent machine.
try {
act(new Unpack(archive));
timestamp.touch(sourceTimestamp);
return true;
} catch (IOException x) {
if (listener != null) {
x.printStackTrace(listener.error("Failed to download " + archive + " from slave; will retry from master"));
x.printStackTrace(listener.error("Failed to download " + archive + " from agent; will retry from master"));
}
}
}
@@ -51,7 +51,7 @@ import java.io.OutputStream;
* STILL A WORK IN PROGRESS. SUBJECT TO CHANGE! DO NOT EXTEND.
*
* TODO: is this per {@link Computer}? Per {@link Job}?
* -> probably per slave.
* -> probably per agent.
*
* <h2>Design Problems</h2>
* <ol>
@@ -71,7 +71,7 @@ import java.io.OutputStream;
* one more configuration option. It's especially tricky because
* during the configuration we don't know the OS type.
*
* OTOH special slave type like the ones for network.com grid can
* OTOH special agent type like the ones for network.com grid can
* hide this.
* </ol>
*
@@ -80,8 +80,8 @@ import java.io.OutputStream;
*
* To recap,
*
* - when a slave connects, we auto-detect the file system provisioner.
* (for example, ZFS FSP would check the slave root user prop
* - when an agent connects, we auto-detect the file system provisioner.
* (for example, ZFS FSP would check the agent root user prop
* and/or attempt to "pfexec zfs create" and take over.)
*
* - the user may configure jobs for snapshot collection, along with
@@ -38,9 +38,9 @@ public abstract class FileSystemProvisionerDescriptor extends Descriptor<FileSys
* Called to clean up a workspace that may potentially belong to this {@link FileSystemProvisioner}.
*
* <p>
* Because users may modify the file system behind Hudson, and slaves may come and go when
* Because users may modify the file system behind Hudson, and agents may come and go when
* configuration changes happen, in general case Hudson is unable to keep track of which jobs
* have workspaces in which slaves.
* have workspaces in which agents.
*
* <p>
* So instead we rey on a garbage collection mechanism, to look at workspaces left in the file system
+1 -1
View File
@@ -1577,7 +1577,7 @@ public class Functions {
* Obtains the host name of the Hudson server that clients can use to talk back to.
* <p>
* This is primarily used in <tt>slave-agent.jnlp.jelly</tt> to specify the destination
* that the slaves talk to.
* that the agents talk to.
*/
public String getServerName() {
// Try to infer this from the configured root URL.
+2 -2
View File
@@ -297,7 +297,7 @@ public abstract class Launcher {
*
* <p>
* In adition to what the current process
* is inherited (if this is going to be launched from a slave agent, that
* is inherited (if this is going to be launched from a agent agent, that
* becomes the "current" process), these variables will be also set.
*/
public ProcStarter envs(Map<String, String> overrides) {
@@ -611,7 +611,7 @@ public abstract class Launcher {
* from the current process
* @param envVars
* Environment variable overrides. In addition to what the current process
* is inherited (if this is going to be launched from a slave agent, that
* is inherited (if this is going to be launched from an agent, that
* becomes the "current" process), these variables will be also set.
*/
public abstract Channel launchChannel(String[] cmd, OutputStream out, FilePath workDir, Map<String,String> envVars) throws IOException, InterruptedException;
@@ -159,6 +159,13 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
*/
public final File rootDir;
/**
* If non-null, the base directory for all exploded .hpi/.jpi plugins. Controlled by the system property / servlet
* context parameter {@literal hudson.PluginManager.workDir}.
*/
@CheckForNull
private final File workDir;
/**
* @deprecated as of 1.355
* {@link PluginManager} can now live longer than {@link jenkins.model.Jenkins} instance, so
@@ -204,6 +211,11 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
this.rootDir = rootDir;
if(!rootDir.exists())
rootDir.mkdirs();
String workDir = System.getProperty(PluginManager.class.getName()+".workDir");
if (workDir == null && context != null) {
workDir = context.getInitParameter(PluginManager.class.getName() + ".workDir");
}
this.workDir = workDir == null ? null : new File(workDir);
strategy = createPluginStrategy();
@@ -223,6 +235,15 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
return new Api(this);
}
/**
* If non-null, the base directory for all exploded .hpi/.jpi plugins.
* @return the base directory for all exploded .hpi/.jpi plugins or {@code null} to leave this up to the strategy.
*/
@CheckForNull
public File getWorkDir() {
return workDir;
}
/**
* Find all registered overrides (intended to allow overriding/adding views)
* @return List of extensions
@@ -215,7 +215,7 @@ public final class ProxyConfiguration extends AbstractDescribableImpl<ProxyConfi
* This method should be used wherever {@link URL#openConnection()} to internet URLs is invoked directly.
*/
public static URLConnection open(URL url) throws IOException {
Jenkins h = Jenkins.getInstance(); // this code might run on slaves
Jenkins h = Jenkins.getInstance(); // this code might run on agents
ProxyConfiguration p = h!=null ? h.proxy : null;
if(p==null)
return url.openConnection();
@@ -241,7 +241,7 @@ public final class ProxyConfiguration extends AbstractDescribableImpl<ProxyConfi
}
public static InputStream getInputStream(URL url) throws IOException {
Jenkins h = Jenkins.getInstance(); // this code might run on slaves
Jenkins h = Jenkins.getInstance(); // this code might run on agents
final ProxyConfiguration p = (h != null) ? h.proxy : null;
if (p == null)
return new RetryableHttpStream(url);
@@ -39,11 +39,11 @@ import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Listens to incoming TCP connections from JNLP slave agents and CLI.
* Listens to incoming TCP connections from JNLP agents and CLI.
*
* <p>
* Aside from the HTTP endpoint, Jenkins runs {@link TcpSlaveAgentListener} that listens on a TCP socket.
* Historically this was used for inbound connection from slave agents (hence the name), but over time
* Historically this was used for inbound connection from agents (hence the name), but over time
* it was extended and made generic, so that multiple protocols of different purposes can co-exist on the
* same socket.
*
@@ -66,7 +66,7 @@ public final class TcpSlaveAgentListener extends Thread {
* Use 0 to choose a random port.
*/
public TcpSlaveAgentListener(int port) throws IOException {
super("TCP slave agent listener port="+port);
super("TCP agent listener port="+port);
try {
serverSocket = ServerSocketChannel.open();
serverSocket.socket().bind(new InetSocketAddress(port));
@@ -75,7 +75,7 @@ public final class TcpSlaveAgentListener extends Thread {
}
this.configuredPort = port;
LOGGER.log(Level.FINE, "JNLP slave agent listener started on TCP port {0}", getPort());
LOGGER.log(Level.FINE, "JNLP agent listener started on TCP port {0}", getPort());
start();
}
@@ -105,7 +105,7 @@ public final class TcpSlaveAgentListener extends Thread {
}
} catch (IOException e) {
if(!shuttingDown) {
LOGGER.log(Level.SEVERE,"Failed to accept JNLP slave agent connections",e);
LOGGER.log(Level.SEVERE,"Failed to accept JNLP agent connections",e);
}
}
}
@@ -134,7 +134,7 @@ public final class TcpSlaveAgentListener extends Thread {
synchronized(getClass()) {
id = iotaGen++;
}
setName("TCP slave agent connection handler #"+id+" with "+s.getRemoteSocketAddress());
setName("TCP agent connection handler #"+id+" with "+s.getRemoteSocketAddress());
}
@Override
-27
View File
@@ -117,8 +117,6 @@ public class WebAppMain implements ServletContextListener {
installLogger();
markCookieAsHttpOnly(context);
final FileAndDescription describedHomeDir = getHomeDir(event);
home = describedHomeDir.file.getAbsoluteFile();
home.mkdirs();
@@ -254,31 +252,6 @@ public class WebAppMain implements ServletContextListener {
}
}
/**
* Set the session cookie as HTTP only.
*
* @see <a href="https://www.owasp.org/index.php/HttpOnly">discussion of this topic in OWASP</a>
*/
private void markCookieAsHttpOnly(ServletContext context) {
try {
Method m;
try {
m = context.getClass().getMethod("getSessionCookieConfig");
} catch (NoSuchMethodException x) { // 3.0+
LOGGER.log(Level.FINE, "Failed to set secure cookie flag", x);
return;
}
Object sessionCookieConfig = m.invoke(context);
// not exposing session cookie to JavaScript to mitigate damage caused by XSS
Class scc = Class.forName("javax.servlet.SessionCookieConfig");
Method setHttpOnly = scc.getMethod("setHttpOnly",boolean.class);
setHttpOnly.invoke(sessionCookieConfig,true);
} catch (Exception e) {
LOGGER.log(Level.WARNING, "Failed to set HTTP-only cookie flag", e);
}
}
public void joinInit() throws InterruptedException {
initThread.join();
}
@@ -31,7 +31,6 @@ import hudson.model.DirectlyModifiableView;
import hudson.model.View;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.CmdLineException;
/**
* @author ogondza
@@ -55,9 +54,8 @@ public class AddJobToViewCommand extends CLICommand {
protected int run() throws Exception {
view.checkPermission(View.CONFIGURE);
if (!(view instanceof DirectlyModifiableView)) throw new CmdLineException(
null, "'" + view.getDisplayName() + "' view can not be modified directly"
);
if (!(view instanceof DirectlyModifiableView)) throw new IllegalStateException(
"'" + view.getDisplayName() + "' view can not be modified directly");
for (TopLevelItem job: jobs) {
((DirectlyModifiableView) view).add(job);
+19 -14
View File
@@ -42,6 +42,7 @@ import hudson.scm.PollingResult.Change;
import hudson.util.EditDistance;
import hudson.util.StreamTaskListener;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.Option;
import java.util.Map;
@@ -90,7 +91,7 @@ public class BuildCommand extends CLICommand {
@Option(name="-r") @Deprecated
public int retryCnt = 10;
protected static final String BUILD_SCHEDULING_REFUSED = "Build scheduling Refused by an extension, hence not in Queue";
protected static final String BUILD_SCHEDULING_REFUSED = "Build scheduling Refused by an extension, hence not in Queue.";
protected int run() throws Exception {
job.checkPermission(Item.BUILD);
@@ -99,7 +100,7 @@ public class BuildCommand extends CLICommand {
if (!parameters.isEmpty()) {
ParametersDefinitionProperty pdp = job.getProperty(ParametersDefinitionProperty.class);
if (pdp==null)
throw new AbortException(job.getFullDisplayName()+" is not parameterized but the -p option was specified");
throw new IllegalStateException(job.getFullDisplayName()+" is not parameterized but the -p option was specified.");
//TODO: switch to type annotations after the migration to Java 1.8
List<ParameterValue> values = new ArrayList<ParameterValue>();
@@ -108,12 +109,14 @@ public class BuildCommand extends CLICommand {
String name = e.getKey();
ParameterDefinition pd = pdp.getParameterDefinition(name);
if (pd==null) {
throw new AbortException(String.format("\'%s\' is not a valid parameter. Did you mean %s?",
name, EditDistance.findNearest(name, pdp.getParameterDefinitionNames())));
String nearest = EditDistance.findNearest(name, pdp.getParameterDefinitionNames());
throw new CmdLineException(null, nearest == null ?
String.format("'%s' is not a valid parameter.", name) :
String.format("'%s' is not a valid parameter. Did you mean %s?", name, nearest));
}
ParameterValue val = pd.createValue(this, Util.fixNull(e.getValue()));
if (val == null) {
throw new AbortException(String.format("Cannot resolve the value for the parameter \'%s\'.",name));
throw new CmdLineException(null, String.format("Cannot resolve the value for the parameter '%s'.",name));
}
values.add(val);
}
@@ -126,7 +129,7 @@ public class BuildCommand extends CLICommand {
// not passed in use default
ParameterValue defaultValue = pd.getDefaultParameterValue();
if (defaultValue == null) {
throw new AbortException(String.format("No default value for the parameter \'%s\'.",pd.getName()));
throw new CmdLineException(null, String.format("No default value for the parameter '%s'.",pd.getName()));
}
values.add(defaultValue);
}
@@ -147,16 +150,14 @@ public class BuildCommand extends CLICommand {
} else if (job.isHoldOffBuildUntilSave()){
msg = Messages.BuildCommand_CLICause_CannotBuildConfigNotSaved(job.getFullDisplayName());
}
stderr.println(msg);
return -1;
throw new IllegalStateException(msg);
}
QueueTaskFuture<? extends AbstractBuild> f = job.scheduleBuild2(0, new CLICause(Jenkins.getAuthentication().getName()), a);
if (wait || sync || follow) {
if (f == null) {
stderr.println(BUILD_SCHEDULING_REFUSED);
return -1;
throw new IllegalStateException(BUILD_SCHEDULING_REFUSED);
}
AbstractBuild b = f.waitForStart(); // wait for the start
stdout.println("Started "+b.getFullDisplayName());
@@ -177,7 +178,9 @@ public class BuildCommand extends CLICommand {
}
catch (FileNotFoundException e) {
if ( i == retryCnt ) {
throw e;
Exception myException = new AbortException();
myException.initCause(e);
throw myException;
}
i++;
Thread.sleep(retryInterval);
@@ -193,7 +196,9 @@ public class BuildCommand extends CLICommand {
} else {
// if the CLI is aborted, try to abort the build as well
f.cancel(true);
throw e;
Exception myException = new AbortException();
myException.initCause(e);
throw myException;
}
}
}
@@ -214,9 +219,9 @@ public class BuildCommand extends CLICommand {
"With the -f option, this command changes the exit code based on\n" +
"the outcome of the build (exit code 0 indicates a success)\n" +
"however, unlike -s, interrupting the command will not interrupt\n" +
"the job (exit code 125 indicates the command was interrupted)\n" +
"the job (exit code 125 indicates the command was interrupted).\n" +
"With the -c option, a build will only run if there has been\n" +
"an SCM change"
"an SCM change."
);
}
+3 -3
View File
@@ -71,14 +71,14 @@ public class CLIAction implements UnprotectedRootAction, StaplerProxy {
}
public void doCommand(StaplerRequest req, StaplerResponse rsp) throws ServletException, IOException {
final Jenkins jenkins = Jenkins.getInstance();
final Jenkins jenkins = Jenkins.getActiveInstance();
jenkins.checkPermission(Jenkins.READ);
// Strip trailing slash
final String commandName = req.getRestOfPath().substring(1);
CLICommand command = CLICommand.clone(commandName);
if (command == null) {
rsp.sendError(HttpServletResponse.SC_NOT_FOUND, "No such command " + commandName);
rsp.sendError(HttpServletResponse.SC_NOT_FOUND, "No such command");
return;
}
@@ -112,7 +112,7 @@ public class CLIAction implements UnprotectedRootAction, StaplerProxy {
FullDuplexHttpChannel server;
if(req.getHeader("Side").equals("download")) {
duplexChannels.put(uuid,server=new FullDuplexHttpChannel(uuid, !Jenkins.getInstance().hasPermission(Jenkins.ADMINISTER)) {
duplexChannels.put(uuid,server=new FullDuplexHttpChannel(uuid, !Jenkins.getActiveInstance().hasPermission(Jenkins.ADMINISTER)) {
@Override
protected void main(Channel channel) throws IOException, InterruptedException {
// capture the identity given by the transport, since this can be useful for SecurityRealm.createCliAuthenticator()
+74 -33
View File
@@ -195,7 +195,7 @@ public abstract class CLICommand implements ExtensionPoint, Cloneable {
* to an empty method.
* You would however then have to consider {@link CliAuthenticator} and {@link #getTransportAuthentication},
* so this is not really recommended.
*
*
* @param args
* Arguments to the sub command. For example, if the CLI is invoked like "java -jar cli.jar foo bar zot",
* then "foo" is the sub-command and the argument list is ["bar","zot"].
@@ -209,7 +209,23 @@ public abstract class CLICommand implements ExtensionPoint, Cloneable {
* @param stderr
* Connected to the stderr of the CLI client.
* @return
* Exit code from the command.
* Exit code from the CLI command execution
*
* <p>
* Jenkins standard exit codes from CLI:
* 0 means everything went well.
* 1 means further unspecified exception is thrown while performing the command.
* 2 means CmdLineException is thrown while performing the command.
* 3 means IllegalArgumentException is thrown while performing the command.
* 4 mean IllegalStateException is thrown while performing the command.
* 5 means AbortException is thrown while performing the command.
* 6 means AccessDeniedException is thrown while performing the command.
* 7 means BadCredentialsException is thrown while performing the command.
* 8-15 are reserved for future usage
* 16+ mean a custom CLI exit error code (meaning defined by the CLI command itself)
*
* <p>
* Note: For details - see JENKINS-32273
*/
public int main(List<String> args, Locale locale, InputStream stdin, PrintStream stdout, PrintStream stderr) {
this.stdin = new BufferedInputStream(stdin);
@@ -220,49 +236,65 @@ public abstract class CLICommand implements ExtensionPoint, Cloneable {
CmdLineParser p = getCmdLineParser();
// add options from the authenticator
SecurityContext sc = SecurityContextHolder.getContext();
Authentication old = sc.getAuthentication();
CliAuthenticator authenticator = Jenkins.getInstance().getSecurityRealm().createCliAuthenticator(this);
sc.setAuthentication(getTransportAuthentication());
new ClassParser().parse(authenticator,p);
SecurityContext sc = null;
Authentication old = null;
try {
sc = SecurityContextHolder.getContext();
old = sc.getAuthentication();
CliAuthenticator authenticator = Jenkins.getActiveInstance().getSecurityRealm().createCliAuthenticator(this);
sc.setAuthentication(getTransportAuthentication());
new ClassParser().parse(authenticator,p);
p.parseArgument(args.toArray(new String[args.size()]));
Authentication auth = authenticator.authenticate();
if (auth==Jenkins.ANONYMOUS)
auth = loadStoredAuthentication();
sc.setAuthentication(auth); // run the CLI with the right credential
if (!(this instanceof LoginCommand || this instanceof HelpCommand))
Jenkins.getInstance().checkPermission(Jenkins.READ);
Jenkins.getActiveInstance().checkPermission(Jenkins.READ);
return run();
} catch (CmdLineException e) {
stderr.println(e.getMessage());
stderr.println("");
stderr.println("ERROR: " + e.getMessage());
printUsage(stderr, p);
return -1;
} catch (AccessDeniedException e) {
stderr.println(e.getMessage());
return -1;
return 2;
} catch (IllegalStateException e) {
stderr.println("");
stderr.println("ERROR: " + e.getMessage());
return 4;
} catch (IllegalArgumentException e) {
stderr.println("");
stderr.println("ERROR: " + e.getMessage());
return 3;
} catch (AbortException e) {
// signals an error without stack trace
stderr.println(e.getMessage());
return -1;
stderr.println("");
stderr.println("ERROR: " + e.getMessage());
return 5;
} catch (AccessDeniedException e) {
stderr.println("");
stderr.println("ERROR: " + e.getMessage());
return 6;
} catch (BadCredentialsException e) {
// to the caller, we can't reveal whether the user didn't exist or the password didn't match.
// do that to the server log instead
String id = UUID.randomUUID().toString();
LOGGER.log(Level.INFO, "CLI login attempt failed: "+id, e);
stderr.println("Bad Credentials. Search the server log for "+id+" for more details.");
return -1;
} catch (Exception e) {
final String errorMsg = String.format("Unexpected exception occurred while performing %s command!",
LOGGER.log(Level.INFO, "CLI login attempt failed: " + id, e);
stderr.println("");
stderr.println("ERROR: Bad Credentials. Search the server log for " + id + " for more details.");
return 7;
} catch (Throwable e) {
final String errorMsg = String.format("Unexpected exception occurred while performing %s command.",
getName());
stderr.println(errorMsg);
stderr.println("");
stderr.println("ERROR: " + errorMsg);
LOGGER.log(Level.WARNING, errorMsg, e);
e.printStackTrace(stderr);
return -1;
return 1;
} finally {
sc.setAuthentication(old); // restore
if(sc != null)
sc.setAuthentication(old); // restore
}
}
@@ -351,13 +383,22 @@ public abstract class CLICommand implements ExtensionPoint, Cloneable {
* To execute CLI method from outside, use {@link #main(List, Locale, InputStream, PrintStream, PrintStream)}
*
* @return
* 0 to indicate a success, otherwise an error code.
* @throws AbortException
* If the processing should be aborted. Hudson will report the error message
* without stack trace, and then exits this command.
* 0 to indicate a success, otherwise a custom error code.
* Error codes 1-15 shouldn;t be used in {@link #run()} as a custom error code.
* @throws Exception
* All the other exceptions cause the stack trace to be dumped, and then
* the command exits with an error code.
* If a further unspecified exception is thrown; means: Unknown and/or unexpected issue occurred
* @throws CmdLineException
* If a wrong parameter specified, input value can't be decoded etc.
* @throws IllegalArgumentException
* If the execution can't continue due to wrong input parameter (job doesn't exist etc.)
* @throws IllegalStateException
* If the execution can't continue due to an incorect state of Jenkins, job, build etc.
* @throws AbortException
* If the execution can't continue due to an other (rare, but foreseeable) issue
* @throws AccessDeniedException
* If the caller doesn't have sufficent rights for requested action
* @throws BadCredentialsException
* If bad credentials were provided to CLI
*/
protected abstract int run() throws Exception;
@@ -494,7 +535,7 @@ public abstract class CLICommand implements ExtensionPoint, Cloneable {
*/
protected void registerOptionHandlers() {
try {
for (Class c : Index.list(OptionHandlerExtension.class, Jenkins.getInstance().pluginManager.uberClassLoader,Class.class)) {
for (Class c : Index.list(OptionHandlerExtension.class, Jenkins.getActiveInstance().pluginManager.uberClassLoader,Class.class)) {
Type t = Types.getBaseClass(c, OptionHandler.class);
CmdLineParser.registerHandler(Types.erasure(Types.getTypeArgument(t,0)), c);
}
@@ -546,7 +587,7 @@ public abstract class CLICommand implements ExtensionPoint, Cloneable {
static {
// register option handlers that are defined
ClassLoaders cls = new ClassLoaders();
Jenkins j = Jenkins.getInstance();
Jenkins j = Jenkins.getActiveInstance();
if (j!=null) {// only when running on the master
cls.put(j.getPluginManager().uberClassLoader);
@@ -77,7 +77,7 @@ public class CliProtocol extends AgentProtocol {
Channel channel = cb
.withMode(Mode.BINARY)
.withRestricted(true)
.withBaseLoader(Jenkins.getInstance().pluginManager.uberClassLoader)
.withBaseLoader(Jenkins.getActiveInstance().pluginManager.uberClassLoader)
.build(new BufferedInputStream(c.in), new BufferedOutputStream(c.out));
channel.setProperty(CliEntryPoint.class.getName(),new CliManagerImpl(channel));
@@ -60,7 +60,7 @@ public class CliProtocol2 extends CliProtocol {
try {
// HACK: TODO: move the transport support into modules
Class<?> cls = Jenkins.getInstance().pluginManager.uberClassLoader.loadClass("org.jenkinsci.main.modules.instance_identity.InstanceIdentity");
Class<?> cls = Jenkins.getActiveInstance().pluginManager.uberClassLoader.loadClass("org.jenkinsci.main.modules.instance_identity.InstanceIdentity");
Object iid = cls.getDeclaredMethod("get").invoke(null);
PrivateKey instanceId = (PrivateKey)cls.getDeclaredMethod("getPrivate").invoke(iid);
@@ -66,7 +66,7 @@ public class ClientAuthenticationCache implements Serializable {
* @return {@link jenkins.model.Jenkins#ANONYMOUS} if no such credential is found, or if the stored credential is invalid.
*/
public Authentication get() {
Jenkins h = Jenkins.getInstance();
Jenkins h = Jenkins.getActiveInstance();
Secret userName = Secret.decrypt(props.getProperty(getPropertyKey()));
if (userName==null) return Jenkins.ANONYMOUS; // failed to decrypt
try {
@@ -83,7 +83,7 @@ public class ClientAuthenticationCache implements Serializable {
* Computes the key that identifies this Hudson among other Hudsons that the user has a credential for.
*/
private String getPropertyKey() {
String url = Jenkins.getInstance().getRootUrl();
String url = Jenkins.getActiveInstance().getRootUrl();
if (url!=null) return url;
return Secret.fromString("key").toString();
}
@@ -92,7 +92,7 @@ public class ClientAuthenticationCache implements Serializable {
* Persists the specified authentication.
*/
public void set(Authentication a) throws IOException, InterruptedException {
Jenkins h = Jenkins.getInstance();
Jenkins h = Jenkins.getActiveInstance();
// make sure that this security realm is capable of retrieving the authentication by name,
// as it's not required.
@@ -45,7 +45,7 @@ public abstract class CommandDuringBuild extends CLICommand {
protected Run getCurrentlyBuilding() throws CmdLineException {
Run r = optCurrentlyBuilding();
if (r==null)
throw new CmdLineException("This CLI command works only when invoked from inside a build");
throw new IllegalStateException("This CLI command works only when invoked from inside a build");
return r;
}
@@ -55,29 +55,32 @@ public abstract class CommandDuringBuild extends CLICommand {
protected Run optCurrentlyBuilding() throws CmdLineException {
try {
CLICommand c = CLICommand.getCurrent();
if (c==null) throw new IllegalStateException("Not executing a CLI command");
if (c==null)
throw new IllegalStateException("Not executing a CLI command");
String[] envs = c.checkChannel().call(new GetCharacteristicEnvironmentVariables());
if (envs[0]==null || envs[1]==null)
return null;
Job j = Jenkins.getInstance().getItemByFullName(envs[0],Job.class);
if (j==null) throw new CmdLineException("No such job: "+envs[0]);
Job j = Jenkins.getActiveInstance().getItemByFullName(envs[0],Job.class);
if (j==null)
throw new IllegalArgumentException("No such job: "+envs[0]);
try {
Run r = j.getBuildByNumber(Integer.parseInt(envs[1]));
if (r==null) throw new CmdLineException("No such build #"+envs[1]+" in "+envs[0]);
if (r==null)
throw new IllegalArgumentException("No such build #"+envs[1]+" in "+envs[0]);
if (!r.isBuilding()) {
throw new CmdLineException(r + " is not currently being built");
throw new IllegalStateException(r + " is not currently being built");
}
return r;
} catch (NumberFormatException e) {
throw new CmdLineException("Invalid build number: "+envs[1]);
throw new IllegalArgumentException("Invalid build number: "+envs[1]);
}
} catch (IOException e) {
throw new CmdLineException("Failed to identify the build being executed",e);
throw new IllegalArgumentException("Failed to identify the build being executed",e);
} catch (InterruptedException e) {
throw new CmdLineException("Failed to identify the build being executed",e);
throw new IllegalArgumentException("Failed to identify the build being executed",e);
}
}
@@ -50,17 +50,20 @@ public class ConsoleCommand extends CLICommand {
int n = Integer.parseInt(build);
run = job.getBuildByNumber(n);
if (run==null)
throw new CmdLineException("No such build #"+n);
throw new IllegalArgumentException("No such build #"+n);
} catch (NumberFormatException e) {
// maybe a permalink?
Permalink p = job.getPermalinks().get(build);
if (p!=null) {
run = (AbstractBuild)p.resolve(job);
if (run==null)
throw new CmdLineException("Permalink "+build+" produced no build");
throw new IllegalStateException("Permalink "+build+" produced no build");
} else {
Permalink nearest = job.getPermalinks().findNearest(build);
throw new CmdLineException(String.format("Not sure what you meant by \"%s\". Did you mean \"%s\"?", build, nearest.getId()));
throw new IllegalArgumentException(nearest == null ?
String.format("Not sure what you meant by \"%s\".", build) :
String.format("Not sure what you meant by \"%s\". Did you mean \"%s\"?",
build, nearest.getId()));
}
}
@@ -50,11 +50,10 @@ public class CopyJobCommand extends CLICommand {
public String dst;
protected int run() throws Exception {
Jenkins jenkins = Jenkins.getInstance();
Jenkins jenkins = Jenkins.getActiveInstance();
if (jenkins.getItemByFullName(dst)!=null) {
stderr.println("Job '"+dst+"' already exists");
return -1;
throw new IllegalStateException("Job '"+dst+"' already exists");
}
ModifiableTopLevelItemGroup ig = jenkins;
@@ -69,7 +68,7 @@ public class CopyJobCommand extends CLICommand {
if (item instanceof ModifiableTopLevelItemGroup) {
ig = (ModifiableTopLevelItemGroup) item;
} else {
throw new IllegalArgumentException("Can't create job from CLI in " + group);
throw new IllegalStateException("Can't create job from CLI in " + group);
}
dst = dst.substring(i + 1);
}
@@ -45,11 +45,10 @@ public class CreateJobCommand extends CLICommand {
public String name;
protected int run() throws Exception {
Jenkins h = Jenkins.getInstance();
Jenkins h = Jenkins.getActiveInstance();
if (h.getItemByFullName(name)!=null) {
stderr.println("Job '"+name+"' already exists");
return -1;
throw new IllegalStateException("Job '"+name+"' already exists");
}
ModifiableTopLevelItemGroup ig = h;
@@ -64,7 +63,7 @@ public class CreateJobCommand extends CLICommand {
if (item instanceof ModifiableTopLevelItemGroup) {
ig = (ModifiableTopLevelItemGroup) item;
} else {
throw new IllegalArgumentException("Can't create job from CLI in " + group);
throw new IllegalStateException("Can't create job from CLI in " + group);
}
name = name.substring(i + 1);
}
@@ -53,7 +53,7 @@ public class CreateNodeCommand extends CLICommand {
@Override
protected int run() throws Exception {
final Jenkins jenkins = Jenkins.getInstance();
final Jenkins jenkins = Jenkins.getActiveInstance();
jenkins.checkPermission(Computer.CREATE);
final Node newNode = (Node) Jenkins.XSTREAM2.fromXML(stdin);
@@ -70,10 +70,7 @@ public class CreateNodeCommand extends CLICommand {
}
if (jenkins.getNode(newNode.getNodeName()) != null) {
throw new CmdLineException(
null, "Node '" + newNode.getNodeName() + "' already exists"
);
throw new IllegalStateException("Node '" + newNode.getNodeName() + "' already exists");
}
jenkins.addNode(newNode);
@@ -49,7 +49,7 @@ public class CreateViewCommand extends CLICommand {
@Override
protected int run() throws Exception {
final Jenkins jenkins = Jenkins.getInstance();
final Jenkins jenkins = Jenkins.getActiveInstance();
jenkins.checkPermission(View.CREATE);
View newView;
@@ -57,16 +57,12 @@ public class CreateViewCommand extends CLICommand {
newView = View.createViewFromXML(viewName, stdin);
} catch (Failure ex) {
stderr.format("Invalid view name: %s\n", ex.getMessage());
return -1;
throw new IllegalArgumentException("Invalid view name: " + ex.getMessage());
}
final String newName = newView.getViewName();
if (jenkins.getView(newName) != null) {
stderr.format("View '%s' already exists\n", newName);
return -1;
throw new IllegalStateException("View '" + newName + "' already exists");
}
jenkins.addView(newView);
@@ -23,6 +23,7 @@
*/
package hudson.cli;
import hudson.AbortException;
import hudson.Extension;
import hudson.model.AbstractItem;
import jenkins.model.Jenkins;
@@ -42,8 +43,6 @@ 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() {
@@ -54,12 +53,7 @@ public class DeleteJobCommand extends CLICommand {
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 Jenkins jenkins = Jenkins.getActiveInstance();
final HashSet<String> hs = new HashSet<String>();
hs.addAll(jobs);
@@ -71,31 +65,26 @@ public class DeleteJobCommand extends CLICommand {
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;
throw new IllegalArgumentException("No such job '" + job_s + "'");
}
job.checkPermission(AbstractItem.DELETE);
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());
if(hs.size() == 1) {
throw e;
}
final String errorMsg = String.format(job_s + ": " + e.getMessage());
stderr.println(errorMsg);
LOGGER.warning(errorMsg);
errorOccurred = true;
//noinspection UnnecessaryContinue
continue;
}
}
return errorOccurred ? -1 : 0;
if (errorOccurred) {
throw new AbortException("Error occured while performing this command, see previous stderr output.");
}
return 0;
}
}
@@ -23,9 +23,9 @@
*/
package hudson.cli;
import hudson.AbortException;
import hudson.Extension;
import hudson.model.Node;
import org.acegisecurity.AccessDeniedException;
import jenkins.model.Jenkins;
import org.kohsuke.args4j.Argument;
@@ -43,8 +43,6 @@ 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() {
@@ -55,12 +53,7 @@ public class DeleteNodeCommand extends CLICommand {
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 Jenkins jenkins = Jenkins.getActiveInstance();
final HashSet<String> hs = new HashSet<String>();
hs.addAll(nodes);
@@ -71,29 +64,26 @@ public class DeleteNodeCommand extends CLICommand {
try {
node = jenkins.getNode(node_s);
if(node == null) {
stderr.format("No such node '%s'\n", node_s);
errorOccurred = true;
continue;
if (node == null) {
throw new IllegalArgumentException("No such node '" + node_s + "'");
}
node.toComputer().doDoDelete();
} catch (AccessDeniedException e) {
stderr.println(e.getMessage());
errorOccurred = true;
//noinspection UnnecessaryContinue
continue;
} 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());
if(hs.size() == 1) {
throw e;
}
final String errorMsg = String.format(node_s + ": " + e.getMessage());
stderr.println(errorMsg);
LOGGER.warning(errorMsg);
errorOccurred = true;
//noinspection UnnecessaryContinue
continue;
}
}
return errorOccurred ? -1 : 0;
if (errorOccurred) {
throw new AbortException("Error occured while performing this command, see previous stderr output.");
}
return 0;
}
}
@@ -23,6 +23,7 @@
*/
package hudson.cli;
import hudson.AbortException;
import hudson.Extension;
import hudson.cli.handlers.ViewOptionHandler;
import hudson.model.ViewGroup;
@@ -44,8 +45,6 @@ public class DeleteViewCommand extends CLICommand {
@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() {
@@ -67,42 +66,37 @@ public class DeleteViewCommand extends CLICommand {
View view = null;
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;
view = voh.getView(view_s);
if (view == null) {
throw new IllegalArgumentException("View name is empty");
}
view.checkPermission(View.DELETE);
ViewGroup group = view.getOwner();
if (!group.canDelete(view)) {
stderr.format("%s does not allow to delete '%s' view\n",
throw new IllegalStateException(String.format("%s does not allow to delete '%s' view",
group.getDisplayName(),
view.getViewName()
);
errorOccurred = true;
continue;
view.getViewName()));
}
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());
if(hs.size() == 1) {
throw e;
}
final String errorMsg = String.format(view_s + ": " + e.getMessage());
stderr.println(errorMsg);
LOGGER.warning(errorMsg);
errorOccurred = true;
//noinspection UnnecessaryContinue
continue;
}
}
return errorOccurred ? -1 : 0;
if (errorOccurred) {
throw new AbortException("Error occured while performing this command, see previous stderr output.");
}
return 0;
}
}
@@ -63,7 +63,7 @@ public class GroovyCommand extends CLICommand {
protected int run() throws Exception {
// this allows the caller to manipulate the JVM state, so require the execute script privilege.
Jenkins.getInstance().checkPermission(Jenkins.RUN_SCRIPTS);
Jenkins.getActiveInstance().checkPermission(Jenkins.RUN_SCRIPTS);
Binding binding = new Binding();
binding.setProperty("out",new PrintWriter(stdout,true));
@@ -73,7 +73,7 @@ public class GroovyCommand extends CLICommand {
binding.setProperty("channel",channel);
String j = getClientEnvironmentVariable("JOB_NAME");
if (j!=null) {
Item job = Jenkins.getInstance().getItemByFullName(j);
Item job = Jenkins.getActiveInstance().getItemByFullName(j);
binding.setProperty("currentJob", job);
String b = getClientEnvironmentVariable("BUILD_NUMBER");
if (b!=null && job instanceof AbstractProject) {
@@ -82,7 +82,7 @@ public class GroovyCommand extends CLICommand {
}
}
GroovyShell groovy = new GroovyShell(Jenkins.getInstance().getPluginManager().uberClassLoader, binding);
GroovyShell groovy = new GroovyShell(Jenkins.getActiveInstance().getPluginManager().uberClassLoader, binding);
groovy.run(loadScript(),"RemoteClass",remaining.toArray(new String[remaining.size()]));
return 0;
}
@@ -61,7 +61,7 @@ public class GroovyshCommand extends CLICommand {
@Override
protected int run() {
// this allows the caller to manipulate the JVM state, so require the admin privilege.
Jenkins.getInstance().checkPermission(Jenkins.RUN_SCRIPTS);
Jenkins.getActiveInstance().checkPermission(Jenkins.RUN_SCRIPTS);
// this being remote means no jline capability is available
System.setProperty("jline.terminal", UnsupportedTerminal.class.getName());
@@ -78,12 +78,12 @@ public class GroovyshCommand extends CLICommand {
Binding binding = new Binding();
// redirect "println" to the CLI
binding.setProperty("out", new PrintWriter(stdout,true));
binding.setProperty("hudson", Jenkins.getInstance()); // backward compatibility
binding.setProperty("jenkins", Jenkins.getInstance());
binding.setProperty("hudson", Jenkins.getActiveInstance()); // backward compatibility
binding.setProperty("jenkins", Jenkins.getActiveInstance());
IO io = new IO(new BufferedInputStream(stdin),stdout,stderr);
final ClassLoader cl = Jenkins.getInstance().pluginManager.uberClassLoader;
final ClassLoader cl = Jenkins.getActiveInstance().pluginManager.uberClassLoader;
Closure registrar = new Closure(null, null) {
private static final long serialVersionUID = 1L;
@@ -23,12 +23,14 @@
*/
package hudson.cli;
import hudson.AbortException;
import hudson.Extension;
import jenkins.model.Jenkins;
import java.util.Map;
import java.util.TreeMap;
import org.acegisecurity.AccessDeniedException;
import org.kohsuke.args4j.Argument;
/**
@@ -48,11 +50,10 @@ public class HelpCommand extends CLICommand {
}
@Override
protected int run() {
if (!Jenkins.getInstance().hasPermission(Jenkins.READ)) {
stderr.println("You must authenticate to access this Jenkins.\n"
protected int run() throws Exception {
if (!Jenkins.getActiveInstance().hasPermission(Jenkins.READ)) {
throw new AccessDeniedException("You must authenticate to access this Jenkins.\n"
+ "Use --username/--password/--password-file parameters or login command.");
return -1;
}
if (command != null)
@@ -76,12 +77,11 @@ public class HelpCommand extends CLICommand {
return 0;
}
private int showCommandDetails() {
private int showCommandDetails() throws Exception {
CLICommand command = CLICommand.clone(this.command);
if (command == null) {
stderr.format("No such command %s. Awailable commands are: ", this.command);
showAllCommands();
return -1;
throw new AbortException(String.format("No such command %s. Available commands are above. ", this.command));
}
command.printUsage(stderr, command.getCmdLineParser());
@@ -23,6 +23,7 @@
*/
package hudson.cli;
import hudson.AbortException;
import hudson.Extension;
import hudson.FilePath;
import hudson.PluginManager;
@@ -57,20 +58,20 @@ public class InstallPluginCommand extends CLICommand {
@Argument(metaVar="SOURCE",required=true,usage="If this points to a local file, that file will be installed. " +
"If this is an URL, Jenkins downloads the URL and installs that as a plugin." +
"Otherwise the name is assumed to be the short name of the plugin in the existing update center (like \"findbugs\")," +
"and the plugin will be installed from the update center")
"and the plugin will be installed from the update center.")
public List<String> sources = new ArrayList<String>();
@Option(name="-name",usage="If specified, the plugin will be installed as this short name (whereas normally the name is inferred from the source name automatically.)")
@Option(name="-name",usage="If specified, the plugin will be installed as this short name (whereas normally the name is inferred from the source name automatically).")
public String name;
@Option(name="-restart",usage="Restart Jenkins upon successful installation")
@Option(name="-restart",usage="Restart Jenkins upon successful installation.")
public boolean restart;
@Option(name="-deploy",usage="Deploy plugins right away without postponing them until the reboot.")
public boolean dynamicLoad;
protected int run() throws Exception {
Jenkins h = Jenkins.getInstance();
Jenkins h = Jenkins.getActiveInstance();
h.checkPermission(PluginManager.UPLOAD_PLUGINS);
PluginManager pm = h.getPluginManager();
@@ -113,8 +114,11 @@ public class InstallPluginCommand extends CLICommand {
if (p!=null) {
stdout.println(Messages.InstallPluginCommand_InstallingFromUpdateCenter(source));
Throwable e = p.deploy(dynamicLoad).get().getError();
if (e!=null)
throw new IOException("Failed to install plugin "+source,e);
if (e!=null) {
AbortException myException = new AbortException("Failed to install plugin " + source);
myException.initCause(e);
throw myException;
}
continue;
}
@@ -137,7 +141,7 @@ public class InstallPluginCommand extends CLICommand {
}
}
return 1;
throw new AbortException("Error occurred, see previous output.");
}
if (restart)
@@ -150,6 +154,6 @@ public class InstallPluginCommand extends CLICommand {
}
private File getTargetFile() {
return new File(Jenkins.getInstance().getPluginManager().rootDir,name+".jpi");
return new File(Jenkins.getActiveInstance().getPluginManager().rootDir,name+".jpi");
}
}
@@ -62,18 +62,18 @@ public class InstallToolCommand extends CLICommand {
}
protected int run() throws Exception {
Jenkins h = Jenkins.getInstance();
Jenkins h = Jenkins.getActiveInstance();
h.checkPermission(Jenkins.READ);
// where is this build running?
BuildIDs id = checkChannel().call(new BuildIDs());
if (!id.isComplete())
throw new AbortException("This command can be only invoked from a build executing inside Hudson");
throw new IllegalStateException("This command can be only invoked from a build executing inside Hudson");
AbstractProject p = Jenkins.getInstance().getItemByFullName(id.job, AbstractProject.class);
AbstractProject p = h.getItemByFullName(id.job, AbstractProject.class);
if (p==null)
throw new AbortException("No such job found: "+id.job);
throw new IllegalStateException("No such job found: "+id.job);
p.checkPermission(Item.CONFIGURE);
List<String> toolTypes = new ArrayList<String>();
@@ -101,9 +101,9 @@ public class InstallToolCommand extends CLICommand {
private int error(List<String> candidates, String given, String noun) throws AbortException {
if (given ==null)
throw new AbortException("No tool "+ noun +" was specified. Valid values are "+candidates.toString());
throw new IllegalArgumentException("No tool "+ noun +" was specified. Valid values are "+candidates.toString());
else
throw new AbortException("Unrecognized tool "+noun+". Perhaps you meant '"+ EditDistance.findNearest(given,candidates)+"'?");
throw new IllegalArgumentException("Unrecognized tool "+noun+". Perhaps you meant '"+ EditDistance.findNearest(given,candidates)+"'?");
}
/**
@@ -113,15 +113,15 @@ public class InstallToolCommand extends CLICommand {
Run b = p.getBuildByNumber(Integer.parseInt(id.number));
if (b==null)
throw new AbortException("No such build: "+id.number);
throw new IllegalStateException("No such build: "+id.number);
Executor exec = b.getExecutor();
if (exec==null)
throw new AbortException(b.getFullDisplayName()+" is not building");
throw new IllegalStateException(b.getFullDisplayName()+" is not building");
Node node = exec.getOwner().getNode();
if (node == null) {
throw new AbortException("The node " + exec.getOwner().getDisplayName() + " has been deleted");
throw new IllegalStateException("The node " + exec.getOwner().getDisplayName() + " has been deleted");
}
t = t.translate(node, EnvVars.getRemote(checkChannel()), new StreamTaskListener(stderr));
@@ -50,7 +50,7 @@ public class ListJobsCommand extends CLICommand {
public String name;
protected int run() throws Exception {
Jenkins h = Jenkins.getInstance();
Jenkins h = Jenkins.getActiveInstance();
final Collection<TopLevelItem> jobs;
// If name is given retrieve jobs for the given view.
@@ -70,8 +70,7 @@ public class ListJobsCommand extends CLICommand {
}
// No view and no item group with the given name found.
else {
stderr.println("No view or item group with the given name found");
return -1;
throw new IllegalArgumentException("No view or item group with the given name '" + name + "' found.");
}
}
}
@@ -47,7 +47,7 @@ public class ListPluginsCommand extends CLICommand {
public String name;
protected int run() {
Jenkins h = Jenkins.getInstance();
Jenkins h = Jenkins.getActiveInstance();
PluginManager pluginManager = h.getPluginManager();
if (this.name != null) {
@@ -57,7 +57,7 @@ public class ListPluginsCommand extends CLICommand {
printPlugin(plugin, plugin.getShortName().length(), plugin.getDisplayName().length());
}
else {
stderr.println(String.format("No plugin with the name '%s' found", this.name));
throw new IllegalArgumentException("No plugin with the name '" + name + "' found");
}
}
else {
@@ -40,11 +40,9 @@ import java.util.logging.Logger;
@Extension
public class OnlineNodeCommand extends CLICommand {
@Argument(metaVar="NAME", usage="Slave name, or empty string for master")
@Argument(metaVar="NAME", usage="Agent name, or empty string for master")
public String computerName;
private static final Logger LOGGER = Logger.getLogger(OnlineNodeCommand.class.getName());
@Override
public String getShortDescription() {
@@ -54,32 +52,17 @@ public class OnlineNodeCommand extends CLICommand {
@Override
protected int run() throws Exception {
boolean errorOccurred = false;
final Jenkins jenkins = Jenkins.getInstance();
final Jenkins jenkins = Jenkins.getActiveInstance();
Computer computer = jenkins.getComputer(computerName);
if (computer == null) {
stderr.println(hudson.model.Messages.Computer_NoSuchSlaveExists(computerName, null));
errorOccurred = true;
throw new IllegalArgumentException(hudson.model.Messages.Computer_NoSuchSlaveExists(computerName, null));
} 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;
}
computer.cliOnline();
}
return errorOccurred ? 1 : 0;
return 0;
}
}
@@ -23,6 +23,7 @@
*/
package hudson.cli;
import hudson.AbortException;
import hudson.Extension;
import hudson.model.AbstractItem;
import hudson.model.AbstractProject;
@@ -58,12 +59,7 @@ public class ReloadJobCommand extends CLICommand {
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 Jenkins jenkins = Jenkins.getActiveInstance();
final HashSet<String> hs = new HashSet<String>();
hs.addAll(jobs);
@@ -83,35 +79,29 @@ public class ReloadJobCommand extends CLICommand {
if(job == null) {
// TODO: JENKINS-30785
AbstractProject project = AbstractProject.findNearest(job_s);
if(project == null) {
stderr.format("No such job \u2018%s\u2019 exists.\n", job_s);
} else {
stderr.format("No such job \u2018%s\u2019 exists. Perhaps you meant \u2018%s\u2019?", job_s, project.getFullName());
}
errorOccurred = true;
continue;
}
try {
job.checkPermission(AbstractItem.CONFIGURE);
} catch (Exception e) {
stderr.println(e.getMessage());
errorOccurred = true;
continue;
throw new IllegalArgumentException(project == null ?
"No such job \u2018" + job_s + "\u2019 exists." :
String.format("No such job \u2018%s\u2019 exists. Perhaps you meant \u2018%s\u2019?",
job_s, project.getFullName()));
}
job.checkPermission(AbstractItem.CONFIGURE);
job.doReload();
} catch (Exception e) {
final String errorMsg = String.format("Unexpected exception occurred during reloading of job '%s': %s",
job == null ? "(null)" : job.getFullName(),
e.getMessage());
if(hs.size() == 1) {
throw e;
}
final String errorMsg = String.format(job_s + ": " + e.getMessage());
stderr.println(errorMsg);
LOGGER.warning(errorMsg);
errorOccurred = true;
//noinspection UnnecessaryContinue
continue;
}
}
return errorOccurred ? 1 : 0;
if (errorOccurred) {
throw new AbortException("Error occured while performing this command, see previous stderr output.");
}
return 0;
}
}
@@ -55,9 +55,8 @@ public class RemoveJobFromViewCommand extends CLICommand {
protected int run() throws Exception {
view.checkPermission(View.CONFIGURE);
if (!(view instanceof DirectlyModifiableView)) throw new CmdLineException(
null, "'" + view.getDisplayName() + "' view can not be modified directly"
);
if (!(view instanceof DirectlyModifiableView))
throw new IllegalStateException("'" + view.getDisplayName() + "' view can not be modified directly");
for (TopLevelItem job: jobs) {
((DirectlyModifiableView) view).remove(job);
@@ -30,8 +30,7 @@ public class SetBuildDisplayNameCommand extends CLICommand implements Serializab
protected int run() throws Exception {
Run<?, ?> run = job.getBuildByNumber(number);
if (run == null) {
stderr.format("Build #%d does not exist\n", number);
return -1;
throw new IllegalArgumentException("Build #" + number + " does not exist");
}
run.checkPermission(Run.UPDATE);
@@ -23,6 +23,7 @@
*/
package hudson.cli.declarative;
import hudson.AbortException;
import hudson.Extension;
import hudson.ExtensionComponent;
import hudson.ExtensionFinder;
@@ -34,7 +35,9 @@ import jenkins.ExtensionComponentSet;
import jenkins.ExtensionRefreshException;
import jenkins.model.Jenkins;
import hudson.security.CliAuthenticator;
import org.acegisecurity.AccessDeniedException;
import org.acegisecurity.Authentication;
import org.acegisecurity.BadCredentialsException;
import org.acegisecurity.context.SecurityContext;
import org.acegisecurity.context.SecurityContextHolder;
import org.jvnet.hudson.annotation_indexer.Index;
@@ -56,6 +59,9 @@ import java.util.List;
import java.util.Locale;
import java.util.Stack;
import static java.util.logging.Level.SEVERE;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
@@ -151,6 +157,40 @@ public class CLIRegisterer extends ExtensionFinder {
return parser;
}
/**
* Envelope an annotated CLI command
*
* @param args
* Arguments to the sub command. For example, if the CLI is invoked like "java -jar cli.jar foo bar zot",
* then "foo" is the sub-command and the argument list is ["bar","zot"].
* @param locale
* Locale of the client (which can be different from that of the server.) Good behaving command implementation
* would use this locale for formatting messages.
* @param stdin
* Connected to the stdin of the CLI client.
* @param stdout
* Connected to the stdout of the CLI client.
* @param stderr
* Connected to the stderr of the CLI client.
* @return
* Exit code from the CLI command execution
*
* <p>
* Jenkins standard exit codes from CLI:
* 0 means everything went well.
* 1 means further unspecified exception is thrown while performing the command.
* 2 means CmdLineException is thrown while performing the command.
* 3 means IllegalArgumentException is thrown while performing the command.
* 4 mean IllegalStateException is thrown while performing the command.
* 5 means AbortException is thrown while performing the command.
* 6 means AccessDeniedException is thrown while performing the command.
* 7 means BadCredentialsException is thrown while performing the command.
* 8-15 are reserved for future usage
* 16+ mean a custom CLI exit error code (meaning defined by the CLI command itself)
*
* <p>
* Note: For details - see JENKINS-32273
*/
@Override
public int main(List<String> args, Locale locale, InputStream stdin, PrintStream stdout, PrintStream stderr) {
this.stdout = stdout;
@@ -166,13 +206,13 @@ public class CLIRegisterer extends ExtensionFinder {
try {
// authentication
CliAuthenticator authenticator = Jenkins.getInstance().getSecurityRealm().createCliAuthenticator(this);
new ClassParser().parse(authenticator,parser);
new ClassParser().parse(authenticator, parser);
// fill up all the binders
parser.parseArgument(args);
Authentication auth = authenticator.authenticate();
if (auth== Jenkins.ANONYMOUS)
if (auth == Jenkins.ANONYMOUS)
auth = loadStoredAuthentication();
sc.setAuthentication(auth); // run the CLI with the right credential
hudson.checkPermission(Jenkins.READ);
@@ -195,10 +235,40 @@ public class CLIRegisterer extends ExtensionFinder {
sc.setAuthentication(old); // restore
}
} catch (CmdLineException e) {
stderr.println(e.getMessage());
printUsage(stderr,parser);
return 1;
} catch (Exception e) {
stderr.println("");
stderr.println("ERROR: " + e.getMessage());
printUsage(stderr, parser);
return 2;
} catch (IllegalStateException e) {
stderr.println("");
stderr.println("ERROR: " + e.getMessage());
return 4;
} catch (IllegalArgumentException e) {
stderr.println("");
stderr.println("ERROR: " + e.getMessage());
return 3;
} catch (AbortException e) {
stderr.println("");
stderr.println("ERROR: " + e.getMessage());
return 5;
} catch (AccessDeniedException e) {
stderr.println("");
stderr.println("ERROR: " + e.getMessage());
return 6;
} catch (BadCredentialsException e) {
// to the caller, we can't reveal whether the user didn't exist or the password didn't match.
// do that to the server log instead
String id = UUID.randomUUID().toString();
LOGGER.log(Level.INFO, "CLI login attempt failed: " + id, e);
stderr.println("");
stderr.println("ERROR: Bad Credentials. Search the server log for " + id + " for more details.");
return 7;
} catch (Throwable e) {
final String errorMsg = String.format("Unexpected exception occurred while performing %s command.",
getName());
stderr.println("");
stderr.println("ERROR: " + errorMsg);
LOGGER.log(Level.WARNING, errorMsg, e);
e.printStackTrace(stderr);
return 1;
}
@@ -213,7 +283,7 @@ public class CLIRegisterer extends ExtensionFinder {
}
}
} catch (IOException e) {
LOGGER.log(SEVERE, "Failed to discvoer @CLIMethod",e);
LOGGER.log(SEVERE, "Failed to discover @CLIMethod",e);
}
return r;
@@ -73,9 +73,9 @@ public abstract class GenericItemOptionHandler<T extends Item> extends OptionHan
});
T nearest = Items.findNearest(type(), src, j);
if (nearest != null) {
throw new CmdLineException(owner, "No such job '" + src + "'; perhaps you meant '" + nearest.getFullName() + "'?");
throw new IllegalArgumentException("No such job '" + src + "'; perhaps you meant '" + nearest.getFullName() + "'?");
} else {
throw new CmdLineException(owner, "No such job '" + src + "'");
throw new IllegalArgumentException("No such job '" + src + "'");
}
}
setter.addValue(s);
@@ -54,7 +54,7 @@ public class NodeOptionHandler extends OptionHandler<Node> {
String nodeName = params.getParameter(0);
final Node node = Jenkins.getInstance().getNode(nodeName);
if (node == null) throw new CmdLineException(owner, "No such node '" + nodeName + "'");
if (node == null) throw new IllegalArgumentException("No such node '" + nodeName + "'");
setter.addValue(node);
return 1;
@@ -30,6 +30,7 @@ import java.util.StringTokenizer;
import jenkins.model.Jenkins;
import org.acegisecurity.AccessDeniedException;
import org.kohsuke.MetaInfServices;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
@@ -81,22 +82,21 @@ public class ViewOptionHandler extends OptionHandler<View> {
* 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
* @return The {@link View} instance. Null if name is empty string
* @throws IllegalArgumentException
* If the view isn't found
* @throws IllegalStateException
* If cannot get active Jenkins instance or view can't contain a views
* @throws AccessDeniedException
* If user doens't have a READ permission for the view
* @since TODO
*/
@CheckForNull
public View getView(final String name) throws CmdLineException {
public View getView(final String name) {
ViewGroup group = Jenkins.getInstance();
ViewGroup group = Jenkins.getActiveInstance();
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()) {
@@ -104,23 +104,16 @@ public class ViewOptionHandler extends OptionHandler<View> {
view = group.getView(viewName);
if (view == null)
throw new CmdLineException(owner, String.format(
throw new IllegalArgumentException(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());
}
view.checkPermission(View.READ);
if (view instanceof ViewGroup) {
group = (ViewGroup) view;
} else if (tok.hasMoreTokens()) {
throw new CmdLineException(
owner, view.getViewName() + " view can not contain views"
);
throw new IllegalStateException(view.getViewName() + " view can not contain views");
}
}
@@ -88,10 +88,10 @@ public abstract class ConsoleLogFilter implements ExtensionPoint {
}
/**
* Called to decorate logger for master/slave communication.
* Called to decorate logger for master/agent communication.
*
* @param computer
* Slave computer for which the logger is getting decorated. Useful to do
* Agent computer for which the logger is getting decorated. Useful to do
* contextual decoration.
* @since 1.632
*/
@@ -208,7 +208,7 @@ public class LogRecorder extends AbstractModelObject implements Saveable {
}
private static final class SetLevel extends MasterToSlaveCallable<Void,Error> {
/** known loggers (kept per slave), to avoid GC */
/** known loggers (kept per agent), to avoid GC */
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection") private static final Set<Logger> loggers = new HashSet<Logger>();
private final String name;
private final Level level;
@@ -363,7 +363,7 @@ public class LogRecorder extends AbstractModelObject implements Saveable {
}
/**
* Gets a view of log records per slave matching this recorder.
* Gets a view of log records per agent matching this recorder.
* @return a map (sorted by display name) from computer to (nonempty) list of log records
* @since 1.519
*/
@@ -109,7 +109,7 @@ public abstract class AbstractBuild<P extends AbstractProject<P,R>,R extends Abs
private static final boolean upstreamCulprits = Boolean.getBoolean("hudson.upstreamCulprits");
/**
* Name of the slave this project was built on.
* Name of the agent this project was built on.
* Null or "" if built by the master. (null happens when we read old record that didn't have this information.)
*/
private String builtOn;
@@ -205,7 +205,7 @@ public abstract class AbstractBuild<P extends AbstractProject<P,R>,R extends Abs
* Returns a {@link Slave} on which this build was done.
*
* @return
* null, for example if the slave that this build run no longer exists.
* null, for example if the agent that this build run no longer exists.
*/
public @CheckForNull Node getBuiltOn() {
if (builtOn==null || builtOn.equals(""))
@@ -215,7 +215,7 @@ public abstract class AbstractBuild<P extends AbstractProject<P,R>,R extends Abs
}
/**
* Returns the name of the slave it was built on; null or "" if built by the master.
* Returns the name of the agent it was built on; null or "" if built by the master.
* (null happens when we read old record that didn't have this information.)
*/
@Exported(name="builtOn")
@@ -276,7 +276,7 @@ public abstract class AbstractBuild<P extends AbstractProject<P,R>,R extends Abs
* {@link AbstractBuildExecution#decideWorkspace(Node,WorkspaceList)}.
*
* @return
* null if the workspace is on a slave that's not connected. Note that once the build is completed,
* null if the workspace is on an agent that's not connected. Note that once the build is completed,
* the workspace may be used to build something else, so the value returned from this method may
* no longer show a workspace as it was used for this build.
* @since 1.319
@@ -808,7 +808,7 @@ public abstract class AbstractBuild<P extends AbstractProject<P,R>,R extends Abs
private void reportBrokenChannel(BuildListener listener) throws IOException {
final Node node = getCurrentNode();
listener.hyperlink("/" + node.toComputer().getUrl() + "log", "Slave went offline during the build");
listener.hyperlink("/" + node.toComputer().getUrl() + "log", "Agent went offline during the build");
listener.getLogger().println();
final OfflineCause offlineCause = node.toComputer().getOfflineCause();
if (offlineCause != null) {
@@ -55,7 +55,6 @@ import hudson.model.queue.CauseOfBlockage;
import hudson.model.queue.QueueTaskFuture;
import hudson.model.queue.SubTask;
import hudson.model.queue.SubTaskContributor;
import hudson.node_monitors.DiskSpaceMonitor;
import hudson.scm.ChangeLogSet;
import hudson.scm.ChangeLogSet.Entry;
import hudson.scm.NullSCM;
@@ -270,7 +269,7 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A
final Jenkins j = Jenkins.getInstance();
final List<Node> nodes = j != null ? j.getNodes() : null;
if(nodes!=null && !nodes.isEmpty()) {
// if a new job is configured with Hudson that already has slave nodes
// if a new job is configured with Hudson that already has agent nodes
// make it roamable by default
canRoam = true;
}
@@ -409,7 +408,7 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A
/**
* Set of labels relevant to this job.
*
* This method is used to determine what slaves are relevant to jobs, for example by {@link View}s.
* This method is used to determine what agents are relevant to jobs, for example by {@link View}s.
* It does not affect the scheduling. This information is informational and the best-effort basis.
*
* @since 1.456
@@ -502,7 +501,7 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A
* Gets the directory where the module is checked out.
*
* @return
* null if the workspace is on a slave that's not connected.
* null if the workspace is on an agent that's not connected.
* @deprecated as of 1.319
* To support concurrent builds of the same project, this method is moved to {@link AbstractBuild}.
* For backward compatibility, this method returns the right {@link AbstractBuild#getWorkspace()} if called
@@ -1265,13 +1264,7 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A
return true; // no SCM
FilePath workspace = build.getWorkspace();
try {
workspace.mkdirs();
} catch (IOException e) {
// Can't create workspace dir - Is slave disk full ?
new DiskSpaceMonitor().markNodeOfflineIfDiskspaceIsTooLow(build.getBuiltOn().toComputer());
throw e;
}
workspace.mkdirs();
boolean r = scm.checkout(build, launcher, workspace, listener, changelogFile);
if (r) {
@@ -1402,7 +1395,7 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A
// At this point we start thinking about triggering a build just to get a workspace,
// because otherwise there's no way we can detect changes.
// However, first there are some conditions in which we do not want to do so.
// give time for slaves to come online if we are right after reconnection (JENKINS-8408)
// give time for agents to come online if we are right after reconnection (JENKINS-8408)
long running = Jenkins.getInstance().getInjector().getInstance(Uptime.class).getUptime();
long remaining = TimeUnit2.MINUTES.toMillis(10)-running;
if (remaining>0 && /* this logic breaks tests of polling */!Functions.getIsUnitTest()) {
@@ -1411,7 +1404,7 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A
return NO_CHANGES;
}
// Do not trigger build, if no suitable slave is online
// Do not trigger build, if no suitable agent is online
if (workspaceOfflineReason.equals(WorkspaceOfflineReason.all_suitable_nodes_are_offline)) {
// No suitable executor is online
listener.getLogger().print(Messages.AbstractProject_AwaitingWorkspaceToComeOnline(running/1000));
@@ -1422,7 +1415,7 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A
Label label = getAssignedLabel();
if (label != null && label.isSelfLabel()) {
// if the build is fixed on a node, then attempting a build will do us
// no good. We should just wait for the slave to come back.
// no good. We should just wait for the agent to come back.
listener.getLogger().print(Messages.AbstractProject_NoWorkspace());
listener.getLogger().println( " (" + workspaceOfflineReason.name() + ")");
return NO_CHANGES;
@@ -25,11 +25,11 @@ package hudson.model;
import hudson.security.ACL;
import hudson.util.StreamTaskListener;
import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import jenkins.model.Jenkins;
/**
@@ -40,8 +40,48 @@ import jenkins.model.Jenkins;
* @since 1.410
*/
public abstract class AsyncAperiodicWork extends AperiodicWork {
/**
/**
* The default number of minutes after which to try and rotate the log file used by {@link #createListener()}.
* This value is controlled by the system property {@code hudson.model.AsyncAperiodicWork.logRotateMinutes}.
* Each individual AsyncAperiodicWork can also have a per-extension override using the system property
* based on their fully qualified class name with {@code .logRotateMinutes} appended.
*
* @since 1.651
*/
private static final long LOG_ROTATE_MINUTES = Long.getLong(AsyncAperiodicWork.class.getName() +
".logRotateMinutes", TimeUnit.DAYS.toMinutes(1));
/**
* The default file size after which to try and rotate the log file used by {@link #createListener()}.
* A value of {@code -1L} disables rotation based on file size.
* This value is controlled by the system property {@code hudson.model.AsyncAperiodicWork.logRotateSize}.
* Each individual AsyncAperiodicWork can also have a per-extension override using the system property
* based on their fully qualified class name with {@code .logRotateSize} appended.
*
* @since 1.651
*/
private static final long LOG_ROTATE_SIZE = Long.getLong(AsyncAperiodicWork.class.getName() + ".logRotateSize",
-1L);
/**
* The number of milliseconds (since startup or previous rotation) after which to try and rotate the log file.
*
* @since 1.651
*/
private final long logRotateMillis;
/**
* {@code -1L} disabled file size based log rotation, otherwise when starting an {@link #execute(TaskListener)},
* if the log file size is above this number of bytes then the log file will be rotated beforehand.
*
* @since 1.651
*/
private final long logRotateSize;
/**
* The last time the log files were rotated. On start-up this will be {@link Long#MIN_VALUE} to ensure that the
* logs are always rotated every time Jenkins starts up to make it easier to correlate events with the main log.
*
* @since 1.651
*/
private long lastRotateMillis = Long.MIN_VALUE;
/**
* Name of the work.
*/
public final String name;
@@ -50,6 +90,9 @@ public abstract class AsyncAperiodicWork extends AperiodicWork {
protected AsyncAperiodicWork(String name) {
this.name = name;
this.logRotateMillis = TimeUnit.MINUTES.toMillis(
Long.getLong(getClass().getName()+".logRotateMinutes", LOG_ROTATE_MINUTES));
this.logRotateSize = Long.getLong(getClass().getName() +".logRotateSize", LOG_ROTATE_SIZE);
}
/**
@@ -59,16 +102,18 @@ public abstract class AsyncAperiodicWork extends AperiodicWork {
public final void doAperiodicRun() {
try {
if(thread!=null && thread.isAlive()) {
logger.log(Level.INFO, name+" thread is still running. Execution aborted.");
logger.log(getSlowLoggingLevel(), "{0} thread is still running. Execution aborted.", name);
return;
}
thread = new Thread(new Runnable() {
public void run() {
logger.log(Level.INFO, "Started "+name);
logger.log(getNormalLoggingLevel(), "Started {0}", name);
long startTime = System.currentTimeMillis();
long stopTime;
StreamTaskListener l = createListener();
try {
l.getLogger().printf("Started at %tc%n", new Date(startTime));
ACL.impersonate(ACL.SYSTEM);
execute(l);
@@ -77,11 +122,16 @@ public abstract class AsyncAperiodicWork extends AperiodicWork {
} catch (InterruptedException e) {
e.printStackTrace(l.fatalError("aborted"));
} finally {
l.closeQuietly();
stopTime = System.currentTimeMillis();
try {
l.getLogger().printf("Finished at %tc. %dms%n", new Date(stopTime), stopTime - startTime);
} finally {
l.closeQuietly();
}
}
logger.log(Level.INFO, "Finished "+name+". "+
(System.currentTimeMillis()-startTime)+" ms");
logger.log(getNormalLoggingLevel(), "Finished {0}. {1,number} ms",
new Object[]{name, stopTime - startTime});
}
},name+" thread");
thread.start();
@@ -91,8 +141,54 @@ public abstract class AsyncAperiodicWork extends AperiodicWork {
}
protected StreamTaskListener createListener() {
File f = getLogFile();
if (!f.getParentFile().isDirectory()) {
if (!f.getParentFile().mkdirs()) {
logger.log(getErrorLoggingLevel(), "Could not create directory {0}", f.getParentFile());
}
}
if (f.isFile()) {
if ((lastRotateMillis + logRotateMillis < System.currentTimeMillis())
|| (logRotateSize > 0 && f.length() > logRotateSize)) {
lastRotateMillis = System.currentTimeMillis();
File prev = null;
for (int i = 5; i >= 0; i--) {
File curr = i == 0 ? f : new File(f.getParentFile(), f.getName() + "." + i);
if (curr.isFile()) {
if (prev != null && !prev.exists()) {
if (!curr.renameTo(prev)) {
logger.log(getErrorLoggingLevel(), "Could not rotate log files {0} to {1}",
new Object[]{curr, prev});
}
} else {
if (!curr.delete()) {
logger.log(getErrorLoggingLevel(), "Could not delete log file {0} to enable rotation",
curr);
}
}
}
prev = curr;
}
}
} else {
lastRotateMillis = System.currentTimeMillis();
// migrate old log files the first time we start-up
File oldFile = new File(Jenkins.getActiveInstance().getRootDir(), f.getName());
if (oldFile.isFile()) {
File newFile = new File(f.getParentFile(), f.getName() + ".1");
if (!newFile.isFile()) {
// if there has never been rotation then this is the first time
if (oldFile.renameTo(newFile)) {
logger.log(getNormalLoggingLevel(), "Moved {0} to {1}", new Object[]{oldFile, newFile});
} else {
logger.log(getErrorLoggingLevel(), "Could not move {0} to {1}",
new Object[]{oldFile, newFile});
}
}
}
}
try {
return new StreamTaskListener(getLogFile());
return new StreamTaskListener(f, true, null);
} catch (IOException e) {
throw new Error(e);
}
@@ -102,7 +198,37 @@ public abstract class AsyncAperiodicWork extends AperiodicWork {
* Determines the log file that records the result of this task.
*/
protected File getLogFile() {
return new File(Jenkins.getInstance().getRootDir(),name+".log");
return new File(Jenkins.getActiveInstance().getRootDir(),"logs/tasks/"+name+".log");
}
/**
* Returns the logging level at which normal messages are displayed.
*
* @return The logging level.
* @since 1.651
*/
protected Level getNormalLoggingLevel() {
return Level.INFO;
}
/**
* Returns the logging level at which previous task still executing messages is displayed.
*
* @return The logging level.
* @since 1.651
*/
protected Level getSlowLoggingLevel() {
return getNormalLoggingLevel();
}
/**
* Returns the logging level at which error messages are displayed.
*
* @return The logging level.
* @since 1.651
*/
protected Level getErrorLoggingLevel() {
return Level.SEVERE;
}
/**
@@ -2,12 +2,13 @@ package hudson.model;
import hudson.security.ACL;
import hudson.util.StreamTaskListener;
import jenkins.model.Jenkins;
import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import jenkins.model.Jenkins;
/**
* {@link PeriodicWork} that takes a long time to run.
@@ -20,6 +21,46 @@ import java.util.logging.LogRecord;
* @author Kohsuke Kawaguchi
*/
public abstract class AsyncPeriodicWork extends PeriodicWork {
/**
* The default number of minutes after which to try and rotate the log file used by {@link #createListener()}.
* This value is controlled by the system property {@code hudson.model.AsyncPeriodicWork.logRotateMinutes}.
* Each individual AsyncPeriodicWork can also have a per-extension override using the system property
* based on their fully qualified class name with {@code .logRotateMinutes} appended.
*
* @since 1.651
*/
private static final long LOG_ROTATE_MINUTES = Long.getLong(AsyncPeriodicWork.class.getName() + ".logRotateMinutes",
TimeUnit.DAYS.toMinutes(1));
/**
* The default file size after which to try and rotate the log file used by {@link #createListener()}.
* A value of {@code -1L} disables rotation based on file size.
* This value is controlled by the system property {@code hudson.model.AsyncPeriodicWork.logRotateSize}.
* Each individual AsyncPeriodicWork can also have a per-extension override using the system property
* based on their fully qualified class name with {@code .logRotateSize} appended.
*
* @since 1.651
*/
private static final long LOG_ROTATE_SIZE = Long.getLong(AsyncPeriodicWork.class.getName() + ".logRotateSize", -1L);
/**
* The number of milliseconds (since startup or previous rotation) after which to try and rotate the log file.
*
* @since 1.651
*/
private final long logRotateMillis;
/**
* {@code -1L} disabled file size based log rotation, otherwise when starting an {@link #execute(TaskListener)},
* if the log file size is above this number of bytes then the log file will be rotated beforehand.
*
* @since 1.651
*/
private final long logRotateSize;
/**
* The last time the log files were rotated. On start-up this will be {@link Long#MIN_VALUE} to ensure that the
* logs are always rotated every time Jenkins starts up to make it easier to correlate events with the main log.
*
* @since 1.651
*/
private long lastRotateMillis = Long.MIN_VALUE;
/**
* Human readable name of the work.
*/
@@ -29,6 +70,9 @@ public abstract class AsyncPeriodicWork extends PeriodicWork {
protected AsyncPeriodicWork(String name) {
this.name = name;
this.logRotateMillis = TimeUnit.MINUTES.toMillis(
Long.getLong(getClass().getName() + ".logRotateMinutes", LOG_ROTATE_MINUTES));
this.logRotateSize = Long.getLong(getClass().getName() + ".logRotateSize", LOG_ROTATE_SIZE);
}
/**
@@ -45,9 +89,11 @@ public abstract class AsyncPeriodicWork extends PeriodicWork {
public void run() {
logger.log(getNormalLoggingLevel(), "Started {0}", name);
long startTime = System.currentTimeMillis();
long stopTime;
StreamTaskListener l = createListener();
try {
l.getLogger().printf("Started at %tc%n", new Date(startTime));
ACL.impersonate(ACL.SYSTEM);
execute(l);
@@ -56,11 +102,16 @@ public abstract class AsyncPeriodicWork extends PeriodicWork {
} catch (InterruptedException e) {
e.printStackTrace(l.fatalError("aborted"));
} finally {
l.closeQuietly();
stopTime = System.currentTimeMillis();
try {
l.getLogger().printf("Finished at %tc. %dms%n", new Date(stopTime), stopTime - startTime);
} finally {
l.closeQuietly();
}
}
logger.log(getNormalLoggingLevel(), "Finished {0}. {1,number} ms",
new Object[]{name, (System.currentTimeMillis()-startTime)});
new Object[]{name, stopTime - startTime});
}
},name+" thread");
thread.start();
@@ -73,8 +124,54 @@ public abstract class AsyncPeriodicWork extends PeriodicWork {
}
protected StreamTaskListener createListener() {
File f = getLogFile();
if (!f.getParentFile().isDirectory()) {
if (!f.getParentFile().mkdirs()) {
logger.log(getErrorLoggingLevel(), "Could not create directory {0}", f.getParentFile());
}
}
if (f.isFile()) {
if ((lastRotateMillis + logRotateMillis < System.currentTimeMillis())
|| (logRotateSize > 0 && f.length() > logRotateSize)) {
lastRotateMillis = System.currentTimeMillis();
File prev = null;
for (int i = 5; i >= 0; i--) {
File curr = i == 0 ? f : new File(f.getParentFile(), f.getName() + "." + i);
if (curr.isFile()) {
if (prev != null && !prev.exists()) {
if (!curr.renameTo(prev)) {
logger.log(getErrorLoggingLevel(), "Could not rotate log files {0} to {1}",
new Object[]{curr, prev});
}
} else {
if (!curr.delete()) {
logger.log(getErrorLoggingLevel(), "Could not delete log file {0} to enable rotation",
curr);
}
}
}
prev = curr;
}
}
} else {
lastRotateMillis = System.currentTimeMillis();
// migrate old log files the first time we start-up
File oldFile = new File(Jenkins.getActiveInstance().getRootDir(), f.getName());
if (oldFile.isFile()) {
File newFile = new File(f.getParentFile(), f.getName() + ".1");
if (!newFile.isFile()) {
// if there has never been rotation then this is the first time
if (oldFile.renameTo(newFile)) {
logger.log(getNormalLoggingLevel(), "Moved {0} to {1}", new Object[]{oldFile, newFile});
} else {
logger.log(getErrorLoggingLevel(), "Could not move {0} to {1}",
new Object[]{oldFile, newFile});
}
}
}
}
try {
return new StreamTaskListener(getLogFile());
return new StreamTaskListener(f, true, null);
} catch (IOException e) {
throw new Error(e);
}
@@ -84,7 +181,7 @@ public abstract class AsyncPeriodicWork extends PeriodicWork {
* Determines the log file that records the result of this task.
*/
protected File getLogFile() {
return new File(Jenkins.getInstance().getRootDir(),name+".log");
return new File(Jenkins.getActiveInstance().getRootDir(),"logs/tasks/"+name+".log");
}
/**
+20 -20
View File
@@ -284,7 +284,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
}
/**
* Directory where rotated slave logs are stored.
* Directory where rotated agent logs are stored.
*
* The method also creates a log directory if required.
*
@@ -293,7 +293,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
protected @Nonnull File getLogDir() {
File dir = new File(Jenkins.getInstance().getRootDir(),"logs/slaves/"+nodeName);
if (!dir.exists() && !dir.mkdirs()) {
LOGGER.severe("Failed to create slave log directory " + dir.getAbsolutePath());
LOGGER.severe("Failed to create agent log directory " + dir.getAbsolutePath());
}
return dir;
}
@@ -306,7 +306,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
}
/**
* Gets the string representation of the slave log.
* Gets the string representation of the agent log.
*/
public String getLog() throws IOException {
return Util.loadFile(getLogFile());
@@ -383,12 +383,12 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
public abstract Charset getDefaultCharset();
/**
* Gets the logs recorded by this slave.
* Gets the logs recorded by this agent.
*/
public abstract List<LogRecord> getLogRecords() throws IOException, InterruptedException;
/**
* If {@link #getChannel()}==null, attempts to relaunch the slave agent.
* If {@link #getChannel()}==null, attempts to relaunch the agent.
*/
public abstract void doLaunchSlaveAgent( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException;
@@ -618,8 +618,8 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
}
/**
* This method is called to determine whether manual launching of the slave is allowed at this point in time.
* @return {@code true} if manual launching of the slave is allowed at this point in time.
* This method is called to determine whether manual launching of the agent is allowed at this point in time.
* @return {@code true} if manual launching of the agent is allowed at this point in time.
*/
@Exported
public boolean isManualLaunchAllowed() {
@@ -647,8 +647,8 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
* Returns true if this computer can be launched by Hudson proactively and automatically.
*
* <p>
* For example, JNLP slaves return {@code false} from this, because the launch process
* needs to be initiated from the slave side.
* For example, JNLP agents return {@code false} from this, because the launch process
* needs to be initiated from the agent side.
*/
@Exported
public boolean isLaunchSupported() {
@@ -661,7 +661,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
* <p>
* In contrast, {@link #isOffline()} represents the actual online/offline
* state. For example, this method may return false while {@link #isOffline()}
* returns true if the slave agent failed to launch.
* returns true if the agent failed to launch.
*
* @deprecated
* You should almost always want {@link #isOffline()}.
@@ -1142,7 +1142,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
}
/**
* Gets the thread dump of the slave JVM.
* Gets the thread dump of the agent JVM.
* @return
* key is the thread name, and the value is the pre-formatted dump.
*/
@@ -1161,14 +1161,14 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
* This method tries to compute the name of the host that's reachable by all the other nodes.
*
* <p>
* Since it's possible that the slave is not reachable from the master (it may be behind a firewall,
* Since it's possible that the agent is not reachable from the master (it may be behind a firewall,
* connecting to master via JNLP), this method may return null.
*
* It's surprisingly tricky for a machine to know a name that other systems can get to,
* especially between things like DNS search suffix, the hosts file, and YP.
*
* <p>
* So the technique here is to compute possible interfaces and names on the slave,
* So the technique here is to compute possible interfaces and names on the agent,
* then try to ping them from the master, and pick the one that worked.
*
* <p>
@@ -1177,7 +1177,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
* @since 1.300
* @return
* null if the host name cannot be computed (for example because this computer is offline,
* because the slave is behind the firewall, etc.)
* because the agent is behind the firewall, etc.)
*/
public String getHostName() throws IOException, InterruptedException {
if(hostNameCached)
@@ -1339,7 +1339,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
if (vc instanceof Channel) {
w.println("Master to slave");
((Channel)vc).dumpExportTable(w);
w.flush(); // flush here once so that even if the dump from the slave fails, the client gets some useful info
w.flush(); // flush here once so that even if the dump from the agent fails, the client gets some useful info
w.println("\n\n\nSlave to master");
w.print(vc.call(new DumpExportTableTask()));
@@ -1401,7 +1401,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
Node result = node.reconfigure(req, req.getSubmittedForm());
replaceBy(result);
// take the user back to the slave top page.
// take the user back to the agent top page.
rsp.sendRedirect2("../" + result.getNodeName() + '/');
}
@@ -1446,7 +1446,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
Node node = getNode();
int i = (node != null) ? nodes.indexOf(node) : -1;
if(i<0) {
throw new IOException("This slave appears to be removed while you were editing the configuration");
throw new IOException("This agent appears to be removed while you were editing the configuration");
}
nodes.set(i, newNode);
app.setNodes(nodes);
@@ -1467,7 +1467,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
}
/**
* Really deletes the slave.
* Really deletes the agent.
*/
@RequirePOST
public HttpResponse doDoDelete() throws IOException {
@@ -1520,7 +1520,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
}
/**
* Returns {@code true} if the computer is accepting tasks. Needed to allow slaves programmatic suspension of task
* Returns {@code true} if the computer is accepting tasks. Needed to allow agents programmatic suspension of task
* scheduling that does not overlap with being offline.
*
* @return {@code true} if the computer is accepting tasks
@@ -1538,7 +1538,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
*/
@CLIResolver
public static Computer resolveForCLI(
@Argument(required=true,metaVar="NAME",usage="Slave name, or empty string for master") String name) throws CmdLineException {
@Argument(required=true,metaVar="NAME",usage="Agent name, or empty string for master") String name) throws CmdLineException {
Jenkins h = Jenkins.getInstance();
Computer item = h.getComputer(name);
if (item==null) {
@@ -137,7 +137,7 @@ public final class ComputerSet extends AbstractModelObject implements Describabl
}
/**
* Gets all the slave names.
* Gets all the agent names.
*/
public List<String> get_slaveNames() {
return new AbstractList<String>() {
@@ -229,7 +229,7 @@ public final class ComputerSet extends AbstractModelObject implements Describabl
}
/**
* First check point in creating a new slave.
* First check point in creating a new agent.
*/
public synchronized void doCreateItem( StaplerRequest req, StaplerResponse rsp,
@QueryParameter String name, @QueryParameter String mode,
@@ -278,7 +278,7 @@ public final class ComputerSet extends AbstractModelObject implements Describabl
}
/**
* Really creates a new slave.
* Really creates a new agent.
*/
public synchronized void doDoCreateItem( StaplerRequest req, StaplerResponse rsp,
@QueryParameter String name,
@@ -295,12 +295,12 @@ public final class ComputerSet extends AbstractModelObject implements Describabl
Node result = NodeDescriptor.all().find(type).newInstance(req, formData);
app.addNode(result);
// take the user back to the slave list top page
// take the user back to the agent list top page
rsp.sendRedirect2(".");
}
/**
* Makes sure that the given name is good as a slave name.
* Makes sure that the given name is good as an agent name.
* @return trimmed name if valid; throws ParseException if not
*/
public String checkName(String name) throws Failure {
@@ -318,7 +318,7 @@ public final class ComputerSet extends AbstractModelObject implements Describabl
}
/**
* Makes sure that the given name is good as a slave name.
* Makes sure that the given name is good as an agent name.
*/
public FormValidation doCheckName(@QueryParameter String value) throws IOException, ServletException {
Jenkins.getInstance().checkPermission(Computer.CREATE);
@@ -694,16 +694,24 @@ public class Fingerprint implements ModelObject, Saveable {
try {
if(s.contains("-")) {
String[] tokens = Util.tokenize(s,"-");
rs.ranges.add(new Range(Integer.parseInt(tokens[0]),Integer.parseInt(tokens[1])+1));
if (tokens.length == 2) {
rs.ranges.add(new Range(Integer.parseInt(tokens[0]), Integer.parseInt(tokens[1]) + 1));
} else {
if (!skipError) {
throw new IllegalArgumentException(
String.format("Unable to parse %s, expected string with a range M-N", list));
}
// ignore malformed text like "1-10-50"
}
} else {
int n = Integer.parseInt(s);
rs.ranges.add(new Range(n,n+1));
}
} catch (NumberFormatException e) {
if (!skipError)
throw new IllegalArgumentException("Unable to parse "+list);
throw new IllegalArgumentException(
String.format("Unable to parse %s, expected number", list));
// ignore malformed text
}
}
return rs;
+2 -2
View File
@@ -106,7 +106,7 @@ public class Hudson extends Jenkins {
}
/**
* Gets the slave node of the give name, hooked under this Hudson.
* Gets the agent of the give name, hooked under this Hudson.
*
* @deprecated
* Use {@link #getNode(String)}. Since 1.252.
@@ -129,7 +129,7 @@ public class Hudson extends Jenkins {
}
/**
* Updates the slave list.
* Updates the agent list.
*
* @deprecated
* Use {@link #setNodes(List)}. Since 1.252.
+2 -2
View File
@@ -237,8 +237,8 @@ public abstract class Label extends Actionable implements Comparable<Label>, Mod
* <p>
* The answer is yes if there is a reasonable basis to believe that Hudson can have
* an executor under this label, given the current configuration. This includes
* situations such as (1) there are offline slaves that have this label (2) clouds exist
* that can provision slaves that have this label.
* situations such as (1) there are offline agents that have this label (2) clouds exist
* that can provision agents that have this label.
*/
public boolean isAssignable() {
for (Node n : getNodes())
@@ -390,7 +390,7 @@ public abstract class LoadStatistics {
Jenkins j = Jenkins.getInstance();
List<Queue.BuildableItem> bis = j.getQueue().getBuildableItems();
// update statistics on slaves
// update statistics on agents
for( Label l : j.getLabels() ) {
l.loadStatistics.updateCounts(l.loadStatistics.computeSnapshot(bis));
}
@@ -421,7 +421,7 @@ public abstract class LoadStatistics {
private static final long serialVersionUID = 1L;
/**
* The total number of executors that Jenkins currently knows, this includes all off-line slaves.
* The total number of executors that Jenkins currently knows, this includes all off-line agents.
*/
private final int definedExecutors;
/**
@@ -467,7 +467,7 @@ public abstract class LoadStatistics {
}
/**
* The total number of executors that Jenkins currently knows, this includes all off-line slaves.
* The total number of executors that Jenkins currently knows, this includes all off-line agents.
*/
@Exported
public int getDefinedExecutors() {
+7 -7
View File
@@ -72,7 +72,7 @@ import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.export.ExportedBean;
/**
* Base type of Jenkins slaves (although in practice, you probably extend {@link Slave} to define a new slave type).
* Base type of Jenkins agents (although in practice, you probably extend {@link Slave} to define a new agent type).
*
* <p>
* As a special case, {@link Jenkins} extends from here.
@@ -83,7 +83,7 @@ import org.kohsuke.stapler.export.ExportedBean;
*
* <p>
* There is no URL binding for {@link Node}. {@link Computer} and {@link TransientComputerActionFactory} must
* be used to associate new {@link Action}s to slaves.
* be used to associate new {@link Action}s to agents.
*
* @author Kohsuke Kawaguchi
* @see NodeDescriptor
@@ -95,7 +95,7 @@ public abstract class Node extends AbstractModelObject implements Reconfigurable
private static final Logger LOGGER = Logger.getLogger(Node.class.getName());
/**
* Newly copied slaves get this flag set, so that Jenkins doesn't try to start/remove this node until its configuration
* Newly copied agents get this flag set, so that Jenkins doesn't try to start/remove this node until its configuration
* is saved once.
*/
protected volatile transient boolean holdOffLaunchUntilSave;
@@ -216,7 +216,7 @@ public abstract class Node extends AbstractModelObject implements Reconfigurable
protected abstract Computer createComputer();
/**
* Returns {@code true} if the node is accepting tasks. Needed to allow slaves programmatic suspension of task
* Returns {@code true} if the node is accepting tasks. Needed to allow agents programmatic suspension of task
* scheduling that does not overlap with being offline. Called by {@link Computer#isAcceptingTasks()}.
* This method is distinct from {@link Computer#isAcceptingTasks()} as sometimes the {@link Node} concrete
* class may not have control over the {@link hudson.model.Computer} concrete class associated with it.
@@ -281,7 +281,7 @@ public abstract class Node extends AbstractModelObject implements Reconfigurable
* {@link LabelFinder} extension point.
*
* This method has a side effect of updating the hudson-wide set of labels
* and should be called after events that will change that - e.g. a slave
* and should be called after events that will change that - e.g. a agent
* connecting.
*/
@Exported
@@ -499,7 +499,7 @@ public abstract class Node extends AbstractModelObject implements Reconfigurable
public abstract NodeDescriptor getDescriptor();
/**
* Estimates the clock difference with this slave.
* Estimates the clock difference with this agent.
*
* @return
* always non-null.
@@ -524,7 +524,7 @@ public abstract class Node extends AbstractModelObject implements Reconfigurable
public abstract Callable<ClockDifference,IOException> getClockDifferenceCallable();
/**
* Constants that control how Hudson allocates jobs to slaves.
* Constants that control how Hudson allocates jobs to agents.
*/
public enum Mode {
NORMAL(Messages._Node_Mode_NORMAL()),
@@ -32,7 +32,7 @@ import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.export.Exported;
/**
* {@link LoadStatistics} for the entire system (the master and all the slaves combined),
* {@link LoadStatistics} for the entire system (the master and all the agents combined),
* and all the jobs that are running on it.
*
* @author Kohsuke Kawaguchi
+4 -44
View File
@@ -171,13 +171,6 @@ import org.kohsuke.stapler.interceptor.RequirePOST;
@ExportedBean
public class Queue extends ResourceController implements Saveable {
/**
* Defines the refresh period for of the internal cache ({@link #itemsView}).
* Data should be defined in milliseconds, default value - 1000;
* @since 1.577
*/
private static int CACHE_REFRESH_PERIOD = Integer.getInteger(Queue.class.getName() + ".cacheRefreshPeriod", 1000);
/**
* Items that are waiting for its quiet period to pass.
*
@@ -218,41 +211,6 @@ public class Queue extends ResourceController implements Saveable {
*/
private final Cache<Long,LeftItem> leftItems = CacheBuilder.newBuilder().expireAfterWrite(5*60, TimeUnit.SECONDS).build();
private final CachedItemList itemsView = new CachedItemList();
/**
* Maintains a copy of {@link Queue#getItems()}
*
* @see Queue#getApproximateItemsQuickly()
*/
private class CachedItemList {
/**
* The current cached value.
*/
@CopyOnWrite
private volatile List<Item> itemsView = Collections.emptyList();
/**
* When does the cache info expire?
*/
private final AtomicLong expires = new AtomicLong();
List<Item> get() {
long t = System.currentTimeMillis();
long d = expires.get();
if (t>d) {// need to refresh the cache
long next = t+CACHE_REFRESH_PERIOD;
if (expires.compareAndSet(d,next)) {
// avoid concurrent cache update via CAS.
// if the getItems() lock is contended,
// some threads will end up serving stale data,
// but that's OK.
itemsView = ImmutableList.copyOf(getItems());
}
}
return itemsView;
}
}
/**
* Data structure created for each idle {@link Executor}.
* This is a job offer from the queue to an executor.
@@ -864,9 +822,11 @@ public class Queue extends ResourceController implements Saveable {
* This method is primarily added to make UI threads run faster.
*
* @since 1.483
* @deprecated Use {@link #getItems()} directly. As of 1.607 the approximation is no longer needed.
*/
@Deprecated
public List<Item> getApproximateItemsQuickly() {
return itemsView.get();
return Arrays.asList(getItems());
}
public Item getItem(long id) {
@@ -2518,7 +2478,7 @@ public class Queue extends ResourceController implements Saveable {
Label label = getAssignedLabel();
List<Node> allNodes = jenkins.getNodes();
if (allNodes.isEmpty())
label = null; // no master/slave. pointless to talk about nodes
label = null; // no master/agent. pointless to talk about nodes
if (label != null) {
Set<Node> nodes = label.getNodes();
+1 -1
View File
@@ -161,7 +161,7 @@ public final class Result implements Serializable, CustomExportedBean {
return l;
}
// Maintain each Result as a singleton deserialized (like build result from a slave node)
// Maintain each Result as a singleton deserialized (like build result from an agent node)
private Object readResolve() {
for (Result r : all)
if (ordinal==r.ordinal)
+1 -1
View File
@@ -624,7 +624,7 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run
/**
* When the build has started running in an executor.
*
* For example, if a build is scheduled 1pm, and stayed in the queue for 1 hour (say, no idle slaves),
* For example, if a build is scheduled 1pm, and stayed in the queue for 1 hour (say, no idle agents),
* then this method returns 2pm, which is the time the job moved from the queue to the building state.
*
* @see #getTimestamp()
+16 -11
View File
@@ -73,7 +73,7 @@ import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
/**
* Information about a Hudson slave node.
* Information about a Hudson agent node.
*
* <p>
* Ideally this would have been in the <tt>hudson.slaves</tt> package,
@@ -81,12 +81,17 @@ import org.kohsuke.stapler.StaplerResponse;
*
* <p>
* TODO: move out more stuff to {@link DumbSlave}.
*
* On Febrary, 2016 a general renaming was done internally: the "slave" term was replaced by
* "Agent". This change was applied in: UI labels/HTML pages, javadocs and log messages.
* Java classes, fields, methods, etc were not renamed to avoid compatibility issues.
* See <a href="https://issues.jenkins-ci.org/browse/JENKINS-27268">JENKINS-27268</a>.
*
* @author Kohsuke Kawaguchi
*/
public abstract class Slave extends Node implements Serializable {
/**
* Name of this slave node.
* Name of this agent node.
*/
protected String name;
@@ -116,12 +121,12 @@ public abstract class Slave extends Node implements Serializable {
private Mode mode;
/**
* Slave availablility strategy.
* Agent availablility strategy.
*/
private RetentionStrategy retentionStrategy;
/**
* The starter that will startup this slave.
* The starter that will startup this agent.
*/
private ComputerLauncher launcher;
@@ -139,7 +144,7 @@ public abstract class Slave extends Node implements Serializable {
private transient volatile Set<Label> labels;
/**
* Id of user which creates this slave {@link User}.
* Id of user which creates this agent {@link User}.
*/
private String userId;
@@ -173,7 +178,7 @@ public abstract class Slave extends Node implements Serializable {
Slave node = (Slave) Jenkins.getInstance().getNode(name);
if(node!=null){
this.userId= node.getUserId(); //slave has already existed
this.userId= node.getUserId(); //agent has already existed
}
else{
User user = User.current();
@@ -190,7 +195,7 @@ public abstract class Slave extends Node implements Serializable {
}
/**
* Return id of user which created this slave
* Return id of user which created this agent
*
* @return id of user
*/
@@ -300,7 +305,7 @@ public abstract class Slave extends Node implements Serializable {
}
/**
* Root directory on this slave where all the job workspaces are laid out.
* Root directory on this agent where all the job workspaces are laid out.
* @return
* null if not connected.
*/
@@ -372,7 +377,7 @@ public abstract class Slave extends Node implements Serializable {
}
/**
* Creates a launcher for the slave.
* Creates a launcher for the agent.
*
* @return
* If there is no computer it will return a {@link hudson.Launcher.DummyLauncher}, otherwise it
@@ -381,7 +386,7 @@ public abstract class Slave extends Node implements Serializable {
public Launcher createLauncher(TaskListener listener) {
SlaveComputer c = getComputer();
if (c == null) {
listener.error("Issue with creating launcher for slave " + name + ".");
listener.error("Issue with creating launcher for agent " + name + ".");
return new Launcher.DummyLauncher(listener);
} else {
return new RemoteLauncher(listener, c.getChannel(), c.isUnix()).decorateFor(this);
@@ -438,7 +443,7 @@ public abstract class Slave extends Node implements Serializable {
}
/**
* Performs syntactical check on the remote FS for slaves.
* Performs syntactical check on the remote FS for agents.
*/
public FormValidation doCheckRemoteFS(@QueryParameter String value) throws IOException, ServletException {
if(Util.fixEmptyAndTrim(value)==null)
@@ -37,7 +37,7 @@ import java.io.Serializable;
/**
* Receives events that happen during some lengthy operation
* that has some chance of failures, such as a build, SCM change polling,
* slave launch, and so on.
* agent launch, and so on.
*
* <p>
* This interface is implemented by Hudson core and passed to extension points so that
@@ -480,6 +480,11 @@ public abstract class View extends AbstractModelObject implements AccessControll
return filterQueue(Arrays.asList(Jenkins.getInstance().getQueue().getItems()));
}
/**
* @deprecated Use {@link #getQueueItems()}. As of 1.607 the approximation is no longer needed.
* @return The items in the queue.
*/
@Deprecated
public List<Queue.Item> getApproximateQueueItemsQuickly() {
return filterQueue(Jenkins.getInstance().getQueue().getApproximateItemsQuickly());
}
@@ -33,8 +33,8 @@ import javax.annotation.CheckForNull;
/**
* Allows to access a workspace as an alternative to online build node.
* <p>
* Primary use case is {@link hudson.slaves.Cloud} implementations that don't keep the slave
* node online to browse workspace, but maintain a copy of node workspace on master.
* Primary use case is {@link hudson.slaves.Cloud} implementations that don't keep the agent
* online to browse workspace, but maintain a copy of node workspace on master.
*
* @author <a href="mailto:nicolas.deloof@gmail.com">Nicolas De Loof</a>
* @since 1.502
@@ -38,7 +38,7 @@ import jenkins.model.Jenkins;
import jenkins.model.ModifiableTopLevelItemGroup;
/**
* Clean up old left-over workspaces from slaves.
* Clean up old left-over workspaces from agents.
*
* @author Kohsuke Kawaguchi
*/
@@ -20,7 +20,7 @@ import static java.util.logging.Level.WARNING;
/**
* Sophisticated version of {@link AbstractNodeMonitorDescriptor} that
* performs monitoring on all slaves concurrently and asynchronously.
* performs monitoring on all agents concurrently and asynchronously.
*
* @param <T>
* represents the the result of the monitoring.
@@ -33,7 +33,7 @@ import org.kohsuke.stapler.StaplerRequest;
import java.io.IOException;
/**
* Discovers the architecture of the system to display in the slave list page.
* Discovers the architecture of the system to display in the agent list page.
*
* @author Kohsuke Kawaguchi
*/
@@ -114,11 +114,11 @@ public abstract class NodeMonitor implements ExtensionPoint, Describable<NodeMon
}
/**
* True if this monitoring shouldn't mark the slaves offline.
* True if this monitoring shouldn't mark the agents offline.
*
* <p>
* Many {@link NodeMonitor}s implement a logic that if the value goes above/below
* a threshold, the slave will be marked offline as a preventive measure.
* a threshold, the agent will be marked offline as a preventive measure.
* This flag controls that.
*
* <p>
@@ -14,7 +14,7 @@ import java.util.concurrent.TimeUnit;
import jenkins.util.Timer;
/**
* When a slave is connected, redo the node monitoring.
* When an agent is connected, redo the node monitoring.
*
* @author Kohsuke Kawaguchi
*/
@@ -34,7 +34,7 @@ public class NodeMonitorUpdater extends ComputerListener {
/**
* Triggers the update with 5 seconds quiet period, to avoid triggering data check too often
* when multiple slaves become online at about the same time.
* when multiple agents become online at about the same time.
*/
@Override
public void onOnline(Computer c, TaskListener listener) throws IOException, InterruptedException {
@@ -40,7 +40,7 @@ import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.export.ExportedBean;
/**
* Monitors the round-trip response time to this slave.
* Monitors the round-trip response time to this agent.
*
* @author Kohsuke Kawaguchi
*/
@@ -64,9 +64,9 @@ public class ResponseTimeMonitor extends NodeMonitor {
}
if(d.hasTooManyTimeouts() && !isIgnored()) {
// unlike other monitors whose failure still allow us to communicate with the slave,
// unlike other monitors whose failure still allow us to communicate with the agent,
// the failure in this monitor indicates that we are just unable to make any requests
// to this slave. So we should severe the connection, as opposed to marking it temporarily
// to this agent. So we should severe the connection, as opposed to marking it temporarily
// off line, which still keeps the underlying channel open.
c.disconnect(d);
LOGGER.warning(Messages.ResponseTimeMonitor_MarkedOffline(c.getName()));
@@ -23,5 +23,5 @@ THE SOFTWARE.
-->
<html><head/><body>
Code that monitors the health of slaves
Code that monitors the health of agents
</body></html>
@@ -59,7 +59,7 @@ public class ZFSProvisioner extends FileSystemProvisioner implements Serializabl
ZFSFileSystem fs = libzfs.getFileSystemByMountPoint(f);
if(fs!=null) return fs.getName();
// TODO: for now, only support slaves that are already on ZFS.
// TODO: for now, only support agents that are already on ZFS.
throw new IOException("Not on ZFS");
}
});
+5 -5
View File
@@ -152,7 +152,7 @@ public abstract class SCM implements Describable<SCM>, ExtensionPoint {
*
* <p>
* This flag affects the behavior of Hudson when a job lost its workspace
* (typically due to a slave outage.) If this method returns false and
* (typically due to a agent outage.) If this method returns false and
* polling is configured, then that would immediately trigger a new build.
*
* <p>
@@ -177,9 +177,9 @@ public abstract class SCM implements Describable<SCM>, ExtensionPoint {
* Called before a workspace is deleted on the given node, to provide SCM an opportunity to perform clean up.
*
* <p>
* Hudson periodically scans through all the slaves and removes old workspaces that are deemed unnecessary.
* Hudson periodically scans through all the agents and removes old workspaces that are deemed unnecessary.
* This behavior is implemented in {@link WorkspaceCleanupThread}, and it is necessary to control the
* disk consumption on slaves. If we don't do this, in a long run, all the slaves will have workspaces
* disk consumption on agents. If we don't do this, in a long run, all the agents will have workspaces
* for all the projects, which will be prohibitive in big Hudson.
*
* <p>
@@ -191,7 +191,7 @@ public abstract class SCM implements Describable<SCM>, ExtensionPoint {
* recursive directory deletion happens.
*
* <p>
* Note that this method does not guarantee that such a clean up will happen. For example, slaves can be
* Note that this method does not guarantee that such a clean up will happen. For example, agents can be
* taken offline by being physically removed from the network, and in such a case there's no opportunity
* to perform this clean up.
*
@@ -233,7 +233,7 @@ public abstract class SCM implements Describable<SCM>, ExtensionPoint {
* Checks if there has been any changes to this module in the repository.
*
* TODO: we need to figure out a better way to communicate an error back,
* so that we won't keep retrying the same node (for example a slave might be down.)
* so that we won't keep retrying the same node (for example an agent might be down.)
*
* <p>
* If the SCM doesn't implement polling, have the {@link #supportsPolling()} method
@@ -5,6 +5,7 @@
*/
package hudson.security.csrf;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.logging.Level;
@@ -95,7 +96,9 @@ public class DefaultCrumbIssuer extends CrumbIssuer {
if (request instanceof HttpServletRequest) {
String newCrumb = issueCrumb(request, salt);
if ((newCrumb != null) && (crumb != null)) {
return newCrumb.equals(crumb);
// String.equals() is not constant-time, but this is
return MessageDigest.isEqual(newCrumb.getBytes(Charset.forName("US-ASCII")),
crumb.getBytes(Charset.forName("US-ASCII")));
}
}
return false;
@@ -50,7 +50,7 @@ public class AbstractCloudComputer<T extends AbstractCloudSlave> extends SlaveCo
}
/**
* When the slave is deleted, free the node right away.
* When the agent is deleted, free the node right away.
*/
@Override
public HttpResponse doDoDelete() throws IOException {
@@ -55,7 +55,7 @@ public abstract class AbstractCloudSlave extends Slave {
public abstract AbstractCloudComputer createComputer();
/**
* Releases and removes this slave.
* Releases and removes this agent.
*/
public void terminate() throws InterruptedException, IOException {
final Computer computer = toComputer();
@@ -74,7 +74,7 @@ public class ChannelPinger extends ComputerListener {
public void install(Channel channel) {
if (pingInterval < 1) {
LOGGER.fine("Slave ping is disabled");
LOGGER.fine("Agent ping is disabled");
return;
}
+6 -6
View File
@@ -44,23 +44,23 @@ import org.kohsuke.stapler.DataBoundConstructor;
import java.util.Collection;
/**
* Creates {@link Node}s to dynamically expand/shrink the slaves attached to Hudson.
* Creates {@link Node}s to dynamically expand/shrink the agents attached to Hudson.
*
* <p>
* Put another way, this class encapsulates different communication protocols
* needed to start a new slave programmatically.
* needed to start a new agent programmatically.
*
* <h2>Notes for implementers</h2>
* <h4>Automatically delete idle slaves</h4>
* <h4>Automatically delete idle agents</h4>
* <p>
* Nodes provisioned from a cloud do not automatically get released just because it's created from {@link Cloud}.
* Doing so requires a use of {@link RetentionStrategy}. Instantiate your {@link Slave} subtype with something
* like {@link CloudSlaveRetentionStrategy} so that it gets automatically deleted after some idle time.
*
* <h4>Freeing an external resource when a slave is removed</h4>
* <h4>Freeing an external resource when an agent is removed</h4>
* <p>
* Whether you do auto scale-down or not, you often want to release an external resource tied to a cloud-allocated
* slave when it is removed.
* agent when it is removed.
*
* <p>
* To do this, have your {@link Slave} subtype remember the necessary handle (such as EC2 instance ID)
@@ -137,7 +137,7 @@ public abstract class Cloud extends AbstractModelObject implements ExtensionPoin
* @param excessWorkload
* Number of total executors needed to meet the current demand.
* Always >= 1. For example, if this is 3, the implementation
* should launch 3 slaves with 1 executor each, or 1 slave with
* should launch 3 agents with 1 executor each, or 1 agent with
* 3 executors, etc.
* @return
* {@link PlannedNode}s that represent asynchronous {@link Node}
@@ -11,9 +11,9 @@ import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Default convenience implementation of {@link RetentionStrategy} for slaves provisioned from {@link Cloud}.
* Default convenience implementation of {@link RetentionStrategy} for agents provisioned from {@link Cloud}.
*
* If a slave is idle for 10 mins, this retention strategy will remove the slave. This can be used as-is for
* If an agent is idle for 10 mins, this retention strategy will remove the agent. This can be used as-is for
* a {@link Node} provisioned by cloud to implement the auto-scaling semantics, it can be subtyped to tweak
* the behavior, or it can be used as an example.
* <p>TODO {@link CloudRetentionStrategy} seems to be a better implementation.
@@ -64,7 +64,7 @@ public class CloudSlaveRetentionStrategy<T extends Computer> extends RetentionSt
}
/**
* If the computer has been idle longer than this time, we'll kill the slave.
* If the computer has been idle longer than this time, we'll kill the agent.
*/
protected long getIdleMaxTime() {
return TIMEOUT;
@@ -140,7 +140,7 @@ public class CommandLauncher extends ComputerLauncher {
}
});
LOGGER.info("slave agent launched for " + computer.getDisplayName());
LOGGER.info("agent launched for " + computer.getDisplayName());
} catch (InterruptedException e) {
e.printStackTrace(listener.error(Messages.ComputerLauncher_abortedLaunch()));
} catch (RuntimeException e) {
@@ -37,7 +37,7 @@ import org.apache.tools.ant.util.DeweyDecimal;
/**
* Extension point to allow control over how {@link Computer}s are "launched",
* meaning how they get connected to their slave agent program.
* meaning how they get connected to their agent program.
*
* <h2>Associated View</h2>
* <dl>
@@ -54,17 +54,17 @@ import org.apache.tools.ant.util.DeweyDecimal;
public abstract class ComputerLauncher extends AbstractDescribableImpl<ComputerLauncher> implements ExtensionPoint {
/**
* Returns true if this {@link ComputerLauncher} supports
* programatic launch of the slave agent in the target {@link Computer}.
* programatic launch of the agent in the target {@link Computer}.
*/
public boolean isLaunchSupported() {
return true;
}
/**
* Launches the slave agent for the given {@link Computer}.
* Launches the agent for the given {@link Computer}.
*
* <p>
* If the slave agent is launched successfully, {@link SlaveComputer#setChannel(InputStream, OutputStream, TaskListener, Channel.Listener)}
* If the agent is launched successfully, {@link SlaveComputer#setChannel(InputStream, OutputStream, TaskListener, Channel.Listener)}
* should be invoked in the end to notify Hudson of the established connection.
* The operation could also fail, in which case there's no need to make any callback notification,
* (except to notify the user of the failure through {@link StreamTaskListener}.)
@@ -51,7 +51,7 @@ public abstract class ComputerListener implements ExtensionPoint {
*
* <p>
* This enables you to do some configurable checks to see if we
* want to bring this slave online or if there are considerations
* want to bring this agent online or if there are considerations
* that would keep us from doing so.
*
* <p>
@@ -61,7 +61,7 @@ public abstract class ComputerListener implements ExtensionPoint {
* @param c
* Computer that's being launched. Never null.
* @param taskListener
* Connected to the slave console log. Useful for reporting progress/errors on a lengthy operation.
* Connected to the agent console log. Useful for reporting progress/errors on a lengthy operation.
* Never null.
* @throws AbortException
* Exceptions will be recorded to the listener, and
@@ -73,12 +73,12 @@ public abstract class ComputerListener implements ExtensionPoint {
}
/**
* Called when a slave attempted to connect via {@link ComputerLauncher} but it failed.
* Called when an agent attempted to connect via {@link ComputerLauncher} but it failed.
*
* @param c
* Computer that was trying to launch. Never null.
* @param taskListener
* Connected to the slave console log. Useful for reporting progress/errors on a lengthy operation.
* Connected to the agent console log. Useful for reporting progress/errors on a lengthy operation.
* Never null.
*
* @since 1.402
@@ -90,19 +90,19 @@ public abstract class ComputerListener implements ExtensionPoint {
* Called before a {@link Computer} is marked online.
*
* <p>
* This enables you to do some work on all the slaves
* This enables you to do some work on all the agents
* as they get connected. Unlike {@link #onOnline(Computer, TaskListener)},
* a failure to carry out this function normally will prevent
* a computer from marked as online.
*
* @param channel
* This is the channel object to talk to the slave.
* This is the channel object to talk to the agent.
* (This is the same object returned by {@link Computer#getChannel()} once
* it's connected.
* @param root
* The directory where this slave stores files.
* The directory where this agent stores files.
* The same as {@link Node#getRootPath()}, except that method returns
* null until the slave is connected. So this parameter is passed explicitly instead.
* null until the agent is connected. So this parameter is passed explicitly instead.
* @param listener
* This is connected to the launch log of the computer.
* Since this method is called synchronously from the thread
@@ -136,11 +136,11 @@ public abstract class ComputerListener implements ExtensionPoint {
* Called right after a {@link Computer} comes online.
*
* <p>
* This enables you to do some work on all the slaves
* This enables you to do some work on all the agents
* as they get connected.
*
* <p>
* Starting Hudson 1.312, this method is also invoked for the master, not just for slaves.
* Starting Hudson 1.312, this method is also invoked for the master, not just for agents.
*
* @param listener
* This is connected to the launch log of the computer.
@@ -199,7 +199,7 @@ public abstract class ComputerListener implements ExtensionPoint {
* Called when configuration of the node was changed, a node is added/removed, etc.
*
* <p>
* This callback is to signal when there's any change to the list of slaves registered to the system,
* This callback is to signal when there's any change to the list of agents registered to the system,
* including addition, removal, changing of the setting, and so on.
*
* @since 1.377
@@ -34,7 +34,7 @@ import hudson.model.PeriodicWork;
import hudson.Extension;
/**
* Periodically checks the slaves and try to reconnect dead slaves.
* Periodically checks the agents and try to reconnect dead agents.
*
* @author Kohsuke Kawaguchi
* @author Stephen Connolly
@@ -37,7 +37,7 @@ import java.io.IOException;
import java.util.logging.Logger;
/**
* Makes sure that connections to slaves are alive, and if they are not, cut them off.
* Makes sure that connections to agents are alive, and if they are not, cut them off.
*
* <p>
* If we only rely on TCP retransmission time out for this, the time it takes to detect a bad connection
@@ -49,7 +49,7 @@ import java.util.logging.Logger;
@Extension
public class ConnectionActivityMonitor extends AsyncPeriodicWork {
public ConnectionActivityMonitor() {
super("Connection Activity monitoring to slaves");
super("Connection Activity monitoring to agents");
}
protected void execute(TaskListener listener) throws IOException, InterruptedException {
@@ -61,7 +61,7 @@ public class ConnectionActivityMonitor extends AsyncPeriodicWork {
if (ch instanceof Channel) {
Channel channel = (Channel) ch;
if (now-channel.getLastHeard() > TIME_TILL_PING) {
// haven't heard from this slave for a while.
// haven't heard from this agent for a while.
Long lastPing = (Long)channel.getProperty(ConnectionActivityMonitor.class);
if (lastPing!=null && now-lastPing > TIMEOUT) {
@@ -47,7 +47,7 @@ import java.util.List;
public class EnvironmentVariablesNodeProperty extends NodeProperty<Node> {
/**
* Slave-specific environment variables
* Agent-specific environment variables
*/
private final EnvVars envVars;
@@ -37,7 +37,7 @@ import org.kohsuke.stapler.DataBoundConstructor;
*/
public class JNLPLauncher extends ComputerLauncher {
/**
* If the slave needs to tunnel the connection to the master,
* If the agent needs to tunnel the connection to the master,
* specify the "host:port" here. This can include the special
* syntax "host:" and ":port" to indicate the default host/port
* shall be used.
@@ -160,7 +160,7 @@ public abstract class NodeProperty<N extends Node> implements ReconfigurableDesc
* @param env
* Manipulate this variable (normally by adding more entries.)
* Note that this is an override, so it doesn't contain environment variables that are
* currently set for the slave process itself.
* currently set for the agent process itself.
* @param listener
* Can be used to send messages.
*
@@ -228,13 +228,13 @@ public class NodeProvisioner {
} catch (InterruptedException e) {
throw new AssertionError(e); // since we confirmed that the future is already done
} catch (ExecutionException e) {
LOGGER.log(Level.WARNING, "Provisioned slave " + f.displayName + " failed to launch",
LOGGER.log(Level.WARNING, "Provisioned agent " + f.displayName + " failed to launch",
e.getCause());
for (CloudProvisioningListener cl : CloudProvisioningListener.all()) {
cl.onFailure(f, e.getCause());
}
} catch (IOException e) {
LOGGER.log(Level.WARNING, "Provisioned slave " + f.displayName + " failed to launch",
LOGGER.log(Level.WARNING, "Provisioned agent " + f.displayName + " failed to launch",
e);
for (CloudProvisioningListener cl : CloudProvisioningListener.all()) {
cl.onFailure(f, e);
@@ -244,7 +244,7 @@ public class NodeProvisioner {
throw e;
} catch (Throwable e) {
LOGGER.log(Level.SEVERE, "Unexpected uncaught exception encountered while "
+ "processing provisioned slave " + f.displayName, e);
+ "processing provisioned agent " + f.displayName, e);
} finally {
while (true) {
List<PlannedNode> orig = pendingLaunches.get();
@@ -607,23 +607,23 @@ public class NodeProvisioner {
@Override
public StrategyDecision apply(@Nonnull StrategyState state) {
/*
Here we determine how many additional slaves we need to keep up with the load (if at all),
Here we determine how many additional agents we need to keep up with the load (if at all),
which involves a simple math.
Broadly speaking, first we check that all the executors are fully utilized before attempting
to start any new slave (this also helps to ignore the temporary gap between different numbers,
to start any new agent (this also helps to ignore the temporary gap between different numbers,
as changes in them are not necessarily synchronized --- for example, there's a time lag between
when a slave launches (thus bringing the planned capacity down) and the time when its executors
when an agent launches (thus bringing the planned capacity down) and the time when its executors
pick up builds (thus bringing the queue length down.)
Once we confirm that, we compare the # of buildable items against the additional slaves
that are being brought online. If we have more jobs than our executors can handle, we'll launch a new slave.
Once we confirm that, we compare the # of buildable items against the additional agents
that are being brought online. If we have more jobs than our executors can handle, we'll launch a new agent.
So this computation involves three stats:
1. # of idle executors
2. # of jobs that are starving for executors
3. # of additional slaves being provisioned (planned capacities.)
3. # of additional agents being provisioned (planned capacities.)
To ignore a temporary surge/drop, we make conservative estimates on each one of them. That is,
we take the current snapshot value, and we take the current exponential moving average (EMA) value,
@@ -675,14 +675,14 @@ public class NodeProvisioner {
CLOUD:
for (Cloud c : Jenkins.getInstance().clouds) {
if (excessWorkload < 0) {
break; // enough slaves allocated
break; // enough agents allocated
}
// Make sure this cloud actually can provision for this label.
if (c.canProvision(state.getLabel())) {
// provisioning a new node should be conservative --- for example if excessWorkload is 1.4,
// we don't want to allocate two nodes but just one.
// OTOH, because of the exponential decay, even when we need one slave,
// OTOH, because of the exponential decay, even when we need one agent,
// excess workload is always
// something like 0.95, in which case we want to allocate one node.
// so the threshold here is 1-MARGIN, and hence floor(excessWorkload+MARGIN) is needed to
@@ -774,7 +774,7 @@ public class NodeProvisioner {
@Extension
public static class NodeProvisionerInvoker extends PeriodicWork {
/**
* Give some initial warm up time so that statically connected slaves
* Give some initial warm up time so that statically connected agents
* can be brought online before we start allocating more.
*/
public static int INITIALDELAY = Integer.getInteger(NodeProvisioner.class.getName()+".initialDelay",LoadStatistics.CLOCK*10);
@@ -48,7 +48,7 @@ import java.util.logging.Logger;
public abstract class RetentionStrategy<T extends Computer> extends AbstractDescribableImpl<RetentionStrategy<?>> implements ExtensionPoint {
/**
* This method will be called periodically to allow this strategy to decide what to do with it's owning slave.
* This method will be called periodically to allow this strategy to decide what to do with it's owning agent.
*
* @param c {@link Computer} for which this strategy is assigned. This computer may be online or offline.
* This object also exposes a bunch of properties that the callee can use to decide what action to take.
@@ -59,11 +59,11 @@ public abstract class RetentionStrategy<T extends Computer> extends AbstractDesc
public abstract long check(T c);
/**
* This method is called to determine whether manual launching of the slave is allowed at this point in time.
* This method is called to determine whether manual launching of the agent is allowed at this point in time.
* @param c {@link Computer} for which this strategy is assigned. This computer may be online or offline.
* This object also exposes a bunch of properties that the callee can use to decide if manual launching is
* allowed at this time.
* @return {@code true} if manual launching of the slave is allowed at this point in time.
* @return {@code true} if manual launching of the agent is allowed at this point in time.
*/
public boolean isManualLaunchAllowed(T c) {
return true;
@@ -85,7 +85,7 @@ public abstract class RetentionStrategy<T extends Computer> extends AbstractDesc
/**
* Called when a new {@link Computer} object is introduced (such as when Hudson started, or when
* a new slave is added.)
* a new agent is added.)
*
* <p>
* The default implementation of this method delegates to {@link #check(Computer)},
@@ -180,12 +180,12 @@ public abstract class RetentionStrategy<T extends Computer> extends AbstractDesc
private static final Logger logger = Logger.getLogger(Demand.class.getName());
/**
* The delay (in minutes) for which the slave must be in demand before tring to launch it.
* The delay (in minutes) for which the agent must be in demand before tring to launch it.
*/
private final long inDemandDelay;
/**
* The delay (in minutes) for which the slave must be idle before taking it offline.
* The delay (in minutes) for which the agent must be idle before taking it offline.
*/
private final long idleDelay;
@@ -45,7 +45,7 @@ import java.util.logging.Logger;
import static java.util.logging.Level.INFO;
/**
* {@link RetentionStrategy} that controls the slave based on a schedule.
* {@link RetentionStrategy} that controls the agent based on a schedule.
*
* @author Stephen Connolly
* @since 1.275
@@ -99,7 +99,7 @@ public class SlaveComputer extends Computer {
private Boolean isUnix;
/**
* Effective {@link ComputerLauncher} that hides the details of
* how we launch a slave agent on this computer.
* how we launch a agent agent on this computer.
*
* <p>
* This is normally the same as {@link Slave#getLauncher()} but
@@ -162,7 +162,7 @@ public class SlaveComputer extends Computer {
@Override
@OverrideMustInvoke(When.ANYTIME)
public boolean isAcceptingTasks() {
// our boolean flag is an override on any additional programmatic reasons why this slave might not be
// our boolean flag is an override on any additional programmatic reasons why this agent might not be
// accepting tasks.
return acceptingTasks && super.isAcceptingTasks();
}
@@ -175,13 +175,13 @@ public class SlaveComputer extends Computer {
}
/**
* Allows suspension of tasks being accepted by the slave computer. While this could be called by a
* Allows suspension of tasks being accepted by the agent computer. While this could be called by a
* {@linkplain hudson.slaves.ComputerLauncher} or a {@linkplain hudson.slaves.RetentionStrategy}, such usage
* can result in fights between multiple actors calling setting differential values. A better approach
* is to override {@link hudson.slaves.RetentionStrategy#isAcceptingTasks(hudson.model.Computer)} if the
* {@link hudson.slaves.RetentionStrategy} needs to control availability.
*
* @param acceptingTasks {@code true} if the slave can accept tasks.
* @param acceptingTasks {@code true} if the agent can accept tasks.
*/
public void setAcceptingTasks(boolean acceptingTasks) {
this.acceptingTasks = acceptingTasks;
@@ -274,7 +274,7 @@ public class SlaveComputer extends Computer {
}
if (channel==null)
throw new IOException("Slave failed to connect, even though the launcher didn't report it. See the log output for details.");
throw new IOException("Agent failed to connect, even though the launcher didn't report it. See the log output for details.");
return null;
}
});
@@ -350,7 +350,7 @@ public class SlaveComputer extends Computer {
}
/**
* Creates a {@link Channel} from the given stream and sets that to this slave.
* Creates a {@link Channel} from the given stream and sets that to this agent.
*
* @param in
* Stream connected to the remote "slave.jar". It's the caller's responsibility to do
@@ -426,11 +426,11 @@ public class SlaveComputer extends Computer {
}
/**
* Returns the remote FS root absolute path or {@code null} if the slave is off-line. The absolute path may change
* Returns the remote FS root absolute path or {@code null} if the agent is off-line. The absolute path may change
* between connections if the connection method does not provide a consistent working directory and the node's
* remote FS is specified as a relative path.
*
* @return the remote FS root absolute path or {@code null} if the slave is off-line.
* @return the remote FS root absolute path or {@code null} if the agent is off-line.
* @since 1.606
*/
@CheckForNull
@@ -575,7 +575,7 @@ public class SlaveComputer extends Computer {
} finally {
SecurityContextHolder.setContext(old);
}
log.println("Slave successfully connected and online");
log.println("Agent successfully connected and online");
Jenkins.getInstance().getQueue().scheduleMaintenance();
}
@@ -644,11 +644,11 @@ public class SlaveComputer extends Computer {
}
/**
* Serves jar files for JNLP slave agents.
* Serves jar files for JNLP agents.
*
* @deprecated since 2008-08-18.
* This URL binding is no longer used and moved up directly under to {@link jenkins.model.Jenkins},
* but it's left here for now just in case some old JNLP slave agents request it.
* but it's left here for now just in case some old JNLP agents request it.
*/
@Deprecated
public Slave.JnlpJar getJnlpJars(String fileName) {
@@ -669,7 +669,7 @@ public class SlaveComputer extends Computer {
try {
Util.deleteRecursive(getLogDir());
} catch (IOException ex) {
logger.log(Level.WARNING, "Unable to delete slave logs", ex);
logger.log(Level.WARNING, "Unable to delete agent logs", ex);
}
}
@@ -706,7 +706,7 @@ public class SlaveComputer extends Computer {
super.setNode(node);
launcher = grabLauncher(node);
// maybe the configuration was changed to relaunch the slave, so try to re-launch now.
// maybe the configuration was changed to relaunch the agent, so try to re-launch now.
// "constructed==null" test is an ugly hack to avoid launching before the object is fully
// constructed.
if(constructed!=null) {
@@ -740,7 +740,7 @@ public class SlaveComputer extends Computer {
}
/**
* Get the slave version
* Get the agent version
*/
public String getSlaveVersion() throws IOException, InterruptedException {
return channel.call(new SlaveVersion());
@@ -794,7 +794,7 @@ public class SlaveComputer extends Computer {
*/
static final class LogHolder {
/**
* This field is used on each slave node to record log records on the slave.
* This field is used on each agent to record logs on the agent.
*/
static final RingBufferLogHandler SLAVE_LOG_HANDLER = new RingBufferLogHandler();
}
@@ -827,9 +827,9 @@ public class SlaveComputer extends Computer {
/**
* Obtains a {@link VirtualChannel} that allows some computation to be performed on the master.
* This method can be called from any thread on the master, or from slave (more precisely,
* it only works from the remoting request-handling thread in slaves, which means if you've started
* separate thread on slaves, that'll fail.)
* This method can be called from any thread on the master, or from agent (more precisely,
* it only works from the remoting request-handling thread in agents, which means if you've started
* separate thread on agents, that'll fail.)
*
* @return null if the calling thread doesn't have any trace of where its master is.
* @since 1.362
@@ -838,7 +838,7 @@ public class SlaveComputer extends Computer {
if (Jenkins.getInstance()!=null)
return FilePath.localChannel;
// if this method is called from within the slave computation thread, this should work
// if this method is called from within the agent computation thread, this should work
Channel c = Channel.current();
if (c!=null && Boolean.TRUE.equals(c.getProperty("slave")))
return c;
@@ -23,5 +23,5 @@ THE SOFTWARE.
-->
<html><head/><body>
Code related to slaves.
Code related to agents.
</body></html>
@@ -53,8 +53,8 @@ import jenkins.model.Jenkins;
* this class, but choosing this class as a base class has several benefits:
*
* <ul>
* <li>Hudson allows admins to specify different locations for tools on some slaves.
* For example, JDK on the master might be on /usr/local/java but on a Windows slave
* <li>Hudson allows admins to specify different locations for tools on some agents.
* For example, JDK on the master might be on /usr/local/java but on a Windows agent
* it could be at c:\Program Files\Java
* <li>Hudson can verify the existence of tools and provide warnings and diagnostics for
* admins. (TBD)
@@ -97,7 +97,7 @@ public abstract class ToolInstaller implements Describable<ToolInstaller>, Exten
* @return the (directory) path at which the tool can be found,
* typically coming from {@link #preferredLocation}
* @throws IOException if installation fails
* @throws InterruptedException if communication with a slave is interrupted
* @throws InterruptedException if communication with a agent is interrupted
*/
public abstract FilePath performInstallation(ToolInstallation tool, Node node, TaskListener log) throws IOException, InterruptedException;
Loaded 100 of 572 files, more files were not shown because too many files have changed in this diff. Show more