11
11
import hudson .model .Result ;
12
12
import hudson .model .Run ;
13
13
import hudson .model .queue .Executables ;
14
+ import hudson .plugins .build_timeout .impl .AbsoluteTimeOutStrategy ;
15
+ import hudson .plugins .build_timeout .impl .ElasticTimeOutStrategy ;
16
+ import hudson .plugins .build_timeout .impl .LikelyStuckTimeOutStrategy ;
14
17
import hudson .tasks .BuildWrapper ;
15
18
import hudson .tasks .BuildWrapperDescriptor ;
16
19
import hudson .triggers .SafeTimerTask ;
22
25
import java .io .IOException ;
23
26
import java .util .List ;
24
27
import java .util .Set ;
28
+
29
+ import jenkins .model .Jenkins ;
25
30
import net .sf .json .JSONObject ;
26
31
import org .kohsuke .stapler .DataBoundConstructor ;
27
32
import org .kohsuke .stapler .StaplerRequest ;
33
38
*/
34
39
public class BuildTimeoutWrapper extends BuildWrapper {
35
40
36
- protected static final int NUMBER_OF_BUILDS_TO_AVERAGE = 3 ;
37
41
public static long MINIMUM_TIMEOUT_MILLISECONDS = Long .getLong (BuildTimeoutWrapper .class .getName ()+ ".MINIMUM_TIMEOUT_MILLISECONDS" , 3 * 60 * 1000 );
38
42
39
-
40
- public static final String ABSOLUTE = "absolute" ;
41
- public static final String ELASTIC = "elastic" ;
42
- public static final String STUCK = "likelyStuck" ;
43
-
44
- /**
45
- * If the build took longer than this amount of minutes,
46
- * it will be terminated.
47
- */
48
- public int timeoutMinutes ;
43
+
44
+ private /* final */ BuildTimeOutStrategy strategy ;
49
45
50
46
/**
51
47
* Fail the build rather than aborting it
52
48
*/
53
49
public boolean failBuild ;
54
50
55
-
56
51
/**
57
52
* Writing the build description when timeout occurred.
58
53
*/
59
54
public boolean writingDescription ;
60
55
61
- /**
62
- * The percentage of the mean of the duration of the last n successful builds
63
- * to wait before killing the build.
64
- *
65
- * IE, if the last n successful builds averaged a 10 minute duration,
66
- * then 200% of that would be 20 minutes.
67
- */
68
- public int timeoutPercentage ;
69
-
70
- /**
71
- * Values can be "elastic" or "absolute"
72
- */
73
- public String timeoutType ;
74
-
75
- /**
76
- * The timeout to use if there are no valid builds in the build
77
- * history (ie, no successful or unstable builds)
78
- */
79
- public Integer timeoutMinutesElasticDefault ;
80
56
81
57
@ DataBoundConstructor
82
- public BuildTimeoutWrapper (int timeoutMinutes , boolean failBuild , boolean writingDescription ,
83
- int timeoutPercentage , int timeoutMinutesElasticDefault , String timeoutType ) {
84
- this .timeoutMinutes = Math .max (3 ,timeoutMinutes );
58
+ public BuildTimeoutWrapper (BuildTimeOutStrategy strategy , boolean failBuild , boolean writingDescription ) {
59
+ this .strategy = strategy ;
85
60
this .failBuild = failBuild ;
86
61
this .writingDescription = writingDescription ;
87
- this .timeoutPercentage = timeoutPercentage ;
88
- this .timeoutMinutesElasticDefault = Math .max (3 , timeoutMinutesElasticDefault );
89
- this .timeoutType = timeoutType ;
90
62
}
91
63
92
64
@ Override
@@ -131,47 +103,11 @@ public void doRun() {
131
103
132
104
private final TimeoutTimerTask task ;
133
105
134
- private final long effectiveTimeout ;
106
+ private final long effectiveTimeout = strategy . getTimeOut ( build ) ;
135
107
136
108
public EnvironmentImpl () {
137
- long timeout ;
138
- if (ELASTIC .equals (timeoutType )) {
139
- timeout = getEffectiveTimeout (timeoutMinutes * 60L * 1000L , timeoutPercentage ,
140
- timeoutMinutesElasticDefault * 60 *1000 , timeoutType , build .getProject ().getBuilds ());
141
- } else if (STUCK .equals (timeoutType )) {
142
- timeout = getLikelyStuckTime ();
143
- } else {
144
- timeout = timeoutMinutes * 60L * 1000L ;
145
- }
146
-
147
- this .effectiveTimeout = timeout ;
148
109
task = new TimeoutTimerTask (build , listener );
149
- Trigger .timer .schedule (task , timeout );
150
- }
151
-
152
- /**
153
- * Get the time considered it stuck.
154
- *
155
- * @return 10 times as much as eta if eta is available, else 24 hours.
156
- * @see Executor#isLikelyStuck()
157
- */
158
- private long getLikelyStuckTime () {
159
- Executor executor = build .getExecutor ();
160
- if (executor == null ) {
161
- return TimeUnit2 .HOURS .toMillis (24 );
162
- }
163
-
164
- Queue .Executable executable = executor .getCurrentExecutable ();
165
- if (executable == null ) {
166
- return TimeUnit2 .HOURS .toMillis (24 );
167
- }
168
-
169
- long eta = Executables .getEstimatedDurationFor (executable );
170
- if (eta >= 0 ) {
171
- return eta * 10 ;
172
- } else {
173
- return TimeUnit2 .HOURS .toMillis (24 );
174
- }
110
+ Trigger .timer .schedule (task , effectiveTimeout );
175
111
}
176
112
177
113
@ Override
@@ -184,44 +120,15 @@ public boolean tearDown(AbstractBuild build, BuildListener listener) throws IOEx
184
120
return new EnvironmentImpl ();
185
121
}
186
122
187
- public static long getEffectiveTimeout (long timeoutMilliseconds , int timeoutPercentage , int timeoutMillsecondsElasticDefault ,
188
- String timeoutType , List <Run > builds ) {
189
-
190
- if (ELASTIC .equals (timeoutType )) {
191
- double elasticTimeout = getElasticTimeout (timeoutPercentage , builds );
192
- if (elasticTimeout == 0 ) {
193
- return Math .max (MINIMUM_TIMEOUT_MILLISECONDS , timeoutMillsecondsElasticDefault );
194
- } else {
195
- return (long ) Math .max (MINIMUM_TIMEOUT_MILLISECONDS , elasticTimeout );
196
- }
197
- } else {
198
- return (long ) Math .max (MINIMUM_TIMEOUT_MILLISECONDS , timeoutMilliseconds );
199
- }
200
- }
201
-
202
- private static double getElasticTimeout (int timeoutPercentage , List <Run > builds ) {
203
- return timeoutPercentage * .01D * (timeoutPercentage > 0 ? averageDuration (builds ) : 0 );
204
- }
205
-
206
- private static double averageDuration (List <Run > builds ) {
207
- int nonFailingBuilds = 0 ;
208
- int durationSum = 0 ;
209
-
210
- for (int i = 0 ; i < builds .size () && nonFailingBuilds < NUMBER_OF_BUILDS_TO_AVERAGE ; i ++) {
211
- Run run = builds .get (i );
212
- if (run .getResult () != null &&
213
- run .getResult ().isBetterOrEqualTo (Result .UNSTABLE )) {
214
- durationSum += run .getDuration ();
215
- nonFailingBuilds ++;
216
- }
217
- }
218
-
219
- return nonFailingBuilds > 0 ? durationSum / nonFailingBuilds : 0 ;
220
- }
221
-
222
123
protected Object readResolve () {
223
- if (timeoutType == null ) {
224
- timeoutType = ABSOLUTE ;
124
+ if ("elastic" .equalsIgnoreCase (timeoutType )) {
125
+ strategy = new ElasticTimeOutStrategy (timeoutPercentage ,
126
+ timeoutMinutesElasticDefault != null ? timeoutMinutesElasticDefault .intValue () : 60 ,
127
+ 3 );
128
+ } else if ("likelyStuck" .equalsIgnoreCase (timeoutType )) {
129
+ strategy = new LikelyStuckTimeOutStrategy ();
130
+ } else if (strategy == null ) {
131
+ strategy = new AbsoluteTimeOutStrategy (timeoutMinutes );
225
132
}
226
133
return this ;
227
134
}
@@ -246,52 +153,23 @@ public String getDisplayName() {
246
153
public boolean isApplicable (AbstractProject <?, ?> item ) {
247
154
return true ;
248
155
}
156
+ }
249
157
250
- public int [] getPercentages () {
251
- return new int [] {150 ,200 ,250 ,300 ,350 ,400 };
252
- }
253
-
254
- @ Override
255
- public BuildWrapper newInstance (StaplerRequest req , JSONObject formData )
256
- throws hudson .model .Descriptor .FormException {
257
- JSONObject timeoutObject = formData .getJSONObject ("timeoutType" );
158
+ public BuildTimeOutStrategy getStrategy () {
159
+ return strategy ;
160
+ }
258
161
259
- // we would ideally do this on the form itself (to show the default)
260
- //but there is a show/hide bug when using radioOptions inside an optionBlock
261
- if (timeoutObject .isNullObject () || timeoutObject .isEmpty ()) {
262
- formData .put ("timeoutType" , ABSOLUTE );
263
- } else {
264
- // Jenkins 1.427
265
- // {"timeoutType": {
266
- // "value": "elastic", "timeoutPercentage": "150",
267
- // "timeoutMinutesElasticDefault": "3333333"}}
268
- // Jenkins 1.420
269
- // {"timeoutMinutes": "3",
270
- // "timeoutType": {"value": "elastic"},
271
- // "timeoutPercentage": "150", "timeoutMinutesElasticDefault": "3333333",
272
- // "failBuild": false, "writingDescription": false}
273
- // => to keep comaptibility
274
- // "timeoutType": "elastic", "timeoutPercentage": "150",
275
- // "timeoutMinutesElasticDefault": "3333333"...
276
- String timeoutType = timeoutObject .getString ("value" );
277
- timeoutObject .remove ("value" );
278
- for (String key : (Set <String >) timeoutObject .keySet ()) {
279
- formData .put (key , timeoutObject .get (key ));
280
- }
281
- formData .put ("timeoutType" , timeoutType );
282
- }
162
+ public List <BuildTimeOutStrategyDescriptor > getStrategies () {
163
+ return Jenkins .getInstance ().getDescriptorList (BuildTimeOutStrategy .class );
164
+ }
283
165
284
- return super .newInstance (req , formData );
285
- }
166
+ // --- legacy attributes, kept for backward compatibility
286
167
287
- public ListBoxModel doFillTimeoutPercentageItems () {
288
- ListBoxModel m = new ListBoxModel ();
289
- for (int option : getPercentages ()) {
290
- String s = String .valueOf (option );
291
- m .add (s + "%" , s );
292
- }
293
- return m ;
294
- }
295
-
296
- }
168
+ public transient int timeoutMinutes ;
169
+
170
+ public transient int timeoutPercentage ;
171
+
172
+ public transient String timeoutType ;
173
+
174
+ public transient Integer timeoutMinutesElasticDefault ;
297
175
}
0 commit comments