提交 fdabb525 编写于 作者: O Oleg Nenashev

Merge branch 'master' into JENKINS-34755

Conflicts:
	core/src/main/java/hudson/FilePath.java
......@@ -60,18 +60,45 @@ Upcoming changes</a>
Internal/build: <code>jenkins-ui</code> (NPM module) is private, used only internally.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-34629">issue 34629</a>)
<li class=rfe>
Do not print stack trace if a plugin is missing its dependencies.
Do not print stack trace during a plugin installation if it is missing its dependencies.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-34683">issue 34683</a>)
<li class=rfe>
Allow specifying custom <code>AbortException</code>s.
(<a href="https://github.com/jenkinsci/jenkins/pull/2288">pull 2288</a>)
<li class=rfe>
Internal: CLI command disconnect-node extracted from core to CLI.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-34328">issue 34328</a>)
Add a <code>hudson.model.UpdateCenter.defaultUpdateSiteId</code> system property,
which allows specifying an alternate default Update Site ID.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-34674">issue 34674</a>)
<li class=rfe>
Developer API: Switch Jenkins.getInstance() to @Nonnull
(new <a href="https://wiki.jenkins-ci.org/display/JENKINS/Features+controlled+by+system+properties">system property</a>).
Remove the historical initialization of CVS changelog parser for jobs without explicit SCM definition.
<b>Warning!</b> This change may potentially cause a regression if a Jenkins plugin depends on this default behavior and injects changelogs without SCM.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-4610">issue 4610</a>)
<li class=bug>
Prevent hanging of the Installation Wizard if the default Update Site ID cannot be resolved.
In such case an error message will be displayed.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-34675">issue 34675</a>)
<li class=bug>
Do not fail with error when enabling a plugin, which has been already enabled.
It prevents errors in the new Installation Wizard, which installs plugins in parallel.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-34710">issue 34710</a>)
<li class=bug>
Disable JSESSIONID in URLs when running in the JBoss web container.
It prevents Error 404 due to invalid links starting from Jenkins 1.556.
More info: <a href="https://issues.jboss.org/browse/WFLY-4782">WFLY-4782</a>
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-34675">issue 34675</a>)
<li class=rfe>
Internal: CLI commands <code>disconnect-node</code> and <code>reload-configuration</code> were extracted from the core to CLI.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-34328">issue 34328</a> and
<a href="https://issues.jenkins-ci.org/browse/JENKINS-31900">issue 31900</a>)
<li class=rfe>
Internal: Support latest source version to avoid compile time warnings with JDK7.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-32978">issue 32978</a>)
<li class=rfe>
Developer API: Switch <code>Jenkins.getInstance()</code> to <code>@Nonnull</code>.
(new <a href="https://wiki.jenkins-ci.org/display/JENKINS/Features+controlled+by+system+properties">system property</a>)
(<a href="https://github.com/jenkinsci/jenkins/pull/2297">pull 2297</a>)
<li class=bug>
Tool installers now follow http redirects properly (<a href="https://issues.jenkins-ci.org/browse/JENKINS-23507">issue 23507</a>)
</ul>
</div><!--=TRUNK-END=-->
......
......@@ -195,7 +195,7 @@ THE SOFTWARE.
<dependency>
<groupId>org.jenkins-ci</groupId>
<artifactId>annotation-indexer</artifactId>
<version>1.7</version>
<version>1.11</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci</groupId>
......@@ -539,7 +539,7 @@ THE SOFTWARE.
<dependency>
<groupId>net.java.sezpoz</groupId>
<artifactId>sezpoz</artifactId>
<version>1.9</version>
<version>1.11</version>
</dependency>
<dependency>
<groupId>org.kohsuke.jinterop</groupId>
......
......@@ -26,6 +26,7 @@
package hudson;
import jenkins.util.SystemProperties;
import com.google.common.annotations.VisibleForTesting;
import com.jcraft.jzlib.GZIPInputStream;
import com.jcraft.jzlib.GZIPOutputStream;
import hudson.Launcher.LocalLauncher;
......@@ -72,6 +73,8 @@ import org.apache.tools.ant.Project;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipFile;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.Stapler;
import javax.annotation.CheckForNull;
......@@ -92,6 +95,7 @@ import java.io.RandomAccessFile;
import java.io.Serializable;
import java.io.Writer;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
......@@ -189,6 +193,11 @@ import org.jenkinsci.remoting.RoleSensitive;
* @see VirtualFile
*/
public final class FilePath implements Serializable {
/**
* Maximum http redirects we will follow. This defaults to the same number as Firefox/Chrome tolerates.
*/
private static final int MAX_REDIRECTS = 20;
/**
* When this {@link FilePath} represents the remote path,
* this field is always non-null on master (the field represents
......@@ -756,6 +765,10 @@ public final class FilePath implements Serializable {
* @since 1.299
*/
public boolean installIfNecessaryFrom(@Nonnull URL archive, @CheckForNull TaskListener listener, @Nonnull String message) throws IOException, InterruptedException {
return installIfNecessaryFrom(archive, listener, message, MAX_REDIRECTS);
}
private boolean installIfNecessaryFrom(@Nonnull URL archive, @CheckForNull TaskListener listener, @Nonnull String message, int maxRedirects) throws InterruptedException, IOException {
try {
FilePath timestamp = this.child(".timestamp");
long lastModified = timestamp.lastModified();
......@@ -778,14 +791,28 @@ public final class FilePath implements Serializable {
}
}
if (lastModified != 0 && con instanceof HttpURLConnection) {
if (con instanceof HttpURLConnection) {
HttpURLConnection httpCon = (HttpURLConnection) con;
int responseCode = httpCon.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_NOT_MODIFIED) {
return false;
} else if (responseCode != HttpURLConnection.HTTP_OK) {
listener.getLogger().println("Skipping installation of " + archive + " to " + remote + " due to server error: " + responseCode + " " + httpCon.getResponseMessage());
return false;
if (responseCode == HttpURLConnection.HTTP_MOVED_PERM
|| responseCode == HttpURLConnection.HTTP_MOVED_TEMP) {
// follows redirect
if (maxRedirects > 0) {
String location = httpCon.getHeaderField("Location");
listener.getLogger().println("Following redirect " + archive.toExternalForm() + " -> " + location);
return installIfNecessaryFrom(getUrlFactory().newURL(location), listener, message, maxRedirects - 1);
} else {
listener.getLogger().println("Skipping installation of " + archive + " to " + remote + " due to too many redirects.");
return false;
}
}
if (lastModified != 0) {
if (responseCode == HttpURLConnection.HTTP_NOT_MODIFIED) {
return false;
} else if (responseCode != HttpURLConnection.HTTP_OK) {
listener.getLogger().println("Skipping installation of " + archive + " to " + remote + " due to server error: " + responseCode + " " + httpCon.getResponseMessage());
return false;
}
}
}
......@@ -2521,6 +2548,31 @@ public final class FilePath implements Serializable {
});
}
private static final UrlFactory DEFAULT_URL_FACTORY = new UrlFactory();
@Restricted(NoExternalUse.class)
static class UrlFactory {
public URL newURL(String location) throws MalformedURLException {
return new URL(location);
}
}
private UrlFactory urlFactory;
@VisibleForTesting
@Restricted(NoExternalUse.class)
void setUrlFactory(UrlFactory urlFactory) {
this.urlFactory = urlFactory;
}
private UrlFactory getUrlFactory() {
if (urlFactory != null) {
return urlFactory;
} else {
return DEFAULT_URL_FACTORY;
}
}
/**
* Short for {@code validateFileMask(path, value, true)}
*/
......
......@@ -559,9 +559,10 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
}
if (dependencyURL != null) {
dependencySet.add(dependencyURL);
// And transitive deps...
addDependencies(dependencyURL, fromPath, dependencySet);
// And then add the current plugin
dependencySet.add(dependencyURL);
}
}
}
......
......@@ -369,6 +369,7 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
env.put("JENKINS_SERVER_COOKIE",SERVER_COOKIE.get());
env.put("HUDSON_SERVER_COOKIE",SERVER_COOKIE.get()); // Legacy compatibility
env.put("JOB_NAME",getFullName());
env.put("JOB_BASE_NAME", getName());
return env;
}
......
......@@ -2394,7 +2394,7 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run
public String getEntryID(Run entry) {
return "tag:" + "hudson.dev.java.net,"
+ entry.getTimestamp().get(Calendar.YEAR) + ":"
+ entry.getParent().getName()+':'+entry.getId();
+ entry.getParent().getFullName()+':'+entry.getId();
}
public String getEntryDescription(Run entry) {
......
......@@ -198,6 +198,12 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
* Connection status has not started yet.
*/
PRECHECK,
/**
* Connection status check has been skipped.
* As example, it may happen if there is no connection check URL defined for the site.
* @since TODO
*/
SKIPPED,
/**
* Connection status is being checked at this time.
*/
......@@ -1379,6 +1385,10 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas
connectionStates.put(ConnectionStatus.INTERNET, ConnectionStatus.OK);
}
});
} else {
LOGGER.log(WARNING, "Update site '{0}' does not declare the connection check URL. "
+ "Skipping the network availability check.", site.getId());
connectionStates.put(ConnectionStatus.INTERNET, ConnectionStatus.SKIPPED);
}
connectionStates.put(ConnectionStatus.UPDATE_SITE, ConnectionStatus.CHECKING);
......
......@@ -342,9 +342,11 @@ public class UpdateSite {
}
/**
* Returns an "always up" server for Internet connectivity testing, or null if we are going to skip the test.
* Gets a URL for the Internet connection check.
* @return an "always up" server for Internet connectivity testing, or {@code null} if we are going to skip the test.
*/
@Exported
@CheckForNull
public String getConnectionCheckUrl() {
Data dt = getData();
if(dt==null) return "http://www.google.com/";
......
......@@ -48,6 +48,9 @@ import static org.acegisecurity.ui.rememberme.TokenBasedRememberMeServices.ACEGI
import org.acegisecurity.userdetails.UserDetailsService;
import org.acegisecurity.userdetails.UserDetails;
import org.acegisecurity.userdetails.UsernameNotFoundException;
import org.apache.commons.lang.StringUtils;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.DoNotUse;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.StaplerRequest;
......@@ -62,6 +65,7 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpSession;
import javax.servlet.http.Cookie;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
......@@ -484,6 +488,56 @@ public abstract class SecurityRealm extends AbstractDescribableImpl<SecurityReal
*/
public static final SecurityRealm NO_AUTHENTICATION = new None();
/**
* Perform a calculation where we should go back after successful login
*
* @return Encoded URI where we should go back after successful login
* or "/" if no way back or an issue occurred
*
* @since TODO
*/
@Restricted(DoNotUse.class)
public static String getFrom() {
String from = null, returnValue = null;
final StaplerRequest request = Stapler.getCurrentRequest();
// Try to obtain a return point either from the Session
// or from the QueryParameter in this order
if (request != null
&& request.getSession(false) != null) {
from = (String) request.getSession().getAttribute("from");
} else if (request != null) {
from = request.getParameter("from");
}
// If entry point was not found, try to deduce it from the request URI
// except pages related to login process
if (from == null
&& request != null
&& request.getRequestURI() != null
&& !request.getRequestURI().equals("/loginError")
&& !request.getRequestURI().equals("/login")) {
from = request.getRequestURI();
}
// If deduced entry point isn't deduced yet or the content is a blank value
// use the root web point "/" as a fallback
if (StringUtils.isBlank(from)) {
from = "/";
}
from.trim();
// Encode the return value
try {
returnValue = java.net.URLEncoder.encode(from, "UTF-8");
} catch (UnsupportedEncodingException e) { }
// Return encoded value or at least "/" in the case exception occurred during encode()
// or if the encoded content is blank value
return StringUtils.isBlank(returnValue) ? "/" : returnValue;
}
private static class None extends SecurityRealm {
public SecurityComponents createSecurityComponents() {
return new SecurityComponents(new AuthenticationManager() {
......
......@@ -25,9 +25,5 @@ THE SOFTWARE.
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler">
<j:invokeStatic var="from" className="java.net.URLEncoder" method="encode">
<j:arg value="${if (request.session.attribute('from')!=null) request.session.getAttribute('from'); else if (request.getParameter('from')!=null) request.getParameter('from'); else if (request.requestURI=='/loginError' || request.requestURI=='/login') '/'; else request.requestURI;}"/>
<j:arg value="UTF-8"/>
</j:invokeStatic>
<a href="${rootURL}/${app.securityRealm.loginUrl}?from=${from}"><b>${%login}</b></a>
<a href="${rootURL}/${app.securityRealm.loginUrl}?from=${app.securityRealm.from}"><b>${%login}</b></a>
</j:jelly>
......@@ -73,8 +73,10 @@
H/15 * * * *
# every ten minutes in the first half of every hour (three times, perhaps at :04, :14, :24)
H(0-29)/10 * * * *
# once every two hours every weekday (perhaps at 9:38 AM, 11:38 AM, 1:38 PM, 3:38 PM)
H 9-16/2 * * 1-5
# once every two hours at 45 minutes past the hour starting at 9:45 AM and finishing at 3:45 PM every weekday.
45 9-16/2 * * 1-5
# once in every two hours slot between 9 AM and 5 PM every weekday (perhaps at 10:38 AM, 12:38 PM, 2:38 PM, 4:38 PM)
H H(9-16)/2 * * 1-5
# once a day on the 1st and 15th of every month except December
H H 1,15 1-11 *
</pre>
......
......@@ -3,7 +3,7 @@ package jenkins.model.CoreEnvironmentContributor;
def l = namespace(lib.JenkinsTagLib)
// also advertises those contributed by Run.getCharacteristicEnvVars()
["BUILD_NUMBER","BUILD_ID","BUILD_DISPLAY_NAME","JOB_NAME","BUILD_TAG","EXECUTOR_NUMBER","NODE_NAME","NODE_LABELS","WORKSPACE","JENKINS_HOME","JENKINS_URL","BUILD_URL","JOB_URL"].each { name ->
["BUILD_NUMBER","BUILD_ID","BUILD_DISPLAY_NAME","JOB_NAME", "JOB_BASE_NAME","BUILD_TAG","EXECUTOR_NUMBER","NODE_NAME","NODE_LABELS","WORKSPACE","JENKINS_HOME","JENKINS_URL","BUILD_URL","JOB_URL"].each { name ->
l.buildEnvVar(name:name) {
raw(_("${name}.blurb"))
}
......
BUILD_NUMBER.blurb=The current build number, such as "153"
BUILD_ID.blurb=The current build ID, identical to BUILD_NUMBER for builds created in 1.597+, but a YYYY-MM-DD_hh-mm-ss timestamp for older builds
BUILD_DISPLAY_NAME.blurb=The display name of the current build, which is something like "#153" by default.
JOB_NAME.blurb=Name of the project of this build, such as "foo" or "foo/bar". (To strip off folder paths from a Bourne shell script, try: <tt>$'{'JOB_NAME##*/}</tt>)
JOB_NAME.blurb=Name of the project of this build, such as "foo" or "foo/bar".
JOB_BASE_NAME.blurb=Short Name of the project of this build stripping off folder paths, such as "foo" for "bar/foo".
BUILD_TAG.blurb=String of "jenkins-<i>$'{'JOB_NAME}</i>-<i>$'{'BUILD_NUMBER}</i>". Convenient to put into a resource file, a jar file, etc for easier identification.
EXECUTOR_NUMBER.blurb=\
The unique number that identifies the current executor \
......
......@@ -38,9 +38,11 @@ import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.net.URLStreamHandlerFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
......@@ -637,6 +639,28 @@ public class FilePathTest {
assertTrue(log, log.contains("504 Gateway Timeout"));
}
@Issue("JENKINS-23507")
@Test public void installIfNecessaryFollowsRedirects() throws Exception{
File tmp = temp.getRoot();
final FilePath d = new FilePath(tmp);
FilePath.UrlFactory urlFactory = mock(FilePath.UrlFactory.class);
d.setUrlFactory(urlFactory);
final HttpURLConnection con = mock(HttpURLConnection.class);
final HttpURLConnection con2 = mock(HttpURLConnection.class);
final URL url = someUrlToZipFile(con);
when(con.getResponseCode()).thenReturn(HttpURLConnection.HTTP_MOVED_TEMP);
URL url2 = someUrlToZipFile(con2);
String someUrl = url2.toExternalForm();
when(con.getHeaderField("Location")).thenReturn(someUrl);
when(urlFactory.newURL(someUrl)).thenReturn(url2);
when(con2.getResponseCode()).thenReturn(HttpURLConnection.HTTP_OK);
when(con2.getInputStream()).thenReturn(someZippedContent());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
String message = "going ahead";
assertTrue(d.installIfNecessaryFrom(url, new StreamTaskListener(baos), message));
}
private URL someUrlToZipFile(final URLConnection con) throws IOException {
final URLStreamHandler urlHandler = new URLStreamHandler() {
......
......@@ -577,7 +577,7 @@ THE SOFTWARE.
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>animal-sniffer-maven-plugin</artifactId>
<version>1.9</version>
<version>1.15</version>
</plugin>
</plugins>
</pluginManagement>
......
/*
* The MIT License
*
* Copyright 2016 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.cli;
import hudson.model.ExecutorTest;
import hudson.model.FreeStyleProject;
import hudson.model.Job;
import hudson.model.Run;
import hudson.model.labels.LabelAtom;
import hudson.tasks.Shell;
import jenkins.model.Jenkins;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;
import static hudson.cli.CLICommandInvoker.Matcher.failedWith;
import static hudson.cli.CLICommandInvoker.Matcher.hasNoStandardOutput;
import static hudson.cli.CLICommandInvoker.Matcher.succeeded;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
/**
* @author pjanouse
*/
public class DeleteBuildsCommandTest {
private CLICommandInvoker command;
@Rule public final JenkinsRule j = new JenkinsRule();
@Before public void setUp() {
command = new CLICommandInvoker(j, "delete-builds");
}
@Test public void deleteBuildsShouldFailWithoutJobReadPermission() throws Exception {
j.createFreeStyleProject("aProject").scheduleBuild2(0).get();
final CLICommandInvoker.Result result = command
.authorizedTo(Jenkins.READ)
.invokeWithArgs("aProject", "1");
assertThat(result, failedWith(3));
assertThat(result, hasNoStandardOutput());
assertThat(result.stderr(), containsString("ERROR: No such job 'aProject'"));
}
@Test public void deleteBuildsShouldFailWithoutRunDeletePermission() throws Exception {
j.createFreeStyleProject("aProject").scheduleBuild2(0).get();
final CLICommandInvoker.Result result = command
.authorizedTo(Jenkins.READ, Job.READ)
.invokeWithArgs("aProject", "1");
assertThat(result, failedWith(6));
assertThat(result, hasNoStandardOutput());
assertThat(result.stderr(), containsString("ERROR: user is missing the Run/Delete permission"));
}
@Test public void deleteBuildsShouldFailIfJobDoesNotExist() throws Exception {
final CLICommandInvoker.Result result = command
.authorizedTo(Jenkins.READ, Job.READ, Run.DELETE)
.invokeWithArgs("never_created", "1");
assertThat(result, failedWith(3));
assertThat(result, hasNoStandardOutput());
assertThat(result.stderr(), containsString("ERROR: No such job 'never_created'"));
}
@Test public void deleteBuildsShouldFailIfJobNameIsEmpty() throws Exception {
j.createFreeStyleProject("aProject").scheduleBuild2(0).get();
assertThat(((FreeStyleProject) j.jenkins.getItem("aProject")).getBuilds(), hasSize(1));
final CLICommandInvoker.Result result = command
.authorizedTo(Jenkins.READ, Job.READ, Run.DELETE)
.invokeWithArgs("", "1");
assertThat(result, failedWith(3));
assertThat(result, hasNoStandardOutput());
assertThat(result.stderr(), containsString("ERROR: No such job ''; perhaps you meant 'aProject'?"));
}
@Test public void deleteBuildsShouldSuccess() throws Exception {
j.createFreeStyleProject("aProject").scheduleBuild2(0).get();
assertThat(((FreeStyleProject) j.jenkins.getItem("aProject")).getBuilds(), hasSize(1));
final CLICommandInvoker.Result result = command
.authorizedTo(Jenkins.READ, Job.READ, Run.DELETE)
.invokeWithArgs("aProject", "1");
assertThat(result, succeeded());
assertThat(result.stdout(), containsString("Deleted 1 builds"));
assertThat(((FreeStyleProject) j.jenkins.getItem("aProject")).getBuilds(), hasSize(0));
}
@Test public void deleteBuildsShouldSuccessIfBuildDoesNotExist() throws Exception {
j.createFreeStyleProject("aProject").scheduleBuild2(0).get();
assertThat(((FreeStyleProject) j.jenkins.getItem("aProject")).getBuilds(), hasSize(1));
final CLICommandInvoker.Result result = command
.authorizedTo(Jenkins.READ, Job.READ, Run.DELETE)
.invokeWithArgs("aProject", "2");
assertThat(result, succeeded());
assertThat(result.stdout(), containsString("Deleted 0 builds"));
}
@Test public void deleteBuildsShouldSuccessIfBuildNumberZeroSpecified() throws Exception {
j.createFreeStyleProject("aProject").scheduleBuild2(0).get();
assertThat(((FreeStyleProject) j.jenkins.getItem("aProject")).getBuilds(), hasSize(1));
final CLICommandInvoker.Result result = command
.authorizedTo(Jenkins.READ, Job.READ, Run.DELETE)
.invokeWithArgs("aProject", "0");
assertThat(result, succeeded());
assertThat(result.stdout(), containsString("Deleted 0 builds"));
}
@Test public void deleteBuildsShouldSuccessEvenTheBuildIsRunning() throws Exception {
FreeStyleProject project = j.createFreeStyleProject("aProject");
ExecutorTest.startBlockingBuild(project);
assertThat(((FreeStyleProject) j.jenkins.getItem("aProject")).getBuilds(), hasSize(1));
final CLICommandInvoker.Result result = command
.authorizedTo(Jenkins.READ, Job.READ, Run.DELETE)
.invokeWithArgs("aProject", "1");
assertThat(result, succeeded());
assertThat(result.stdout(), containsString("Deleted 1 builds"));
assertThat(((FreeStyleProject) j.jenkins.getItem("aProject")).getBuilds(), hasSize(0));
assertThat(project.isBuilding(), equalTo(false));
}
@Test public void deleteBuildsShouldSuccessEvenTheBuildIsStuckInTheQueue() throws Exception {
FreeStyleProject project = j.createFreeStyleProject("aProject");
project.getBuildersList().add(new Shell("echo 1"));
project.setAssignedLabel(new LabelAtom("never_created"));
assertThat("Job wasn't scheduled properly", project.scheduleBuild(0), equalTo(true));
Thread.sleep(1000);
assertThat("Job wasn't scheduled properly - it isn't in the queue", project.isInQueue(), equalTo(true));
assertThat("Job wasn't scheduled properly - it is running on non-exist node", project.isBuilding(), equalTo(false));
final CLICommandInvoker.Result result = command
.authorizedTo(Jenkins.READ, Job.READ, Run.DELETE)
.invokeWithArgs("aProject", "1");
assertThat(result, succeeded());
assertThat(result.stdout(), containsString("Deleted 0 builds"));
assertThat(((FreeStyleProject) j.jenkins.getItem("aProject")).getBuilds(), hasSize(0));
assertThat(((FreeStyleProject) j.jenkins.getItem("aProject")).isBuilding(), equalTo(false));
assertThat(((FreeStyleProject) j.jenkins.getItem("aProject")).isInQueue(), equalTo(true));
Jenkins.getInstance().getQueue().cancel(project);
assertThat(((FreeStyleProject) j.jenkins.getItem("aProject")).getBuilds(), hasSize(0));
assertThat(((FreeStyleProject) j.jenkins.getItem("aProject")).isBuilding(), equalTo(false));
assertThat(((FreeStyleProject) j.jenkins.getItem("aProject")).isInQueue(), equalTo(false));
}
@Test public void deleteBuildsManyShouldSuccess() throws Exception {
FreeStyleProject project = j.createFreeStyleProject("aProject");
j.buildAndAssertSuccess(project);
j.buildAndAssertSuccess(project);
j.buildAndAssertSuccess(project);
j.buildAndAssertSuccess(project);
j.buildAndAssertSuccess(project);
assertThat(((FreeStyleProject) j.jenkins.getItem("aProject")).getBuilds(), hasSize(5));
CLICommandInvoker.Result result = command
.authorizedTo(Jenkins.READ, Job.READ, Run.DELETE)
.invokeWithArgs("aProject", "1,2");
assertThat(result, succeeded());
assertThat(result.stdout(), containsString("Deleted 2 builds"));
assertThat(((FreeStyleProject) j.jenkins.getItem("aProject")).getBuilds(), hasSize(3));
result = command
.authorizedTo(Jenkins.READ, Job.READ, Run.DELETE)
.invokeWithArgs("aProject", "3-5");
assertThat(result, succeeded());
assertThat(result.stdout(), containsString("Deleted 3 builds"));
assertThat(((FreeStyleProject) j.jenkins.getItem("aProject")).getBuilds(), hasSize(0));
}
@Test public void deleteBuildsManyShouldSuccessEvenABuildIsSpecifiedTwice() throws Exception {
FreeStyleProject project = j.createFreeStyleProject("aProject");
j.buildAndAssertSuccess(project);
j.buildAndAssertSuccess(project);
assertThat(((FreeStyleProject) j.jenkins.getItem("aProject")).getBuilds(), hasSize(2));
CLICommandInvoker.Result result = command
.authorizedTo(Jenkins.READ, Job.READ, Run.DELETE)
.invokeWithArgs("aProject", "1,1");
assertThat(result, succeeded());
assertThat(result.stdout(), containsString("Deleted 1 builds"));
assertThat(((FreeStyleProject) j.jenkins.getItem("aProject")).getBuilds(), hasSize(1));
result = command
.authorizedTo(Jenkins.READ, Job.READ, Run.DELETE)
.invokeWithArgs("aProject", "1-1,1-2,2-2");
assertThat(result, succeeded());
assertThat(result.stdout(), containsString("Deleted 1 builds"));
assertThat(((FreeStyleProject) j.jenkins.getItem("aProject")).getBuilds(), hasSize(0));
}
@Test public void deleteBuildsManyShouldSuccessEvenLastBuildDoesNotExist() throws Exception {
FreeStyleProject project = j.createFreeStyleProject("aProject");
j.buildAndAssertSuccess(project);
j.buildAndAssertSuccess(project);
assertThat(((FreeStyleProject) j.jenkins.getItem("aProject")).getBuilds(), hasSize(2));
CLICommandInvoker.Result result = command
.authorizedTo(Jenkins.READ, Job.READ, Run.DELETE)
.invokeWithArgs("aProject", "1,3");
assertThat(result, succeeded());
assertThat(result.stdout(), containsString("Deleted 1 builds"));
assertThat(((FreeStyleProject) j.jenkins.getItem("aProject")).getBuilds(), hasSize(1));
result = command
.authorizedTo(Jenkins.READ, Job.READ, Run.DELETE)
.invokeWithArgs("aProject", "2-3");
assertThat(result, succeeded());
assertThat(result.stdout(), containsString("Deleted 1 builds"));
assertThat(((FreeStyleProject) j.jenkins.getItem("aProject")).getBuilds(), hasSize(0));
}
@Test public void deleteBuildsManyShouldSuccessEvenMiddleBuildDoesNotExist() throws Exception {
FreeStyleProject project = j.createFreeStyleProject("aProject");
j.buildAndAssertSuccess(project);
j.buildAndAssertSuccess(project);
j.buildAndAssertSuccess(project);
j.buildAndAssertSuccess(project);
j.buildAndAssertSuccess(project);
j.buildAndAssertSuccess(project);
project.getBuildByNumber(2).delete();
project.getBuildByNumber(5).delete();
assertThat(((FreeStyleProject) j.jenkins.getItem("aProject")).getBuilds(), hasSize(4));
CLICommandInvoker.Result result = command
.authorizedTo(Jenkins.READ, Job.READ, Run.DELETE)
.invokeWithArgs("aProject", "1,2,3");
assertThat(result, succeeded());
assertThat(result.stdout(), containsString("Deleted 2 builds"));
assertThat(((FreeStyleProject) j.jenkins.getItem("aProject")).getBuilds(), hasSize(2));
result = command
.authorizedTo(Jenkins.READ, Job.READ, Run.DELETE)
.invokeWithArgs("aProject", "4-6");
assertThat(result, succeeded());
assertThat(result.stdout(), containsString("Deleted 2 builds"));
assertThat(((FreeStyleProject) j.jenkins.getItem("aProject")).getBuilds(), hasSize(0));
}
@Test public void deleteBuildsManyShouldSuccessEvenFirstBuildDoesNotExist() throws Exception {
FreeStyleProject project = j.createFreeStyleProject("aProject");
j.buildAndAssertSuccess(project);
j.buildAndAssertSuccess(project);
j.buildAndAssertSuccess(project);
project.getBuildByNumber(1).delete();
assertThat(((FreeStyleProject) j.jenkins.getItem("aProject")).getBuilds(), hasSize(2));
CLICommandInvoker.Result result = command
.authorizedTo(Jenkins.READ, Job.READ, Run.DELETE)
.invokeWithArgs("aProject", "1,2");
assertThat(result, succeeded());
assertThat(result.stdout(), containsString("Deleted 1 builds"));
assertThat(((FreeStyleProject) j.jenkins.getItem("aProject")).getBuilds(), hasSize(1));
result = command
.authorizedTo(Jenkins.READ, Job.READ, Run.DELETE)
.invokeWithArgs("aProject", "2-3");
assertThat(result, succeeded());
assertThat(result.stdout(), containsString("Deleted 1 builds"));
assertThat(((FreeStyleProject) j.jenkins.getItem("aProject")).getBuilds(), hasSize(0));
}
@Test public void deleteBuildsManyShouldSuccessEvenTheFirstAndLastBuildDoesNotExist() throws Exception {
FreeStyleProject project = j.createFreeStyleProject("aProject");
j.buildAndAssertSuccess(project);
j.buildAndAssertSuccess(project);
j.buildAndAssertSuccess(project);
j.buildAndAssertSuccess(project);
j.buildAndAssertSuccess(project);
project.getBuildByNumber(1).delete();
project.getBuildByNumber(3).delete();
project.getBuildByNumber(5).delete();
assertThat(((FreeStyleProject) j.jenkins.getItem("aProject")).getBuilds(), hasSize(2));
CLICommandInvoker.Result result = command
.authorizedTo(Jenkins.READ, Job.READ, Run.DELETE)
.invokeWithArgs("aProject", "1,2,3");
assertThat(result, succeeded());
assertThat(result.stdout(), containsString("Deleted 1 builds"));
assertThat(((FreeStyleProject) j.jenkins.getItem("aProject")).getBuilds(), hasSize(1));
result = command
.authorizedTo(Jenkins.READ, Job.READ, Run.DELETE)
.invokeWithArgs("aProject", "3-5");
assertThat(result, succeeded());
assertThat(result.stdout(), containsString("Deleted 1 builds"));
assertThat(((FreeStyleProject) j.jenkins.getItem("aProject")).getBuilds(), hasSize(0));
}
}
......@@ -144,14 +144,21 @@ public class ExecutorTest {
assertThat(log, containsString("Disconnected by Johnny : Taking offline to break your buil"));
}
private Future<FreeStyleBuild> startBlockingBuild(FreeStyleProject p) throws Exception {
/**
* Start a project with an infinite build step
*
* @param project {@link FreeStyleProject} to start
* @return A {@link Future} object represents the started build
* @throws Exception if somethink wrong happened
*/
public static Future<FreeStyleBuild> startBlockingBuild(FreeStyleProject project) throws Exception {
final OneShotEvent e = new OneShotEvent();
p.getBuildersList().add(new BlockingBuilder(e));
project.getBuildersList().add(new BlockingBuilder(e));
Future<FreeStyleBuild> r = p.scheduleBuild2(0);
Future<FreeStyleBuild> r = project.scheduleBuild2(0);
e.block(); // wait until we are safe to interrupt
assertTrue(p.getLastBuild().isBuilding());
assertTrue(project.getLastBuild().isBuilding());
return r;
}
......
......@@ -200,12 +200,17 @@ exports.testConnectivity = function(siteId, handler) {
handler(false, true, response.message);
}
// Define statuses, which need additional check iteration via async job on the Jenkins master
// Statuses like "OK" or "SKIPPED" are considered as fine.
var uncheckedStatuses = ['PRECHECK', 'CHECKING', 'UNCHECKED'];
if(uncheckedStatuses.indexOf(response.data.updatesite) >= 0 || uncheckedStatuses.indexOf(response.data.internet) >= 0) {
setTimeout(testConnectivity, 100);
}
else {
if(response.status !== 'ok' || response.data.updatesite !== 'OK' || response.data.internet !== 'OK') {
// Update site should be always reachable, but we do not require the internet connection
// if it's explicitly skipped by the update center
if(response.status !== 'ok' || response.data.updatesite !== 'OK' ||
(response.data.internet !== 'OK' && response.data.internet !== 'SKIPPED')) {
// no connectivity, but not fatal
handler(false, false);
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册