mirror of
https://github.com/jenkinsci/jenkins.git
synced 2026-11-09 10:06:13 +00:00
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:
commit
03291cf891
572 files changed
+4385
-3218
No files matched your search
Vendored
+191
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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>
|
||||
|
||||
+2
-1
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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> — 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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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."
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
Reference in new issue
Block a user