From d42670e45e6828e7c5f9fee57d551dd42e902b66 Mon Sep 17 00:00:00 2001 From: gcummings Date: Mon, 9 Jun 2014 12:31:09 +0100 Subject: [PATCH] JENKINS-23368 - Enforce start interval/delay between running builds. --- .../ThrottleJobProperty.java | 19 ++++++++++++++++- .../ThrottleQueueTaskDispatcher.java | 21 +++++++++++++++++++ .../throttleconcurrents/Messages.properties | 4 ++-- .../ThrottleJobProperty/config.jelly | 4 ++++ .../ThrottleJobPropertyTest.java | 6 +++--- 5 files changed, 48 insertions(+), 6 deletions(-) diff --git a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleJobProperty.java b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleJobProperty.java index f913a885..dfcb947f 100644 --- a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleJobProperty.java +++ b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleJobProperty.java @@ -39,6 +39,7 @@ public class ThrottleJobProperty extends JobProperty> { private Integer maxConcurrentPerNode; private Integer maxConcurrentTotal; + private Long startInterval; private List categories; private boolean throttleEnabled; private String throttleOption; @@ -57,10 +58,12 @@ public ThrottleJobProperty(Integer maxConcurrentPerNode, List categories, boolean throttleEnabled, String throttleOption, - @CheckForNull ThrottleMatrixProjectOptions matrixOptions + @CheckForNull ThrottleMatrixProjectOptions matrixOptions, + Long startInterval ) { this.maxConcurrentPerNode = maxConcurrentPerNode == null ? 0 : maxConcurrentPerNode; this.maxConcurrentTotal = maxConcurrentTotal == null ? 0 : maxConcurrentTotal; + this.startInterval = startInterval == null ? 0L : startInterval; this.categories = categories; this.throttleEnabled = throttleEnabled; this.throttleOption = throttleOption; @@ -100,6 +103,10 @@ public Object readResolve() { matrixOptions = new ThrottleMatrixProjectOptions(false, true); } + if (startInterval == null) { + startInterval = 0L; + } + return this; } @@ -146,6 +153,13 @@ public Integer getMaxConcurrentTotal() { return maxConcurrentTotal; } + public Long getStartInterval() { + if (startInterval == null) + startInterval = 0L; + + return startInterval; + } + @CheckForNull public ThrottleMatrixProjectOptions getMatrixOptions() { return matrixOptions; @@ -276,6 +290,9 @@ public FormValidation doCheckMaxConcurrentTotal(@QueryParameter String value) { return checkNullOrInt(value); } + public FormValidation doCheckStartInterval(@QueryParameter String value) { + return checkNullOrInt(value); + } public ThrottleCategory getCategoryByName(String categoryName) { ThrottleCategory category = null; diff --git a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java index 7ff3c2b8..b02f9d10 100644 --- a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java +++ b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java @@ -8,6 +8,7 @@ import hudson.model.Executor; import hudson.model.Hudson; import hudson.model.Node; +import hudson.model.Run; import hudson.model.Queue; import hudson.model.Queue.Task; import hudson.model.labels.LabelAtom; @@ -139,6 +140,26 @@ public CauseOfBlockage canRun(Task task, ThrottleJobProperty tjp) { return CauseOfBlockage.fromMessage(Messages._ThrottleQueueTaskDispatcher_MaxCapacityTotal(totalRunCount)); } } + + // check start interval between runs + if (tjp.getStartInterval().longValue() > 0) { + if (task instanceof AbstractProject) { + AbstractProject p = (AbstractProject) task; + Run lastBuild = p.getLastBuild(); + if (null != lastBuild && lastBuild.isBuilding()) { + + // Probably better to use lastBuild.getStartTimeInMillis() which was introduced in Jenkins 1.494. + // We are building against 1.424 so getTimeInMillis() is an OK approximation + long timeSinceLastBuildStartedInSeconds = (System.currentTimeMillis() - lastBuild.getTimeInMillis()) / 1000; + long remainingIntervalSeconds = tjp.getStartInterval().longValue() - timeSinceLastBuildStartedInSeconds; + if(remainingIntervalSeconds > 0) { + long minutes = remainingIntervalSeconds / 60; + long seconds = remainingIntervalSeconds % 60; + return CauseOfBlockage.fromMessage(Messages._ThrottleQueueTaskDispatcher_StartIntervalLimit(minutes, seconds)); + } + } + } + } } // If the project is in one or more categories... else if (tjp.getThrottleOption().equals("category")) { diff --git a/src/main/resources/hudson/plugins/throttleconcurrents/Messages.properties b/src/main/resources/hudson/plugins/throttleconcurrents/Messages.properties index 949b92e3..1d4225f7 100644 --- a/src/main/resources/hudson/plugins/throttleconcurrents/Messages.properties +++ b/src/main/resources/hudson/plugins/throttleconcurrents/Messages.properties @@ -1,5 +1,5 @@ ThrottleQueueTaskDispatcher.MaxCapacityOnNode=Already running {0} builds on node ThrottleQueueTaskDispatcher.MaxCapacityTotal=Already running {0} builds across all nodes ThrottleQueueTaskDispatcher.BuildPending=A build is pending launch - -ThrottleMatrixProjectOptions.DisplayName=Additional options for Matrix projects \ No newline at end of file +ThrottleQueueTaskDispatcher.StartIntervalLimit=Waiting for required start interval. [{0}m {1}s] +ThrottleMatrixProjectOptions.DisplayName=Additional options for Matrix projects diff --git a/src/main/resources/hudson/plugins/throttleconcurrents/ThrottleJobProperty/config.jelly b/src/main/resources/hudson/plugins/throttleconcurrents/ThrottleJobProperty/config.jelly index e18c2cf3..b742918f 100644 --- a/src/main/resources/hudson/plugins/throttleconcurrents/ThrottleJobProperty/config.jelly +++ b/src/main/resources/hudson/plugins/throttleconcurrents/ThrottleJobProperty/config.jelly @@ -20,6 +20,10 @@ field="maxConcurrentPerNode"> + + + diff --git a/src/test/java/hudson/plugins/throttleconcurrents/ThrottleJobPropertyTest.java b/src/test/java/hudson/plugins/throttleconcurrents/ThrottleJobPropertyTest.java index 47f73fbc..120294b5 100644 --- a/src/test/java/hudson/plugins/throttleconcurrents/ThrottleJobPropertyTest.java +++ b/src/test/java/hudson/plugins/throttleconcurrents/ThrottleJobPropertyTest.java @@ -21,11 +21,11 @@ public void testGetCategoryProjects() throws Exception { String alpha = "alpha", beta = "beta", gamma = "gamma"; // category names FreeStyleProject p1 = createFreeStyleProject("p1"); FreeStyleProject p2 = createFreeStyleProject("p2"); - p2.addProperty(new ThrottleJobProperty(1, 1, Arrays.asList(alpha), false, THROTTLE_OPTION_CATEGORY, ThrottleMatrixProjectOptions.DEFAULT)); + p2.addProperty(new ThrottleJobProperty(1, 1, Arrays.asList(alpha), false, THROTTLE_OPTION_CATEGORY, ThrottleMatrixProjectOptions.DEFAULT, 0L)); FreeStyleProject p3 = createFreeStyleProject("p3"); - p3.addProperty(new ThrottleJobProperty(1, 1, Arrays.asList(alpha, beta), true, THROTTLE_OPTION_CATEGORY, ThrottleMatrixProjectOptions.DEFAULT)); + p3.addProperty(new ThrottleJobProperty(1, 1, Arrays.asList(alpha, beta), true, THROTTLE_OPTION_CATEGORY, ThrottleMatrixProjectOptions.DEFAULT, 0L)); FreeStyleProject p4 = createFreeStyleProject("p4"); - p4.addProperty(new ThrottleJobProperty(1, 1, Arrays.asList(beta, gamma), true, THROTTLE_OPTION_CATEGORY, ThrottleMatrixProjectOptions.DEFAULT)); + p4.addProperty(new ThrottleJobProperty(1, 1, Arrays.asList(beta, gamma), true, THROTTLE_OPTION_CATEGORY, ThrottleMatrixProjectOptions.DEFAULT, 0L)); // TODO when core dep ≥1.480.3, add cloudbees-folder as a test dependency so we can check jobs inside folders assertProjects(alpha, p3); assertProjects(beta, p3, p4);