├── src
├── main
│ ├── webapp
│ │ └── images
│ │ │ ├── original.jpg
│ │ │ ├── icon-16x16.jpg
│ │ │ ├── icon-32x32.jpg
│ │ │ ├── icon-64x64.jpg
│ │ │ ├── original-rotated.jpg
│ │ │ └── icon-rotated-64x64.jpg
│ ├── resources
│ │ ├── org
│ │ │ └── terracotta
│ │ │ │ └── jenkins
│ │ │ │ └── plugins
│ │ │ │ └── acceleratedbuildnow
│ │ │ │ ├── AcceleratedBuildNowAction
│ │ │ │ └── index.jelly
│ │ │ │ ├── AcceleratedBuildNowBadgeAction
│ │ │ │ └── badge.jelly
│ │ │ │ └── AcceleratedBuildNowVictimBadgeAction
│ │ │ │ └── badge.jelly
│ │ └── index.jelly
│ └── java
│ │ └── org
│ │ └── terracotta
│ │ └── jenkins
│ │ └── plugins
│ │ └── acceleratedbuildnow
│ │ ├── AcceleratedBuildNowPlugin.java
│ │ ├── AcceleratedBuildNowActionFactory.java
│ │ ├── AcceleratedBuildNowComparator.java
│ │ ├── AcceleratedBuildNowBadgeAction.java
│ │ ├── AcceleratedBuildNowVictimBadgeAction.java
│ │ ├── AcceleratedBuildNowSorter.java
│ │ └── AcceleratedBuildNowAction.java
└── test
│ └── java
│ └── org
│ └── terracotta
│ └── jenkins
│ └── plugins
│ └── acceleratedbuildnow
│ ├── AcceleratedBuildNowSorterTest.java
│ └── AcceleratedBuildNowComparatorTest.java
├── .gitignore
├── LICENSE.txt
├── README.md
└── pom.xml
/src/main/webapp/images/original.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/accelerated-build-now-plugin/master/src/main/webapp/images/original.jpg
--------------------------------------------------------------------------------
/src/main/webapp/images/icon-16x16.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/accelerated-build-now-plugin/master/src/main/webapp/images/icon-16x16.jpg
--------------------------------------------------------------------------------
/src/main/webapp/images/icon-32x32.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/accelerated-build-now-plugin/master/src/main/webapp/images/icon-32x32.jpg
--------------------------------------------------------------------------------
/src/main/webapp/images/icon-64x64.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/accelerated-build-now-plugin/master/src/main/webapp/images/icon-64x64.jpg
--------------------------------------------------------------------------------
/src/main/webapp/images/original-rotated.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/accelerated-build-now-plugin/master/src/main/webapp/images/original-rotated.jpg
--------------------------------------------------------------------------------
/src/main/webapp/images/icon-rotated-64x64.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/accelerated-build-now-plugin/master/src/main/webapp/images/icon-rotated-64x64.jpg
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # IntelliJ
2 | .idea/
3 | *.iml
4 | *.iws
5 |
6 | # Eclipse
7 | .classpath
8 | .project
9 | .settings/
10 |
11 | # Maven
12 | target/
13 |
14 | # MacOS
15 | .DS_Store
16 |
--------------------------------------------------------------------------------
/src/main/resources/org/terracotta/jenkins/plugins/acceleratedbuildnow/AcceleratedBuildNowAction/index.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/org/terracotta/jenkins/plugins/acceleratedbuildnow/AcceleratedBuildNowBadgeAction/badge.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/org/terracotta/jenkins/plugins/acceleratedbuildnow/AcceleratedBuildNowVictimBadgeAction/badge.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/index.jelly:
--------------------------------------------------------------------------------
1 |
6 |
7 | A plug-in that enables you to instantly run a job on a busy Jenkins cluster (top of the queue, can kill a build to execute now)
8 |
--------------------------------------------------------------------------------
/src/main/java/org/terracotta/jenkins/plugins/acceleratedbuildnow/AcceleratedBuildNowPlugin.java:
--------------------------------------------------------------------------------
1 | package org.terracotta.jenkins.plugins.acceleratedbuildnow;
2 |
3 | import hudson.Plugin;
4 |
5 | import java.util.logging.Logger;
6 |
7 | /**
8 | * @author Anthony Dahanne
9 | */
10 | public class AcceleratedBuildNowPlugin extends Plugin {
11 | private final static Logger LOG = Logger.getLogger(AcceleratedBuildNowPlugin.class.getName());
12 |
13 | public void start() throws Exception {
14 | LOG.info("Starting Accelerated Build Now Plugin");
15 | }
16 | }
17 |
18 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright 2013 Terracotta Inc.
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 |
--------------------------------------------------------------------------------
/src/main/java/org/terracotta/jenkins/plugins/acceleratedbuildnow/AcceleratedBuildNowActionFactory.java:
--------------------------------------------------------------------------------
1 | package org.terracotta.jenkins.plugins.acceleratedbuildnow;
2 |
3 | import hudson.Extension;
4 | import hudson.model.AbstractProject;
5 | import hudson.model.Action;
6 | import hudson.model.TransientProjectActionFactory;
7 |
8 | import java.util.ArrayList;
9 | import java.util.Collection;
10 |
11 | /**
12 | * @author : Anthony Dahanne
13 | */
14 | @Extension
15 | public class AcceleratedBuildNowActionFactory extends TransientProjectActionFactory{
16 | @Override
17 | public Collection extends Action> createFor(AbstractProject target) {
18 | ArrayList actions = new ArrayList();
19 | actions.add(new AcceleratedBuildNowAction(target));
20 | return actions;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/org/terracotta/jenkins/plugins/acceleratedbuildnow/AcceleratedBuildNowComparator.java:
--------------------------------------------------------------------------------
1 | package org.terracotta.jenkins.plugins.acceleratedbuildnow;
2 |
3 | import hudson.model.AbstractProject;
4 | import hudson.model.Queue;
5 |
6 | import java.util.Comparator;
7 |
8 | /**
9 | * @author : Anthony Dahanne
10 | */
11 | public class AcceleratedBuildNowComparator implements Comparator {
12 |
13 | private final AbstractProject mostPriorityProject;
14 |
15 | public AcceleratedBuildNowComparator(AbstractProject mostPriorityProject) {
16 | this.mostPriorityProject = mostPriorityProject;
17 | }
18 |
19 | public int compare(Queue.BuildableItem buildableItem0, Queue.BuildableItem buildableItem1) {
20 | AbstractProject, ?> project0 = (AbstractProject, ?>) buildableItem0.task;
21 | AbstractProject, ?> project1 = (AbstractProject, ?>) buildableItem1.task;
22 | if(project0.equals(mostPriorityProject)) {
23 | return -1;
24 | }
25 | if(project1.equals(mostPriorityProject)) {
26 | return 1;
27 | }
28 | return 0;
29 | }
30 |
31 | }
--------------------------------------------------------------------------------
/src/main/java/org/terracotta/jenkins/plugins/acceleratedbuildnow/AcceleratedBuildNowBadgeAction.java:
--------------------------------------------------------------------------------
1 | package org.terracotta.jenkins.plugins.acceleratedbuildnow;
2 |
3 | import hudson.model.AbstractBuild;
4 | import hudson.model.BuildBadgeAction;
5 | import org.kohsuke.stapler.export.Exported;
6 |
7 | /**
8 | * @author : Anthony Dahanne
9 | */
10 | public class AcceleratedBuildNowBadgeAction implements BuildBadgeAction{
11 | private final static String ICON_PATH = "/plugin/accelerated-build-now-plugin/images/icon-64x64.jpg";;
12 | private final AbstractBuild killedBuild;
13 |
14 | public AcceleratedBuildNowBadgeAction(AbstractBuild killedBuild) {
15 | this.killedBuild = killedBuild;
16 | }
17 |
18 | @Exported
19 | public String getIconPath() { return ICON_PATH; }
20 |
21 | @Exported
22 | public String getKilledBuildUrl() { return killedBuild.getProject().getUrl(); }
23 |
24 | @Exported
25 | public String getText() { return "This build was top priority built. It aborted and re scheduled " + killedBuild.getProject().getName() + " #" + killedBuild.getNumber(); }
26 |
27 | public String getDisplayName() {
28 | return "";
29 | }
30 |
31 | public String getIconFileName() {
32 | return "";
33 | }
34 |
35 | public String getUrlName() {
36 | return "";
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/org/terracotta/jenkins/plugins/acceleratedbuildnow/AcceleratedBuildNowVictimBadgeAction.java:
--------------------------------------------------------------------------------
1 | package org.terracotta.jenkins.plugins.acceleratedbuildnow;
2 |
3 | import hudson.model.AbstractBuild;
4 | import hudson.model.BuildBadgeAction;
5 | import org.kohsuke.stapler.export.Exported;
6 |
7 | /**
8 | * @author : Anthony Dahanne
9 | */
10 | public class AcceleratedBuildNowVictimBadgeAction implements BuildBadgeAction{
11 | private final static String ICON_PATH = "/plugin/accelerated-build-now-plugin/images/icon-rotated-64x64.jpg";;
12 | private final AbstractBuild killerBuild;
13 |
14 | public AcceleratedBuildNowVictimBadgeAction(AbstractBuild killerBuild) {
15 | this.killerBuild = killerBuild;
16 | }
17 |
18 | @Exported
19 | public String getIconPath() { return ICON_PATH; }
20 |
21 | @Exported
22 | public String getKillerBuildUrl() { return killerBuild.getProject().getUrl(); }
23 |
24 | @Exported
25 | public String getText() { return "This build was aborted and re scheduled by top priority build : " + killerBuild.getProject().getName() + " #" + killerBuild.getNumber(); }
26 |
27 | public String getDisplayName() {
28 | return "";
29 | }
30 |
31 | public String getIconFileName() {
32 | return "";
33 | }
34 |
35 | public String getUrlName() {
36 | return "";
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/org/terracotta/jenkins/plugins/acceleratedbuildnow/AcceleratedBuildNowSorter.java:
--------------------------------------------------------------------------------
1 | package org.terracotta.jenkins.plugins.acceleratedbuildnow;
2 |
3 | import hudson.model.AbstractProject;
4 | import hudson.model.Queue;
5 | import hudson.model.queue.QueueSorter;
6 |
7 | import java.util.Collections;
8 | import java.util.List;
9 |
10 | /**
11 | * @author : Anthony Dahanne
12 | */
13 | public class AcceleratedBuildNowSorter extends QueueSorter {
14 |
15 | private final AbstractProject project;
16 | private final QueueSorter originalQueueSorter;
17 | private final AcceleratedBuildNowComparator comparator;
18 |
19 | public AcceleratedBuildNowSorter(AbstractProject project, QueueSorter originalQueueSorter) {
20 | this.project = project;
21 | this.originalQueueSorter = originalQueueSorter;
22 | comparator = new AcceleratedBuildNowComparator(this.project);
23 | }
24 |
25 | @Override
26 | public void sortBuildableItems(List buildables) {
27 | if(this.originalQueueSorter != null) {
28 | this.originalQueueSorter.sortBuildableItems(buildables);
29 | }
30 | Collections.sort(buildables, comparator);
31 | }
32 |
33 | public QueueSorter getOriginalQueueSorter() {
34 | return originalQueueSorter;
35 | }
36 |
37 | public AbstractProject getProject() {
38 | return project;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/test/java/org/terracotta/jenkins/plugins/acceleratedbuildnow/AcceleratedBuildNowSorterTest.java:
--------------------------------------------------------------------------------
1 | package org.terracotta.jenkins.plugins.acceleratedbuildnow;
2 |
3 | import hudson.model.AbstractProject;
4 | import hudson.model.Queue;
5 | import hudson.model.queue.QueueSorter;
6 | import org.junit.Test;
7 | import org.mockito.Mockito;
8 |
9 | import java.util.ArrayList;
10 | import java.util.List;
11 |
12 | import static org.junit.Assert.assertEquals;
13 | import static org.junit.Assert.assertTrue;
14 | import static org.mockito.Mockito.when;
15 |
16 | /**
17 | * @author : Anthony Dahanne
18 | */
19 | public class AcceleratedBuildNowSorterTest {
20 |
21 | @Test
22 | public void shouldWrapOriginalSorterTest() {
23 | DummyQueueSorter originalQueueSorter = new DummyQueueSorter();
24 | AcceleratedBuildNowSorter acceleratedBuildNowSorter = new AcceleratedBuildNowSorter(getAbstractProject("myProject"), originalQueueSorter);
25 | acceleratedBuildNowSorter.sortBuildableItems(new ArrayList());
26 | assertTrue(originalQueueSorter.wasCalled());
27 | }
28 |
29 | @Test
30 | public void shouldNotWrapAcceleratedBuildNowSorterTest() {
31 | DummyQueueSorter originalQueueSorter = new DummyQueueSorter();
32 | AcceleratedBuildNowSorter acceleratedBuildNowSorter = new AcceleratedBuildNowSorter(getAbstractProject("myProject"), originalQueueSorter);
33 | AcceleratedBuildNowSorter acceleratedBuildNowSorter2 = new AcceleratedBuildNowSorter(getAbstractProject("myProject"), acceleratedBuildNowSorter);
34 | assertEquals(acceleratedBuildNowSorter, acceleratedBuildNowSorter2.getOriginalQueueSorter());
35 | }
36 |
37 |
38 | private AbstractProject getAbstractProject(final String importantProject) {
39 | AbstractProject abstractProject = Mockito.mock(AbstractProject.class);
40 | when(abstractProject.getName()).thenReturn(importantProject);
41 | return abstractProject;
42 | }
43 |
44 | private class DummyQueueSorter extends QueueSorter{
45 | private boolean wasCalled = false;
46 |
47 | @Override
48 | public void sortBuildableItems(List buildables) {
49 | wasCalled = true;
50 | }
51 |
52 | public boolean wasCalled() {
53 | return wasCalled;
54 | }
55 |
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Accelerated Build Now Plugin
2 | ============================
3 | The Jenkins Accelerated Build Now Plugin allows Jenkins users to launch a project's build right away, even if the queue is long (moving it to the top of the queue) and even if no executor is available (killing and rescheduling builds not launched by "humans")
4 |
5 | ## How to build and test :
6 | Simply clone this repo and run mvn clean install hpi:run
7 |
8 | ## How to install :
9 | Download the [latest release](https://github.com/Terracotta-OSS/accelerated-build-now-plugin/releases) and use Jenkins installer advanced tab to upload and install it to Jenkins.
10 |
11 | ## How to use :
12 | When your Jenkins cluster is overloaded with jobs (a queue with 10+ builds, all the executors busy with nightly builds that take ages), you know you will wait ages before the job you want to run effectively starts running.
13 | Relax ! With the Accelerated Build Now Plugin, your job will run right away !
14 |
15 | 1. The queue is full of automatically (not user launched) scheduled jobs, and the only executor available is busy ...
16 |
17 | 
18 |
19 | 2. You want your job maven-surefire to run ASAP, so you click on the Accelerated Build Now button
20 |
21 | 
22 |
23 | 3. Your maven-surefire job just got priorized to the top of the queue and just started running (it had to kill the quartz job, but it rescheduled it already)
24 |
25 | 
26 |
27 | 4. A nice rhyno badge was added to your build that got "acceleratedly built" ; if you click on it you will see the killed build got a killed rhyno badge
28 |
29 | 
30 |
31 | 
32 |
33 | ## How does that work ?
34 | When you click on the Accelerated Build Now button, the plugin will :
35 | * make sure the queue is not empty and all the excutors are busy ( if not, it will normally schedule the build and exit)
36 | * sort the queue using a QueueSorter wrapping any existing QueueSorter (such as the Priority Sorter Plugin queue sorter)
37 | * look for any executors compatible with this job (checking labels) and running a job not scheduled by a "human" (SCM triggered, cron style triggered, etc..); if none is found, exit
38 | * if a compatible executor is found: abort the build (and re schedule it for later) and mark it as killed by the plugin, wait for the accelerated build to start, mark it as accelerated
39 |
40 | ## Authors :
41 | This plugin was developed by Terracotta, by
42 |
43 | - [Anthony Dahanne](https://github.com/anthonydahanne/)
44 |
45 | ## License
46 | Apache 2 licensed (see LICENSE.txt)
47 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 | 4.0.0
3 |
4 | org.jenkins-ci.plugins
5 | plugin
6 | 1.523
7 |
8 |
9 | org.terracotta.jenkins.plugins
10 | accelerated-build-now-plugin
11 | 1.0.2-SNAPSHOT
12 | hpi
13 |
14 |
15 |
16 | org.mockito
17 | mockito-all
18 | 1.8.5
19 | test
20 |
21 |
22 | junit
23 | junit
24 | 4.11
25 | test
26 |
27 |
28 | org.jenkins-ci.main
29 | maven-plugin
30 |
31 |
32 |
33 |
34 |
35 |
36 | repo.jenkins-ci.org
37 | http://repo.jenkins-ci.org/public/
38 |
39 |
40 |
41 |
42 |
43 | repo.jenkins-ci.org
44 | http://repo.jenkins-ci.org/public/
45 |
46 |
47 |
48 |
49 |
50 | https://github.com/Terracotta-OSS/accelerated-build-now-plugin
51 | HEAD
52 | scm:git:ssh://git@github.com/Terracotta-OSS/accelerated-build-now-plugin.git
53 | scm:git:ssh://git@github.com/Terracotta-OSS/accelerated-build-now-plugin.git
54 |
55 |
56 |
57 |
70 |
71 |
72 |
73 | anthonydahanne
74 | Anthony Dahanne
75 | anthony.dahanne@gmail.com
76 | Terracotta
77 | http://www.terracotta.org
78 |
79 |
80 |
81 | https://wiki.jenkins-ci.org/display/JENKINS/Accelerated+Build+Now+Plugin
82 |
83 |
--------------------------------------------------------------------------------
/src/test/java/org/terracotta/jenkins/plugins/acceleratedbuildnow/AcceleratedBuildNowComparatorTest.java:
--------------------------------------------------------------------------------
1 | package org.terracotta.jenkins.plugins.acceleratedbuildnow;
2 |
3 | import hudson.model.*;
4 | import hudson.model.Queue;
5 | import hudson.model.queue.CauseOfBlockage;
6 | import jenkins.util.NonLocalizable;
7 | import org.junit.Assert;
8 | import org.junit.Test;
9 | import org.jvnet.localizer.Localizable;
10 | import org.mockito.Mockito;
11 |
12 | import java.util.*;
13 |
14 | import static org.hamcrest.Matchers.*;
15 | import static org.mockito.Mockito.when;
16 |
17 | /**
18 | * @author : Anthony Dahanne
19 | */
20 | public class AcceleratedBuildNowComparatorTest {
21 |
22 |
23 | @Test
24 | public void sortBuildItemsTest() {
25 | AbstractProject notImportant = getAbstractProject("notImportant");
26 | AbstractProject notImportantEither = getAbstractProject("notImportantEither");
27 | AbstractProject importantProject = getAbstractProject("importantProject");
28 |
29 |
30 | List buildableItemList = new ArrayList();
31 | Queue.BuildableItem notImportantBuildableItem = new Queue.BuildableItem(new Queue.WaitingItem(Calendar.getInstance(),notImportant,new ArrayList()));
32 | buildableItemList.add(notImportantBuildableItem);
33 | Queue.BuildableItem notImportantEitherBuildableItem = new Queue.BuildableItem(new Queue.WaitingItem(Calendar.getInstance(),notImportantEither,new ArrayList()));
34 | buildableItemList.add(notImportantEitherBuildableItem);
35 | Queue.BuildableItem importantBuildableItem = new Queue.BuildableItem(new Queue.WaitingItem(Calendar.getInstance(),importantProject,new ArrayList()));
36 | buildableItemList.add(importantBuildableItem);
37 |
38 | Assert.assertThat(buildableItemList,contains(notImportantBuildableItem, notImportantEitherBuildableItem, importantBuildableItem));
39 |
40 | Comparator acceleratedBuildNowComparator = new AcceleratedBuildNowComparator(importantProject);
41 | Collections.sort(buildableItemList, acceleratedBuildNowComparator);
42 |
43 | Assert.assertThat(buildableItemList,contains(importantBuildableItem, notImportantBuildableItem,notImportantEitherBuildableItem));
44 | }
45 |
46 |
47 | @Test
48 | public void sortBuildItemsTest2() {
49 | AbstractProject notImportant = getAbstractProject("notImportant");
50 | AbstractProject importantProject = getAbstractProject("importantProject");
51 |
52 |
53 | List buildableItemList = new ArrayList();
54 | Queue.BuildableItem importantBuildableItem = new Queue.BuildableItem(new Queue.WaitingItem(Calendar.getInstance(),importantProject,new ArrayList()));
55 | buildableItemList.add(importantBuildableItem);
56 | Queue.BuildableItem notImportantBuildableItem = new Queue.BuildableItem(new Queue.WaitingItem(Calendar.getInstance(),notImportant,new ArrayList()));
57 | buildableItemList.add(notImportantBuildableItem);
58 |
59 | Assert.assertThat(buildableItemList,contains(importantBuildableItem, notImportantBuildableItem));
60 |
61 | Comparator acceleratedBuildNowComparator = new AcceleratedBuildNowComparator(importantProject);
62 | Collections.sort(buildableItemList, acceleratedBuildNowComparator);
63 |
64 | Assert.assertThat(buildableItemList,contains(importantBuildableItem, notImportantBuildableItem));
65 | }
66 |
67 |
68 | private AbstractProject getAbstractProject(final String importantProject) {
69 | AbstractProject abstractProject = Mockito.mock(AbstractProject.class);
70 | when(abstractProject.getName()).thenReturn(importantProject);
71 | return abstractProject;
72 | }
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/src/main/java/org/terracotta/jenkins/plugins/acceleratedbuildnow/AcceleratedBuildNowAction.java:
--------------------------------------------------------------------------------
1 | package org.terracotta.jenkins.plugins.acceleratedbuildnow;
2 |
3 | import hudson.maven.MavenBuild;
4 | import hudson.model.*;
5 | import hudson.model.queue.QueueSorter;
6 | import hudson.model.queue.QueueTaskFuture;
7 | import jenkins.model.Jenkins;
8 | import org.kohsuke.stapler.StaplerRequest;
9 | import org.kohsuke.stapler.StaplerResponse;
10 |
11 | import javax.servlet.ServletException;
12 | import java.io.IOException;
13 | import java.util.HashSet;
14 | import java.util.List;
15 | import java.util.Set;
16 | import java.util.concurrent.ExecutionException;
17 | import java.util.concurrent.Future;
18 | import java.util.logging.Logger;
19 |
20 | /**
21 | * This class contains the main logic of the plugin
22 | *
23 | * @author : Anthony Dahanne
24 | */
25 | public class AcceleratedBuildNowAction implements Action {
26 |
27 | private static final Logger LOG = Logger.getLogger(AcceleratedBuildNowAction.class.getName());
28 | private final AbstractProject project;
29 |
30 | public AcceleratedBuildNowAction(AbstractProject abstractProject) {
31 | this.project = abstractProject;
32 | }
33 |
34 | public String getDisplayName() {
35 | if (project.hasPermission(Item.BUILD)) {
36 | return "Accelerated Build Now !";
37 | }
38 | return null;
39 | }
40 |
41 | public String getIconFileName() {
42 | if (project.hasPermission(Item.BUILD)) {
43 | return "/plugin/accelerated-build-now-plugin/images/icon-64x64.jpg";
44 | }
45 | return null;
46 | }
47 |
48 | public String getUrlName() {
49 | if (project.hasPermission(Item.BUILD)) {
50 | return "accelerated";
51 | }
52 | return null;
53 | }
54 |
55 | public void doBuild(final StaplerRequest request, final StaplerResponse response) throws ServletException,
56 | IOException, InterruptedException, ExecutionException {
57 |
58 | if (!project.hasPermission(Item.BUILD)) {
59 | // Jenkins is secured AND the user is not supposed to build this job
60 | response.sendRedirect(request.getContextPath() + '/' + project.getUrl());
61 | }
62 |
63 | LOG.info("project : " + project.getName() + " needs to be built NOW !");
64 |
65 | Label assignedLabel = project.getAssignedLabel();
66 |
67 | // what if the user clicks repeatedly on the link ?
68 | boolean alreadyTakenCareOf = project.getLastBuild() != null && project.getLastBuild().isBuilding() || queueSorterPriorityOn(project);
69 |
70 | // what if the queue is empty and all executors are already busy ?
71 | boolean jenkinsIsFreeToBuild = Jenkins.getInstance().getQueue().isEmpty() && atLeastOneExecutorIsIdle(assignedLabel);
72 |
73 | if (alreadyTakenCareOf || jenkinsIsFreeToBuild) {
74 | LOG.info("No need for AcceleratedBuildNow plugin (already building or empty queue with idle executors");
75 | project.scheduleBuild2(0, new Cause.UserIdCause(), new Action[0]);
76 | if(Jenkins.getInstance().getQueue().getSorter() !=null && Jenkins.getInstance().getQueue().getBuildableItems()!=null) {
77 | Jenkins.getInstance().getQueue().getSorter().sortBuildableItems(Jenkins.getInstance().getQueue().getBuildableItems());
78 | }
79 | response.sendRedirect(request.getContextPath() + '/' + project.getUrl());
80 | return;
81 | }
82 |
83 | QueueTaskFuture queueTaskFuture = project.scheduleBuild2(0, new Cause.UserIdCause(), new Action[0]);
84 | LOG.info("project : " + project.getName() + " is scheduled to build now !");
85 |
86 |
87 | //replace the original queue sorter with one that will place our project build first in the queue
88 | QueueSorter originalQueueSorter = Jenkins.getInstance().getQueue().getSorter();
89 | AcceleratedBuildNowSorter acceleratedBuildNowSorter = new AcceleratedBuildNowSorter(project, originalQueueSorter);
90 | Jenkins.getInstance().getQueue().setSorter(acceleratedBuildNowSorter);
91 | // we sort the queue so that our project is next to be built on the list
92 | Jenkins.getInstance().getQueue().getSorter().sortBuildableItems(Jenkins.getInstance().getQueue().getBuildableItems());
93 |
94 | AbstractBuild killedBuild = null;
95 | List allItems = Jenkins.getInstance().getAllItems(AbstractProject.class);
96 | for (AbstractProject projectConsidered : allItems) {
97 | AbstractBuild lastBuild = getLastBuild(projectConsidered);
98 |
99 | if (lastBuild != null && lastBuild.isBuilding()) {
100 | if (isBuildNotTriggeredByHuman(lastBuild) && slaveRunningBuildCompatible(lastBuild, assignedLabel)) {
101 | LOG.info("project : " + lastBuild.getProject().getName() + " #" + lastBuild.getNumber() + " was not scheduled by a human, killing it right now to re schedule it later !");
102 | Executor executor = getExecutor(lastBuild);
103 | executor.interrupt(Result.ABORTED);
104 | killedBuild = lastBuild;
105 | break;
106 | }
107 | }
108 | }
109 |
110 | if (killedBuild == null) {
111 | LOG.info("project : " + project.getName() + " could not be built : no way to build it now !");
112 | } else {
113 | AbstractBuild projectBuild = ((Future) queueTaskFuture.getStartCondition()).get();
114 | LOG.info("build #" + projectBuild.getNumber() + " for " + project.getName() + " was launched successfully !");
115 |
116 | // we add a nice badge to the killer build
117 | projectBuild.getActions().add(new AcceleratedBuildNowBadgeAction(killedBuild));
118 |
119 | // we add a nice badge to the killeD build
120 | killedBuild.getActions().add(new AcceleratedBuildNowVictimBadgeAction(projectBuild));
121 |
122 | // we re schedule the build that got killed
123 | rescheduleKilledBuild(killedBuild, new AcceleratedBuildNowKilledCause(), originalQueueSorter);
124 | }
125 | Jenkins.getInstance().getQueue().setSorter(originalQueueSorter);
126 |
127 | response.sendRedirect(request.getContextPath() + '/' + project.getUrl());
128 | }
129 |
130 | private Executor getExecutor(AbstractBuild lastBuild) {
131 | Executor executorConsidered = null;
132 | for (Executor executor : lastBuild.getBuiltOn().toComputer().getExecutors()) {
133 | if (lastBuild.equals(executor.getCurrentExecutable())) {
134 | executorConsidered = executor;
135 | }
136 | }
137 | return executorConsidered;
138 | }
139 |
140 | private AbstractBuild getLastBuild(AbstractProject projectConsidered) {
141 | AbstractBuild lastBuild = projectConsidered.getLastBuild();
142 | // be careful that lastBuild can be a maven module building !
143 | if (lastBuild instanceof MavenBuild) {
144 | lastBuild = ((MavenBuild) lastBuild).getParentBuild();
145 | }
146 | return lastBuild;
147 | }
148 |
149 | private boolean atLeastOneExecutorIsIdle(Label assignedLabel) {
150 |
151 | int idleExecutors = 0;
152 |
153 | if (assignedLabel == null) {
154 | // no assignedLabel ? that's fine, let's find any idle executor
155 | // code based on Label.class code
156 | Set nodes = new HashSet();
157 | Jenkins h = Jenkins.getInstance();
158 | nodes.add(h);
159 | for (Node n : h.getNodes()) {
160 | nodes.add(n);
161 | }
162 | for (Node n : nodes) {
163 | Computer c = n.toComputer();
164 | if (c != null && (c.isOnline() || c.isConnecting()) && c.isAcceptingTasks()) {
165 | idleExecutors += c.countIdle();
166 | }
167 | }
168 | } else {
169 | idleExecutors = assignedLabel.getIdleExecutors();
170 | }
171 | return idleExecutors > 0;
172 | }
173 |
174 | private boolean queueSorterPriorityOn(AbstractProject project) {
175 | QueueSorter originalQueueSorter = Jenkins.getInstance().getQueue().getSorter();
176 | if (originalQueueSorter instanceof AcceleratedBuildNowSorter) {
177 | if (((AcceleratedBuildNowSorter) originalQueueSorter).getProject().equals(project)) {
178 | return true;
179 | }
180 | }
181 | return false;
182 | }
183 |
184 | private boolean isBuildNotTriggeredByHuman(AbstractBuild lastBuild) {
185 | return lastBuild.getCause(Cause.UserIdCause.class) == null && lastBuild.getCause(Cause.UserCause.class) == null;
186 | }
187 |
188 | private boolean slaveRunningBuildCompatible(AbstractBuild lastBuild, Label assignedLabel) {
189 | boolean contains = assignedLabel == null ? true : lastBuild.getBuiltOn().getAssignedLabels().contains(assignedLabel);
190 | if (!contains) {
191 | LOG.info("build : " + lastBuild.getNumber() + " of project " + lastBuild.getProject().getName() + " is not running on node compatible with " + assignedLabel.getName());
192 | }
193 | return contains;
194 | }
195 |
196 | private void rescheduleKilledBuild(AbstractBuild killedBuild, Cause cause, QueueSorter originalQueueSorter) throws ExecutionException, InterruptedException {
197 | //replace the original queue sorter with one that will place our project build first in the queue
198 | AcceleratedBuildNowSorter acceleratedBuildNowSorter = new AcceleratedBuildNowSorter(project, originalQueueSorter);
199 | Jenkins.getInstance().getQueue().setSorter(acceleratedBuildNowSorter);
200 |
201 | QueueTaskFuture queueTaskFuture = killedBuild.getProject().scheduleBuild2(0, cause, new Action[0]);
202 | LOG.info("build that was killed : " + killedBuild.getProject().getName() + " #" + killedBuild.getNumber() + " is scheduled to build next !");
203 |
204 | // we sort the queue so that our project is next to be built on the list
205 | Jenkins.getInstance().getQueue().getSorter().sortBuildableItems(Jenkins.getInstance().getQueue().getBuildableItems());
206 |
207 | }
208 |
209 |
210 | public static class AcceleratedBuildNowKilledCause extends Cause {
211 |
212 | public AcceleratedBuildNowKilledCause() {
213 | }
214 |
215 | public String getShortDescription() {
216 | return "Started by AcceleratedBuildNow plugin";
217 | }
218 |
219 | @Override
220 | public boolean equals(Object o) {
221 | return o instanceof AcceleratedBuildNowKilledCause;
222 | }
223 |
224 | @Override
225 | public int hashCode() {
226 | return 42;
227 | }
228 | }
229 |
230 | }
231 |
--------------------------------------------------------------------------------